Fossil SCM
Update the tmstmpvfs.c extension module to the latest version.
Commit
2a1a9c357e1a8822e089c89b410b69ee8bd9c3b2042226783a5ea86db3283551
Parent
69d7454515b9806…
1 file changed
+51
-18
+51
-18
| --- extsrc/tmstmpvfs.c | ||
| +++ extsrc/tmstmpvfs.c | ||
| @@ -40,10 +40,20 @@ | ||
| 40 | 40 | ** (windows) cl tmstmpvfs.c -link -dll -out:tmstmpvfs.dll |
| 41 | 41 | ** |
| 42 | 42 | ** You may want to add additional compiler options, of course, |
| 43 | 43 | ** according to the needs of your project. |
| 44 | 44 | ** |
| 45 | +** Another option is to statically link both SQLite and this extension | |
| 46 | +** into your application. If both this file and "sqlite3.c" are statically | |
| 47 | +** linked, and if "sqlite3.c" is compiled with -DSQLITE_EXTRA_INIT= | |
| 48 | +** SQLite amalgamation "sqlite3.c" file with the option like: | |
| 49 | +** | |
| 50 | +** -DSQLITE_EXTRA_INIT=sqlite3_register_tmstmpvfs | |
| 51 | +** | |
| 52 | +** Then SQLite will use the tmstmp VFS by default throughout your | |
| 53 | +** application. | |
| 54 | +** | |
| 45 | 55 | ** LOADING |
| 46 | 56 | ** |
| 47 | 57 | ** To load this extension as a shared library, you first have to |
| 48 | 58 | ** bring up a dummy SQLite database connection to use as the argument |
| 49 | 59 | ** to the sqlite3_load_extension() API call. Then you invoke the |
| @@ -61,10 +71,18 @@ | ||
| 61 | 71 | ** down in the stack. This is normally what you want. However, in |
| 62 | 72 | ** complex situations where multiple VFS shims are being loaded, |
| 63 | 73 | ** it might be important to ensure that tmstmpvfs is loaded in the |
| 64 | 74 | ** correct order so that it sequences itself into the default VFS |
| 65 | 75 | ** Shim stack in the right order. |
| 76 | +** | |
| 77 | +** When running the CLI, you can load this extension at invocation by | |
| 78 | +** adding a command-line option like this: "--vfs ./tmstmpvfs.so". | |
| 79 | +** The --vfs option usually specifies the symbolic name of a built-in VFS. | |
| 80 | +** But if the argument to --vfs is not a built-in VFS but is instead the | |
| 81 | +** name of a file, the CLI tries to load that file as an extension. Note | |
| 82 | +** that the full name of the extension file must be provided, including | |
| 83 | +** the ".so" or ".dylib" or ".dll" suffix. | |
| 66 | 84 | ** |
| 67 | 85 | ** An application can see if the tmstmpvfs is being used by examining |
| 68 | 86 | ** the results from SQLITE_FCNTL_VFSNAME (or the .vfsname command in |
| 69 | 87 | ** the CLI). If the answer include "tmstmp", then this VFS is being |
| 70 | 88 | ** used. |
| @@ -94,17 +112,20 @@ | ||
| 94 | 112 | ** It never hurts to run the VACUUM, even if you don't need it. |
| 95 | 113 | ** |
| 96 | 114 | ** From the CLI, use the ".filectrl reserve_bytes 16" command, |
| 97 | 115 | ** followed by "VACUUM;". |
| 98 | 116 | ** |
| 99 | -** Note that SQLite allows the number of reserve-bytes to be | |
| 100 | -** increased but not decreased. So if a database file already | |
| 101 | -** has a reserve-bytes value greater than 16, there is no way to | |
| 102 | -** activate timestamping on that database, other than to dump | |
| 103 | -** and restore the database file. Note also that other extensions | |
| 104 | -** might also make use of the reserve-bytes. Timestamping will | |
| 105 | -** be incompatible with those other extensions. | |
| 117 | +** SQLite allows the number of reserve-bytes to be increased, but | |
| 118 | +** not decreased. If you want to restore the reserve-bytes to 0 | |
| 119 | +** (to disable tmstmpvfs), the easiest approach is to use VACUUM INTO | |
| 120 | +** with a URI filename as the argument and include "reserve=0" query | |
| 121 | +** parameter on the URI. Example: | |
| 122 | +** | |
| 123 | +** VACUUM INTO 'file:notimestamps.db?reserve=0'; | |
| 124 | +** | |
| 125 | +** Then switch over to using the new database file. The reserve=0 query | |
| 126 | +** parameter only works on SQLite 3.52.0 and later. | |
| 106 | 127 | ** |
| 107 | 128 | ** IMPLEMENTATION NOTES |
| 108 | 129 | ** |
| 109 | 130 | ** The timestamp information is stored in the last 16 bytes of each page. |
| 110 | 131 | ** This module only operates if the "bytes of reserved space on each page" |
| @@ -169,10 +190,11 @@ | ||
| 169 | 190 | ** op = 0x05 |
| 170 | 191 | ** |
| 171 | 192 | ** ELOG_CKPT_PAGE "Page xfer from WAL to database" |
| 172 | 193 | ** op = 0x06 |
| 173 | 194 | ** a2 = database page number |
| 195 | +** a3 = frame number in the WAL file | |
| 174 | 196 | ** |
| 175 | 197 | ** ELOG_CKPT_END "Start of a checkpoint operation" |
| 176 | 198 | ** op = 0x07 |
| 177 | 199 | ** |
| 178 | 200 | ** ELOG_WAL_RESET "WAL file header overwritten" |
| @@ -246,11 +268,11 @@ | ||
| 246 | 268 | FILE *log; /* Open log file */ |
| 247 | 269 | int n; /* Bytes of a[] used */ |
| 248 | 270 | unsigned char a[16*6]; /* Buffered header for the log */ |
| 249 | 271 | }; |
| 250 | 272 | |
| 251 | -/* An open WAL file */ | |
| 273 | +/* An open WAL or DB file */ | |
| 252 | 274 | struct TmstmpFile { |
| 253 | 275 | sqlite3_file base; /* IO methods */ |
| 254 | 276 | u32 uMagic; /* Magic number for sanity checking */ |
| 255 | 277 | u32 salt1; /* Last WAL salt-1 value */ |
| 256 | 278 | u32 iFrame; /* Last WAL frame number */ |
| @@ -432,11 +454,12 @@ | ||
| 432 | 454 | static void tmstmpEvent( |
| 433 | 455 | TmstmpFile *p, |
| 434 | 456 | u8 op, |
| 435 | 457 | u8 a1, |
| 436 | 458 | u32 a2, |
| 437 | - u32 a3 | |
| 459 | + u32 a3, | |
| 460 | + u8 *pTS | |
| 438 | 461 | ){ |
| 439 | 462 | unsigned char *a; |
| 440 | 463 | TmstmpLog *pLog; |
| 441 | 464 | if( p->isWal ){ |
| 442 | 465 | p = p->pPartner; |
| @@ -449,11 +472,15 @@ | ||
| 449 | 472 | if( tmstmpLogFlush(p) ) return; |
| 450 | 473 | } |
| 451 | 474 | a = pLog->a + pLog->n; |
| 452 | 475 | a[0] = op; |
| 453 | 476 | a[1] = a1; |
| 454 | - tmstmpPutTS(p, a+2); | |
| 477 | + if( pTS ){ | |
| 478 | + memcpy(a+2, pTS, 6); | |
| 479 | + }else{ | |
| 480 | + tmstmpPutTS(p, a+2); | |
| 481 | + } | |
| 455 | 482 | tmstmpPutU32(a2, a+8); |
| 456 | 483 | tmstmpPutU32(a3, a+12); |
| 457 | 484 | pLog->n += 16; |
| 458 | 485 | if( pLog->log || (op>=ELOG_WAL_PAGE && op<=ELOG_WAL_RESET) ){ |
| 459 | 486 | (void)tmstmpLogFlush(p); |
| @@ -464,11 +491,11 @@ | ||
| 464 | 491 | ** Close a connection |
| 465 | 492 | */ |
| 466 | 493 | static int tmstmpClose(sqlite3_file *pFile){ |
| 467 | 494 | TmstmpFile *p = (TmstmpFile *)pFile; |
| 468 | 495 | if( p->hasCorrectReserve ){ |
| 469 | - tmstmpEvent(p, p->isDb ? ELOG_CLOSE_DB : ELOG_CLOSE_WAL, 0, 0, 0); | |
| 496 | + tmstmpEvent(p, p->isDb ? ELOG_CLOSE_DB : ELOG_CLOSE_WAL, 0, 0, 0, 0); | |
| 470 | 497 | } |
| 471 | 498 | tmstmpLogFree(p->pLog); |
| 472 | 499 | if( p->pPartner ){ |
| 473 | 500 | assert( p->pPartner->pPartner==p ); |
| 474 | 501 | p->pPartner->pPartner = 0; |
| @@ -503,10 +530,16 @@ | ||
| 503 | 530 | if( p->pPartner ){ |
| 504 | 531 | p->pPartner->hasCorrectReserve = p->hasCorrectReserve; |
| 505 | 532 | p->pPartner->pgsz = p->pgsz; |
| 506 | 533 | } |
| 507 | 534 | } |
| 535 | + if( p->isWal | |
| 536 | + && p->inCkpt | |
| 537 | + && iAmt>=512 && iAmt<=65535 && (iAmt&(iAmt-1))==0 | |
| 538 | + ){ | |
| 539 | + p->pPartner->iFrame = (iOfst - 8)/(p->pgsz + 48) + 1; | |
| 540 | + } | |
| 508 | 541 | return rc; |
| 509 | 542 | } |
| 510 | 543 | |
| 511 | 544 | /* |
| 512 | 545 | ** Write data to a tmstmp-file. |
| @@ -537,26 +570,26 @@ | ||
| 537 | 570 | memset(s, 0, TMSTMP_RESERVE); |
| 538 | 571 | tmstmpPutTS(p, s+2); |
| 539 | 572 | tmstmpPutU32(p->iFrame, s+8); |
| 540 | 573 | tmstmpPutU32(p->salt1, s+12); |
| 541 | 574 | s[12] = p->isCommit ? 1 : 0; |
| 542 | - tmstmpEvent(p, ELOG_WAL_PAGE, s[12], p->pgno, p->iFrame); | |
| 575 | + tmstmpEvent(p, ELOG_WAL_PAGE, s[12], p->pgno, p->iFrame, s+2); | |
| 543 | 576 | }else if( iAmt==32 && iOfst==0 ){ |
| 544 | 577 | u32 salt1 = tmstmpGetU32(((const u8*)zBuf)+16); |
| 545 | - tmstmpEvent(p, ELOG_WAL_RESET, 0, 0, salt1); | |
| 578 | + tmstmpEvent(p, ELOG_WAL_RESET, 0, 0, salt1, 0); | |
| 546 | 579 | } |
| 547 | 580 | }else if( p->inCkpt ){ |
| 548 | 581 | assert( p->pgsz>0 ); |
| 549 | - tmstmpEvent(p, ELOG_CKPT_PAGE, 0, (iOfst/p->pgsz)+1, 0); | |
| 582 | + tmstmpEvent(p, ELOG_CKPT_PAGE, 0, (iOfst/p->pgsz)+1, p->iFrame, 0); | |
| 550 | 583 | }else if( p->pPartner==0 ){ |
| 551 | 584 | /* Writing into a database in rollback mode */ |
| 552 | 585 | unsigned char *s = (unsigned char*)zBuf+iAmt-TMSTMP_RESERVE; |
| 553 | 586 | memset(s, 0, TMSTMP_RESERVE); |
| 554 | 587 | tmstmpPutTS(p, s+2); |
| 555 | 588 | s[12] = 2; |
| 556 | 589 | assert( p->pgsz>0 ); |
| 557 | - tmstmpEvent(p, ELOG_DB_PAGE, 0, (u32)(iOfst/p->pgsz), 0); | |
| 590 | + tmstmpEvent(p, ELOG_DB_PAGE, 0, (u32)(iOfst/p->pgsz), 0, s+2); | |
| 558 | 591 | } |
| 559 | 592 | return pSub->pMethods->xWrite(pSub,zBuf,iAmt,iOfst); |
| 560 | 593 | } |
| 561 | 594 | |
| 562 | 595 | /* |
| @@ -627,11 +660,11 @@ | ||
| 627 | 660 | p->inCkpt = 1; |
| 628 | 661 | assert( p->isDb ); |
| 629 | 662 | assert( p->pPartner!=0 ); |
| 630 | 663 | p->pPartner->inCkpt = 1; |
| 631 | 664 | if( p->hasCorrectReserve ){ |
| 632 | - tmstmpEvent(p, ELOG_CKPT_START, 0, 0, 0); | |
| 665 | + tmstmpEvent(p, ELOG_CKPT_START, 0, 0, 0, 0); | |
| 633 | 666 | } |
| 634 | 667 | rc = SQLITE_OK; |
| 635 | 668 | break; |
| 636 | 669 | } |
| 637 | 670 | case SQLITE_FCNTL_CKPT_DONE: { |
| @@ -638,11 +671,11 @@ | ||
| 638 | 671 | p->inCkpt = 0; |
| 639 | 672 | assert( p->isDb ); |
| 640 | 673 | assert( p->pPartner!=0 ); |
| 641 | 674 | p->pPartner->inCkpt = 0; |
| 642 | 675 | if( p->hasCorrectReserve ){ |
| 643 | - tmstmpEvent(p, ELOG_CKPT_DONE, 0, 0, 0); | |
| 676 | + tmstmpEvent(p, ELOG_CKPT_DONE, 0, 0, 0, 0); | |
| 644 | 677 | } |
| 645 | 678 | rc = SQLITE_OK; |
| 646 | 679 | break; |
| 647 | 680 | } |
| 648 | 681 | } |
| @@ -813,11 +846,11 @@ | ||
| 813 | 846 | pid = GETPID; |
| 814 | 847 | pLog->zLogname = sqlite3_mprintf( |
| 815 | 848 | "%s-tmstmp/%04d%02d%02dT%02d%02d%02d%03d-%08d-%08x", |
| 816 | 849 | zName, Y, M, D, h, m, s, f, pid, r2); |
| 817 | 850 | } |
| 818 | - tmstmpEvent(p, p->isWal ? ELOG_OPEN_WAL : ELOG_OPEN_DB, 0, GETPID, 0); | |
| 851 | + tmstmpEvent(p, p->isWal ? ELOG_OPEN_WAL : ELOG_OPEN_DB, 0, GETPID, 0, 0); | |
| 819 | 852 | |
| 820 | 853 | tmstmp_open_done: |
| 821 | 854 | if( rc ) pFile->pMethods = 0; |
| 822 | 855 | return rc; |
| 823 | 856 | } |
| 824 | 857 |
| --- extsrc/tmstmpvfs.c | |
| +++ extsrc/tmstmpvfs.c | |
| @@ -40,10 +40,20 @@ | |
| 40 | ** (windows) cl tmstmpvfs.c -link -dll -out:tmstmpvfs.dll |
| 41 | ** |
| 42 | ** You may want to add additional compiler options, of course, |
| 43 | ** according to the needs of your project. |
| 44 | ** |
| 45 | ** LOADING |
| 46 | ** |
| 47 | ** To load this extension as a shared library, you first have to |
| 48 | ** bring up a dummy SQLite database connection to use as the argument |
| 49 | ** to the sqlite3_load_extension() API call. Then you invoke the |
| @@ -61,10 +71,18 @@ | |
| 61 | ** down in the stack. This is normally what you want. However, in |
| 62 | ** complex situations where multiple VFS shims are being loaded, |
| 63 | ** it might be important to ensure that tmstmpvfs is loaded in the |
| 64 | ** correct order so that it sequences itself into the default VFS |
| 65 | ** Shim stack in the right order. |
| 66 | ** |
| 67 | ** An application can see if the tmstmpvfs is being used by examining |
| 68 | ** the results from SQLITE_FCNTL_VFSNAME (or the .vfsname command in |
| 69 | ** the CLI). If the answer include "tmstmp", then this VFS is being |
| 70 | ** used. |
| @@ -94,17 +112,20 @@ | |
| 94 | ** It never hurts to run the VACUUM, even if you don't need it. |
| 95 | ** |
| 96 | ** From the CLI, use the ".filectrl reserve_bytes 16" command, |
| 97 | ** followed by "VACUUM;". |
| 98 | ** |
| 99 | ** Note that SQLite allows the number of reserve-bytes to be |
| 100 | ** increased but not decreased. So if a database file already |
| 101 | ** has a reserve-bytes value greater than 16, there is no way to |
| 102 | ** activate timestamping on that database, other than to dump |
| 103 | ** and restore the database file. Note also that other extensions |
| 104 | ** might also make use of the reserve-bytes. Timestamping will |
| 105 | ** be incompatible with those other extensions. |
| 106 | ** |
| 107 | ** IMPLEMENTATION NOTES |
| 108 | ** |
| 109 | ** The timestamp information is stored in the last 16 bytes of each page. |
| 110 | ** This module only operates if the "bytes of reserved space on each page" |
| @@ -169,10 +190,11 @@ | |
| 169 | ** op = 0x05 |
| 170 | ** |
| 171 | ** ELOG_CKPT_PAGE "Page xfer from WAL to database" |
| 172 | ** op = 0x06 |
| 173 | ** a2 = database page number |
| 174 | ** |
| 175 | ** ELOG_CKPT_END "Start of a checkpoint operation" |
| 176 | ** op = 0x07 |
| 177 | ** |
| 178 | ** ELOG_WAL_RESET "WAL file header overwritten" |
| @@ -246,11 +268,11 @@ | |
| 246 | FILE *log; /* Open log file */ |
| 247 | int n; /* Bytes of a[] used */ |
| 248 | unsigned char a[16*6]; /* Buffered header for the log */ |
| 249 | }; |
| 250 | |
| 251 | /* An open WAL file */ |
| 252 | struct TmstmpFile { |
| 253 | sqlite3_file base; /* IO methods */ |
| 254 | u32 uMagic; /* Magic number for sanity checking */ |
| 255 | u32 salt1; /* Last WAL salt-1 value */ |
| 256 | u32 iFrame; /* Last WAL frame number */ |
| @@ -432,11 +454,12 @@ | |
| 432 | static void tmstmpEvent( |
| 433 | TmstmpFile *p, |
| 434 | u8 op, |
| 435 | u8 a1, |
| 436 | u32 a2, |
| 437 | u32 a3 |
| 438 | ){ |
| 439 | unsigned char *a; |
| 440 | TmstmpLog *pLog; |
| 441 | if( p->isWal ){ |
| 442 | p = p->pPartner; |
| @@ -449,11 +472,15 @@ | |
| 449 | if( tmstmpLogFlush(p) ) return; |
| 450 | } |
| 451 | a = pLog->a + pLog->n; |
| 452 | a[0] = op; |
| 453 | a[1] = a1; |
| 454 | tmstmpPutTS(p, a+2); |
| 455 | tmstmpPutU32(a2, a+8); |
| 456 | tmstmpPutU32(a3, a+12); |
| 457 | pLog->n += 16; |
| 458 | if( pLog->log || (op>=ELOG_WAL_PAGE && op<=ELOG_WAL_RESET) ){ |
| 459 | (void)tmstmpLogFlush(p); |
| @@ -464,11 +491,11 @@ | |
| 464 | ** Close a connection |
| 465 | */ |
| 466 | static int tmstmpClose(sqlite3_file *pFile){ |
| 467 | TmstmpFile *p = (TmstmpFile *)pFile; |
| 468 | if( p->hasCorrectReserve ){ |
| 469 | tmstmpEvent(p, p->isDb ? ELOG_CLOSE_DB : ELOG_CLOSE_WAL, 0, 0, 0); |
| 470 | } |
| 471 | tmstmpLogFree(p->pLog); |
| 472 | if( p->pPartner ){ |
| 473 | assert( p->pPartner->pPartner==p ); |
| 474 | p->pPartner->pPartner = 0; |
| @@ -503,10 +530,16 @@ | |
| 503 | if( p->pPartner ){ |
| 504 | p->pPartner->hasCorrectReserve = p->hasCorrectReserve; |
| 505 | p->pPartner->pgsz = p->pgsz; |
| 506 | } |
| 507 | } |
| 508 | return rc; |
| 509 | } |
| 510 | |
| 511 | /* |
| 512 | ** Write data to a tmstmp-file. |
| @@ -537,26 +570,26 @@ | |
| 537 | memset(s, 0, TMSTMP_RESERVE); |
| 538 | tmstmpPutTS(p, s+2); |
| 539 | tmstmpPutU32(p->iFrame, s+8); |
| 540 | tmstmpPutU32(p->salt1, s+12); |
| 541 | s[12] = p->isCommit ? 1 : 0; |
| 542 | tmstmpEvent(p, ELOG_WAL_PAGE, s[12], p->pgno, p->iFrame); |
| 543 | }else if( iAmt==32 && iOfst==0 ){ |
| 544 | u32 salt1 = tmstmpGetU32(((const u8*)zBuf)+16); |
| 545 | tmstmpEvent(p, ELOG_WAL_RESET, 0, 0, salt1); |
| 546 | } |
| 547 | }else if( p->inCkpt ){ |
| 548 | assert( p->pgsz>0 ); |
| 549 | tmstmpEvent(p, ELOG_CKPT_PAGE, 0, (iOfst/p->pgsz)+1, 0); |
| 550 | }else if( p->pPartner==0 ){ |
| 551 | /* Writing into a database in rollback mode */ |
| 552 | unsigned char *s = (unsigned char*)zBuf+iAmt-TMSTMP_RESERVE; |
| 553 | memset(s, 0, TMSTMP_RESERVE); |
| 554 | tmstmpPutTS(p, s+2); |
| 555 | s[12] = 2; |
| 556 | assert( p->pgsz>0 ); |
| 557 | tmstmpEvent(p, ELOG_DB_PAGE, 0, (u32)(iOfst/p->pgsz), 0); |
| 558 | } |
| 559 | return pSub->pMethods->xWrite(pSub,zBuf,iAmt,iOfst); |
| 560 | } |
| 561 | |
| 562 | /* |
| @@ -627,11 +660,11 @@ | |
| 627 | p->inCkpt = 1; |
| 628 | assert( p->isDb ); |
| 629 | assert( p->pPartner!=0 ); |
| 630 | p->pPartner->inCkpt = 1; |
| 631 | if( p->hasCorrectReserve ){ |
| 632 | tmstmpEvent(p, ELOG_CKPT_START, 0, 0, 0); |
| 633 | } |
| 634 | rc = SQLITE_OK; |
| 635 | break; |
| 636 | } |
| 637 | case SQLITE_FCNTL_CKPT_DONE: { |
| @@ -638,11 +671,11 @@ | |
| 638 | p->inCkpt = 0; |
| 639 | assert( p->isDb ); |
| 640 | assert( p->pPartner!=0 ); |
| 641 | p->pPartner->inCkpt = 0; |
| 642 | if( p->hasCorrectReserve ){ |
| 643 | tmstmpEvent(p, ELOG_CKPT_DONE, 0, 0, 0); |
| 644 | } |
| 645 | rc = SQLITE_OK; |
| 646 | break; |
| 647 | } |
| 648 | } |
| @@ -813,11 +846,11 @@ | |
| 813 | pid = GETPID; |
| 814 | pLog->zLogname = sqlite3_mprintf( |
| 815 | "%s-tmstmp/%04d%02d%02dT%02d%02d%02d%03d-%08d-%08x", |
| 816 | zName, Y, M, D, h, m, s, f, pid, r2); |
| 817 | } |
| 818 | tmstmpEvent(p, p->isWal ? ELOG_OPEN_WAL : ELOG_OPEN_DB, 0, GETPID, 0); |
| 819 | |
| 820 | tmstmp_open_done: |
| 821 | if( rc ) pFile->pMethods = 0; |
| 822 | return rc; |
| 823 | } |
| 824 |
| --- extsrc/tmstmpvfs.c | |
| +++ extsrc/tmstmpvfs.c | |
| @@ -40,10 +40,20 @@ | |
| 40 | ** (windows) cl tmstmpvfs.c -link -dll -out:tmstmpvfs.dll |
| 41 | ** |
| 42 | ** You may want to add additional compiler options, of course, |
| 43 | ** according to the needs of your project. |
| 44 | ** |
| 45 | ** Another option is to statically link both SQLite and this extension |
| 46 | ** into your application. If both this file and "sqlite3.c" are statically |
| 47 | ** linked, and if "sqlite3.c" is compiled with -DSQLITE_EXTRA_INIT= |
| 48 | ** SQLite amalgamation "sqlite3.c" file with the option like: |
| 49 | ** |
| 50 | ** -DSQLITE_EXTRA_INIT=sqlite3_register_tmstmpvfs |
| 51 | ** |
| 52 | ** Then SQLite will use the tmstmp VFS by default throughout your |
| 53 | ** application. |
| 54 | ** |
| 55 | ** LOADING |
| 56 | ** |
| 57 | ** To load this extension as a shared library, you first have to |
| 58 | ** bring up a dummy SQLite database connection to use as the argument |
| 59 | ** to the sqlite3_load_extension() API call. Then you invoke the |
| @@ -61,10 +71,18 @@ | |
| 71 | ** down in the stack. This is normally what you want. However, in |
| 72 | ** complex situations where multiple VFS shims are being loaded, |
| 73 | ** it might be important to ensure that tmstmpvfs is loaded in the |
| 74 | ** correct order so that it sequences itself into the default VFS |
| 75 | ** Shim stack in the right order. |
| 76 | ** |
| 77 | ** When running the CLI, you can load this extension at invocation by |
| 78 | ** adding a command-line option like this: "--vfs ./tmstmpvfs.so". |
| 79 | ** The --vfs option usually specifies the symbolic name of a built-in VFS. |
| 80 | ** But if the argument to --vfs is not a built-in VFS but is instead the |
| 81 | ** name of a file, the CLI tries to load that file as an extension. Note |
| 82 | ** that the full name of the extension file must be provided, including |
| 83 | ** the ".so" or ".dylib" or ".dll" suffix. |
| 84 | ** |
| 85 | ** An application can see if the tmstmpvfs is being used by examining |
| 86 | ** the results from SQLITE_FCNTL_VFSNAME (or the .vfsname command in |
| 87 | ** the CLI). If the answer include "tmstmp", then this VFS is being |
| 88 | ** used. |
| @@ -94,17 +112,20 @@ | |
| 112 | ** It never hurts to run the VACUUM, even if you don't need it. |
| 113 | ** |
| 114 | ** From the CLI, use the ".filectrl reserve_bytes 16" command, |
| 115 | ** followed by "VACUUM;". |
| 116 | ** |
| 117 | ** SQLite allows the number of reserve-bytes to be increased, but |
| 118 | ** not decreased. If you want to restore the reserve-bytes to 0 |
| 119 | ** (to disable tmstmpvfs), the easiest approach is to use VACUUM INTO |
| 120 | ** with a URI filename as the argument and include "reserve=0" query |
| 121 | ** parameter on the URI. Example: |
| 122 | ** |
| 123 | ** VACUUM INTO 'file:notimestamps.db?reserve=0'; |
| 124 | ** |
| 125 | ** Then switch over to using the new database file. The reserve=0 query |
| 126 | ** parameter only works on SQLite 3.52.0 and later. |
| 127 | ** |
| 128 | ** IMPLEMENTATION NOTES |
| 129 | ** |
| 130 | ** The timestamp information is stored in the last 16 bytes of each page. |
| 131 | ** This module only operates if the "bytes of reserved space on each page" |
| @@ -169,10 +190,11 @@ | |
| 190 | ** op = 0x05 |
| 191 | ** |
| 192 | ** ELOG_CKPT_PAGE "Page xfer from WAL to database" |
| 193 | ** op = 0x06 |
| 194 | ** a2 = database page number |
| 195 | ** a3 = frame number in the WAL file |
| 196 | ** |
| 197 | ** ELOG_CKPT_END "Start of a checkpoint operation" |
| 198 | ** op = 0x07 |
| 199 | ** |
| 200 | ** ELOG_WAL_RESET "WAL file header overwritten" |
| @@ -246,11 +268,11 @@ | |
| 268 | FILE *log; /* Open log file */ |
| 269 | int n; /* Bytes of a[] used */ |
| 270 | unsigned char a[16*6]; /* Buffered header for the log */ |
| 271 | }; |
| 272 | |
| 273 | /* An open WAL or DB file */ |
| 274 | struct TmstmpFile { |
| 275 | sqlite3_file base; /* IO methods */ |
| 276 | u32 uMagic; /* Magic number for sanity checking */ |
| 277 | u32 salt1; /* Last WAL salt-1 value */ |
| 278 | u32 iFrame; /* Last WAL frame number */ |
| @@ -432,11 +454,12 @@ | |
| 454 | static void tmstmpEvent( |
| 455 | TmstmpFile *p, |
| 456 | u8 op, |
| 457 | u8 a1, |
| 458 | u32 a2, |
| 459 | u32 a3, |
| 460 | u8 *pTS |
| 461 | ){ |
| 462 | unsigned char *a; |
| 463 | TmstmpLog *pLog; |
| 464 | if( p->isWal ){ |
| 465 | p = p->pPartner; |
| @@ -449,11 +472,15 @@ | |
| 472 | if( tmstmpLogFlush(p) ) return; |
| 473 | } |
| 474 | a = pLog->a + pLog->n; |
| 475 | a[0] = op; |
| 476 | a[1] = a1; |
| 477 | if( pTS ){ |
| 478 | memcpy(a+2, pTS, 6); |
| 479 | }else{ |
| 480 | tmstmpPutTS(p, a+2); |
| 481 | } |
| 482 | tmstmpPutU32(a2, a+8); |
| 483 | tmstmpPutU32(a3, a+12); |
| 484 | pLog->n += 16; |
| 485 | if( pLog->log || (op>=ELOG_WAL_PAGE && op<=ELOG_WAL_RESET) ){ |
| 486 | (void)tmstmpLogFlush(p); |
| @@ -464,11 +491,11 @@ | |
| 491 | ** Close a connection |
| 492 | */ |
| 493 | static int tmstmpClose(sqlite3_file *pFile){ |
| 494 | TmstmpFile *p = (TmstmpFile *)pFile; |
| 495 | if( p->hasCorrectReserve ){ |
| 496 | tmstmpEvent(p, p->isDb ? ELOG_CLOSE_DB : ELOG_CLOSE_WAL, 0, 0, 0, 0); |
| 497 | } |
| 498 | tmstmpLogFree(p->pLog); |
| 499 | if( p->pPartner ){ |
| 500 | assert( p->pPartner->pPartner==p ); |
| 501 | p->pPartner->pPartner = 0; |
| @@ -503,10 +530,16 @@ | |
| 530 | if( p->pPartner ){ |
| 531 | p->pPartner->hasCorrectReserve = p->hasCorrectReserve; |
| 532 | p->pPartner->pgsz = p->pgsz; |
| 533 | } |
| 534 | } |
| 535 | if( p->isWal |
| 536 | && p->inCkpt |
| 537 | && iAmt>=512 && iAmt<=65535 && (iAmt&(iAmt-1))==0 |
| 538 | ){ |
| 539 | p->pPartner->iFrame = (iOfst - 8)/(p->pgsz + 48) + 1; |
| 540 | } |
| 541 | return rc; |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Write data to a tmstmp-file. |
| @@ -537,26 +570,26 @@ | |
| 570 | memset(s, 0, TMSTMP_RESERVE); |
| 571 | tmstmpPutTS(p, s+2); |
| 572 | tmstmpPutU32(p->iFrame, s+8); |
| 573 | tmstmpPutU32(p->salt1, s+12); |
| 574 | s[12] = p->isCommit ? 1 : 0; |
| 575 | tmstmpEvent(p, ELOG_WAL_PAGE, s[12], p->pgno, p->iFrame, s+2); |
| 576 | }else if( iAmt==32 && iOfst==0 ){ |
| 577 | u32 salt1 = tmstmpGetU32(((const u8*)zBuf)+16); |
| 578 | tmstmpEvent(p, ELOG_WAL_RESET, 0, 0, salt1, 0); |
| 579 | } |
| 580 | }else if( p->inCkpt ){ |
| 581 | assert( p->pgsz>0 ); |
| 582 | tmstmpEvent(p, ELOG_CKPT_PAGE, 0, (iOfst/p->pgsz)+1, p->iFrame, 0); |
| 583 | }else if( p->pPartner==0 ){ |
| 584 | /* Writing into a database in rollback mode */ |
| 585 | unsigned char *s = (unsigned char*)zBuf+iAmt-TMSTMP_RESERVE; |
| 586 | memset(s, 0, TMSTMP_RESERVE); |
| 587 | tmstmpPutTS(p, s+2); |
| 588 | s[12] = 2; |
| 589 | assert( p->pgsz>0 ); |
| 590 | tmstmpEvent(p, ELOG_DB_PAGE, 0, (u32)(iOfst/p->pgsz), 0, s+2); |
| 591 | } |
| 592 | return pSub->pMethods->xWrite(pSub,zBuf,iAmt,iOfst); |
| 593 | } |
| 594 | |
| 595 | /* |
| @@ -627,11 +660,11 @@ | |
| 660 | p->inCkpt = 1; |
| 661 | assert( p->isDb ); |
| 662 | assert( p->pPartner!=0 ); |
| 663 | p->pPartner->inCkpt = 1; |
| 664 | if( p->hasCorrectReserve ){ |
| 665 | tmstmpEvent(p, ELOG_CKPT_START, 0, 0, 0, 0); |
| 666 | } |
| 667 | rc = SQLITE_OK; |
| 668 | break; |
| 669 | } |
| 670 | case SQLITE_FCNTL_CKPT_DONE: { |
| @@ -638,11 +671,11 @@ | |
| 671 | p->inCkpt = 0; |
| 672 | assert( p->isDb ); |
| 673 | assert( p->pPartner!=0 ); |
| 674 | p->pPartner->inCkpt = 0; |
| 675 | if( p->hasCorrectReserve ){ |
| 676 | tmstmpEvent(p, ELOG_CKPT_DONE, 0, 0, 0, 0); |
| 677 | } |
| 678 | rc = SQLITE_OK; |
| 679 | break; |
| 680 | } |
| 681 | } |
| @@ -813,11 +846,11 @@ | |
| 846 | pid = GETPID; |
| 847 | pLog->zLogname = sqlite3_mprintf( |
| 848 | "%s-tmstmp/%04d%02d%02dT%02d%02d%02d%03d-%08d-%08x", |
| 849 | zName, Y, M, D, h, m, s, f, pid, r2); |
| 850 | } |
| 851 | tmstmpEvent(p, p->isWal ? ELOG_OPEN_WAL : ELOG_OPEN_DB, 0, GETPID, 0, 0); |
| 852 | |
| 853 | tmstmp_open_done: |
| 854 | if( rc ) pFile->pMethods = 0; |
| 855 | return rc; |
| 856 | } |
| 857 |