github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/filename.go (about) 1 package vfs 2 3 import ( 4 "context" 5 "net/url" 6 7 "github.com/ncruces/go-sqlite3/internal/util" 8 "github.com/tetratelabs/wazero/api" 9 ) 10 11 // Filename is used by SQLite to pass filenames 12 // to the Open method of a VFS. 13 // 14 // https://sqlite.org/c3ref/filename.html 15 type Filename struct { 16 ctx context.Context 17 mod api.Module 18 zPath uint32 19 flags OpenFlag 20 stack [2]uint64 21 } 22 23 // OpenFilename is an internal API users should not call directly. 24 func OpenFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename { 25 if id == 0 { 26 return nil 27 } 28 return &Filename{ 29 ctx: ctx, 30 mod: mod, 31 zPath: id, 32 flags: flags, 33 } 34 } 35 36 // String returns this filename as a string. 37 func (n *Filename) String() string { 38 if n == nil || n.zPath == 0 { 39 return "" 40 } 41 return util.ReadString(n.mod, n.zPath, _MAX_PATHNAME) 42 } 43 44 // Database returns the name of the corresponding database file. 45 // 46 // https://sqlite.org/c3ref/filename_database.html 47 func (n *Filename) Database() string { 48 return n.path("sqlite3_filename_database") 49 } 50 51 // Journal returns the name of the corresponding rollback journal file. 52 // 53 // https://sqlite.org/c3ref/filename_database.html 54 func (n *Filename) Journal() string { 55 return n.path("sqlite3_filename_journal") 56 } 57 58 // Journal returns the name of the corresponding WAL file. 59 // 60 // https://sqlite.org/c3ref/filename_database.html 61 func (n *Filename) WAL() string { 62 return n.path("sqlite3_filename_wal") 63 } 64 65 func (n *Filename) path(method string) string { 66 if n == nil || n.zPath == 0 { 67 return "" 68 } 69 n.stack[0] = uint64(n.zPath) 70 fn := n.mod.ExportedFunction(method) 71 if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil { 72 panic(err) 73 } 74 return util.ReadString(n.mod, uint32(n.stack[0]), _MAX_PATHNAME) 75 } 76 77 // DatabaseFile returns the main database [File] corresponding to a journal. 78 // 79 // https://sqlite.org/c3ref/database_file_object.html 80 func (n *Filename) DatabaseFile() File { 81 if n == nil || n.zPath == 0 { 82 return nil 83 } 84 if n.flags&(OPEN_MAIN_DB|OPEN_MAIN_JOURNAL|OPEN_WAL) == 0 { 85 return nil 86 } 87 88 n.stack[0] = uint64(n.zPath) 89 fn := n.mod.ExportedFunction("sqlite3_database_file_object") 90 if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil { 91 panic(err) 92 } 93 file, _ := vfsFileGet(n.ctx, n.mod, uint32(n.stack[0])).(File) 94 return file 95 } 96 97 // URIParameter returns the value of a URI parameter. 98 // 99 // https://sqlite.org/c3ref/uri_boolean.html 100 func (n *Filename) URIParameter(key string) string { 101 if n == nil || n.zPath == 0 { 102 return "" 103 } 104 105 uriKey := n.mod.ExportedFunction("sqlite3_uri_key") 106 n.stack[0] = uint64(n.zPath) 107 n.stack[1] = uint64(0) 108 if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { 109 panic(err) 110 } 111 112 ptr := uint32(n.stack[0]) 113 if ptr == 0 { 114 return "" 115 } 116 117 // Parse the format from: 118 // https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840 119 // This avoids having to alloc/free the key just to find a value. 120 for { 121 k := util.ReadString(n.mod, ptr, _MAX_NAME) 122 if k == "" { 123 return "" 124 } 125 ptr += uint32(len(k)) + 1 126 127 v := util.ReadString(n.mod, ptr, _MAX_NAME) 128 if k == key { 129 return v 130 } 131 ptr += uint32(len(v)) + 1 132 } 133 } 134 135 // URIParameters obtains values for URI parameters. 136 // 137 // https://sqlite.org/c3ref/uri_boolean.html 138 func (n *Filename) URIParameters() url.Values { 139 if n == nil || n.zPath == 0 { 140 return nil 141 } 142 143 uriKey := n.mod.ExportedFunction("sqlite3_uri_key") 144 n.stack[0] = uint64(n.zPath) 145 n.stack[1] = uint64(0) 146 if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { 147 panic(err) 148 } 149 150 ptr := uint32(n.stack[0]) 151 if ptr == 0 { 152 return nil 153 } 154 155 var params url.Values 156 157 // Parse the format from: 158 // https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840 159 // This is the only way to support multiple valued keys. 160 for { 161 k := util.ReadString(n.mod, ptr, _MAX_NAME) 162 if k == "" { 163 return params 164 } 165 ptr += uint32(len(k)) + 1 166 167 v := util.ReadString(n.mod, ptr, _MAX_NAME) 168 if params == nil { 169 params = url.Values{} 170 } 171 params.Add(k, v) 172 ptr += uint32(len(v)) + 1 173 } 174 }