When a connection disconnects from a shared-cache database, only delete the in-memory schema if there are no other connections.
FossilOrigin-Name: 46f4eb5430d7bc9a339cdf7124ff4bd518eaa39b
diff --git a/src/main.c b/src/main.c
index d148b4b..869fbb3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -721,6 +721,30 @@
}
/*
+** Disconnect all sqlite3_vtab objects that belong to database connection
+** db. This is called when db is being closed.
+*/
+static void disconnectAllVtab(sqlite3 *db){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ int i;
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ Schema *pSchema = db->aDb[i].pSchema;
+ if( db->aDb[i].pSchema ){
+ HashElem *p;
+ for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
+ Table *pTab = (Table *)sqliteHashData(p);
+ if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
+ }
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+#else
+ UNUSED_PARAMETER(db);
+#endif
+}
+
+/*
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){
@@ -735,10 +759,10 @@
}
sqlite3_mutex_enter(db->mutex);
- /* Force xDestroy calls on all virtual tables */
- sqlite3ResetInternalSchema(db, -1);
+ /* Force xDisconnect calls on all virtual tables */
+ disconnectAllVtab(db);
- /* If a transaction is open, the ResetInternalSchema() call above
+ /* If a transaction is open, the disconnectAllVtab() call above
** will not have called the xDisconnect() method on any virtual
** tables in the db->aVTrans[] array. The following sqlite3VtabRollback()
** call will do so. We need to do this before the check for active
@@ -779,15 +803,18 @@
}
}
}
+
+ /* This call frees the schema associated with the temp database only (if
+ ** any). It also frees the db->aDb array, if required. */
sqlite3ResetInternalSchema(db, -1);
+ assert( db->nDb<=2 );
+ assert( db->aDb==db->aDbStatic );
/* Tell the code in notify.c that the connection no longer holds any
** locks and does not require any further unlock-notify callbacks.
*/
sqlite3ConnectionClosed(db);
- assert( db->nDb<=2 );
- assert( db->aDb==db->aDbStatic );
for(j=0; j<ArraySize(db->aFunc.a); j++){
FuncDef *pNext, *pHash, *p;
for(p=db->aFunc.a[j]; p; p=pHash){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index cb178ff..cefff07 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3101,6 +3101,7 @@
# define sqlite3GetVTable(X,Y) ((VTable*)0)
#else
void sqlite3VtabClear(sqlite3 *db, Table*);
+ void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
int sqlite3VtabSync(sqlite3 *db, char **);
int sqlite3VtabRollback(sqlite3 *db);
int sqlite3VtabCommit(sqlite3 *db);
diff --git a/src/vtab.c b/src/vtab.c
index c561f71..f660b54 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -180,6 +180,31 @@
return pRet;
}
+/*
+** Table *p is a virtual table. This function removes the VTable object
+** for table *p associated with database connection db from the linked
+** list in p->pVTab. It also decrements the VTable ref count. This is
+** used when closing database connection db to free all of its VTable
+** objects without disturbing the rest of the Schema object (which may
+** be being used by other shared-cache connections).
+*/
+void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
+ VTable **ppVTab;
+
+ assert( IsVirtual(p) );
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){
+ if( (*ppVTab)->db==db ){
+ VTable *pVTab = *ppVTab;
+ *ppVTab = pVTab->pNext;
+ sqlite3VtabUnlock(pVTab);
+ break;
+ }
+ }
+}
+
/*
** Disconnect all the virtual table objects in the sqlite3.pDisconnect list.