]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Try new approach ensuring that each Schema object is only used by one connection...
authordan <dan@noemail.net>
Sat, 2 Feb 2019 21:02:22 +0000 (21:02 +0000)
committerdan <dan@noemail.net>
Sat, 2 Feb 2019 21:02:22 +0000 (21:02 +0000)
FossilOrigin-Name: 9e8e5f52cf90579c3071f04a3c6857bb76b4ca1b1900a7f865de789ed0bb8678

manifest
manifest.uuid
src/build.c
src/callback.c
src/main.c
src/prepare.c
src/shell.c.in
src/sqliteInt.h
src/status.c
test/reuse1.test
test/reuse2.test [new file with mode: 0644]

index ec694b92e2b4d37ff149b7455223a2f2d72ab64d..0484dfe6e6c2d168678b0720d9670288e494dbdd 100644 (file)
--- 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
index cfc614a65da572d9268f4a29a5987bf28cc5e92c..beefc05a73580d6a7efb13de3328ced1fe424ca6 100644 (file)
@@ -1 +1 @@
-ae88f8e1ffc33748e45308c21ba83499228ae05344df81e41dd61e14a1270588
\ No newline at end of file
+9e8e5f52cf90579c3071f04a3c6857bb76b4ca1b1900a7f865de789ed0bb8678
\ No newline at end of file
index f3be764cca1b5158abd6e92d3784f30bfde85540..a7c9eee61b4470de63a26d7cef86e48e0eb5381a 100644 (file)
@@ -530,11 +530,7 @@ void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
   if( db->nSchemaLock==0 ){
     for(i=0; i<db->nDb; 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; i<db->nDb; 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);
index e136045fbe9e7919e9221c1b9e08b8acd89dd04a..8afa1d9bf634d018618c64b3b79bc64a8a57f913 100644 (file)
 
 #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; i<db->nDb; 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;
 }
index 940064382219fe21c1bbd5e77b9eef53ea3890c3..25476ba88d6953e0f9679c068fe947caedee9b8d 100644 (file)
@@ -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;
       }
     }
index 305c3b9841c580dedacdae8ca542ed750f06ad5b..1e8095faba2ef6d11543b0c4593aced1662e780d 100644 (file)
@@ -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 && iDb<db->nDb );
-  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);
index 19c32be35096493d43ced2a2e78d8a3210ebbf8b..6df26602ecc468b6b09f5ff3eae42f3dc75bdf04 100644 (file)
@@ -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
index 61abb3aa708e9d5adea76eec3ebc5661612ee73a..f3efccb44c016d2cba4ab06a825f9abcf6a0fc99 100644 (file)
@@ -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 *);
index 4a3235439193c87784ea8fe0096e8ddbd9a3c025..d0dc0b734f2337c5bbd1987eee7999a7e2cb4ec6 100644 (file)
@@ -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;
index b21779b9947cbb4d3a6232a564b6ddbea77ffa88..c1443c083424b569d7809606b9441270492049c7 100644 (file)
@@ -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 (file)
index 0000000..0cc2b05
--- /dev/null
@@ -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