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:
1
main.mk
1
main.mk
@@ -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 \
|
||||
|
44
manifest
44
manifest
@@ -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-----
|
||||
|
@@ -1 +1 @@
|
||||
648dd157ef3b7b790764698fd4dd7107c25212c9
|
||||
ebf74015f09fe241c1c6902dc8954f2b59ab41ec
|
10
src/btree.c
10
src/btree.c
@@ -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;
|
||||
}
|
||||
|
@@ -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*);
|
||||
|
@@ -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);
|
||||
|
83
src/pager.c
83
src/pager.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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*);
|
||||
|
@@ -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:
|
||||
|
@@ -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()]
|
||||
|
@@ -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
|
||||
|
@@ -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
330
src/test_superlock.c
Normal 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
98
test/superlock.test
Normal 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
|
Reference in New Issue
Block a user