Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -302,11 +302,11 @@ ){ struct sqlite3InitInfo sv = db->init; memset(&db->init, 0, sizeof(struct sqlite3InitInfo)); rc = sqlite3InitOne(db, iDb, pzErr, 0); db->init = sv; - *pbUnload = (iDb!=1); + *pbUnload = (rc==SQLITE_OK && (iDb!=1)); } return rc; } /* Index: src/callback.c ================================================================== --- src/callback.c +++ src/callback.c @@ -676,11 +676,11 @@ if( p ) p->nRef++; /* If the SchemaPool contains one or more free schemas at the moment, ** delete one of them. */ - if( p->pSchema ){ + if( p && p->pSchema ){ Schema *pDel = p->pSchema; p->pSchema = pDel->pNext; schemaDelete(pDel); } @@ -705,22 +705,21 @@ ** schema-pool is not disconnected from. Or, if no OOM error occurs, ** SQLITE_OK is returned. */ int sqlite3SchemaDisconnect(sqlite3 *db, int iDb, int bNew){ int rc = SQLITE_OK; - if( IsReuseSchema(db) && iDb!=1 ){ + if( IsReuseSchema(db) ){ Db *pDb = &db->aDb[iDb]; SchemaPool *pSPool = pDb->pSPool; assert_schema_state_ok(db); assert( pDb->pSchema ); if( pSPool==0 ){ assert( pDb->pVTable==0 ); - if( bNew==0 ){ - schemaDelete(pDb->pSchema); - pDb->pSchema = 0; - } + assert( bNew==0 ); + schemaDelete(pDb->pSchema); + pDb->pSchema = 0; }else{ VTable *p; VTable *pNext; for(p=pDb->pVTable; p; p=pNext){ pNext = p->pNext; @@ -766,19 +765,17 @@ ** 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) ); - } + 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; } /* ** Return all sharable schemas held by database handle db back to their @@ -809,13 +806,11 @@ void sqlite3SchemaRelease(sqlite3 *db, int iDb){ Db *pDb = &db->aDb[iDb]; assert( iDb!=1 ); assert_schema_state_ok(db); sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); - if( pDb->pSPool && DbHasProperty(db, iDb, DB_SchemaLoaded) ){ - schemaRelease(db, pDb); - } + schemaRelease(db, pDb); sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); } /* ** In most cases, this function finds and returns the schema associated @@ -825,13 +820,15 @@ ** sqlite3_malloc() is always returned. */ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ Schema *p; if( pBt && (db->openFlags & SQLITE_OPEN_SHARED_SCHEMA)==0 ){ - p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear); + p = (Schema*)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear); }else{ - p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema)); + db->lookaside.bDisable++; + p = (Schema*)sqlite3DbMallocZero(db, sizeof(Schema)); + db->lookaside.bDisable--; } if( !p ){ sqlite3OomFault(db); }else if ( 0==p->file_format ){ sqlite3HashInit(&p->tblHash); Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -138,11 +138,14 @@ assert( iDb==1 ); }else{ pData->rc = rc; if( rc==SQLITE_NOMEM ){ sqlite3OomFault(db); - }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){ + }else if( rc!=SQLITE_INTERRUPT + && (rc&0xFF)!=SQLITE_LOCKED + && (rc&0xFF)!=SQLITE_IOERR + ){ corruptSchema(pData, argv[0], sqlite3_errmsg(db)); } } } sqlite3_finalize(pStmt); Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -3290,17 +3290,10 @@ assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); if( pOp->p5 && (iMeta!=pOp->p3 || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i) ){ - /* - ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema - ** version is checked to ensure that the schema has not changed since the - ** SQL statement was prepared. - */ - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie ** stored with the in-memory representation of the schema, do ** not reload the schema from the database file. ** ** If virtual-tables are in use, this is not just an optimization. @@ -3313,10 +3306,17 @@ ** a v-table method. */ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ sqlite3ResetOneSchema(db, pOp->p1); } + /* + ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema + ** version is checked to ensure that the schema has not changed since the + ** SQL statement was prepared. + */ + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); p->expired = 1; rc = SQLITE_SCHEMA; } if( rc ) goto abort_due_to_error; break; Index: test/reuse3.test ================================================================== --- test/reuse3.test +++ test/reuse3.test @@ -271,7 +271,39 @@ do_test 5.5 { catchsql { SELECT nref,nschema FROM schemapool } db2 } {0 {1 1}} +db2 close +db close +do_test 5.6.1 { + forcedelete test.db2 test.db2-wal test.db2-journal + forcecopy test.db test.db2 + sqlite3 db test.db + sqlite3 db2 test.db -shared-schema 1 + sqlite3 db3 test.db2 -shared-schema 1 + register_schemapool_module db +} {} + +do_execsql_test -db db2 5.6.2 { SELECT * FROM t1 } +do_execsql_test -db db3 5.6.3 { SELECT * FROM t1 } +do_execsql_test 5.6.4 { + SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; + CREATE TABLE t4(x); + DROP TABLE t4; +} {nref=2 nschema=1} +do_execsql_test -db db2 5.6.5 { SELECT * FROM t1 } +do_execsql_test -db db3 5.6.6 { SELECT * FROM t1 } +do_execsql_test 5.6.7 { + SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; + ATTACH 'test.db2' AS db2; + CREATE TABLE db2.t4(x); + DROP TABLE db2.t4; +} {nref=1 nschema=1 nref=1 nschema=1} +do_execsql_test -db db2 5.6.8 { SELECT * FROM t1 } +do_execsql_test -db db3 5.6.9 { SELECT * FROM t1 } +do_execsql_test 5.6.10 { + SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; +} {nref=2 nschema=1} + finish_test ADDED test/reusefault.test Index: test/reusefault.test ================================================================== --- /dev/null +++ test/reusefault.test @@ -0,0 +1,49 @@ +# 2019 February 12 +# +# 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 reusefault + +do_execsql_test 1.0 { + PRAGMA cache_size = 10; + CREATE TABLE t1(a UNIQUE, b UNIQUE); + INSERT INTO t1 VALUES(1, 2), (3, 4); +} +faultsim_save_and_close + +do_faultsim_test 1.1 -prep { + faultsim_restore + sqlite3 db test.db -shared-schema 1 +} -body { + execsql { SELECT * FROM t1 } +} -test { + faultsim_test_result {0 {1 2 3 4}} +} + +do_faultsim_test 1.2 -prep { + faultsim_restore + sqlite3 db test.db -shared-schema 1 + execsql { SELECT * FROM t1 } + sqlite3 db2 test.db + db2 eval {CREATE TABLE a(a)} + db2 close +} -body { + execsql { SELECT * FROM t1 } +} -test { + faultsim_test_result {0 {1 2 3 4}} +} + + +finish_test +