]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When a connection disconnects from a shared-cache database, only delete the in-memory...
authordan <dan@noemail.net>
Tue, 15 May 2012 17:15:34 +0000 (17:15 +0000)
committerdan <dan@noemail.net>
Tue, 15 May 2012 17:15:34 +0000 (17:15 +0000)
FossilOrigin-Name: 46f4eb5430d7bc9a339cdf7124ff4bd518eaa39b

manifest
manifest.uuid
src/main.c
src/sqliteInt.h
src/vtab.c
test/capi3.test
test/shared8.test [new file with mode: 0644]

index 68138a320dc6b2c246aaf4c74735ffd4134f2707..ea85d284ac689f0e01fae737363b89d3552f3155 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sassert()s\sto\sverify\sthat\sTable\sobjects\sin\sthe\sschema\snever\suse\nlookaside\smemory.
-D 2012-05-15T12:49:32.294
+C When\sa\sconnection\sdisconnects\sfrom\sa\sshared-cache\sdatabase,\sonly\sdelete\sthe\sin-memory\sschema\sif\sthere\sare\sno\sother\sconnections.
+D 2012-05-15T17:15:34.812
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 2f37e468503dbe79d35c9f6dffcf3fae1ae9ec20
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -140,7 +140,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
 F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
-F src/main.c 91458c713e9b7f8dbc98d79e78f1150f0ca9c2a1
+F src/main.c 62146c65069408bc66ad72954655c9cc14bd8f01
 F src/malloc.c 15afac5e59b6584efe072e9933aefb4230e74f97
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c b3677415e69603d6a0e7c5410a1b3731d55beda1
@@ -177,7 +177,7 @@ F src/select.c d7b9018b7dd2e821183d69477ab55c39b8272335
 F src/shell.c 04399b2f9942bd02ed5ffee3b84bcdb39c52a1e6
 F src/sqlite.h.in 4f4d4792f6fb00387c877af013cb09d955643f12
 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
-F src/sqliteInt.h c5e917c4f1453f3972b1fd0c81105dfe4f09cc32
+F src/sqliteInt.h cef468b8f16c847c235cb9a3d06253389abe3bc9
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -243,7 +243,7 @@ F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
 F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74
 F src/vdbesort.c b25814d385895544ebc8118245c8311ded7f81c9
 F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
-F src/vtab.c ae657b1c22cff43863458e768a44f915c07bc0e4
+F src/vtab.c 1fbe133809ae542a9cf3a3d8187774b68436fa85
 F src/wal.c 7bb3ad807afc7973406c805d5157ec7a2f65e146
 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
@@ -309,7 +309,7 @@ F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
 F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
 F test/cache.test f64136b0893c293d0b910ed057b3b711249099a7
 F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
-F test/capi3.test 8dedb0050610e9ff95cd9d487beb0ce5f33a31ee
+F test/capi3.test 8a33b82c4a2469977aed91b6eb99ae3ca1546444
 F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
 F test/capi3c.test 01f197d73f4d4d66316483662f475cab7ab5bd60
 F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
@@ -688,6 +688,7 @@ F test/shared3.test ebf77f023f4bdaa8f74f65822b559e86ce5c6257
 F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d
 F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9
 F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
+F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d
 F test/shared_err.test 91e26ec4f3fbe07951967955585137e2f18993de
 F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
 F test/shell1.test cd9f846702d1d471225a988fee590a153be8192c
@@ -997,7 +998,10 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh a8a0a3babda96dfb1ff51adda3cbbf3dfb7266c2
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P 0bb1cfc63f982db7b29c8e6be6698a6dad100f70
-R 040b7c6409bc9c8d46165baae8f02a6f
-U drh
-Z 46fb04131884f3e5ee0e8870b1bd5af5
+P 736d6ea677f58e4aa2914fa79a3156b775c5a3f5
+R cdb1dff646b0010ea62b89ecb700e74c
+T *branch * shared-schema
+T *sym-shared-schema *
+T -sym-trunk *
+U dan
+Z 9910d0bfd0815b5cd0d4b52750333b7f
index d1f1a9b991f2edb81e95abe9314c392849d00570..917ae6d0b9eae2d80d50a61469c399c85b0afd40 100644 (file)
@@ -1 +1 @@
-736d6ea677f58e4aa2914fa79a3156b775c5a3f5
\ No newline at end of file
+46f4eb5430d7bc9a339cdf7124ff4bd518eaa39b
\ No newline at end of file
index d148b4b42cd4bd1170a515fd7865ce6a03c299c2..869fbb376b04bb3dbb1d91f4724bad745065c043 100644 (file)
@@ -720,6 +720,30 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
   }
 }
 
+/*
+** 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
 */
@@ -735,10 +759,10 @@ int sqlite3_close(sqlite3 *db){
   }
   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 @@ int sqlite3_close(sqlite3 *db){
       }
     }
   }
+
+  /* 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){
index cb178ff97cc6a116ae4afd16fe00756c9d011d11..cefff07ec54ae0b91f72631a9eec1054c9d0955b 100644 (file)
@@ -3101,6 +3101,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
 #  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);
index c561f7198f866590f59885fc696b3e2ba9da5ba7..f660b5443953ece9517d8c1f6e86d94e233c62f7 100644 (file)
@@ -180,6 +180,31 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
   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.
index d9106267c5feda58589b93208fcdceaacd68636c..b84ddbbf1a8d5393787a2c4154de37f570e985ab 100644 (file)
@@ -649,13 +649,18 @@ do_test capi3-6.1 {
   db cache flush
   sqlite3_close $DB
 } {SQLITE_BUSY}
+
+# 6.2 and 6.3 used to return SQLITE_ERROR and SQLITE_SCHEMA, respectively.
+# But since attempting to close a connection no longer resets the internal
+# schema and expires all statements, this is no longer the case.
 do_test capi3-6.2 {
   sqlite3_step $STMT
-} {SQLITE_ERROR}
+} {SQLITE_ROW}
 #check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1}
 do_test capi3-6.3 {
   sqlite3_finalize $STMT
-} {SQLITE_SCHEMA}
+} {SQLITE_OK}
+
 do_test capi3-6.4-misuse {
   db cache flush
   sqlite3_close $DB
diff --git a/test/shared8.test b/test/shared8.test
new file mode 100644 (file)
index 0000000..600e02b
--- /dev/null
@@ -0,0 +1,113 @@
+# 2012 May 15
+#
+# 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.
+#
+#***********************************************************************
+#
+# The tests in this file are intended to show that closing one database
+# connection to a shared-cache while there exist other connections (a)
+# does not cause the schema to be reloaded and (b) does not cause any
+# other problems.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable !shared_cache { finish_test ; return }
+set testprefix shared8
+
+db close
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+do_test 0.0 { sqlite3_enable_shared_cache } {1}
+
+proc roman {n} {
+  array set R {1 i 2 ii 3 iii 4 iv 5 v 6 vi 7 vii 8 viii 9 ix 10 x}
+  set R($n)
+}
+
+#-------------------------------------------------------------------------
+# The following tests work as follows:
+#
+#    1.0: Open connection [db1] and populate the database.
+#
+#    1.1: Using "PRAGMA writable_schema", destroy the database schema on
+#         disk. The schema is still in memory, so it is possible to keep
+#         using it, but any attempt to reload it from disk will fail.
+#
+#    1.3-4: Open connection db2. Check that it can see the db schema. Then
+#           close db1 and check that db2 still works. This shows that closing
+#           db1 did not reset the in-memory schema.
+#
+#    1.5-7: Similar to 1.3-4.
+#
+#    1.8: Close all database connections (deleting the in-memory schema).
+#         Then open a new connection and check that it cannot read the db.
+#         
+do_test 1.0 {
+  sqlite3 db1 test.db
+  db1 func roman roman
+  execsql {
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1 VALUES(1, 1);
+    INSERT INTO t1 VALUES(2, 2);
+    INSERT INTO t1 VALUES(3, 3);
+    INSERT INTO t1 VALUES(4, 4);
+    CREATE VIEW v1 AS SELECT a, roman(b) FROM t1;
+    SELECT * FROM v1;
+  } db1
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.1 {
+  execsql { 
+    PRAGMA writable_schema = 1;
+    DELETE FROM sqlite_master WHERE 1;
+    PRAGMA writable_schema = 0;
+    SELECT * FROM sqlite_master;
+  } db1
+} {}
+
+do_test 1.2 {
+  execsql { SELECT * FROM v1 } db1
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.3 {
+  sqlite3 db2 test.db
+  db2 func roman roman
+  execsql { SELECT * FROM v1 } db2
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.4 {
+  db1 close
+  execsql { SELECT * FROM v1 } db2
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.5 {
+  sqlite3 db3 test.db
+  db3 func roman roman
+  execsql { SELECT * FROM v1 } db3
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.6 {
+  execsql { SELECT * FROM v1 } db2
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.7 {
+  db2 close
+  execsql { SELECT * FROM v1 } db3
+} {1 i 2 ii 3 iii 4 iv}
+
+do_test 1.8 {
+  db3 close
+  sqlite3 db4 test.db
+  catchsql { SELECT * FROM v1 } db4
+} {1 {no such table: v1}}
+
+
+foreach db {db1 db2 db3 db4} { catch { $db close } }
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
+