| 1 | // vtest build: present_sqlite3? |
| 2 | import db.sqlite |
| 3 | import rand |
| 4 | |
| 5 | const max_file_name_len = 256 |
| 6 | |
| 7 | fn test_vfs_register() { |
| 8 | org_default_vfs := sqlite.get_default_vfs()! |
| 9 | |
| 10 | assert org_default_vfs.zName != 0 |
| 11 | |
| 12 | vfs_name := 'sometest' |
| 13 | mut vfs_descr := &sqlite.Sqlite3_vfs{ |
| 14 | zName: vfs_name.str |
| 15 | iVersion: 2 |
| 16 | } |
| 17 | |
| 18 | if _ := sqlite.get_vfs(vfs_name) { |
| 19 | panic('expected that vfs is not known') |
| 20 | } |
| 21 | |
| 22 | vfs_descr.register_as_nondefault() or { panic('vfs register failed ${err}') } |
| 23 | |
| 24 | sqlite.get_vfs(vfs_name)! |
| 25 | |
| 26 | now_default_vfs := sqlite.get_default_vfs()! |
| 27 | |
| 28 | assert now_default_vfs.zName == org_default_vfs.zName |
| 29 | |
| 30 | vfs_descr.unregister() or { panic('vfs unregister failed ${err}') } |
| 31 | |
| 32 | if _ := sqlite.get_vfs(vfs_name) { |
| 33 | panic('vfs supposedly unregistered yet somehow still foundable') |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | // minimal vfs based on example https://www.sqlite.org/src/doc/trunk/src/test_demovfs.c |
| 38 | fn test_verify_vfs_is_actually_used() { |
| 39 | wrapped := sqlite.get_default_vfs()! |
| 40 | |
| 41 | vfs_name := 'sometest' |
| 42 | mut vfs_state := &ExampleVfsState{ |
| 43 | log: []string{cap: 100} |
| 44 | } |
| 45 | mut vfs_descr := &sqlite.Sqlite3_vfs{ |
| 46 | iVersion: 2 |
| 47 | szOsFile: int(sizeof(ExampleVfsOpenedFile)) |
| 48 | mxPathname: max_file_name_len |
| 49 | zName: vfs_name.str |
| 50 | pAppData: vfs_state |
| 51 | xOpen: example_vfs_open |
| 52 | xDelete: example_vfs_delete |
| 53 | xAccess: example_vfs_access |
| 54 | xFullPathname: example_vfs_fullpathname |
| 55 | xDlOpen: wrapped.xDlOpen |
| 56 | xDlError: wrapped.xDlError |
| 57 | xDlSym: wrapped.xDlSym |
| 58 | xDlClose: wrapped.xDlClose |
| 59 | xRandomness: wrapped.xRandomness |
| 60 | xSleep: wrapped.xSleep |
| 61 | xCurrentTime: wrapped.xCurrentTime |
| 62 | xGetLastError: example_vfs_getlasterror |
| 63 | xCurrentTimeInt64: wrapped.xCurrentTimeInt64 |
| 64 | } |
| 65 | |
| 66 | vfs_descr.register_as_nondefault()! |
| 67 | |
| 68 | // normally this would be written to disk |
| 69 | mut db := sqlite.connect_full('foo.db', [.readwrite, .create], vfs_name)! |
| 70 | assert ['fullpathname from=foo.db to=foo.db}', 'open temp?=false name=foo.db', 'read file=foo.db'] == vfs_state.log |
| 71 | vfs_state.log.clear() |
| 72 | |
| 73 | db.close()! |
| 74 | assert ['close file=foo.db'] == vfs_state.log |
| 75 | } |
| 76 | |
| 77 | struct ExampleVfsState { |
| 78 | mut: |
| 79 | log []string |
| 80 | } |
| 81 | |
| 82 | struct ExampleVfsOpenedFile { |
| 83 | mut: |
| 84 | base sqlite.Sqlite3_file |
| 85 | name string |
| 86 | vfs_state &ExampleVfsState |
| 87 | } |
| 88 | |
| 89 | fn to_vfsstate(t &sqlite.Sqlite3_vfs) &ExampleVfsState { |
| 90 | unsafe { |
| 91 | p := t.pAppData |
| 92 | if p == 0 { |
| 93 | assert false, 'p should not be 0' |
| 94 | } |
| 95 | return &ExampleVfsState(p) |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | fn to_vfsopenedfile(t &sqlite.Sqlite3_file) &ExampleVfsOpenedFile { |
| 100 | unsafe { |
| 101 | if t == 0 { |
| 102 | assert false, 't should not be 0' |
| 103 | } |
| 104 | return &ExampleVfsOpenedFile(t) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | fn example_vfs_fullpathname(vfs &sqlite.Sqlite3_vfs, input &char, size_of_output int, output &char) int { |
| 109 | println('fullpathname called') |
| 110 | |
| 111 | mut vfs_state := to_vfsstate(vfs) |
| 112 | |
| 113 | from := unsafe { cstring_to_vstring(input) } |
| 114 | |
| 115 | unsafe { |
| 116 | vmemcpy(output, input, from.len) |
| 117 | output[from.len] = u8(0) |
| 118 | } |
| 119 | result := unsafe { cstring_to_vstring(output) } |
| 120 | |
| 121 | vfs_state.log << 'fullpathname from=${from} to=${result}}' |
| 122 | |
| 123 | return sqlite.sqlite_ok |
| 124 | } |
| 125 | |
| 126 | fn example_vfs_access(vfs &sqlite.Sqlite3_vfs, zPath &char, flags int, pResOut &int) int { |
| 127 | println('access called') |
| 128 | mut vfs_state := &ExampleVfsState{} |
| 129 | |
| 130 | unsafe { |
| 131 | assert 0 != vfs.pAppData |
| 132 | vfs_state = &ExampleVfsState(vfs.pAppData) |
| 133 | } |
| 134 | vfs_state.log << 'accessed' |
| 135 | |
| 136 | return sqlite.sqlite_ok |
| 137 | } |
| 138 | |
| 139 | fn example_vfs_open(vfs &sqlite.Sqlite3_vfs, file_name_or_null_for_tempfile &char, vfs_opened_file &sqlite.Sqlite3_file, |
| 140 | in_flags int, out_flags &int) int { |
| 141 | println('open called') |
| 142 | |
| 143 | mut is_temp := false |
| 144 | mut file_name := '' |
| 145 | |
| 146 | unsafe { |
| 147 | if file_name_or_null_for_tempfile == nil { |
| 148 | is_temp = true |
| 149 | file_name = rand.uuid_v4() |
| 150 | } else { |
| 151 | file_name = cstring_to_vstring(file_name_or_null_for_tempfile) |
| 152 | } |
| 153 | } |
| 154 | mut vfs_state := to_vfsstate(vfs) |
| 155 | |
| 156 | unsafe { |
| 157 | mut outp := to_vfsopenedfile(vfs_opened_file) |
| 158 | outp.base.pMethods = &sqlite.Sqlite3_io_methods{ |
| 159 | iVersion: 1 |
| 160 | xClose: example_vfsfile_close |
| 161 | xRead: example_vfsfile_read |
| 162 | xWrite: example_vfsfile_write |
| 163 | xTruncate: example_vfsfile_truncate |
| 164 | xSync: example_vfsfile_sync |
| 165 | xFileSize: example_vfsfile_size |
| 166 | xLock: example_vfsfile_lock |
| 167 | xUnlock: example_vfsfile_unlock |
| 168 | xCheckReservedLock: example_vfsfile_checkreservedlock |
| 169 | xFileControl: example_vfsfile_filecontrol |
| 170 | xSectorSize: example_vfsfile_sectorsize |
| 171 | xDeviceCharacteristics: example_vfsfile_devicecharacteristics |
| 172 | } |
| 173 | |
| 174 | outp.name = file_name.clone() |
| 175 | outp.vfs_state = vfs_state |
| 176 | } |
| 177 | vfs_state.log << 'open temp?=${is_temp} name=${file_name}' |
| 178 | |
| 179 | return sqlite.sqlite_ok |
| 180 | } |
| 181 | |
| 182 | fn example_vfsfile_checkreservedlock(file &sqlite.Sqlite3_file, pResOut &int) int { |
| 183 | println('file checkreservedlock') |
| 184 | |
| 185 | unsafe { |
| 186 | *pResOut = 0 |
| 187 | } |
| 188 | return sqlite.sqlite_ok |
| 189 | } |
| 190 | |
| 191 | fn example_vfsfile_filecontrol(file &sqlite.Sqlite3_file, op int, arg voidptr) int { |
| 192 | println('file filecontrol') |
| 193 | |
| 194 | return 0 |
| 195 | } |
| 196 | |
| 197 | fn example_vfsfile_devicecharacteristics(file &sqlite.Sqlite3_file) int { |
| 198 | println('file devicecharacteristics') |
| 199 | |
| 200 | return 0 |
| 201 | } |
| 202 | |
| 203 | fn example_vfsfile_size(file &sqlite.Sqlite3_file, result &i64) int { |
| 204 | println('file size') |
| 205 | |
| 206 | return sqlite.sqlite_ok |
| 207 | } |
| 208 | |
| 209 | fn example_vfsfile_read(file &sqlite.Sqlite3_file, output voidptr, amount int, offset i64) int { |
| 210 | println('file read') |
| 211 | |
| 212 | assert amount > 0 |
| 213 | |
| 214 | unsafe { |
| 215 | mut vfsfile := to_vfsopenedfile(file) |
| 216 | vfsfile.vfs_state.log << 'read file=${vfsfile.name}' |
| 217 | } |
| 218 | unsafe { |
| 219 | vmemset(output, 0, amount) |
| 220 | } |
| 221 | |
| 222 | return sqlite.sqlite_ioerr_short_read |
| 223 | } |
| 224 | |
| 225 | fn example_vfsfile_truncate(file &sqlite.Sqlite3_file, size i64) int { |
| 226 | println('file truncate') |
| 227 | |
| 228 | return sqlite.sqlite_ok |
| 229 | } |
| 230 | |
| 231 | fn example_vfsfile_sectorsize(file &sqlite.Sqlite3_file) int { |
| 232 | println('file sectorsize') |
| 233 | |
| 234 | return 0 |
| 235 | } |
| 236 | |
| 237 | fn example_vfsfile_sync(file &sqlite.Sqlite3_file, flags int) int { |
| 238 | println('file sync called') |
| 239 | |
| 240 | return sqlite.sqlite_ok |
| 241 | } |
| 242 | |
| 243 | fn example_vfsfile_lock(file &sqlite.Sqlite3_file, elock int) int { |
| 244 | println('file lock called') |
| 245 | |
| 246 | return sqlite.sqlite_ok |
| 247 | } |
| 248 | |
| 249 | fn example_vfsfile_unlock(file &sqlite.Sqlite3_file, elock int) int { |
| 250 | println('file unlock called') |
| 251 | |
| 252 | return sqlite.sqlite_ok |
| 253 | } |
| 254 | |
| 255 | fn example_vfsfile_write(file &sqlite.Sqlite3_file, buf voidptr, amount int, offset i64) int { |
| 256 | println('file write called') |
| 257 | |
| 258 | return sqlite.sqlite_ok |
| 259 | } |
| 260 | |
| 261 | fn example_vfsfile_close(file &sqlite.Sqlite3_file) int { |
| 262 | println('file close called') |
| 263 | |
| 264 | unsafe { |
| 265 | mut vfsfile := to_vfsopenedfile(file) |
| 266 | vfsfile.vfs_state.log << 'close file=${vfsfile.name}' |
| 267 | } |
| 268 | return sqlite.sqlite_ok |
| 269 | } |
| 270 | |
| 271 | fn example_vfs_delete(vfs &sqlite.Sqlite3_vfs, name &char, sync_dir int) int { |
| 272 | println('vfs delete called') |
| 273 | |
| 274 | return sqlite.sqlite_ok |
| 275 | } |
| 276 | |
| 277 | fn example_vfs_getlasterror(vfs &sqlite.Sqlite3_vfs, i int, o &char) int { |
| 278 | println('vfs getlasterror called') |
| 279 | |
| 280 | unsafe { |
| 281 | *o = 0 |
| 282 | } |
| 283 | return sqlite.sqlite_ok |
| 284 | } |
| 285 | |
| 286 | //////////////////////////////////////////////// |
| 287 | |
| 288 | struct Human { |
| 289 | name string |
| 290 | age f32 |
| 291 | } |
| 292 | |
| 293 | fn check_connect_full_default_vfs(vfs_name string) ! { |
| 294 | mut db := sqlite.connect_full(':memory:', [.readwrite, .create, .fullmutex], '')! |
| 295 | sql db { |
| 296 | create table Human |
| 297 | }! |
| 298 | h := Human{'Bilbo', 56} |
| 299 | sql db { |
| 300 | insert h into Human |
| 301 | }! |
| 302 | res := sql db { |
| 303 | select from Human |
| 304 | }! |
| 305 | db.close()! |
| 306 | assert res.len == 1 |
| 307 | assert res[0] == h |
| 308 | } |
| 309 | |
| 310 | fn test_connect_full_default_vfs() { |
| 311 | // passing '' here as vfs_name should work everywhere, and it should be equivalent |
| 312 | // to 'unix' or 'win32', depending on the current system: |
| 313 | check_connect_full_default_vfs('')! |
| 314 | $if windows { |
| 315 | check_connect_full_default_vfs('win32')! |
| 316 | } $else { |
| 317 | check_connect_full_default_vfs('unix')! |
| 318 | } |
| 319 | } |
| 320 | |