Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -306,11 +306,11 @@ ){ struct sqlite3InitInfo sv = db->init; memset(&db->init, 0, sizeof(struct sqlite3InitInfo)); rc = sqlite3InitOne(db, iDb, pzErr, 0); db->init = sv; - *pbUnload = (rc==SQLITE_OK && (iDb!=1)); + if( pbUnload ) *pbUnload = (rc==SQLITE_OK && (iDb!=1)); } return rc; } /* Index: src/callback.c ================================================================== --- src/callback.c +++ src/callback.c @@ -792,11 +792,11 @@ if( pDb->pSPool && DbHasProperty(db,i,DB_SchemaLoaded) ){ schemaRelease(db, pDb); } } } - db->flags &= ~DBFLAG_FreeSchema; + db->mDbFlags &= ~DBFLAG_FreeSchema; sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) ); } /* ** Release any sharable schema held by connection iDb of database handle Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -418,11 +418,17 @@ pPragma = pragmaLocate(zLeft); if( pPragma==0 ) goto pragma_out; /* Make sure the database schema is loaded if the pragma requires that */ if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){ - if( sqlite3ReadSchema(pParse) ) goto pragma_out; + if( IsReuseSchema(db) && (zDb || (pPragma->mPragFlg & PragFlg_OneSchema)) ){ + assert( iDb>=0 && iDbnDb ); + pParse->rc = sqlite3SchemaLoad(db, iDb, 0, &pParse->zErrMsg); + if( pParse->rc ) goto pragma_out; + }else{ + if( sqlite3ReadSchema(pParse) ) goto pragma_out; + } } /* Register the result column names for pragmas that return results */ if( (pPragma->mPragFlg & PragFlg_NoColumns)==0 && ((pPragma->mPragFlg & PragFlg_NoColumns1)==0 || zRight==0) @@ -1800,13 +1806,14 @@ ** ** The user-version is not used internally by SQLite. It may be used by ** applications for any purpose. */ case PragTyp_HEADER_VALUE: { - int iCookie = pPragma->iArg; /* Which cookie to read or write */ + int iCookie; /* Which cookie to read or write */ + iCookie = pPragma->iArg & PRAGMA_HEADER_VALUE_MASK; sqlite3VdbeUsesBtree(v, iDb); - if( zRight && (pPragma->mPragFlg & PragFlg_ReadOnly)==0 ){ + if( zRight && (pPragma->iArg & PRAGMA_HEADER_VALUE_READONLY)==0 ){ /* Write the specified cookie value */ static const VdbeOpList setCookie[] = { { OP_Transaction, 0, 1, 0}, /* 0 */ { OP_SetCookie, 0, 0, 0}, /* 1 */ }; Index: src/pragma.h ================================================================== --- src/pragma.h +++ src/pragma.h @@ -53,15 +53,22 @@ /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ -#define PragFlg_ReadOnly 0x08 /* Read-only HEADER_VALUE */ +#define PragFlg_OneSchema 0x08 /* Only a single schema required */ #define PragFlg_Result0 0x10 /* Acts as query when no argument */ #define PragFlg_Result1 0x20 /* Acts as query when has one argument */ #define PragFlg_SchemaOpt 0x40 /* Schema restricts name search if present */ #define PragFlg_SchemaReq 0x80 /* Schema required - "main" is default */ + +/* For PragTyp_HEADER_VALUE pragmas the Pragma.iArg value is set +** to the index of the header field to access (always 10 or less). +** Ored with HEADER_VALUE_READONLY if the field is read only. */ +#define PRAGMA_HEADER_VALUE_READONLY 0x0100 +#define PRAGMA_HEADER_VALUE_MASK 0x00FF + /* Names of columns for pragmas that return multi-column result ** or that return single-column results where the name of the ** result column is different from the name of the pragma */ @@ -144,11 +151,11 @@ /* iArg: */ BTREE_APPLICATION_ID }, #endif #if !defined(SQLITE_OMIT_AUTOVACUUM) {/* zName: */ "auto_vacuum", /* ePragTyp: */ PragTyp_AUTO_VACUUM, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_AUTOMATIC_INDEX) @@ -165,11 +172,11 @@ /* ColNames: */ 46, 1, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "cache_size", /* ePragTyp: */ PragTyp_CACHE_SIZE, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "cache_spill", @@ -224,25 +231,25 @@ /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "data_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, - /* ePragFlg: */ PragFlg_ReadOnly|PragFlg_Result0, + /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, - /* iArg: */ BTREE_DATA_VERSION }, + /* iArg: */ BTREE_DATA_VERSION|PRAGMA_HEADER_VALUE_READONLY }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_OneSchema, /* ColNames: */ 35, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) {/* zName: */ "default_cache_size", /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema, /* ColNames: */ 45, 1, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) @@ -268,18 +275,18 @@ /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_OneSchema, /* ColNames: */ 31, 4, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FOREIGN_KEY) {/* zName: */ "foreign_key_list", /* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt|PragFlg_OneSchema, /* ColNames: */ 0, 8, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) @@ -291,13 +298,13 @@ #endif #endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "freelist_count", /* ePragTyp: */ PragTyp_HEADER_VALUE, - /* ePragFlg: */ PragFlg_ReadOnly|PragFlg_Result0, + /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, - /* iArg: */ BTREE_FREE_PAGE_COUNT }, + /* iArg: */ BTREE_FREE_PAGE_COUNT|PRAGMA_HEADER_VALUE_READONLY }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "full_column_names", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, @@ -340,11 +347,11 @@ #endif #endif #if !defined(SQLITE_OMIT_AUTOVACUUM) {/* zName: */ "incremental_vacuum", /* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_NoColumns, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_NoColumns|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "index_info", @@ -371,11 +378,11 @@ /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "journal_mode", /* ePragTyp: */ PragTyp_JOURNAL_MODE, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "journal_size_limit", /* ePragTyp: */ PragTyp_JOURNAL_SIZE_LIMIT, /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq, @@ -421,11 +428,11 @@ /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "max_page_count", /* ePragTyp: */ PragTyp_PAGE_COUNT, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "mmap_size", /* ePragTyp: */ PragTyp_MMAP_SIZE, /* ePragFlg: */ 0, @@ -449,11 +456,11 @@ /* ColNames: */ 0, 0, /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "page_count", /* ePragTyp: */ PragTyp_PAGE_COUNT, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "page_size", /* ePragTyp: */ PragTyp_PAGE_SIZE, /* ePragFlg: */ PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, @@ -557,18 +564,18 @@ #endif #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) {/* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_OneSchema, /* ColNames: */ 21, 5, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) {/* zName: */ "synchronous", /* ePragTyp: */ PragTyp_SYNCHRONOUS, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1|PragFlg_OneSchema, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) {/* zName: */ "table_info", @@ -653,11 +660,11 @@ /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, {/* zName: */ "wal_checkpoint", /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, - /* ePragFlg: */ PragFlg_NeedSchema, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_OneSchema, /* ColNames: */ 38, 3, /* iArg: */ 0 }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "writable_schema", Index: test/reuse4.test ================================================================== --- test/reuse4.test +++ test/reuse4.test @@ -112,8 +112,57 @@ 3 3 4 4 5 5 6 6 7 7 8 8 3 3 5 5 7 7 xxx 123 456 xxx xxx } } + +#------------------------------------------------------------------------- +# Test some PRAGMA statements with shared-schema connections. +# +reset_db +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, c); + CREATE INDEX t1abc ON t1(a, b, c); +} + +foreach {tn pragma nSchema nDelete} { + 1 "PRAGMA synchronous = OFF" 1 0 + 2 "PRAGMA cache_size = 200" 1 0 + 3 "PRAGMA aux2.integrity_check" 1 0 + 4 "PRAGMA integrity_check" 1 5 + 5 "PRAGMA index_info=t1abc" 1 5 + 6 "PRAGMA aux3.index_info=t1abc" 1 0 + 7 "PRAGMA journal_mode" 1 0 + 8 "PRAGMA aux2.wal_checkpoint" 1 0 + 9 "PRAGMA wal_checkpoint" 1 0 +} { + do_test 2.$tn.1 { + catch { db close } + catch { db2 close } + for {set i 1} {$i < 6} {incr i} { + forcedelete "test.db$i" "test.db${i}-wal" "test.db${i}-journal" + forcecopy test.db test.db$i + } + sqlite3 db2 test.db -shared-schema 1 + for {set i 1} {$i < 6} {incr i} { + execsql "ATTACH 'test.db$i' AS aux$i" db2 + } + } {} + + sqlite3 db test.db + register_schemapool_module db + + do_test 2.$tn.2 { + execsql $pragma db2 + execsql { SELECT 'nschema='||nschema, 'ndelete='||nDelete FROM schemapool } + } "nschema=$nSchema ndelete=$nDelete" + + do_test 2.$tn.3 { + execsql { + SELECT * FROM main.t1,aux1.t1,aux2.t1,aux3.t1,aux4.t1,aux5.t1 + } db2 + execsql { SELECT 'nschema=' || nschema, 'nref=' || nref FROM schemapool } + } "nschema=6 nref=6" +} finish_test Index: tool/mkpragmatab.tcl ================================================================== --- tool/mkpragmatab.tcl +++ tool/mkpragmatab.tcl @@ -10,11 +10,11 @@ # new pragma in ../src/pragma.c. # # Flag meanings: set flagMeaning(NeedSchema) {Force schema load before running} -set flagMeaning(ReadOnly) {Read-only HEADER_VALUE} +set flagMeaning(OneSchema) {Only a single schema required} set flagMeaning(Result0) {Acts as query when no argument} set flagMeaning(Result1) {Acts as query when has one argument} set flagMeaning(SchemaReq) {Schema required - "main" is default} set flagMeaning(SchemaOpt) {Schema restricts name search if present} set flagMeaning(NoColumns) {OP_ResultRow called with zero columns} @@ -148,11 +148,11 @@ NAME: cell_size_check TYPE: FLAG ARG: SQLITE_CellSizeCk NAME: default_cache_size - FLAG: NeedSchema Result0 SchemaReq NoColumns1 + FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema COLS: cache_size IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) NAME: page_size FLAG: Result0 SchemaReq NoColumns1 @@ -161,43 +161,43 @@ NAME: secure_delete FLAG: Result0 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: page_count - FLAG: NeedSchema Result0 SchemaReq + FLAG: NeedSchema Result0 SchemaReq OneSchema IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: max_page_count TYPE: PAGE_COUNT - FLAG: NeedSchema Result0 SchemaReq + FLAG: NeedSchema Result0 SchemaReq OneSchema IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: locking_mode FLAG: Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: journal_mode - FLAG: NeedSchema Result0 SchemaReq + FLAG: NeedSchema Result0 SchemaReq OneSchema IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: journal_size_limit FLAG: Result0 SchemaReq IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: cache_size - FLAG: NeedSchema Result0 SchemaReq NoColumns1 + FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: mmap_size IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: auto_vacuum - FLAG: NeedSchema Result0 SchemaReq NoColumns1 + FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema IF: !defined(SQLITE_OMIT_AUTOVACUUM) NAME: incremental_vacuum - FLAG: NeedSchema NoColumns + FLAG: NeedSchema NoColumns OneSchema IF: !defined(SQLITE_OMIT_AUTOVACUUM) NAME: temp_store FLAG: Result0 NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) @@ -213,11 +213,11 @@ NAME: lock_proxy_file FLAG: NoColumns1 IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE NAME: synchronous - FLAG: NeedSchema Result0 SchemaReq NoColumns1 + FLAG: NeedSchema Result0 SchemaReq NoColumns1 OneSchema IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) NAME: table_info FLAG: NeedSchema Result1 SchemaOpt ARG: 0 @@ -230,11 +230,11 @@ ARG: 1 COLS: cid name type notnull dflt_value pk hidden IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: stats - FLAG: NeedSchema Result0 SchemaReq + FLAG: NeedSchema Result0 SchemaReq OneSchema COLS: tbl idx wdth hght flgs IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) NAME: index_info TYPE: INDEX_INFO @@ -254,11 +254,11 @@ FLAG: NeedSchema Result1 SchemaOpt COLS: seq name unique origin partial IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: database_list - FLAG: NeedSchema Result0 + FLAG: NeedSchema Result0 OneSchema COLS: seq name file IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: function_list FLAG: Result0 @@ -282,16 +282,16 @@ FLAG: Result0 COLS: seq name IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) NAME: foreign_key_list - FLAG: NeedSchema Result1 SchemaOpt + FLAG: NeedSchema Result1 SchemaOpt OneSchema COLS: id seq table from to on_update on_delete match IF: !defined(SQLITE_OMIT_FOREIGN_KEY) NAME: foreign_key_check - FLAG: NeedSchema Result0 + FLAG: NeedSchema Result0 OneSchema COLS: table rowid parent fkid IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) NAME: parser_trace TYPE: FLAG @@ -327,18 +327,18 @@ FLAG: NoColumns1 Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: data_version TYPE: HEADER_VALUE - ARG: BTREE_DATA_VERSION - FLAG: ReadOnly Result0 + ARG: BTREE_DATA_VERSION|PRAGMA_HEADER_VALUE_READONLY + FLAG: Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: freelist_count TYPE: HEADER_VALUE - ARG: BTREE_FREE_PAGE_COUNT - FLAG: ReadOnly Result0 + ARG: BTREE_FREE_PAGE_COUNT|PRAGMA_HEADER_VALUE_READONLY + FLAG: Result0 IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: application_id TYPE: HEADER_VALUE ARG: BTREE_APPLICATION_ID @@ -348,11 +348,11 @@ NAME: compile_options FLAG: Result0 IF: !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) NAME: wal_checkpoint - FLAG: NeedSchema + FLAG: NeedSchema OneSchema COLS: busy log checkpointed IF: !defined(SQLITE_OMIT_WAL) NAME: wal_autocheckpoint IF: !defined(SQLITE_OMIT_WAL) @@ -530,10 +530,16 @@ foreach f [lsort [array names allflags]] { puts $fd [format {#define PragFlg_%-10s 0x%02x /* %s */} \ $f $fv $flagMeaning($f)] set fv [expr {$fv*2}] } + +puts $fd "\n/* For PragTyp_HEADER_VALUE pragmas the Pragma.iArg value is set" +puts $fd "** to the index of the header field to access (always 10 or less)." +puts $fd "** Ored with HEADER_VALUE_READONLY if the field is read only. */" +puts $fd "#define PRAGMA_HEADER_VALUE_READONLY 0x0100" +puts $fd "#define PRAGMA_HEADER_VALUE_MASK 0x00FF\n" # Sort the column lists so that longer column lists occur first # proc colscmp {a b} { return [expr {[llength $b] - [llength $a]}]