Add the sqlite3_close_v2() interface (from the deferred-close branch) that

allows close operations to happen out-of-order in bindings to
garbage-collected langauges.

FossilOrigin-Name: fb8893abeefabe9de44e34dcf4327764481189f5
This commit is contained in:
drh 2012-06-21 15:51:42 +00:00
commit ed68801b20
9 changed files with 125 additions and 59 deletions

View File

@ -1,5 +1,5 @@
C Merge\schanges\sto\strunk\sthat\sensure\sthat\sfloating\spoint\svalues\sare\sexactly\npreserved\son\sa\sround-trip\sthrough\s".dump". C Add\sthe\ssqlite3_close_v2()\sinterface\s(from\sthe\sdeferred-close\sbranch)\sthat\nallows\sclose\soperations\sto\shappen\sout-of-order\sin\sbindings\sto\ngarbage-collected\slangauges.
D 2012-06-21T15:22:04.664 D 2012-06-21T15:51:42.711
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 8f6d858bf3df9978ba43df19985146a1173025e4 F Makefile.in 8f6d858bf3df9978ba43df19985146a1173025e4
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -120,7 +120,7 @@ F src/alter.c 149cc80d9257971b0bff34e58fb2263e01998289
F src/analyze.c 70c46504c0d2543ea5cdca01140b2cd3e1d886e7 F src/analyze.c 70c46504c0d2543ea5cdca01140b2cd3e1d886e7
F src/attach.c 577bf5675b0c50495fc28549f2fcbdb1bac71143 F src/attach.c 577bf5675b0c50495fc28549f2fcbdb1bac71143
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c d7fb4c6d2ad3fe51a4ce1a897fde7b00f4de5fef F src/backup.c 5524df36810668b3b5a6de2d7e3910b98e721e33
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c f0b71054103cb77eb5e782088c16998ec4f06624 F src/btree.c f0b71054103cb77eb5e782088c16998ec4f06624
@ -145,7 +145,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416 F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
F src/main.c 07e05ba330b5994fa20d3b2e8c1c146133587d68 F src/main.c 02255cf1da50956c5427c469abddb15bccc4ba09
F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6 F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c b3677415e69603d6a0e7c5410a1b3731d55beda1 F src/mem1.c b3677415e69603d6a0e7c5410a1b3731d55beda1
@ -178,9 +178,9 @@ F src/resolve.c b3c70ab28cac60de33684c9aa9e5138dcf71d6dd
F src/rowset.c f6a49f3e9579428024662f6e2931832511f831a1 F src/rowset.c f6a49f3e9579428024662f6e2931832511f831a1
F src/select.c f6c4833c4d8e94714761d99013d74f381e084f1d F src/select.c f6c4833c4d8e94714761d99013d74f381e084f1d
F src/shell.c 74e47ddb99bf7997985dc89bbdd5875637501ad1 F src/shell.c 74e47ddb99bf7997985dc89bbdd5875637501ad1
F src/sqlite.h.in 34640d183e570e81ae1e057a668fdbb55625e802 F src/sqlite.h.in 310ae7e538883fa1619ab0638c775ce11ad43015
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
F src/sqliteInt.h 4e092561a4d9c384c3b9dfeebdef868a3566561c F src/sqliteInt.h 2bc2ebc2ff1a2b530ee5ed9ffd46c6fce93b244c
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208 F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@ -240,8 +240,8 @@ F src/vacuum.c 587a52bb8833d7ac15af8916f25437e2575028bd
F src/vdbe.c f5ad3c06dc3fe647097065829c013f3f1b9eadca F src/vdbe.c f5ad3c06dc3fe647097065829c013f3f1b9eadca
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
F src/vdbeInt.h 6ff4180a05683566a8835d12f7ec504b22932c82 F src/vdbeInt.h 6ff4180a05683566a8835d12f7ec504b22932c82
F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91 F src/vdbeapi.c f8ba09132fe654ffd068058cef490426aca9fca6
F src/vdbeaux.c d52c8a424fdd4b1d5cf1ac93cc7cd20da023ec5c F src/vdbeaux.c dce80038c3c41f2680e5ab4dd0f7e0d8b7ff9071
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74 F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74
F src/vdbesort.c b25814d385895544ebc8118245c8311ded7f81c9 F src/vdbesort.c b25814d385895544ebc8118245c8311ded7f81c9
@ -287,7 +287,7 @@ F test/autovacuum.test fcaf4616ae5bb18098db1cb36262565e5c841c3c
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
F test/backcompat.test bccbc64769d9c755ad65ee7c2f7336b86e3cc0c8 F test/backcompat.test bccbc64769d9c755ad65ee7c2f7336b86e3cc0c8
F test/backup.test 717346db953e9e435c2a94916e4af177330d60d3 F test/backup.test c9cdd23a495864b9edf75a9fa66f5cb7e10fcf62
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0 F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450 F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
@ -1004,7 +1004,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 6131a0002fea5d6f3b416a63a466bf2b69d1e12d 0f706acfc7181f2714a06b2c232677ebeede3a44 P 4cebd60704f92bb3689c126f46feadc3516d3d45 37d9bc061d5ecfe37ccbd6f559986d7ae0449179
R 24446b639b8f30cdeaec2dbb61cdf6f2 R 41d5610ed2033955b4202ff788697dde
U drh U drh
Z b1dc391973e1c634a2e4e6388b5cc239 Z c18db60333d2fd8d859babd08906006b

View File

@ -1 +1 @@
4cebd60704f92bb3689c126f46feadc3516d3d45 fb8893abeefabe9de44e34dcf4327764481189f5

View File

@ -543,14 +543,14 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/ */
int sqlite3_backup_finish(sqlite3_backup *p){ int sqlite3_backup_finish(sqlite3_backup *p){
sqlite3_backup **pp; /* Ptr to head of pagers backup list */ sqlite3_backup **pp; /* Ptr to head of pagers backup list */
MUTEX_LOGIC( sqlite3_mutex *mutex; ) /* Mutex to protect source database */ sqlite3 *pSrcDb; /* Source database connection */
int rc; /* Value to return */ int rc; /* Value to return */
/* Enter the mutexes */ /* Enter the mutexes */
if( p==0 ) return SQLITE_OK; if( p==0 ) return SQLITE_OK;
sqlite3_mutex_enter(p->pSrcDb->mutex); pSrcDb = p->pSrcDb;
sqlite3_mutex_enter(pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc); sqlite3BtreeEnter(p->pSrc);
MUTEX_LOGIC( mutex = p->pSrcDb->mutex; )
if( p->pDestDb ){ if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex); sqlite3_mutex_enter(p->pDestDb->mutex);
} }
@ -576,7 +576,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
/* Exit the mutexes and free the backup context structure. */ /* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){ if( p->pDestDb ){
sqlite3_mutex_leave(p->pDestDb->mutex); sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
} }
sqlite3BtreeLeave(p->pSrc); sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){ if( p->pDestDb ){
@ -585,7 +585,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
** sqlite3_backup_finish(). */ ** sqlite3_backup_finish(). */
sqlite3_free(p); sqlite3_free(p);
} }
sqlite3_mutex_leave(mutex); sqlite3LeaveMutexAndCloseZombie(pSrcDb);
return rc; return rc;
} }

View File

@ -765,13 +765,25 @@ static void disconnectAllVtab(sqlite3 *db){
#endif #endif
} }
/*
** Return TRUE if database connection db has unfinalized prepared
** statements or unfinished sqlite3_backup objects.
*/
static int connectionIsBusy(sqlite3 *db){
int j;
assert( sqlite3_mutex_held(db->mutex) );
if( db->pVdbe ) return 1;
for(j=0; j<db->nDb; j++){
Btree *pBt = db->aDb[j].pBt;
if( pBt && sqlite3BtreeIsInBackup(pBt) ) return 1;
}
return 0;
}
/* /*
** Close an existing SQLite database ** Close an existing SQLite database
*/ */
int sqlite3_close(sqlite3 *db){ static int sqlite3Close(sqlite3 *db, int forceZombie){
HashElem *i; /* Hash table iterator */
int j;
if( !db ){ if( !db ){
return SQLITE_OK; return SQLITE_OK;
} }
@ -792,25 +804,63 @@ int sqlite3_close(sqlite3 *db){
*/ */
sqlite3VtabRollback(db); sqlite3VtabRollback(db);
/* If there are any outstanding VMs, return SQLITE_BUSY. */ /* Legacy behavior (sqlite3_close() behavior) is to return
if( db->pVdbe ){ ** SQLITE_BUSY if the connection can not be closed immediately.
sqlite3Error(db, SQLITE_BUSY, */
"unable to close due to unfinalised statements"); if( !forceZombie && connectionIsBusy(db) ){
sqlite3Error(db, SQLITE_BUSY, "unable to close due to unfinalized "
"statements or unfinished backups");
sqlite3_mutex_leave(db->mutex); sqlite3_mutex_leave(db->mutex);
return SQLITE_BUSY; return SQLITE_BUSY;
} }
assert( sqlite3SafetyCheckSickOrOk(db) );
for(j=0; j<db->nDb; j++){ /* Convert the connection into a zombie and then close it.
Btree *pBt = db->aDb[j].pBt; */
if( pBt && sqlite3BtreeIsInBackup(pBt) ){ db->magic = SQLITE_MAGIC_ZOMBIE;
sqlite3Error(db, SQLITE_BUSY, sqlite3LeaveMutexAndCloseZombie(db);
"unable to close due to unfinished backup operation"); return SQLITE_OK;
sqlite3_mutex_leave(db->mutex); }
return SQLITE_BUSY;
} /*
** Two variations on the public interface for closing a database
** connection. The sqlite3_close() version returns SQLITE_BUSY and
** leaves the connection option if there are unfinalized prepared
** statements or unfinished sqlite3_backups. The sqlite3_close_v2()
** version forces the connection to become a zombie if there are
** unclosed resources, and arranges for deallocation when the last
** prepare statement or sqlite3_backup closes.
*/
int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); }
int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); }
/*
** Close the mutex on database connection db.
**
** Furthermore, if database connection db is a zombie (meaning that there
** has been a prior call to sqlite3_close(db) or sqlite3_close_v2(db)) and
** every sqlite3_stmt has now been finalized and every sqlite3_backup has
** finished, then free all resources.
*/
void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
HashElem *i; /* Hash table iterator */
int j;
/* If there are outstanding sqlite3_stmt or sqlite3_backup objects
** or if the connection has not yet been closed by sqlite3_close_v2(),
** then just leave the mutex and return.
*/
if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){
sqlite3_mutex_leave(db->mutex);
return;
} }
/* If we reach this point, it means that the database connection has
** closed all sqlite3_stmt and sqlite3_backup objects and has been
** pased to sqlite3_close (meaning that it is a zombie). Therefore,
** go ahead and free all resources.
*/
/* Free any outstanding Savepoint structures. */ /* Free any outstanding Savepoint structures. */
sqlite3CloseSavepoints(db); sqlite3CloseSavepoints(db);
@ -898,7 +948,6 @@ int sqlite3_close(sqlite3 *db){
sqlite3_free(db->lookaside.pStart); sqlite3_free(db->lookaside.pStart);
} }
sqlite3_free(db); sqlite3_free(db);
return SQLITE_OK;
} }
/* /*

View File

@ -214,7 +214,8 @@ int sqlite3_threadsafe(void);
** the opaque structure named "sqlite3". It is useful to think of an sqlite3 ** the opaque structure named "sqlite3". It is useful to think of an sqlite3
** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and ** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] ** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
** is its destructor. There are many other interfaces (such as ** and [sqlite3_close_v2()] are its destructors. There are many other
** interfaces (such as
** [sqlite3_prepare_v2()], [sqlite3_create_function()], and ** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
** [sqlite3_busy_timeout()] to name but three) that are methods on an ** [sqlite3_busy_timeout()] to name but three) that are methods on an
** sqlite3 object. ** sqlite3 object.
@ -261,28 +262,46 @@ typedef sqlite_uint64 sqlite3_uint64;
/* /*
** CAPI3REF: Closing A Database Connection ** CAPI3REF: Closing A Database Connection
** **
** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is ** for the [sqlite3] object.
** successfully destroyed and all associated resources are deallocated. ** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if
** the [sqlite3] object is successfully destroyed and all associated
** resources are deallocated.
** **
** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** ^If the database connection is associated with unfinalized prepared
** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** statements or unfinished sqlite3_backup objects then sqlite3_close()
** the [sqlite3] object prior to attempting to close the object. ^If ** will leave the database connection open and return [SQLITE_BUSY].
** ^If sqlite3_close_v2() is called with unfinalized prepared statements
** and unfinished sqlite3_backups, then the database connection becomes
** an unusable "zombie" which will automatically be deallocated when the
** last prepared statement is finalized or the last sqlite3_backup is
** finished. The sqlite3_close_v2() interface is intended for use with
** host languages that are garbage collected, and where the order in which
** destructors are called is arbitrary.
**
** Applications should [sqlite3_finalize | finalize] all [prepared statements],
** [sqlite3_blob_close | close] all [BLOB handles], and
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
** with the [sqlite3] object prior to attempting to close the object. ^If
** sqlite3_close() is called on a [database connection] that still has ** sqlite3_close() is called on a [database connection] that still has
** outstanding [prepared statements] or [BLOB handles], then it returns ** outstanding [prepared statements], [BLOB handles], and/or
** SQLITE_BUSY. ** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
** of resources is deferred until all [prepared statements], [BLOB handles],
** and [sqlite3_backup] objects are also destroyed.
** **
** ^If [sqlite3_close()] is invoked while a transaction is open, ** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back. ** the transaction is automatically rolled back.
** **
** The C parameter to [sqlite3_close(C)] must be either a NULL ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
** must be either a NULL
** pointer or an [sqlite3] object pointer obtained ** pointer or an [sqlite3] object pointer obtained
** from [sqlite3_open()], [sqlite3_open16()], or ** from [sqlite3_open()], [sqlite3_open16()], or
** [sqlite3_open_v2()], and not previously closed. ** [sqlite3_open_v2()], and not previously closed.
** ^Calling sqlite3_close() with a NULL pointer argument is a ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
** harmless no-op. ** argument is a harmless no-op.
*/ */
int sqlite3_close(sqlite3 *); int sqlite3_close(sqlite3*);
int sqlite3_close_v2(sqlite3*);
/* /*
** The type for a callback function. ** The type for a callback function.

View File

@ -983,6 +983,7 @@ struct sqlite3 {
#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ #define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ #define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ #define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */
/* /*
** Each SQL function is defined by an instance of the following ** Each SQL function is defined by an instance of the following
@ -2844,6 +2845,7 @@ void sqlite3CommitTransaction(Parse*);
void sqlite3RollbackTransaction(Parse*); void sqlite3RollbackTransaction(Parse*);
void sqlite3Savepoint(Parse*, int, Token*); void sqlite3Savepoint(Parse*, int, Token*);
void sqlite3CloseSavepoints(sqlite3 *); void sqlite3CloseSavepoints(sqlite3 *);
void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
int sqlite3ExprIsConstant(Expr*); int sqlite3ExprIsConstant(Expr*);
int sqlite3ExprIsConstantNotJoin(Expr*); int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*); int sqlite3ExprIsConstantOrFunction(Expr*);

View File

@ -71,17 +71,12 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
}else{ }else{
Vdbe *v = (Vdbe*)pStmt; Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db; sqlite3 *db = v->db;
#if SQLITE_THREADSAFE
sqlite3_mutex *mutex;
#endif
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT; if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
#if SQLITE_THREADSAFE sqlite3_mutex_enter(db->mutex);
mutex = v->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
rc = sqlite3VdbeFinalize(v); rc = sqlite3VdbeFinalize(v);
if( (rc&0xff)==SQLITE_MISUSE ) rc = SQLITE_OK;
rc = sqlite3ApiExit(db, rc); rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(mutex); sqlite3LeaveMutexAndCloseZombie(db);
} }
return rc; return rc;
} }

View File

@ -2469,6 +2469,7 @@ void sqlite3VdbeDelete(Vdbe *p){
if( NEVER(p==0) ) return; if( NEVER(p==0) ) return;
db = p->db; db = p->db;
assert( sqlite3_mutex_held(db->mutex) );
if( p->pPrev ){ if( p->pPrev ){
p->pPrev->pNext = p->pNext; p->pPrev->pNext = p->pNext;
}else{ }else{

View File

@ -423,7 +423,7 @@ do_test backup-4.3.2 {
} {SQLITE_BUSY} } {SQLITE_BUSY}
do_test backup-4.3.3 { do_test backup-4.3.3 {
sqlite3_errmsg db2 sqlite3_errmsg db2
} {unable to close due to unfinished backup operation} } {unable to close due to unfinalized statements or unfinished backups}
do_test backup-4.3.4 { do_test backup-4.3.4 {
B step 50 B step 50
} {SQLITE_DONE} } {SQLITE_DONE}