aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backup.c7
-rw-r--r--src/main.c51
-rw-r--r--src/sqlite.h.in13
-rw-r--r--src/sqliteInt.h2
-rw-r--r--src/vdbeapi.c11
-rw-r--r--src/vdbeaux.c1
6 files changed, 54 insertions, 31 deletions
diff --git a/src/backup.c b/src/backup.c
index 7a4047f34..6655ee391 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -543,14 +543,13 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
int sqlite3_backup_finish(sqlite3_backup *p){
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
- MUTEX_LOGIC( sqlite3_mutex *mutex; ) /* Mutex to protect source database */
+ sqlite3 *pSrcDb = p->pSrcDb; /* Source database connection */
int rc; /* Value to return */
/* Enter the mutexes */
if( p==0 ) return SQLITE_OK;
sqlite3_mutex_enter(p->pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
- MUTEX_LOGIC( mutex = p->pSrcDb->mutex; )
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
@@ -576,7 +575,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
/* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){
- sqlite3_mutex_leave(p->pDestDb->mutex);
+ sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){
@@ -585,7 +584,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
** sqlite3_backup_finish(). */
sqlite3_free(p);
}
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(pSrcDb);
return rc;
}
diff --git a/src/main.c b/src/main.c
index 4e9a605f4..d0aebd5ee 100644
--- a/src/main.c
+++ b/src/main.c
@@ -703,6 +703,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){
db->isTransactionSavepoint = 0;
}
+
/*
** Invoke the destructor function associated with FuncDef p, if any. Except,
** if this is not the last copy of the function, do not invoke it. Multiple
@@ -724,9 +725,6 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){
- HashElem *i; /* Hash table iterator */
- int j;
-
if( !db ){
return SQLITE_OK;
}
@@ -747,25 +745,51 @@ int sqlite3_close(sqlite3 *db){
*/
sqlite3VtabRollback(db);
- /* If there are any outstanding VMs, return SQLITE_BUSY. */
- if( db->pVdbe ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinalised statements");
+ /*
+ ** Mark this database connection as a zombie. Then try to close it.
+ */
+ db->magic = SQLITE_MAGIC_ZOMBIE;
+ sqlite3LeaveMutexAndCloseZombie(db);
+ return SQLITE_OK;
+}
+
+
+/*
+** 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)) 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;
+
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ /* If there are outstanding sqlite3_stmt or sqlite3_backup objects
+ ** or if the connection has not yet been closed by sqlite3_close, then
+ ** just leave the mutex and return.
+ */
+ if( db->pVdbe || db->magic!=SQLITE_MAGIC_ZOMBIE ){
sqlite3_mutex_leave(db->mutex);
- return SQLITE_BUSY;
+ return;
}
- assert( sqlite3SafetyCheckSickOrOk(db) );
-
for(j=0; j<db->nDb; j++){
Btree *pBt = db->aDb[j].pBt;
if( pBt && sqlite3BtreeIsInBackup(pBt) ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinished backup operation");
sqlite3_mutex_leave(db->mutex);
- return SQLITE_BUSY;
+ 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. */
sqlite3CloseSavepoints(db);
@@ -845,7 +869,6 @@ int sqlite3_close(sqlite3 *db){
sqlite3_free(db->lookaside.pStart);
}
sqlite3_free(db);
- return SQLITE_OK;
}
/*
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index e96c19680..ce07c386f 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -265,12 +265,15 @@ typedef sqlite_uint64 sqlite3_uint64;
** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
** successfully destroyed and all associated resources are deallocated.
**
-** Applications must [sqlite3_finalize | finalize] all [prepared statements]
-** and [sqlite3_blob_close | close] all [BLOB handles] associated with
-** the [sqlite3] object prior to attempting to close the object. ^If
+** 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
-** outstanding [prepared statements] or [BLOB handles], then it returns
-** SQLITE_BUSY.
+** outstanding [prepared statements], [BLOB handles], and/or
+** [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,
** the transaction is automatically rolled back.
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 85dabb0b3..9732c8f22 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -974,6 +974,7 @@ struct sqlite3 {
#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#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
@@ -2833,6 +2834,7 @@ void sqlite3CommitTransaction(Parse*);
void sqlite3RollbackTransaction(Parse*);
void sqlite3Savepoint(Parse*, int, Token*);
void sqlite3CloseSavepoints(sqlite3 *);
+void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
int sqlite3ExprIsConstant(Expr*);
int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*);
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 94db205e1..e25acd944 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -71,17 +71,12 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex;
-#endif
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
-#if SQLITE_THREADSAFE
- mutex = v->db->mutex;
-#endif
- sqlite3_mutex_enter(mutex);
+ sqlite3_mutex_enter(db->mutex);
rc = sqlite3VdbeFinalize(v);
+ if( (rc&0xff)==SQLITE_MISUSE ) rc = SQLITE_OK;
rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(db);
}
return rc;
}
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index caa2bf670..3ccf71161 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -2469,6 +2469,7 @@ void sqlite3VdbeDelete(Vdbe *p){
if( NEVER(p==0) ) return;
db = p->db;
+ assert( sqlite3_mutex_held(db->mutex) );
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{