From: dan Date: Sat, 2 Feb 2019 21:02:22 +0000 (+0000) Subject: Try new approach ensuring that each Schema object is only used by one connection... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e54087db7b580aafb1fe1d884e754b26c9858623;p=thirdparty%2Fsqlite.git Try new approach ensuring that each Schema object is only used by one connection/database at any one time. FossilOrigin-Name: 9e8e5f52cf90579c3071f04a3c6857bb76b4ca1b1900a7f865de789ed0bb8678 --- diff --git a/manifest b/manifest index ec694b92e2..0484dfe6e6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\swith\sthis\sbranch. -D 2018-11-09T18:44:45.344 +C Try\snew\sapproach\sensuring\sthat\seach\sSchema\sobject\sis\sonly\sused\sby\sone\sconnection/database\sat\sany\sone\stime. +D 2019-02-02T21:02:22.247 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in edbb6e20bb1decf65f6c64c9e61004a69bdf8afb39cdce5337c916b03dfcd1e3 @@ -448,8 +448,8 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 3ef104ecae8b1b5f0458be1f5fa7c1ecf25fdc322a9d63bb8151f89eb32d381e F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96 -F src/build.c b48fa5d9eef7fadb84e7f8fd2fe3537cfb5fe56d4da43c3cf8bb61a75f369ef7 -F src/callback.c d102423552dc74edd242c92c5f74c88ac2537e47bb16a2a043929cd394653710 +F src/build.c 8b9dfd5eb548b46cf7e8a1094b9c96787d961efaadbac888e897e686e62e31bf +F src/callback.c 0379d1783b62d21a3eb5e8498ecf4269971a2fa1f5b48e77479075a6e05651ce F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 109e58d00f62e8e71ee1eb5944ac18b90171c928ab2e082e058056e1137cc20b F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957 @@ -468,7 +468,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 6b81aae27b196925d8ff78824f4bbd435d6a40cd38dc324685e21735bb402109 F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e F src/loadext.c 448eab53ecdb566a1259ee2d45ebff9c0bc4a2cf393774488775c33e4fbe89bf -F src/main.c 2bbab4122a28c10ff98e5497a15fdfde1d7a075a659739448a61ef0c09a30d19 +F src/main.c 75e429a1130d7c32e31bb43a86240e5e50e0414d7330367c2bacd92842651dcb F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -499,19 +499,19 @@ F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 F src/pragma.c 0bb05b1788d7af5fdc7f40f5655a79a140dece8fd2523402eb2103925e4739c2 F src/pragma.h fdd03d78a7497f74a3f652909f945328480089189526841ae829ce7313d98d13 -F src/prepare.c d728e4031435277e25769bf27ce90a6c675b9c6aef4b2641b31e9604e23c6340 +F src/prepare.c a1f7b15ec6e91602ca8193cf322cee443417995577d8543cfc07b71258d7b745 F src/printf.c 0f1177cf1dd4d7827bf64d840768514ec76409abecaca9e8b577dbd065150381 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c bc8c79e56439b111e7d9415e44940951f7087e9466c3a9d664558ef0faf31073 F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93 F src/select.c 61e867a906f140b73baf4ce7a201ad6dcba30820969f5618ee40e9a0d32c6f5f -F src/shell.c.in 37a6b4c336eab71cc1feef42c3136d7c018a1833797131a35c21767ad00bb1ce +F src/shell.c.in 9f517c22e3c9a08ab634330789f74454ec9a7e0596c8faed221c6b43ee980b96 F src/sqlite.h.in 29a3b2eab328c5a345ef6be09ff77dc6dabbfe1a36e05c19204591d084a73d80 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683 -F src/sqliteInt.h 925c0379afb30711c1cc171997d21e87f8eee257b0f23791803173dd8ad49385 +F src/sqliteInt.h 0bd3db8ad35cd42a7fa899446fe49799156a273571abdf7011a618853cb7d6a7 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b -F src/status.c b651e68af3cb859e4083c79e0e7177e2aeb35ed23e3892d7bbe5b732d38bf2b9 +F src/status.c 41d9cc5d5f9d2a470dcf0a28432c66e25c15b86e51535283e9aef632adf02ac8 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/tclsqlite.c b15e46bdc1b14e994a358d51ce0bf8b7d7d4615cda96634ca0498ee39bc83b9c F src/test1.c b95363b0bc9e1ecc61964ca5a22c137422af4808a54a7bbb1a4f7d6d6257f704 @@ -1205,7 +1205,8 @@ F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/releasetest.tcl c5b474f9880073fc3b69729ee05d5284653a9ee101af572204917d9dcb1d9015 x F test/resetdb.test 684a6ffde5a5141bba79f3101981cc38dcfc3403f61e643b7b3aa68bef0b8408 F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb -F test/reuse1.test 04ae701b4000113fedc472719e4cf4bb1d34d22dcaf8d3e643d41d1256a9590d +F test/reuse1.test 9c2d2085a996dd97560cf96d8a47ec3aa2fb5700a2beace279e6fa5c385d0b88 +F test/reuse2.test 39f4a78ddf2d9b1fe3e4131c70497db628cd3a313a4520860b98af2e024bf98d F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6 F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a @@ -1777,7 +1778,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3869b2106b9a639b5e739dc9f9acd2f7a9223d3ce562297b877c5d4d2bf76468 531eca6104e41e4301fa2cf58bb8fec811da31e151a0f766c93aece5521d235b -R 3fa5f37112ee4c967fdfda26327db2a3 +P ae88f8e1ffc33748e45308c21ba83499228ae05344df81e41dd61e14a1270588 +R 45f3a1d79a7a50dce1c110c208f15e44 U dan -Z 232d13f5aeabfa21c7a449cba90c71ad +Z 91bd02e6c8d76b8f36b71a3d032602d4 diff --git a/manifest.uuid b/manifest.uuid index cfc614a65d..beefc05a73 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ae88f8e1ffc33748e45308c21ba83499228ae05344df81e41dd61e14a1270588 \ No newline at end of file +9e8e5f52cf90579c3071f04a3c6857bb76b4ca1b1900a7f865de789ed0bb8678 \ No newline at end of file diff --git a/src/build.c b/src/build.c index f3be764cca..a7c9eee61b 100644 --- a/src/build.c +++ b/src/build.c @@ -530,11 +530,7 @@ void sqlite3ResetOneSchema(sqlite3 *db, int iDb){ if( db->nSchemaLock==0 ){ for(i=0; inDb; i++){ if( DbHasProperty(db, i, DB_ResetWanted) ){ - if( i==1 ){ - sqlite3SchemaClear(db->aDb[1].pSchema); - }else{ - sqlite3SchemaUnuse(db, i); - } + sqlite3SchemaClear(db->aDb[1].pSchema); } } } @@ -549,7 +545,10 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){ sqlite3BtreeEnterAll(db); assert( db->nSchemaLock==0 ); for(i=0; inDb; i=(i?i+1:2)){ - sqlite3SchemaUnuse(db, i); + Db *pDb = &db->aDb[i]; + if( pDb->pSchema ){ + sqlite3SchemaClear(pDb->pSchema); + } } sqlite3SchemaClear(db->aDb[1].pSchema); db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk); diff --git a/src/callback.c b/src/callback.c index e136045fbe..8afa1d9bf6 100644 --- a/src/callback.c +++ b/src/callback.c @@ -16,6 +16,14 @@ #include "sqliteInt.h" +struct SchemaPool { + int nRef; /* Number of pointers to this object */ + u64 cksum; /* Checksum for this Schema contents */ + Schema *pSchema; /* Linked list of Schema objects */ + Schema sSchema; /* The single dummy schema object */ + SchemaPool *pNext; /* Next element in schemaPoolList */ +}; + /* ** Invoke the 'collation needed' callback to request a collation sequence ** in the encoding enc of name zName, length nName. @@ -480,10 +488,10 @@ void sqlite3SchemaClear(void *p){ } /* -** Global linked list of sharable Schema objects. Read and write access must +** Global linked list of SchemaPool objects. Read and write access must ** be protected by the SQLITE_MUTEX_STATIC_MASTER mutex. */ -static Schema *SQLITE_WSD sharedSchemaList = 0; +static SchemaPool *SQLITE_WSD schemaPoolList = 0; /* ** Check that the schema of db iDb is writable (either because it is the temp @@ -499,87 +507,120 @@ void sqlite3SchemaWritable(Parse *pParse, int iDb){ } } -/* -** Replace the Schema object currently associated with database iDb with -** an empty schema object, ready to be populated. If there are multiple -** users of the Schema, this means decrementing the current objects ref -** count and replacing it with a pointer to a new, empty, object. Or, if -** the database handle passed as the first argument of the schema object -** is the only user, remove it from the shared list (if applicable) and -** clear it in place. -*/ -void sqlite3SchemaUnuse(sqlite3 *db, int iDb){ - Db *pDb = &db->aDb[iDb]; - Schema *pSchema; - assert( iDb!=1 ); - if( (pSchema = pDb->pSchema) ){ - - if( (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){ - sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); - assert( pSchema->nRef>=1 ); - if( pSchema->nRef==1 ){ - Schema **pp; - for(pp=&sharedSchemaList; (*pp); pp=&(*pp)->pNext){ - if( *pp==pSchema ){ - *pp = pSchema->pNext; - break; - } - } - pSchema->pNext = 0; - }else{ - assert( db->openFlags & SQLITE_OPEN_REUSE_SCHEMA ); - pSchema->nRef--; - pDb->pSchema = pSchema = 0; - } - sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); - } - - if( pSchema==0 ){ - db->aDb[iDb].pSchema = sqlite3SchemaGet(db, 0); - }else{ - sqlite3SchemaClear(pSchema); - } - } +static void schemaDelete(Schema *pSchema){ + sqlite3SchemaClear((void*)pSchema); + sqlite3_free(pSchema); } /* ** The schema for database iDb of database handle db, which was opened -** with SQLITE_OPEN_REUSE_SCHEMA, has just been parsed. This function -** checks the global list (sharedSchemaList) for a matching schema and, -** if one is found, frees the newly parsed Schema object and adds a pointer -** to the existing shared schema in its place. Or, if there is no matching -** schema in the list, then the new schema is added to it. +** with SQLITE_OPEN_REUSE_SCHEMA, has just been parsed. This function either +** finds a matching SchemaPool object on the global list (schemaPoolList) or +** else allocates a new one and sets the Db.pSPool variable accordingly. */ -void sqlite3SchemaReuse(sqlite3 *db, int iDb){ +int sqlite3SchemaConnect(sqlite3 *db, int iDb, u64 cksum){ Schema *pSchema = db->aDb[iDb].pSchema; - Schema *p; - assert( pSchema && iDb!=1 ); + SchemaPool *p; + + assert( pSchema && iDb!=1 && db->aDb[iDb].pSPool==0 ); sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); - for(p=sharedSchemaList; p; p=p->pNext){ - if( p->cksum==pSchema->cksum - && p->schema_cookie==pSchema->schema_cookie - ){ + + /* Search for a matching SchemaPool object */ + for(p=schemaPoolList; p; p=p->pNext){ + if( p->cksum==cksum && p->sSchema.schema_cookie==pSchema->schema_cookie ){ break; } } if( !p ){ - /* No matching schema was found. */ - pSchema->pNext = sharedSchemaList; - sharedSchemaList = pSchema; - }else{ - /* Found a matching schema. Increase its ref count. */ - p->nRef++; + /* No SchemaPool object found. Allocate a new one. */ + p = (SchemaPool*)sqlite3_malloc(sizeof(SchemaPool)); + if( p ){ + memset(p, 0, sizeof(SchemaPool)); + p->cksum = cksum; + p->pNext = schemaPoolList; + schemaPoolList = p; + + p->sSchema.schema_cookie = pSchema->schema_cookie; + p->sSchema.iGeneration = pSchema->iGeneration; + p->sSchema.file_format = pSchema->file_format; + p->sSchema.enc = pSchema->enc; + p->sSchema.cache_size = pSchema->cache_size; + } } + + if( p ) p->nRef++; + + /* If the SchemaPool contains one or more free schemas at the moment, + ** delete one of them. */ + if( p->pSchema ){ + Schema *pDel = p->pSchema; + p->pSchema = pDel->pNext; + schemaDelete(pDel); + } + sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); - /* If a matching schema was found in the shared schema list, free the - ** schema object just parsed, and add a pointer to the matching schema - ** to the db handle. */ - if( p ){ - sqlite3SchemaClear(pSchema); - sqlite3DbFree(0, pSchema); - db->aDb[iDb].pSchema = p; + db->aDb[iDb].pSPool = p; + return (p ? SQLITE_OK : SQLITE_NOMEM); +} + +void sqlite3SchemaDisconnect(sqlite3 *db, int iDb){ + Db *pDb = &db->aDb[iDb]; + if( pDb->pSPool ){ + SchemaPool *pSPool = pDb->pSPool; + pDb->pSPool = 0; + assert( iDb!=1 ); + + sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); + assert( pSPool->nRef>=1 ); + pSPool->nRef--; + if( pSPool->nRef<=0 ){ + Schema *pNext; + while( pSPool->pSchema ){ + Schema *pNext = pSPool->pSchema->pNext; + schemaDelete(pSPool->pSchema); + pSPool->pSchema = pNext; + } + sqlite3_free(pSPool); + } + sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); + } +} + +/* +** Extract and return a pointer to a schema object from the SchemaPool passed +** as the only argument, if one is available. If one is not available, return +** NULL. +*/ +Schema *sqlite3SchemaExtract(SchemaPool *pSPool){ + Schema *pRet = 0; + if( pSPool ){ + sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); + if( pSPool->pSchema ){ + pRet = pSPool->pSchema; + pSPool->pSchema = pRet->pNext; + pRet->pNext = 0; + } + sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); + } + return pRet; +} + +void sqlite3SchemaReleaseAll(sqlite3 *db){ + int i; + for(i=0; inDb; i++){ + if( i!=1 ){ + Db *pDb = &db->aDb[i]; + if( pDb->pSPool && pDb->pSchema && DbHasProperty(db,i,DB_SchemaLoaded) ){ + sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); + pDb->pSchema->pNext = pDb->pSPool->pSchema; + pDb->pSPool->pSchema = pDb->pSchema; + pDb->pSchema = &pDb->pSPool->sSchema; + assert( DbHasProperty(db, i, DB_SchemaLoaded)==0 ); + sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); + } + } } } @@ -602,7 +643,6 @@ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ sqlite3HashInit(&p->trigHash); sqlite3HashInit(&p->fkeyHash); p->enc = SQLITE_UTF8; - p->nRef = 1; } return p; } diff --git a/src/main.c b/src/main.c index 9400643822..25476ba88d 100644 --- a/src/main.c +++ b/src/main.c @@ -1185,10 +1185,7 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ - if( db->openFlags & SQLITE_OPEN_REUSE_SCHEMA ){ - sqlite3SchemaUnuse(db, j); - sqlite3DbFree(0, pDb->pSchema); - } + sqlite3SchemaDisconnect(db, j); pDb->pSchema = 0; } } diff --git a/src/prepare.c b/src/prepare.c index 305c3b9841..1e8095faba 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -49,19 +49,19 @@ static void corruptSchema( ** specified by the three arguments following the first. */ void schemaUpdateChecksum( - Schema *pSchema, /* Schema object being parsed */ + InitData *pData, /* Schema parse context */ const char *zName, /* Name of new database object */ const char *zRoot, /* Root page of new database object */ const char *zSql /* SQL used to create new database object */ ){ int i; - u64 cksum = pSchema->cksum; + u64 cksum = pData->cksum; if( zName ){ for(i=0; zName[i]; i++) cksum += (cksum<<3) + zName[i]; } if( zRoot ) for(i=0; zRoot[i]; i++) cksum += (cksum<<3) + zRoot[i]; if( zSql ) for(i=0; zSql[i]; i++) cksum += (cksum<<3) + zSql[i]; - pSchema->cksum = cksum; + pData->cksum = cksum; } /* @@ -151,13 +151,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ } if( iDb!=1 && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){ - /* If this schema might be used by multiple connections, ensure that - ** the affinity string is allocated here. Otherwise, there might be - ** a race condition where two threads attempt to allocate it - ** simultaneously. */ - Index *pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName); - if( pIndex ) sqlite3IndexAffinityStr(db, pIndex); - schemaUpdateChecksum(db->aDb[iDb].pSchema, argv[0], argv[1], argv[2]); + schemaUpdateChecksum(pData, argv[0], argv[1], argv[2]); } return 0; } @@ -185,10 +179,28 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ); assert( iDb>=0 && iDbnDb ); - assert( db->aDb[iDb].pSchema ); + assert( db->aDb[iDb].pSchema || (IsReuseSchema(db) && iDb!=1) ); assert( sqlite3_mutex_held(db->mutex) ); assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); + pDb = &db->aDb[iDb]; + if( pDb->pSPool ){ + assert( IsReuseSchema(db) ); + /* See if there is a free schema object in the schema-pool. If not, + ** disconnect from said schema pool and continue. This function will + ** connect to a (possibly different) schema-pool before returning. */ + if( (pDb->pSchema = sqlite3SchemaExtract(pDb->pSPool)) ){ + return SQLITE_OK; + } + sqlite3SchemaDisconnect(db, iDb); + assert( pDb->pSchema==0 && pDb->pSPool==0 ); + pDb->pSchema = sqlite3SchemaGet(db, 0); + if( pDb->pSchema==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto error_out; + } + } + db->init.busy = 1; /* Construct the in-memory representation schema tables (sqlite_master or @@ -214,7 +226,6 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ /* Create a cursor to hold the database open */ - pDb = &db->aDb[iDb]; if( pDb->pBt==0 ){ assert( iDb==1 ); DbSetProperty(db, 1, DB_SchemaLoaded); @@ -370,8 +381,8 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ rc = SQLITE_OK; } - if( iDb!=1 && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){ - sqlite3SchemaReuse(db, iDb); + if( rc==SQLITE_OK && iDb!=1 && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){ + rc = sqlite3SchemaConnect(db, iDb, initData.cksum); } /* Jump here for an error that occurs after successfully allocating @@ -446,7 +457,7 @@ int sqlite3ReadSchema(Parse *pParse){ if( rc!=SQLITE_OK ){ pParse->rc = rc; pParse->nErr++; - }else if( db->noSharedCache ){ + }else if( db->noSharedCache && !IsReuseSchema(db) ){ db->mDbFlags |= DBFLAG_SchemaKnownOk; } } @@ -718,6 +729,7 @@ static int sqlite3LockAndPrepare( ){ int rc; int cnt = 0; + int bReleaseSchema = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( ppStmt==0 ) return SQLITE_MISUSE_BKPT; @@ -727,6 +739,12 @@ static int sqlite3LockAndPrepare( return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); + if( IsReuseSchema(db) ){ + if( (db->mDbFlags & DBFLAG_SchemaInuse)==0 ){ + db->mDbFlags |= DBFLAG_SchemaInuse; + bReleaseSchema = 1; + } + } sqlite3BtreeEnterAll(db); do{ /* Make multiple attempts to compile the SQL, until it either succeeds @@ -736,7 +754,14 @@ static int sqlite3LockAndPrepare( assert( rc==SQLITE_OK || *ppStmt==0 ); }while( rc==SQLITE_ERROR_RETRY || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) ); + sqlite3BtreeLeaveAll(db); + + if( bReleaseSchema ){ + db->mDbFlags &= ~DBFLAG_SchemaInuse; + sqlite3SchemaReleaseAll(db); + } + rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); sqlite3_mutex_leave(db->mutex); diff --git a/src/shell.c.in b/src/shell.c.in index 19c32be350..6df26602ec 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1065,7 +1065,7 @@ struct ShellState { #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ #define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */ #define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */ -#define SHELL_OPEN_REUSESCHEMA 5 /* Open for schema reuse */ +#define SHELL_OPEN_REUSESCHEMA 6 /* Open for schema reuse */ /* ** These are the allowed shellFlgs values diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 61abb3aa70..f3efccb44c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1070,6 +1070,7 @@ typedef struct CollSeq CollSeq; typedef struct Column Column; typedef struct Db Db; typedef struct Schema Schema; +typedef struct SchemaPool SchemaPool; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct FKey FKey; @@ -1202,6 +1203,7 @@ struct Db { u8 safety_level; /* How aggressive at syncing data to disk */ u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */ Schema *pSchema; /* Pointer to database schema (possibly shared) */ + SchemaPool *pSPool; /* For REUSE_SCHEMA mode */ }; /* @@ -1233,10 +1235,7 @@ struct Schema { u8 enc; /* Text encoding used by this database */ u16 schemaFlags; /* Flags associated with this schema */ int cache_size; /* Number of pages to use in the cache */ - - int nRef; /* Number of connections using this schema */ - u64 cksum; /* Checksum for this database schema */ - Schema *pNext; /* Next schema in shared schema list */ + Schema *pNext; /* Next Schema object SchemaPool (REUSE_SCHEMA) */ }; /* @@ -1499,6 +1498,8 @@ struct sqlite3 { #endif }; +#define IsReuseSchema(db) (((db)->openFlags & SQLITE_OPEN_REUSE_SCHEMA)!=0) + /* ** A macro to discover the encoding of a database. */ @@ -1564,6 +1565,8 @@ struct sqlite3 { #define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */ #define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */ +#define DBFLAG_SchemaInuse 0x0010 /* Do not free schemas */ + /* ** Bits of the sqlite3.dbOptFlags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to @@ -3353,6 +3356,7 @@ typedef struct { int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ int rc; /* Result code stored here */ u32 mInitFlags; /* Flags controlling error messages */ + u64 cksum; /* Schema checksum for REUSE_SCHEMA mode */ } InitData; /* @@ -4289,8 +4293,10 @@ void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); void sqlite3SchemaClear(void *); -void sqlite3SchemaUnuse(sqlite3*, int); -void sqlite3SchemaReuse(sqlite3*, int); +int sqlite3SchemaConnect(sqlite3*, int, u64); +void sqlite3SchemaDisconnect(sqlite3 *, int); +Schema *sqlite3SchemaExtract(SchemaPool*); +void sqlite3SchemaReleaseAll(sqlite3 *); void sqlite3SchemaWritable(Parse*, int); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); diff --git a/src/status.c b/src/status.c index 4a32354391..d0dc0b734f 100644 --- a/src/status.c +++ b/src/status.c @@ -301,9 +301,6 @@ int sqlite3_db_status( for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ sqlite3DeleteTable(db, (Table *)sqliteHashData(p)); } - if( pSchema->nRef>1 ){ - nByte -= (nByte - nStart)*(pSchema->nRef-1)/pSchema->nRef; - } } } db->pnBytesFreed = 0; diff --git a/test/reuse1.test b/test/reuse1.test index b21779b994..c1443c0834 100644 --- a/test/reuse1.test +++ b/test/reuse1.test @@ -58,9 +58,11 @@ sqlite3 db3 test.db2 do_execsql_test -db db3 1.4.1 { ALTER TABLE t1 ADD COLUMN a; } +breakpoint do_execsql_test -db db2 1.4.2 { SELECT * FROM t1; } {1 2 3 {} 4 5 6 {}} +exit do_execsql_test 1.4.3 { SELECT * FROM t1; } {} diff --git a/test/reuse2.test b/test/reuse2.test new file mode 100644 index 0000000000..0cc2b05356 --- /dev/null +++ b/test/reuse2.test @@ -0,0 +1,45 @@ +# 2017 August 9 +# +# 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. +# +#*********************************************************************** +# +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix reuse2 + + +do_execsql_test 1.0 { + CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z); + CREATE INDEX i1 ON t1(z); + PRAGMA schema_version; +} {2} + +do_test 1.2 { + catch { db close } + catch { db2 close } + sqlite3 db2 test.db -reuse-schema 1 + sqlite3 db test.db -reuse-schema 1 +} {} + +do_execsql_test -db db2 1.3.1 { + INSERT INTO t1 VALUES(1, 2, 3); +} + +do_execsql_test -db db2 1.3.2 { + INSERT INTO t1 VALUES(4, 5, 6); +} + +do_execsql_test 1.3.3 { + SELECT * FROM t1; +} {1 2 3 4 5 6} + +finish_test