diff --git a/manifest b/manifest index 604ec87337..8b9d2a426e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssqlite3_quota_ferror()\sand\ssqlite3_quota_file_available()\sinterfaces\sto\ntest_quota.c.\s\sChange\ssqlite3_quota_fwrite()\sto\suse\sa\sconst\sinput\sbuffer. -D 2012-06-05T13:56:15.352 +C Avoid\sresetting\sthe\sshared-cache\sschema\swhen\son\sof\sthe\sconnections\susing\nthe\sshared\scache\scloses.\s\sDelay\sresetting\sthe\sschema\suntil\sthe\slast\sconnection\ncloses. +D 2012-06-06T19:01:13.928 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 4f37eb61be9d38643cdd839a74b8e3bad724cfcf F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -118,15 +118,15 @@ F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c 149cc80d9257971b0bff34e58fb2263e01998289 F src/analyze.c 70c46504c0d2543ea5cdca01140b2cd3e1d886e7 -F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f +F src/attach.c 577bf5675b0c50495fc28549f2fcbdb1bac71143 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 -F src/backup.c 6be23a344d3301ae38e92fddb3a33b91c309fce4 +F src/backup.c d7fb4c6d2ad3fe51a4ce1a897fde7b00f4de5fef F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c f0b71054103cb77eb5e782088c16998ec4f06624 F src/btree.h 48a013f8964f12d944d90e4700df47b72dd6d923 F src/btreeInt.h 38a639c0542c29fe8331a221c4aed0cb8686249e -F src/build.c 2bb2163bb1e69f59e9f36a9413079ead42fa1d2c +F src/build.c 47c4506afe4bcb4ed1f4b5357582d1cb3402f8ad F src/callback.c 0cb4228cdcd827dcc5def98fb099edcc9142dbcd F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33 @@ -145,7 +145,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 fdd95737fe28db3ad2b740c8a134b40592c6b20a +F src/main.c 41bfe7e14319b44f6322332bce2b48599e524f75 F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c b3677415e69603d6a0e7c5410a1b3731d55beda1 @@ -172,8 +172,8 @@ F src/parse.y f29df90bd3adc64b33114ab1de9fb7768fcf2099 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c b30b1c35908346ecc43d8d9d17f2ddf6817f8f60 -F src/pragma.c e708b3bb5704605816f617e0b1d63a5488060715 -F src/prepare.c 9a00a9612ebf80203fbb41f8a29ab8cb27a05f40 +F src/pragma.c 28d7955a9e9a27d41cb462690228d39e3cec231c +F src/prepare.c 33291b83cca285718048d219c67b8298501fa3a5 F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c b3c70ab28cac60de33684c9aa9e5138dcf71d6dd @@ -182,7 +182,7 @@ F src/select.c f6c4833c4d8e94714761d99013d74f381e084f1d F src/shell.c c16f72e34f611f060546709564c121a67cb2b31b F src/sqlite.h.in 922d2907cc2b0177b2c4a3b462f04937750d6edd F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h 64cffc7ff43725ade0cdfdbab0ef6a56ef3b682f +F src/sqliteInt.h 29b5348f0056d9b46d0bb94d4853db21568afde9 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -194,7 +194,7 @@ F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7 F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013 F src/test6.c 417e1e214734393c24a8ee80b41485a9c4169123 F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843 -F src/test8.c 5ecbffe6712da81d5d10454e9d77d6c5bac95fe8 +F src/test8.c 235f1d19716fa768c46fc461ccbf529b2c9e4399 F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e @@ -238,8 +238,8 @@ F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684 F src/update.c d3076782c887c10e882996550345da9c4c9f9dea F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84 F src/util.c 4f6cfad661b2e3454b0cdd5b1b9d39a54942d0e3 -F src/vacuum.c bfd53f9bd20a8fdb70b0fa8e77182b866875c0d8 -F src/vdbe.c b6cb2ac43263843a5612892c0ad2309609b32c26 +F src/vacuum.c 587a52bb8833d7ac15af8916f25437e2575028bd +F src/vdbe.c 76ae5ef2a0227681c3808ce20fe2d8b194bac634 F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb F src/vdbeInt.h 6ff4180a05683566a8835d12f7ec504b22932c82 F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91 @@ -248,7 +248,7 @@ F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74 F src/vdbesort.c b25814d385895544ebc8118245c8311ded7f81c9 F src/vdbetrace.c 79059ebd17b3c8545fab2a24253713e77e4ab392 -F src/vtab.c ae657b1c22cff43863458e768a44f915c07bc0e4 +F src/vtab.c bb8ea3a26608bb1357538a5d2fc72beba6638998 F src/wal.c 7bb3ad807afc7973406c805d5157ec7a2f65e146 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f @@ -314,7 +314,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 @@ -695,6 +695,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 6e3013bc50e2b73f00d17e491f776decc82a71c8 @@ -906,7 +907,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b F test/view.test b182a67ec43f490b156b5a710827a341be83dd17 -F test/vtab1.test e429a6835faa3870016c55d1178dcfead85f936a +F test/vtab1.test 331ca61ba0b5df4d2f2e55ee2b9596fde4228dbe F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 @@ -1004,7 +1005,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh a8a0a3babda96dfb1ff51adda3cbbf3dfb7266c2 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P 70c419a434be77b042a23174483d6a411899eb5d -R 6f34bdd03e1922ea3b6cb56fd9309a2c +P 61669c95859e187618fb2fb4249306a947ae8d26 c469850b2eb61a63150cc5fc7d2fe98f0b5abffb +R 4e8ba0ec11cbdf49789a46888b404344 U drh -Z 1b017ff1370375c5086bff54a1d45078 +Z 7052addf7652de59fa1313dd54313af2 diff --git a/manifest.uuid b/manifest.uuid index 9d397b734f..c20062a6ca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -61669c95859e187618fb2fb4249306a947ae8d26 \ No newline at end of file +635e3a762ddeb1f952f66a08c1d4d53e3f42c9eb \ No newline at end of file diff --git a/src/attach.c b/src/attach.c index 18f8823b31..e295c30111 100644 --- a/src/attach.c +++ b/src/attach.c @@ -216,7 +216,7 @@ static void attachFunc( db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; @@ -288,7 +288,7 @@ static void detachFunc( sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); return; detach_error: diff --git a/src/backup.c b/src/backup.c index 7a4047f34c..0ada33c3be 100644 --- a/src/backup.c +++ b/src/backup.c @@ -414,7 +414,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1); if( rc==SQLITE_OK ){ if( p->pDestDb ){ - sqlite3ResetInternalSchema(p->pDestDb, -1); + sqlite3ResetAllSchemasOfConnection(p->pDestDb); } if( destMode==PAGER_JOURNALMODE_WAL ){ rc = sqlite3BtreeSetVersion(p->pDest, 2); diff --git a/src/build.c b/src/build.c index 31190f6fe7..9f13b7b11c 100644 --- a/src/build.c +++ b/src/build.c @@ -394,58 +394,15 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ } /* -** Erase all schema information from the in-memory hash tables of -** a single database. This routine is called to reclaim memory -** before the database closes. It is also called during a rollback -** if there were schema changes during the transaction or if a -** schema-cookie mismatch occurs. +** Look through the list of open database files in db->aDb[] and if +** any have been closed, remove them from the list. Reallocate the +** db->aDb[] structure to a smaller size, if possible. ** -** If iDb<0 then reset the internal schema tables for all database -** files. If iDb>=0 then reset the internal schema for only the -** single file indicated. +** Entry 0 (the "main" database) and entry 1 (the "temp" database) +** are never candidates for being collapsed. */ -void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ +void sqlite3CollapseDatabaseArray(sqlite3 *db){ int i, j; - assert( iDbnDb ); - - if( iDb>=0 ){ - /* Case 1: Reset the single schema identified by iDb */ - Db *pDb = &db->aDb[iDb]; - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - assert( pDb->pSchema!=0 ); - sqlite3SchemaClear(pDb->pSchema); - - /* If any database other than TEMP is reset, then also reset TEMP - ** since TEMP might be holding triggers that reference tables in the - ** other database. - */ - if( iDb!=1 ){ - pDb = &db->aDb[1]; - assert( pDb->pSchema!=0 ); - sqlite3SchemaClear(pDb->pSchema); - } - return; - } - /* Case 2 (from here to the end): Reset all schemas for all attached - ** databases. */ - assert( iDb<0 ); - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - Db *pDb = &db->aDb[i]; - if( pDb->pSchema ){ - sqlite3SchemaClear(pDb->pSchema); - } - } - db->flags &= ~SQLITE_InternChanges; - sqlite3VtabUnlockList(db); - sqlite3BtreeLeaveAll(db); - - /* If one or more of the auxiliary database files has been closed, - ** then remove them from the auxiliary database list. We take the - ** opportunity to do this here since we have just deleted all of the - ** schema hash tables and therefore do not have to make any changes - ** to any of those tables. - */ for(i=j=2; inDb; i++){ struct Db *pDb = &db->aDb[i]; if( pDb->pBt==0 ){ @@ -467,6 +424,51 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ } } +/* +** Reset the schema for the database at index iDb. Also reset the +** TEMP schema. +*/ +void sqlite3ResetOneSchema(sqlite3 *db, int iDb){ + Db *pDb; + assert( iDbnDb ); + + /* Case 1: Reset the single schema identified by iDb */ + pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + assert( pDb->pSchema!=0 ); + sqlite3SchemaClear(pDb->pSchema); + + /* If any database other than TEMP is reset, then also reset TEMP + ** since TEMP might be holding triggers that reference tables in the + ** other database. + */ + if( iDb!=1 ){ + pDb = &db->aDb[1]; + assert( pDb->pSchema!=0 ); + sqlite3SchemaClear(pDb->pSchema); + } + return; +} + +/* +** Erase all schema information from all attached databases (including +** "main" and "temp") for a single database connection. +*/ +void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){ + int i; + sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ + Db *pDb = &db->aDb[i]; + if( pDb->pSchema ){ + sqlite3SchemaClear(pDb->pSchema); + } + } + db->flags &= ~SQLITE_InternChanges; + sqlite3VtabUnlockList(db); + sqlite3BtreeLeaveAll(db); + sqlite3CollapseDatabaseArray(db); +} + /* ** This routine is called when a commit occurs. */ @@ -2763,7 +2765,7 @@ Index *sqlite3CreateIndex( }else{ zColl = pTab->aCol[j].zColl; if( !zColl ){ - zColl = db->pDfltColl->zName; + zColl = "BINARY"; } } if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){ diff --git a/src/main.c b/src/main.c index 4e9a605f40..f1ac2542d9 100644 --- a/src/main.c +++ b/src/main.c @@ -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; inDb; 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 @@ -769,6 +793,7 @@ int sqlite3_close(sqlite3 *db){ /* Free any outstanding Savepoint structures. */ sqlite3CloseSavepoints(db); + /* Close all database connections */ for(j=0; jnDb; j++){ struct Db *pDb = &db->aDb[j]; if( pDb->pBt ){ @@ -779,15 +804,22 @@ int sqlite3_close(sqlite3 *db){ } } } - sqlite3ResetInternalSchema(db, -1); + /* Clear the TEMP schema separately and last */ + if( db->aDb[1].pSchema ){ + sqlite3SchemaClear(db->aDb[1].pSchema); + } + sqlite3VtabUnlockList(db); + + /* Free up the array of auxiliary databases */ + sqlite3CollapseDatabaseArray(db); + 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; jaFunc.a); j++){ FuncDef *pNext, *pHash, *p; for(p=db->aFunc.a[j]; p; p=pHash){ @@ -874,7 +906,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){ if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); } /* Any deferred constraint violations have now been resolved. */ diff --git a/src/pragma.c b/src/pragma.c index 2db0b61508..3401c73c1b 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -118,7 +118,7 @@ static int invalidateTempStorage(Parse *pParse){ } sqlite3BtreeClose(db->aDb[1].pBt); db->aDb[1].pBt = 0; - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); } return SQLITE_OK; } diff --git a/src/prepare.c b/src/prepare.c index c46e55ed2c..bd2cf7aafc 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -262,7 +262,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; if( encoding==0 ) encoding = SQLITE_UTF8; ENC(db) = encoding; - db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0); }else{ /* If opening an attached database, the encoding much match ENC(db) */ if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){ @@ -342,7 +341,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } if( db->mallocFailed ){ rc = SQLITE_NOMEM; - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider @@ -395,7 +394,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; rc = sqlite3InitOne(db, i, pzErrMsg); if( rc ){ - sqlite3ResetInternalSchema(db, i); + sqlite3ResetOneSchema(db, i); } } @@ -408,7 +407,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ && !DbHasProperty(db, 1, DB_SchemaLoaded) ){ rc = sqlite3InitOne(db, 1, pzErrMsg); if( rc ){ - sqlite3ResetInternalSchema(db, 1); + sqlite3ResetOneSchema(db, 1); } } #endif @@ -476,7 +475,7 @@ static void schemaIsValid(Parse *pParse){ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ - sqlite3ResetInternalSchema(db, iDb); + sqlite3ResetOneSchema(db, iDb); pParse->rc = SQLITE_SCHEMA; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 85dabb0b39..e696e917ee 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2709,7 +2709,9 @@ void sqlite3ExprListDelete(sqlite3*, ExprList*); int sqlite3Init(sqlite3*, char**); int sqlite3InitCallback(void*, int, char**, char**); void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); -void sqlite3ResetInternalSchema(sqlite3*, int); +void sqlite3ResetAllSchemasOfConnection(sqlite3*); +void sqlite3ResetOneSchema(sqlite3*,int); +void sqlite3CollapseDatabaseArray(sqlite3*); void sqlite3BeginParse(Parse*,int); void sqlite3CommitInternalChanges(sqlite3*); Table *sqlite3ResultSetOfSelect(Parse*,Select*); @@ -3114,6 +3116,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); diff --git a/src/test8.c b/src/test8.c index ba7e37372c..372eef664a 100644 --- a/src/test8.c +++ b/src/test8.c @@ -1300,6 +1300,7 @@ static sqlite3_module echoModuleV2 = { ** Decode a pointer to an sqlite3 object. */ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); +extern const char *sqlite3TestErrorName(int rc); static void moduleDestroy(void *p){ sqlite3_free(p); @@ -1314,6 +1315,7 @@ static int register_echo_module( int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ + int rc; sqlite3 *db; EchoModule *pMod; if( objc!=2 ){ @@ -1325,14 +1327,20 @@ static int register_echo_module( /* Virtual table module "echo" */ pMod = sqlite3_malloc(sizeof(EchoModule)); pMod->interp = interp; - sqlite3_create_module_v2(db, "echo", &echoModule, (void*)pMod, moduleDestroy); + rc = sqlite3_create_module_v2( + db, "echo", &echoModule, (void*)pMod, moduleDestroy + ); /* Virtual table module "echo_v2" */ - pMod = sqlite3_malloc(sizeof(EchoModule)); - pMod->interp = interp; - sqlite3_create_module_v2(db, "echo_v2", - &echoModuleV2, (void*)pMod, moduleDestroy - ); + if( rc==SQLITE_OK ){ + pMod = sqlite3_malloc(sizeof(EchoModule)); + pMod->interp = interp; + rc = sqlite3_create_module_v2(db, "echo_v2", + &echoModuleV2, (void*)pMod, moduleDestroy + ); + } + + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } diff --git a/src/vacuum.c b/src/vacuum.c index c03b4500b3..401d41dfb7 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -339,7 +339,7 @@ end_of_vacuum: /* This both clears the schemas and reduces the size of the db->aDb[] ** array. */ - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); return rc; } diff --git a/src/vdbe.c b/src/vdbe.c index ec4ea2a55d..2432d5b3a7 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2747,7 +2747,7 @@ case OP_Savepoint: { } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, -1); + sqlite3ResetAllSchemasOfConnection(db); db->flags = (db->flags | SQLITE_InternChanges); } } @@ -3051,7 +3051,7 @@ case OP_VerifyCookie: { ** a v-table method. */ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ - sqlite3ResetInternalSchema(db, pOp->p1); + sqlite3ResetOneSchema(db, pOp->p1); } p->expired = 1; @@ -4864,7 +4864,7 @@ case OP_ParseSchema: { db->init.busy = 0; } } - if( rc ) sqlite3ResetInternalSchema(db, -1); + if( rc ) sqlite3ResetAllSchemasOfConnection(db); if( rc==SQLITE_NOMEM ){ goto no_mem; } @@ -6159,7 +6159,7 @@ vdbe_error_halt: if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; if( resetSchemaOnFault>0 ){ - sqlite3ResetInternalSchema(db, resetSchemaOnFault-1); + sqlite3ResetOneSchema(db, resetSchemaOnFault-1); } /* This is the only way out of this procedure. We have to diff --git a/src/vtab.c b/src/vtab.c index c561f7198f..0e082a05d9 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -22,8 +22,8 @@ ** are invoked only from within xCreate and xConnect methods. */ struct VtabCtx { - Table *pTab; - VTable *pVTable; + VTable *pVTable; /* The virtual table being constructed */ + Table *pTab; /* The Table object to which the virtual table belongs */ }; /* @@ -38,33 +38,35 @@ static int createModule( void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ ){ - int rc, nName; - Module *pMod; + int rc = SQLITE_OK; + int nName; sqlite3_mutex_enter(db->mutex); nName = sqlite3Strlen30(zName); - pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1); - if( pMod ){ - Module *pDel; - char *zCopy = (char *)(&pMod[1]); - memcpy(zCopy, zName, nName+1); - pMod->zName = zCopy; - pMod->pModule = pModule; - pMod->pAux = pAux; - pMod->xDestroy = xDestroy; - pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod); - if( pDel && pDel->xDestroy ){ - sqlite3ResetInternalSchema(db, -1); - pDel->xDestroy(pDel->pAux); + if( sqlite3HashFind(&db->aModule, zName, nName) ){ + rc = SQLITE_MISUSE_BKPT; + }else{ + Module *pMod; + pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1); + if( pMod ){ + Module *pDel; + char *zCopy = (char *)(&pMod[1]); + memcpy(zCopy, zName, nName+1); + pMod->zName = zCopy; + pMod->pModule = pModule; + pMod->pAux = pAux; + pMod->xDestroy = xDestroy; + pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,nName,(void*)pMod); + assert( pDel==0 || pDel==pMod ); + if( pDel ){ + db->mallocFailed = 1; + sqlite3DbFree(db, pDel); + } } - sqlite3DbFree(db, pDel); - if( pDel==pMod ){ - db->mallocFailed = 1; - } - }else if( xDestroy ){ - xDestroy(pAux); } - rc = sqlite3ApiExit(db, SQLITE_OK); + rc = sqlite3ApiExit(db, rc); + if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux); + sqlite3_mutex_leave(db->mutex); return rc; } @@ -180,6 +182,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. diff --git a/test/capi3.test b/test/capi3.test index d9106267c5..b84ddbbf1a 100644 --- a/test/capi3.test +++ b/test/capi3.test @@ -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 index 0000000000..600e02bf7e --- /dev/null +++ b/test/shared8.test @@ -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 + diff --git a/test/vtab1.test b/test/vtab1.test index 38aec09eae..9fb843f754 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -1275,4 +1275,18 @@ foreach {tn sql res filter} { } do_execsql_test 18.2.x { PRAGMA case_sensitive_like = OFF } +#------------------------------------------------------------------------- +# Test that an existing module may not be overridden. +# +do_test 19.1 { + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] +} SQLITE_OK +do_test 19.2 { + register_echo_module [sqlite3_connection_pointer db2] +} SQLITE_MISUSE +do_test 19.3 { + db2 close +} {} + finish_test