1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Merge the checkpoint_fullfsync pragma and the superlock demonstration into

the checkpoint-v2 experimental branch.

FossilOrigin-Name: ebf74015f09fe241c1c6902dc8954f2b59ab41ec
This commit is contained in:
drh
2010-11-19 18:51:31 +00:00
14 changed files with 557 additions and 51 deletions

View File

@@ -251,6 +251,7 @@ TESTSRC = \
$(TOP)/src/test_schema.c \
$(TOP)/src/test_server.c \
$(TOP)/src/test_stat.c \
$(TOP)/src/test_superlock.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \

View File

@@ -1,5 +1,8 @@
C Add\sfile\stest/tt3_checkpoint.c\sthat\sadds\sa\smulti-threaded\stest\sfor\sblocking\scheckpoints\sto\sthreadtest3.
D 2010-11-19T09:58:11
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
C Merge\sthe\scheckpoint_fullfsync\spragma\sand\sthe\ssuperlock\sdemonstration\sinto\nthe\scheckpoint-v2\sexperimental\sbranch.
D 2010-11-19T18:51:31
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -99,7 +102,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk 2c360958d2981a394ce021a9dfc842577f7935df
F main.mk 731380a234515bd21ba4f930e12041ed3acfcc3a
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -119,8 +122,8 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c d5b0137bc20327af08c14772227cc35134839c30
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
F src/btree.c d90149f6e0a6f715b58b272ef1028fa249a2a088
F src/btree.h 1d62748eb7d129292782cf65b891b85cbfa024d4
F src/btree.c 3578a5e812ab9b434b1b705aad547939a055be11
F src/btree.h e2f2cd9933bf30724f53ffa12c4c5a3a864bbd6e
F src/btreeInt.h c424f2f131cc61ddf130f9bd736b3df12c8a51f0
F src/build.c 00a327120d81ace6267e714ae8010c997d55de5d
F src/callback.c a1d1b1c9c85415dff013af033e2fed9c8382d33b
@@ -141,7 +144,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e
F src/main.c 5927ac9ca30c46df68148ad68227329d1be41a21
F src/main.c 84fdf5b2d823641f6bb394a81e467b6d7ff9b7ec
F src/malloc.c 3d7284cd9346ab6e3945535761e68c23c6cf40ef
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
@@ -162,13 +165,13 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e
F src/os_unix.c de5be4cdbf3d07018059934eaf7e5d8d594a895c
F src/os_win.c 2f90f7bdec714fad51cd31b4ecad3cc1b4bb5aad
F src/pager.c b46a78a196d99bc855eec3c602777a1bc8db5122
F src/pager.h e2485f2f2fa5264f2bb68d1783c149d3d57d3637
F src/pager.c b3e86e5d27c04e05a73a55ce2c79bb6028196431
F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
F src/pcache1.c e9578a3beac26f229ee558a4e16c863f2498185f
F src/pragma.c f843c877845ddbb911f10eea50c9290bc8354b03
F src/pragma.c 900f480cc266428218d1a745a3e670fbd33cf12d
F src/prepare.c c2b318037d626fed27905c9446730b560637217a
F src/printf.c 8ae5082dd38a1b5456030c3755ec3a392cd51506
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
@@ -176,13 +179,13 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c 550d67688f5e8bc8022faf6d014838afba1415af
F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056
F src/sqlite.h.in 0dc08ed5d4decf11002e5fee2be0d6ef87798960
F src/sqlite.h.in daa5e94df834537708afb917e9b4cc4489c63c00
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
F src/sqliteInt.h 4e7045f17606296bc8e7898d69567fc3cd06b761
F src/sqliteInt.h eea368b138c8c90ea1aef8f06f49ec29aacb2a8b
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c e1c485fa323e3ef02e5b10fe6a016e7638013eb9
F src/tclsqlite.c 77c5c4b8ac7b2d94ee480e1ad626fbd921d948e4
F src/test1.c c2aa29d0fd6db7506fb7f0de7bff1386078296df
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc
@@ -217,6 +220,7 @@ F src/test_rtree.c 30c981837445a4e187ee850a49c4760d9642f7c3
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
F src/test_superlock.c 714bb877e599f96a4923b5429ab36737c3166b09
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0
F src/test_vfs.c e10fcca756cafa89438311b31522ac1f95bf784b
@@ -650,6 +654,7 @@ F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test f9241e8738fe8d05655fd096e4c8a88ee22d8990
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
F test/table.test 04ba066432430657712d167ebf28080fe878d305
F test/tableapi.test 7262a8cbaa9965d429f1cbd2747edc185fa56516
@@ -889,7 +894,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P ac348ae25cb0239dc525bb473cc83cdb1b3de205
R d259a6023fb0ad6db4b616b7c9bd726d
U dan
Z 699d2f3824e67293f11a7075160f739b
P 648dd157ef3b7b790764698fd4dd7107c25212c9 570e79a8eb3bb2d2a15c46c55fbf52c9dd3e3ae8
R 29ee6d2e4bc9e75a0c4b1469b6aa491c
U drh
Z 62a52f8bb2681a24e7959006560a6980
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFM5sdNoxKgR168RlERAn8IAJ4xAREF+dTwudzwR6tkh9wg+ffZegCeIyuW
SnmYKb+i1Y56lE+LONz+l/g=
=HICj
-----END PGP SIGNATURE-----

View File

@@ -1 +1 @@
648dd157ef3b7b790764698fd4dd7107c25212c9
ebf74015f09fe241c1c6902dc8954f2b59ab41ec

View File

@@ -2105,11 +2105,17 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
** probability of damage to near zero but with a write performance reduction.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
int sqlite3BtreeSetSafetyLevel(
Btree *p, /* The btree to set the safety level on */
int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
int fullSync, /* PRAGMA fullfsync. */
int ckptFullSync /* PRAGMA checkpoint_fullfync */
){
BtShared *pBt = p->pBt;
assert( sqlite3_mutex_held(p->db->mutex) );
assert( level>=1 && level<=3 );
sqlite3BtreeEnter(p);
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
sqlite3BtreeLeave(p);
return SQLITE_OK;
}

View File

@@ -75,7 +75,7 @@ int sqlite3BtreeOpen(
int sqlite3BtreeClose(Btree*);
int sqlite3BtreeSetCacheSize(Btree*,int);
int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
int sqlite3BtreeSyncDisabled(Btree*);
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
int sqlite3BtreeGetPageSize(Btree*);

View File

@@ -2385,7 +2385,10 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
assert( pPager!=0 );
fd = sqlite3PagerFile(pPager);
assert( fd!=0 );
if( fd->pMethods ){
if( op==SQLITE_FCNTL_FILE_POINTER ){
*(sqlite3_file**)pArg = fd;
rc = SQLITE_OK;
}else if( fd->pMethods ){
rc = sqlite3OsFileControl(fd, op, pArg);
}
sqlite3BtreeLeave(pBtree);

View File

@@ -615,7 +615,8 @@ struct Pager {
u8 noReadlock; /* Do not bother to obtain readlocks */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
u8 tempFile; /* zFilename is a temporary file */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
@@ -1299,7 +1300,7 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
}
if( rc==SQLITE_OK && !pPager->noSync ){
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
}
/* At this point the transaction is committed but the write lock
@@ -2751,7 +2752,7 @@ end_playback:
if( rc==SQLITE_OK && !pPager->noSync
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
}
if( rc==SQLITE_OK ){
rc = pager_end_transaction(pPager, zMaster[0]!='\0');
@@ -2925,13 +2926,13 @@ static int pagerWalFrames(
PgHdr *pList, /* List of frames to log */
Pgno nTruncate, /* Database size after this commit */
int isCommit, /* True if this is a commit */
int sync_flags /* Flags to pass to OsSync() (or 0) */
int syncFlags /* Flags to pass to OsSync() (or 0) */
){
int rc; /* Return code */
assert( pPager->pWal );
rc = sqlite3WalFrames(pPager->pWal,
pPager->pageSize, pList, nTruncate, isCommit, sync_flags
pPager->pageSize, pList, nTruncate, isCommit, syncFlags
);
if( rc==SQLITE_OK && pPager->pBackup ){
PgHdr *p;
@@ -3261,14 +3262,49 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
** assurance that the journal will not be corrupted to the
** point of causing damage to the database during rollback.
**
** The above is for a rollback-journal mode. For WAL mode, OFF continues
** to mean that no syncs ever occur. NORMAL means that the WAL is synced
** prior to the start of checkpoint and that the database file is synced
** at the conclusion of the checkpoint if the entire content of the WAL
** was written back into the database. But no sync operations occur for
** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL
** file is synced following each commit operation, in addition to the
** syncs associated with NORMAL.
**
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an
** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL
** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the
** synchronous=FULL versus synchronous=NORMAL setting determines when
** the xSync primitive is called and is relevant to all platforms.
**
** Numeric values associated with these states are OFF==1, NORMAL=2,
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
void sqlite3PagerSetSafetyLevel(
Pager *pPager, /* The pager to set safety level for */
int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
int bFullFsync, /* PRAGMA fullfsync */
int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
){
assert( level>=1 && level<=3 );
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
}else if( bFullFsync ){
pPager->syncFlags = SQLITE_SYNC_FULL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else if( bCkptFullFsync ){
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else{
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
}
}
#endif
@@ -3654,10 +3690,7 @@ int sqlite3PagerClose(Pager *pPager){
/* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, pTmp
);
sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);
pPager->pWal = 0;
#endif
pager_reset(pPager);
@@ -3823,7 +3856,7 @@ static int syncJournal(Pager *pPager, int newHdr){
if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
if( rc!=SQLITE_OK ) return rc;
}
IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr));
@@ -3835,8 +3868,8 @@ static int syncJournal(Pager *pPager, int newHdr){
if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
(pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
(pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
);
if( rc!=SQLITE_OK ) return rc;
}
@@ -4426,7 +4459,8 @@ int sqlite3PagerOpen(
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
pPager->fullSync = pPager->noSync ?0:1;
pPager->sync_flags = SQLITE_SYNC_NORMAL;
pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = pPager->syncFlags;
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@@ -5549,10 +5583,10 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
}
/*
** Sync the pager file to disk. This is a no-op for in-memory files
** Sync the database file to disk. This is a no-op for in-memory databases
** or pages with the Pager.noSync flag set.
**
** If successful, or called on a pager for which it is a no-op, this
** If successful, or if called on a pager for which it is a no-op, this
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
int sqlite3PagerSync(Pager *pPager){
@@ -5561,7 +5595,7 @@ int sqlite3PagerSync(Pager *pPager){
if( pPager->noSync ){
rc = SQLITE_OK;
}else{
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
}
return rc;
}
@@ -5650,7 +5684,7 @@ int sqlite3PagerCommitPhaseOne(
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
if( pList ){
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
(pPager->fullSync ? pPager->sync_flags : 0)
(pPager->fullSync ? pPager->syncFlags : 0)
);
}
if( rc==SQLITE_OK ){
@@ -5781,7 +5815,7 @@ int sqlite3PagerCommitPhaseOne(
/* Finally, sync the database file. */
if( !pPager->noSync && !noSync ){
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
@@ -6527,8 +6561,7 @@ int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
if( pPager->pWal ){
rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
pPager->xBusyHandler, pPager->pBusyHandlerArg,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, (u8 *)pPager->pTmpSpace,
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
pnLog, pnCkpt
);
}
@@ -6682,10 +6715,8 @@ int sqlite3PagerCloseWal(Pager *pPager){
if( rc==SQLITE_OK && pPager->pWal ){
rc = pagerExclusiveLock(pPager);
if( rc==SQLITE_OK ){
rc = sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, (u8*)pPager->pTmpSpace
);
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
pPager->pageSize, (u8*)pPager->pTmpSpace);
pPager->pWal = 0;
}
}

View File

@@ -103,7 +103,7 @@ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
int sqlite3PagerSetPagesize(Pager*, u32*, int);
int sqlite3PagerMaxPageCount(Pager*, int);
void sqlite3PagerSetCachesize(Pager*, int);
void sqlite3PagerSetSafetyLevel(Pager*,int,int);
void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
int sqlite3PagerLockingMode(Pager *, int);
int sqlite3PagerSetJournalMode(Pager *, int);
int sqlite3PagerGetJournalMode(Pager*);

View File

@@ -172,6 +172,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
{ "empty_result_callbacks", SQLITE_NullCallback },
{ "legacy_file_format", SQLITE_LegacyFileFmt },
{ "fullfsync", SQLITE_FullFSync },
{ "checkpoint_fullfsync", SQLITE_CkptFullFSync },
{ "reverse_unordered_selects", SQLITE_ReverseOrder },
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
{ "automatic_index", SQLITE_AutoIndex },
@@ -1519,7 +1520,8 @@ void sqlite3Pragma(
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
if( db->autoCommit ){
sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
(db->flags&SQLITE_FullFSync)!=0);
(db->flags&SQLITE_FullFSync)!=0,
(db->flags&SQLITE_CkptFullFSync)!=0);
}
#endif
pragma_out:

View File

@@ -537,6 +537,18 @@ int sqlite3_exec(
** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics.
** If the lower four bits equal SQLITE_SYNC_FULL, that means
** to use Mac OS X style fullsync instead of fsync().
**
** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags
** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL
** settings. The [synchronous pragma] determines when calls to the
** xSync VFS method occur and applies uniformly across all platforms.
** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how
** energetic or rigorous or forceful the sync operations are and
** only make a difference on Mac OSX for the default SQLite code.
** (Third-party VFS implementations might also make the distinction
** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the
** operating systems natively supported by SQLite, only Mac OSX
** cares about the difference.)
*/
#define SQLITE_SYNC_NORMAL 0x00002
#define SQLITE_SYNC_FULL 0x00003
@@ -705,6 +717,8 @@ struct sqlite3_io_methods {
#define SQLITE_LAST_ERRNO 4
#define SQLITE_FCNTL_SIZE_HINT 5
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
/*
** CAPI3REF: Mutex Handle
@@ -5234,7 +5248,7 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
** ^The [sqlite3_file_control()] interface makes a direct call to the
** xFileControl method for the [sqlite3_io_methods] object associated
** with a particular database identified by the second argument. ^The
** name of the database "main" for the main database or "temp" for the
** name of the database is "main" for the main database or "temp" for the
** TEMP database, or the name that appears after the AS keyword for
** databases that are added using the [ATTACH] SQL command.
** ^A NULL pointer can be used in place of "main" to refer to the
@@ -5244,6 +5258,12 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
** the xFileControl method. ^The return value of the xFileControl
** method becomes the return value of this routine.
**
** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes
** a pointer to the underlying [sqlite3_file] object to be written into
** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER
** case is a short-circuit path which does not actually invoke the
** underlying sqlite3_io_methods.xFileControl method.
**
** ^If the second parameter (zDbName) does not match the name of any
** open database file, then SQLITE_ERROR is returned. ^This error
** code is not remembered and will not be recalled by [sqlite3_errcode()]

View File

@@ -914,13 +914,14 @@ struct sqlite3 {
#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */
#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */
#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
#define SQLITE_CkptFullFSync 0x00400000 /* Use full fsync for checkpoint */
#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */
#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */
#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */
#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */
#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */
#define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */
#define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */
/*
** Bits of the sqlite3.flags field that are used by the

View File

@@ -3580,6 +3580,7 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitetestrtree_Init(Tcl_Interp*);
extern int Sqlitequota_Init(Tcl_Interp*);
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
Sqliteconfig_Init(interp);
Sqlitetest1_Init(interp);
@@ -3611,6 +3612,7 @@ static void init_all(Tcl_Interp *interp){
Sqlitetestrtree_Init(interp);
Sqlitequota_Init(interp);
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);

330
src/test_superlock.c Normal file
View File

@@ -0,0 +1,330 @@
/*
** 2010 November 19
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** Example code for obtaining an exclusive lock on an SQLite database
** file. This method is complicated, but works for both WAL and rollback
** mode database files. The interface to the example code in this file
** consists of the following two functions:
**
** sqlite3demo_superlock()
** sqlite3demo_superunlock()
*/
#include <sqlite3.h>
#include <string.h> /* memset(), strlen() */
#include <assert.h> /* assert() */
/*
** A structure to collect a busy-handler callback and argument and a count
** of the number of times it has been invoked.
*/
struct SuperlockBusy {
int (*xBusy)(void*,int); /* Pointer to busy-handler function */
void *pBusyArg; /* First arg to pass to xBusy */
int nBusy; /* Number of times xBusy has been invoked */
};
typedef struct SuperlockBusy SuperlockBusy;
/*
** The pCtx pointer passed to this function is actually a pointer to a
** SuperlockBusy structure. Invoke the busy-handler function encapsulated
** by the structure and return the result.
*/
static int superlockBusyHandler(void *pCtx, int UNUSED){
SuperlockBusy *pBusy = (SuperlockBusy *)pCtx;
if( pBusy->xBusy==0 ) return 0;
return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++);
}
/*
** This function is used to determine if the main database file for
** connection db is open in WAL mode or not. If no error occurs and the
** database file is in WAL mode, set *pbWal to true and return SQLITE_OK.
** If it is not in WAL mode, set *pbWal to false.
**
** If an error occurs, return an SQLite error code. The value of *pbWal
** is undefined in this case.
*/
static int superlockIsWal(sqlite3 *db, int *pbWal){
int rc; /* Return Code */
sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */
rc = sqlite3_prepare(db, "PRAGMA main.journal_mode", -1, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
*pbWal = 0;
if( SQLITE_ROW==sqlite3_step(pStmt) ){
const char *zMode = (const char *)sqlite3_column_text(pStmt, 0);
if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){
*pbWal = 1;
}
}
return sqlite3_finalize(pStmt);
}
/*
** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
** of the file fd. If the lock cannot be obtained immediately, invoke
** the busy-handler until either it is obtained or the busy-handler
** callback returns 0.
*/
static int superlockShmLock(
sqlite3_file *fd, /* Database file handle */
int idx, /* Offset of shm-lock to obtain */
int nByte, /* Number of consective bytes to lock */
SuperlockBusy *pBusy /* Busy-handler wrapper object */
){
int rc;
int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
do {
rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
}while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
return rc;
}
/*
** Obtain the extra locks on the database file required for WAL databases.
** Invoke the supplied busy-handler as required.
*/
static int superlockWalLock(
sqlite3 *db, /* Database handle open on WAL database */
SuperlockBusy *pBusy /* Busy handler wrapper object */
){
int rc; /* Return code */
sqlite3_file *fd = 0; /* Main database file handle */
void volatile *p = 0; /* Pointer to first page of shared memory */
int nBusy = 0; /* Number of calls already made to xBusy */
/* Obtain a pointer to the sqlite3_file object open on the main db file. */
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
if( rc!=SQLITE_OK ) return rc;
/* Obtain the "recovery" lock. Normally, this lock is only obtained by
** clients running database recovery.
*/
rc = superlockShmLock(fd, 2, 1, pBusy);
if( rc!=SQLITE_OK ) return rc;
/* Zero the start of the first shared-memory page. This means that any
** clients that open read or write transactions from this point on will
** have to run recovery before proceeding. Since they need the "recovery"
** lock that this process is holding to do that, no new read or write
** transactions may now be opened. Nor can a checkpoint be run, for the
** same reason.
*/
rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
if( rc!=SQLITE_OK ) return rc;
memset((void *)p, 0, 32);
/* Obtain exclusive locks on all the "read-lock" slots. Once these locks
** are held, it is guaranteed that there are no active reader, writer or
** checkpointer clients.
*/
rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
return rc;
}
/*
** Obtain a superlock on the database file identified by zPath, using the
** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is
** returned and output variable *ppLock is populated with an opaque handle
** that may be used with sqlite3demo_superunlock() to release the lock.
**
** If an error occurs, *ppLock is set to 0 and an SQLite error code
** (e.g. SQLITE_BUSY) is returned.
**
** If a required lock cannot be obtained immediately and the xBusy parameter
** to this function is not NULL, then xBusy is invoked in the same way
** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
** until either the lock can be obtained or the busy-handler function returns
** 0 (indicating "give up").
*/
int sqlite3demo_superlock(
const char *zPath, /* Path to database file to lock */
const char *zVfs, /* VFS to use to access database file */
int (*xBusy)(void*,int), /* Busy handler callback */
void *pBusyArg, /* Context arg for busy handler */
void **ppLock /* OUT: Context to pass to superunlock() */
){
sqlite3 *db = 0; /* Database handle open on zPath */
SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */
int rc; /* Return code */
/* Open a database handle on the file to superlock. */
rc = sqlite3_open_v2(
zPath, &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs
);
/* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not
** a WAL database, this is all we need to do.
**
** A wrapper function is used to invoke the busy-handler instead of
** registering the busy-handler function supplied by the user directly
** with SQLite. This is because the same busy-handler function may be
** invoked directly later on when attempting to obtain the extra locks
** required in WAL mode. By using the wrapper, we are able to guarantee
** that the "nBusy" integer parameter passed to the users busy-handler
** represents the total number of busy-handler invocations made within
** this call to sqlite3demo_superlock(), including any made during the
** "BEGIN EXCLUSIVE".
*/
if( rc==SQLITE_OK ){
busy.xBusy = xBusy;
busy.pBusyArg = pBusyArg;
sqlite3_busy_handler(db, superlockBusyHandler, (void *)&busy);
rc = sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
}
/* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL
** database, call superlockWalLock() to obtain the extra locks required
** to prevent readers, writers and/or checkpointers from accessing the
** db while this process is holding the superlock.
**
** Before attempting any WAL locks, commit the transaction started above
** to drop the WAL read and write locks currently held. Otherwise, the
** new WAL locks may conflict with the old.
*/
if( rc==SQLITE_OK ){
int bWal; /* True for a WAL database, false otherwise */
if( SQLITE_OK==(rc = superlockIsWal(db, &bWal)) && bWal ){
rc = sqlite3_exec(db, "COMMIT", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = superlockWalLock(db, &busy);
}
}
}
if( rc!=SQLITE_OK ){
sqlite3_close(db);
*ppLock = 0;
}else{
*ppLock = (void *)db;
}
return rc;
}
/*
** Release a superlock held on a database file. The argument passed to
** this function must have been obtained from a successful call to
** sqlite3demo_superlock().
*/
void sqlite3demo_superunlock(void *pLock){
sqlite3_close((sqlite3 *)pLock);
}
/*
** End of example code. Everything below here is the test harness.
**************************************************************************
**************************************************************************
*************************************************************************/
#ifdef SQLITE_TEST
#include <tcl.h>
struct InterpAndScript {
Tcl_Interp *interp;
Tcl_Obj *pScript;
};
typedef struct InterpAndScript InterpAndScript;
static void superunlock_del(ClientData cd){
sqlite3demo_superunlock((void *)cd);
}
static int superunlock_cmd(
ClientData cd,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
return TCL_OK;
}
static int superlock_busy(void *pCtx, int nBusy){
InterpAndScript *p = (InterpAndScript *)pCtx;
Tcl_Obj *pEval; /* Script to evaluate */
int iVal = 0; /* Value to return */
pEval = Tcl_DuplicateObj(p->pScript);
Tcl_IncrRefCount(pEval);
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
Tcl_DecrRefCount(pEval);
return iVal;
}
/*
** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
*/
static int superlock_cmd(
ClientData cd,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void *pLock; /* Lock context */
char *zPath;
char *zVfs = 0;
InterpAndScript busy = {0, 0};
int (*xBusy)(void*,int) = 0; /* Busy handler callback */
int rc; /* Return code from sqlite3demo_superlock() */
if( objc<3 || objc>5 ){
Tcl_WrongNumArgs(
interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
return TCL_ERROR;
}
zPath = Tcl_GetString(objv[2]);
if( objc>3 ){
zVfs = Tcl_GetString(objv[3]);
if( strlen(zVfs)==0 ) zVfs = 0;
}
if( objc>4 ){
busy.interp = interp;
busy.pScript = objv[4];
xBusy = superlock_busy;
}
rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
assert( rc==SQLITE_OK || pLock==0 );
assert( rc!=SQLITE_OK || pLock!=0 );
if( rc!=SQLITE_OK ){
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
return TCL_ERROR;
}
Tcl_CreateObjCommand(
interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
int SqliteSuperlock_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
return TCL_OK;
}
#endif

98
test/superlock.test Normal file
View File

@@ -0,0 +1,98 @@
# 2010 November 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix superlock
do_execsql_test 1.1 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
PRAGMA journal_mode = DELETE;
} {delete}
do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock}
do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}}
do_test 1.4 { unlock } {}
do_execsql_test 2.1 {
INSERT INTO t1 VALUES(3, 4);
PRAGMA journal_mode = WAL;
} {wal}
do_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock}
do_catchsql_test 2.3 { SELECT * FROM t1 } {1 {database is locked}}
do_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
do_catchsql_test 2.5 { PRAGMA wal_checkpoint } {1 {database is locked}}
do_test 2.6 { unlock } {}
do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) }
do_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock}
do_catchsql_test 3.3 { SELECT * FROM t1 } {1 {database is locked}}
do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {1 {database is locked}}
do_test 3.6 { unlock } {}
do_execsql_test 4.1 { PRAGMA wal_checkpoint } {}
do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
do_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}}
do_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
do_catchsql_test 4.5 { PRAGMA wal_checkpoint } {1 {database is locked}}
do_test 4.6 { unlock } {}
do_multiclient_test tn {
proc busyhandler {x} {
switch -- $x {
1 { sql1 "COMMIT" }
2 { sql2 "COMMIT" }
3 { sql3 "COMMIT" }
}
lappend ::busylist $x
return 1
}
set ::busylist [list]
do_test 5.$tn.1 {
sql1 {
CREATE TABLE t1(a, b);
PRAGMA journal_mode = WAL;
INSERT INTO t1 VALUES(1, 2);
}
} {wal}
do_test 5.$tn.2 {
sql1 { BEGIN ; SELECT * FROM t1 }
sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) }
sql3 { BEGIN ; SELECT * FROM t1 }
} {1 2}
do_test 5.$tn.3 {
set ::busylist [list]
sqlite3demo_superlock unlock test.db "" busyhandler
set ::busylist
} {0 1 2 3}
do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
do_test 5.$tn.5 {
csql3 { INSERT INTO t1 VALUES(5, 6) }
} {1 {database is locked}}
do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {1 {database is locked}}
do_test 5.$tn.7 { unlock } {}
}
finish_test