Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch appendvfs_fix Excluding Merge-Ins
This is equivalent to a diff from aea12399bf to 25c3186aa1
2021-03-16
| ||
14:26 | Fix recent breakage of the appendvfs extension. (check-in: 7dbbe5b34e user: drh tags: trunk) | |
14:12 | Comment and stylistic changes to the appendvfs.c implementation. (Closed-Leaf check-in: 25c3186aa1 user: drh tags: appendvfs_fix) | |
11:11 | Fix a problem with ALTER TABLE RENAME COLUMN when used on a schema that features generated columns. Fix for [a753a856]. (check-in: 0e255b2687 user: dan tags: trunk) | |
07:06 | Fix assert typo. (check-in: 3aedf818c1 user: larrybr tags: appendvfs_fix) | |
06:50 | Merge from trunk. (check-in: eae8236f3b user: larrybr tags: appendvfs_fix) | |
2021-03-15
| ||
19:34 | Merge version 3.35.1 into the reuse-schema branch. (check-in: 0fe60a63ae user: drh tags: reuse-schema) | |
16:53 | Version 3.35.1 (check-in: aea12399bf user: drh tags: trunk, release, version-3.35.1) | |
15:12 | Improvements to the built-in ".dump" documentation in the CLI. (check-in: 0915f969f4 user: drh tags: trunk) | |
Changes to ext/misc/appendvfs.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 | ** ** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is ** set, then a new database is appended to the already existing file. ** ** (5) Otherwise, SQLITE_CANTOPEN is returned. ** ** To avoid unnecessary complications with the PENDING_BYTE, the size of | | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ** ** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is ** set, then a new database is appended to the already existing file. ** ** (5) Otherwise, SQLITE_CANTOPEN is returned. ** ** To avoid unnecessary complications with the PENDING_BYTE, the size of ** the file containing the database is limited to 1GiB. (1073741824 bytes) ** This VFS will not read or write past the 1GiB mark. This restriction ** might be lifted in future versions. For now, if you need a larger ** database, then keep it in a separate file. ** ** If the file being opened is a plain database (not an appended one), then ** this shim is a pass-through into the default underlying VFS. (rule 3) **/ #include "sqlite3ext.h" |
︙ | ︙ | |||
64 65 66 67 68 69 70 | #define APND_MARK_FOS_SZ 8 #define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ) /* ** Maximum size of the combined prefix + database + append-mark. This ** must be less than 0x40000000 to avoid locking issues on Windows. */ | | | | | > > | < < < < | < > > > | | | > > | | | | | | | | | | | > > > > | > > | < > > > | | < | < | < | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | #define APND_MARK_FOS_SZ 8 #define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ) /* ** Maximum size of the combined prefix + database + append-mark. This ** must be less than 0x40000000 to avoid locking issues on Windows. */ #define APND_MAX_SIZE (0x40000000) /* ** Try to align the database to an even multiple of APND_ROUNDUP bytes. */ #ifndef APND_ROUNDUP #define APND_ROUNDUP 4096 #endif #define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1)) #define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK) /* ** Forward declaration of objects used by this utility */ typedef struct sqlite3_vfs ApndVfs; typedef struct ApndFile ApndFile; /* Access to a lower-level VFS that (might) implement dynamic loading, ** access to randomness, etc. */ #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) /* An open appendvfs file ** ** An instance of this structure describes the appended database file. ** A separate sqlite3_file object is always appended. The appended ** sqlite3_file object (which can be accessed using ORIGFILE()) describes ** the entire file, including the prefix, the database, and the ** append-mark. ** ** The structure of an AppendVFS database is like this: ** ** +-------------+---------+----------+-------------+ ** | prefix-file | padding | database | append-mark | ** +-------------+---------+----------+-------------+ ** ^ ^ ** | | ** iPgOne iMark ** ** ** "prefix file" - file onto which the database has been appended. ** "padding" - zero or more bytes inserted so that "database" ** starts on an APND_ROUNDUP boundary ** "database" - The SQLite database file ** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates ** the offset from the start of prefix-file to the start ** of "database". ** ** The size of the database is iMark - iPgOne. ** ** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value ** of iPgOne stored as a big-ending 64-bit integer. ** ** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE). ** Or, iMark is -1 to indicate that it has not yet been written. */ struct ApndFile { sqlite3_file base; /* Subclass. MUST BE FIRST! */ sqlite3_int64 iPgOne; /* Offset to the start of the database */ sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */ /* Always followed by another sqlite3_file that describes the whole file */ }; /* ** Methods for ApndFile */ static int apndClose(sqlite3_file*); static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); |
︙ | ︙ | |||
247 248 249 250 251 252 253 | ){ sqlite_int64 iPgOne = paf->iPgOne; unsigned char a[APND_MARK_SIZE]; int i = APND_MARK_FOS_SZ; int rc; assert(pFile == ORIGFILE(paf)); memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | ){ sqlite_int64 iPgOne = paf->iPgOne; unsigned char a[APND_MARK_SIZE]; int i = APND_MARK_FOS_SZ; int rc; assert(pFile == ORIGFILE(paf)); memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); while( --i >= 0 ){ a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff); iPgOne >>= 8; } iWriteEnd += paf->iPgOne; if( SQLITE_OK==(rc = pFile->pMethods->xWrite (pFile, a, APND_MARK_SIZE, iWriteEnd)) ){ paf->iMark = iWriteEnd; |
︙ | ︙ | |||
275 276 277 278 279 280 281 | ApndFile *paf = (ApndFile *)pFile; sqlite_int64 iWriteEnd = iOfst + iAmt; if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL; pFile = ORIGFILE(pFile); /* If append-mark is absent or will be overwritten, write it. */ if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){ int rc = apndWriteMark(paf, pFile, iWriteEnd); | | < | < | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | ApndFile *paf = (ApndFile *)pFile; sqlite_int64 iWriteEnd = iOfst + iAmt; if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL; pFile = ORIGFILE(pFile); /* If append-mark is absent or will be overwritten, write it. */ if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){ int rc = apndWriteMark(paf, pFile, iWriteEnd); if( SQLITE_OK!=rc ) return rc; } return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst); } /* ** Truncate an apnd-file. */ static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ ApndFile *paf = (ApndFile *)pFile; pFile = ORIGFILE(pFile); /* The append mark goes out first so truncate failure does not lose it. */ if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR; /* Truncate underlying file just past append mark */ return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE); } /* ** Sync an apnd-file. */ |
︙ | ︙ | |||
405 406 407 408 409 410 411 | static int apndFetch( sqlite3_file *pFile, sqlite3_int64 iOfst, int iAmt, void **pp ){ ApndFile *p = (ApndFile *)pFile; | | > | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | static int apndFetch( sqlite3_file *pFile, sqlite3_int64 iOfst, int iAmt, void **pp ){ ApndFile *p = (ApndFile *)pFile; if( p->iMark < 0 || iOfst+iAmt > p->iMark ){ return SQLITE_IOERR; /* Cannot read what is not yet there. */ } pFile = ORIGFILE(pFile); return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); } /* Release a memory-mapped page */ static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ ApndFile *p = (ApndFile *)pFile; |
︙ | ︙ | |||
457 458 459 460 461 462 463 | ** Return true iff it is such. Parameter sz is the file's size. */ static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){ int rc; char zHdr[16]; sqlite3_int64 iMark = apndReadMark(sz, pFile); if( iMark>=0 ){ | | | | | > | > | > > | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | ** Return true iff it is such. Parameter sz is the file's size. */ static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){ int rc; char zHdr[16]; sqlite3_int64 iMark = apndReadMark(sz, pFile); if( iMark>=0 ){ /* If file has the correct end-marker, the expected odd size, and the ** SQLite DB type marker where the end-marker puts it, then it ** is an appendvfs database. */ rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark); if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0 && (sz & 0x1ff) == APND_MARK_SIZE && sz>=512+APND_MARK_SIZE ){ return 1; /* It's an appendvfs database */ } } return 0; } /* ** Check to see if the file is an ordinary SQLite database file. ** Return true iff so. Parameter sz is the file's size. |
︙ | ︙ | |||
486 487 488 489 490 491 492 | ){ return 0; }else{ return 1; } } | < < < < < | | | | < | > > > | < | < > > | | | > | < > > | > > > | < < | | | < | > | | | | | | | < < > | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 | ){ return 0; }else{ return 1; } } /* ** Open an apnd file handle. */ static int apndOpen( sqlite3_vfs *pApndVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ ApndFile *pApndFile = (ApndFile*)pFile; sqlite3_file *pBaseFile = ORIGFILE(pFile); sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs); int rc; sqlite3_int64 sz; if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ /* The appendvfs is not to be used for transient or temporary databases. ** Just use the base VFS open to initialize the given file object and ** open the underlying file. (Appendvfs is then unused for this file.) */ return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags); } memset(pApndFile, 0, sizeof(ApndFile)); pFile->pMethods = &apnd_io_methods; pApndFile->iMark = -1; /* Append mark not yet written */ rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags); if( rc==SQLITE_OK ){ rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz); } if( rc ){ pBaseFile->pMethods->xClose(pBaseFile); pFile->pMethods = 0; return rc; } if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){ /* The file being opened appears to be just an ordinary DB. Copy ** the base dispatch-table so this instance mimics the base VFS. */ memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile); return SQLITE_OK; } pApndFile->iPgOne = apndReadMark(sz, pFile); if( pApndFile->iPgOne>=0 ){ pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */ return SQLITE_OK; } if( (flags & SQLITE_OPEN_CREATE)==0 ){ pBaseFile->pMethods->xClose(pBaseFile); rc = SQLITE_CANTOPEN; pFile->pMethods = 0; }else{ /* Round newly added appendvfs location to #define'd page boundary. ** Note that nothing has yet been written to the underlying file. ** The append mark will be written along with first content write. ** Until then, paf->iMark value indicates it is not yet written. */ pApndFile->iPgOne = APND_START_ROUNDUP(sz); } return rc; } /* ** Delete an apnd file. ** For an appendvfs, this could mean delete the appendvfs portion, ** leaving the appendee as it was before it gained an appendvfs. |
︙ | ︙ |
Changes to test/avfs.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # avfs-1.1. Test that the DB can be read with correct content upon reopen. # avfs-1.2. Test that an appendvfs DB can be added to a simple text file. # avfs-1.3. Test that the DB can be read with correct content upon reopen. # avfs-1.4. Test that appended DB is aligned to default page boundary. # avfs-2.1. Test that the simple text file retains its initial text. # avfs-3.1. Test that the appendvfs can grow and shrink, remaining intact. # avfs-3.2. Test that appendvfs is intact after grow/shrink/close/reopen. # avfs-4.1. Test shell's ability to append to a non-appendvfs file. # avfs-4.2. Test shell's ability to append to empty or nonexistent file. # avfs-4.3. Test shell's ability to reopen and alter an appendvfs file. # avfs-5.1. Test appendvfs refusal to open too-tiny DB appended onto ZLF. # avfs-5.2. Test appendvfs refusal to open too-tiny DB appended on other. # ... # (more to come) | > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # avfs-1.1. Test that the DB can be read with correct content upon reopen. # avfs-1.2. Test that an appendvfs DB can be added to a simple text file. # avfs-1.3. Test that the DB can be read with correct content upon reopen. # avfs-1.4. Test that appended DB is aligned to default page boundary. # avfs-2.1. Test that the simple text file retains its initial text. # avfs-3.1. Test that the appendvfs can grow and shrink, remaining intact. # avfs-3.2. Test that appendvfs is intact after grow/shrink/close/reopen. # avfs-3.3. Test that appendvfs can grow by many pages and be written. # avfs-3.4. Test that grown appendvfs can be reopened and appear intact. # avfs-3.5. Test that much grown appendvfs can shrink and reopen intact. # avfs-4.1. Test shell's ability to append to a non-appendvfs file. # avfs-4.2. Test shell's ability to append to empty or nonexistent file. # avfs-4.3. Test shell's ability to reopen and alter an appendvfs file. # avfs-5.1. Test appendvfs refusal to open too-tiny DB appended onto ZLF. # avfs-5.2. Test appendvfs refusal to open too-tiny DB appended on other. # ... # (more to come) |
︙ | ︙ | |||
155 156 157 158 159 160 161 162 163 164 165 | set ::result "Appendee changed." } else { set ::result "Appendee intact." } } {Appendee intact.} # Set of repeatable random integers for a couple tests. proc rint {v} { return [::tcl::mathfunc::int [expr $v * 100000]] } array set ::randints [list 0 [rint [::tcl::mathfunc::srand 0]]] | > | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | set ::result "Appendee changed." } else { set ::result "Appendee intact." } } {Appendee intact.} # Set of repeatable random integers for a couple tests. set ::nrint 50000 proc rint {v} { return [::tcl::mathfunc::int [expr $v * 100000]] } array set ::randints [list 0 [rint [::tcl::mathfunc::srand 0]]] for {set i 1} {$i < $::nrint} {incr i} { set ::randints($i) [rint [::tcl::mathfunc::rand]] } do_test 3.1 { set results {} sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 adb eval { DROP TABLE t1; PRAGMA cache_size=10; CREATE TABLE ri (i INTEGER); BEGIN; } for {set i 0} {$i < $::nrint} {incr i} { set r $::randints($i) set s $::randints([incr i]) set t $::randints([incr i]) set u $::randints([incr i]) set v $::randints([incr i]) adb eval { INSERT INTO ri VALUES ($r),($s),($t),($u),($v) |
︙ | ︙ | |||
199 200 201 202 203 204 205 | VACUUM; SELECT integrity_check as ic FROM pragma_integrity_check(); SELECT count(*) as ic FROM ri; } { lappend qr $ic } adb close set adaSz [file size $::fa] set adba [expr ($adbSz + 0.1)/$adaSz] | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | VACUUM; SELECT integrity_check as ic FROM pragma_integrity_check(); SELECT count(*) as ic FROM ri; } { lappend qr $ic } adb close set adaSz [file size $::fa] set adba [expr ($adbSz + 0.1)/$adaSz] # lappend results $adba set results [concat $results [lrange $qr 0 2]] lappend results [expr {$adba > 10.0}] set ::result [join $results " | "] } "ok | $::nrint | ok | ok | 1" do_test 3.2 { set results {} sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 adb eval { SELECT integrity_check as ic FROM pragma_integrity_check(); } { lappend results $ic } adb close set ::result [join $results " | "] } {ok} # avfs-3.3. Test that appendvfs can grow by many pages and be written. do_test 3.3 { set results {} sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 set npages 300 adb eval { BEGIN } while {$npages > 0} { adb eval { INSERT INTO ri VALUES (randomblob(1500)) } incr npages -1 } adb eval { COMMIT } adb eval { SELECT integrity_check as ic FROM pragma_integrity_check(); } { lappend results $ic } adb close set adaSzr [expr [file size $::fa] / 300.0 / 1500 ] set okSzr [expr $adaSzr > 1.0 && $adaSzr < 1.3 ] lappend results $okSzr set ::result [join $results " | "] } {ok | 1} # avfs-3.4. Test that grown appendvfs can be reopened and appear intact. do_test 3.4 { set results {} sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 adb eval { SELECT integrity_check as ic FROM pragma_integrity_check(); } { lappend results $ic } adb close set ::result $ic } {ok} # avfs-3.5. Test that much grown appendvfs can shrink and reopen intact. do_test 3.5 { set results {} set adbsz [file size $::fa] sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 adb eval { DELETE FROM ri WHERE rowid % 8 <> 0; SELECT integrity_check as ic FROM pragma_integrity_check(); VACUUM; SELECT integrity_check as ic FROM pragma_integrity_check(); } { lappend results $ic } adb close set adasz [file size $::fa] lappend results [expr {$adbsz/$adasz > 5}] sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 adb eval { SELECT integrity_check as ic FROM pragma_integrity_check(); } { lappend results $ic } adb close set ::result [join $results " | "] } {ok | ok | 1 | ok} set ::cliDoesAr [shellDoesAr] do_test 4.1 { set shdo "sh_app1.sql" set shod "sh_app1.adb" forcedelete $shdo $shod |
︙ | ︙ | |||
328 329 330 331 332 333 334 | } forcedelete $fake set ::result $res } {Open failed.} forcedelete $::fa $::fza | | | 386 387 388 389 390 391 392 393 394 395 | } forcedelete $fake set ::result $res } {Open failed.} forcedelete $::fa $::fza unset -nocomplain ::fa ::fza ::tlo ::result ::randints ::nrint ::cliDoesAr finish_test |