Merge in the superlock demonstration changes.
FossilOrigin-Name: 570e79a8eb3bb2d2a15c46c55fbf52c9dd3e3ae8
This commit is contained in:
commit
204eaecda4
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 \
|
||||
|
24
manifest
24
manifest
@ -1,8 +1,8 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
C Add\sthe\scheckpoint_fullfsync\spragma\swhich\senables\sF_FULLFSYNC\son\scheckpoint\noperations\sonly,\snot\sduring\sordinary\scommit\sfsyncs.
|
||||
D 2010-11-19T18:23:35
|
||||
C Merge\sin\sthe\ssuperlock\sdemonstration\schanges.
|
||||
D 2010-11-19T18:36:45
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -102,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 497c8cb6ae132c88fa184e5e454b0e6336da5693
|
||||
F main.mk 05d0f3475dd331896bd607cfb45c5e21b94589ad
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@ -144,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 335d2c7ecb093db7b662d9325fe68fb9087a4814
|
||||
F src/main.c 721e5530b14b91a1fb6aead279af8b8729fa9a20
|
||||
F src/malloc.c 3d7284cd9346ab6e3945535761e68c23c6cf40ef
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
|
||||
@ -185,7 +185,7 @@ F src/sqliteInt.h f5b5041bfebd5654212992f6ebaa3f575c4b9c17
|
||||
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
|
||||
@ -220,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
|
||||
@ -653,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
|
||||
@ -890,14 +892,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P 0a95589f2166f9ce420e647b73e8c797fe8f4833
|
||||
R 9ff48f9c3632ebb5c161a79114253731
|
||||
P a069867301de3ca2e1753bd4d2e426d27365be4c 1a3e7417a2184188fe21c3284e58720da9ca11cf
|
||||
R 612a5c6e6ceeba977718ef175cdd20b9
|
||||
U drh
|
||||
Z a789fd2eb497f6a2ee7fc869b62ceca0
|
||||
Z f6076b0dc8e47c984cebf6ec68cb33ea
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
|
||||
iD8DBQFM5sCqoxKgR168RlERApS2AJ4+ntN9Z03wXj7RBuqf9nns23d2XwCcCMoX
|
||||
qdHuj9+tKtaYqLIB5TN29vE=
|
||||
=5kJQ
|
||||
iD8DBQFM5sO/oxKgR168RlERAjMvAJ46ffI5I7vl/OKgwTBinI+EBDsO5QCeLCGK
|
||||
6NmdlpkMD1RVx2Ohr0Mr5i0=
|
||||
=Nkm+
|
||||
-----END PGP SIGNATURE-----
|
||||
|
@ -1 +1 @@
|
||||
a069867301de3ca2e1753bd4d2e426d27365be4c
|
||||
570e79a8eb3bb2d2a15c46c55fbf52c9dd3e3ae8
|
@ -2356,7 +2356,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);
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user