When a connection disconnects from a shared-cache database, only delete the in-memory schema if there are no other connections.

FossilOrigin-Name: 46f4eb5430d7bc9a339cdf7124ff4bd518eaa39b
This commit is contained in:
dan 2012-05-15 17:15:34 +00:00
parent 29ddd3acdd
commit bba02a95d9
7 changed files with 193 additions and 18 deletions

@ -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

@ -1 +1 @@
736d6ea677f58e4aa2914fa79a3156b775c5a3f5
46f4eb5430d7bc9a339cdf7124ff4bd518eaa39b

@ -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){

@ -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);

@ -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.

@ -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

113
test/shared8.test Normal file

@ -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