/ Check-in [ea611d7c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Share schemas between databases attached to the same database handle.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: ea611d7cba604dc901c8088ccfa13367a5ee75f9499ea8d8b62a590daac2eae0
User & Date: dan 2019-02-12 19:20:33
Wiki:reuse-schema
Context
2019-02-12
20:58
Add tests for creating temp schema objects with SQLITE_OPEN_REUSE_SCHEMA connections. check-in: 8c07b609 user: dan tags: reuse-schema
19:20
Share schemas between databases attached to the same database handle. check-in: ea611d7c user: dan tags: reuse-schema
2019-02-11
20:13
Merge latest trunk changes into this branch. check-in: dbedd81b user: dan tags: reuse-schema
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/attach.c.

   227    227   
   228    228     /* If the file was opened successfully, read the schema for the new database.
   229    229     ** If this fails, or if opening the file failed, then close the file and 
   230    230     ** remove the entry from the db->aDb[] array. i.e. put everything back the
   231    231     ** way we found it.
   232    232     */
   233    233     if( rc==SQLITE_OK ){
   234         -    sqlite3BtreeEnterAll(db);
   235    234       db->init.iDb = 0;
   236    235       db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
   237         -    rc = sqlite3Init(db, &zErrDyn);
   238         -    sqlite3BtreeLeaveAll(db);
   239         -    assert( zErrDyn==0 || rc!=SQLITE_OK );
          236  +    if( !IsReuseSchema(db) ){
          237  +      sqlite3BtreeEnterAll(db);
          238  +      rc = sqlite3Init(db, &zErrDyn);
          239  +      sqlite3BtreeLeaveAll(db);
          240  +      assert( zErrDyn==0 || rc!=SQLITE_OK );
          241  +    }
   240    242     }
   241    243   #ifdef SQLITE_USER_AUTHENTICATION
   242    244     if( rc==SQLITE_OK ){
   243    245       u8 newAuth = 0;
   244    246       rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
   245    247       if( newAuth<db->auth.authLevel ){
   246    248         rc = SQLITE_AUTH_USER;

Changes to src/build.c.

   307    307       return 0;
   308    308     }
   309    309   #endif
   310    310     while(1){
   311    311       for(i=OMIT_TEMPDB; i<db->nDb; i++){
   312    312         int j = (i<2) ? i^1 : i;   /* Search TEMP before MAIN */
   313    313         if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
          314  +        int bUnload = 0;
   314    315           assert( sqlite3SchemaMutexHeld(db, j, 0) );
          316  +        if( IsReuseSchema(db) 
          317  +         && DbHasProperty(db, j, DB_SchemaLoaded)==0 
          318  +         && db->init.busy==0
          319  +        ){
          320  +          sqlite3InitOne(db, j, 0, 0);
          321  +          bUnload = (j!=1);
          322  +        }
   315    323           p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
   316    324           if( p ) return p;
          325  +        if( bUnload ){
          326  +          sqlite3SchemaRelease(db, j);
          327  +        }
   317    328         }
   318    329       }
   319    330       /* Not found.  If the name we were looking for was temp.sqlite_master
   320    331       ** then change the name to sqlite_temp_master and try again. */
   321    332       if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break;
   322    333       if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break;
   323    334       zName = TEMP_MASTER_NAME;
................................................................................
   343    354   ){
   344    355     Table *p;
   345    356     sqlite3 *db = pParse->db;
   346    357   
   347    358     /* Read the database schema. If an error occurs, leave an error message
   348    359     ** and code in pParse and return NULL. */
   349    360     if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 
          361  +   && !IsReuseSchema(db)
   350    362      && SQLITE_OK!=sqlite3ReadSchema(pParse)
   351    363     ){
   352    364       return 0;
   353    365     }
   354    366   
   355    367     p = sqlite3FindTable(db, zName, zDbase);
   356    368     if( p==0 ){
................................................................................
   359    371       ** CREATE, then check to see if it is the name of an virtual table that
   360    372       ** can be an eponymous virtual table. */
   361    373       if( pParse->disableVtab==0 ){
   362    374         Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
   363    375         if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
   364    376           pMod = sqlite3PragmaVtabRegister(db, zName);
   365    377         }
   366         -      if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
   367         -        return pMod->pEpoTab;
          378  +      if( pMod ){
          379  +        if( IsReuseSchema(db)
          380  +         && DbHasProperty(db, 0, DB_SchemaLoaded)==0 
          381  +         && db->init.busy==0
          382  +        ){
          383  +          sqlite3InitOne(db, 0, 0, 0);
          384  +        }
          385  +        if( sqlite3VtabEponymousTableInit(pParse, pMod) ){
          386  +          return pMod->pEpoTab;
          387  +        }
   368    388         }
   369    389       }
   370    390   #endif
   371    391       if( flags & LOCATE_NOERR ) return 0;
   372    392       pParse->checkSchema = 1;
   373    393     }else if( IsVirtual(p) && pParse->disableVtab ){
   374    394       p = 0;
................................................................................
   532    552       DbSetProperty(db, iDb, DB_ResetWanted);
   533    553       DbSetProperty(db, 1, DB_ResetWanted);
   534    554       db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
   535    555     }
   536    556     if( db->nSchemaLock==0 ){
   537    557       for(i=0; i<db->nDb; i++){
   538    558         if( DbHasProperty(db, i, DB_ResetWanted) ){
   539         -        sqlite3SchemaZero(db, i);
          559  +        sqlite3SchemaClearOrDisconnect(db, i);
   540    560         }
   541    561       }
   542    562     }
   543    563   }
   544    564   
   545    565   /*
   546    566   ** Erase all schema information from all attached databases (including
................................................................................
   549    569   void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
   550    570     int i;
   551    571     sqlite3BtreeEnterAll(db);
   552    572     for(i=0; i<db->nDb; i=(i?i+1:2)){
   553    573       Db *pDb = &db->aDb[i];
   554    574       if( pDb->pSchema ){
   555    575         if( db->nSchemaLock==0 ){
   556         -        sqlite3SchemaClear(pDb->pSchema);
          576  +        sqlite3SchemaClearOrDisconnect(db, i);
   557    577         }else{
   558    578           DbSetProperty(db, i, DB_ResetWanted);
   559    579         }
   560    580       }
   561    581     }
   562    582     sqlite3SchemaClear(db->aDb[1].pSchema);
   563    583     db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk);

Changes to src/callback.c.

   513    513     pSchema->pSeqTab = 0;
   514    514     if( pSchema->schemaFlags & DB_SchemaLoaded ){
   515    515       pSchema->iGeneration++;
   516    516     }
   517    517     pSchema->schemaFlags &= ~(DB_SchemaLoaded|DB_ResetWanted);
   518    518   }
   519    519   
   520         -void sqlite3SchemaZero(sqlite3 *db, int iDb){
          520  +/*
          521  +** If this database was opened with the SQLITE_OPEN_REUSE_SCHEMA flag
          522  +** and iDb!=1, then disconnect from the schema-pool associated with
          523  +** database iDb. Otherwise, clear the Schema object belonging to
          524  +** database iDb. 
          525  +**
          526  +** If an OOM error occurs while disconnecting from a schema-pool, 
          527  +** the db->mallocFailed flag is set.
          528  +*/
          529  +void sqlite3SchemaClearOrDisconnect(sqlite3 *db, int iDb){
   521    530     Db *pDb = &db->aDb[iDb];
   522    531     if( IsReuseSchema(db) && iDb!=1 ){
   523         -    if( pDb->pSPool ){
   524         -      Schema *pNew = sqlite3SchemaGet(db, 0);
   525         -      if( pNew ){
   526         -        sqlite3SchemaDisconnect(db, iDb, 0);
   527         -        pDb->pSchema = pNew;
   528         -      }
   529         -      return;
   530         -    }
          532  +    sqlite3SchemaDisconnect(db, iDb, 1);
          533  +  }else{
          534  +    sqlite3SchemaClear(pDb->pSchema);
   531    535     }
   532         -  sqlite3SchemaClear(pDb->pSchema);
   533    536   }
   534    537   
   535    538   /*
   536    539   ** Global linked list of SchemaPool objects. Read and write access must
   537    540   ** be protected by the SQLITE_MUTEX_STATIC_MASTER mutex.
   538    541   */
   539    542   static SchemaPool *SQLITE_WSD schemaPoolList = 0;
................................................................................
   704    707   void sqlite3SchemaReleaseAll(sqlite3 *db){
   705    708     int i;
   706    709     assert_schema_state_ok(db);
   707    710     sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   708    711     for(i=0; i<db->nDb; i++){
   709    712       if( i!=1 ){
   710    713         Db *pDb = &db->aDb[i];
   711         -      if( pDb->pSPool && pDb->pSchema && DbHasProperty(db,i,DB_SchemaLoaded) ){
          714  +      if( pDb->pSPool && DbHasProperty(db,i,DB_SchemaLoaded) ){
   712    715           schemaRelease(pDb);
   713    716         }
   714    717       }
          718  +  }
          719  +  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          720  +}
          721  +
          722  +void sqlite3SchemaRelease(sqlite3 *db, int iDb){
          723  +  Db *pDb = &db->aDb[iDb];
          724  +  assert( iDb!=1 );
          725  +  assert_schema_state_ok(db);
          726  +  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          727  +  if( pDb->pSPool && DbHasProperty(db, iDb, DB_SchemaLoaded) ){
          728  +    schemaRelease(pDb);
   715    729     }
   716    730     sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   717    731   }
   718    732   
   719    733   /*
   720    734   ** Find and return the schema associated with a BTree.  Create
   721    735   ** a new one if necessary.

Changes to src/fkey.c.

  1391   1391   ** table pTab. Remove the deleted foreign keys from the Schema.fkeyHash
  1392   1392   ** hash table.
  1393   1393   */
  1394   1394   void sqlite3FkDelete(sqlite3 *db, Table *pTab){
  1395   1395     FKey *pFKey;                    /* Iterator variable */
  1396   1396     FKey *pNext;                    /* Copy of pFKey->pNextFrom */
  1397   1397   
  1398         -  assert( db==0 || IsVirtual(pTab)
  1399         -         || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
  1400   1398     for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
         1399  +    assert( db==0 || IsVirtual(pTab)
         1400  +         || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
  1401   1401   
  1402   1402       /* Remove the FK from the fkeyHash hash table. */
  1403   1403       if( !db || db->pnBytesFreed==0 ){
  1404   1404         if( pFKey->pPrevTo ){
  1405   1405           pFKey->pPrevTo->pNextTo = pFKey->pNextTo;
  1406   1406         }else{
  1407   1407           void *p = (void *)pFKey->pNextTo;

Changes to src/prepare.c.

   191    191     assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
   192    192     assert( iDb>=0 && iDb<db->nDb );
   193    193     assert( db->aDb[iDb].pSchema || (IsReuseSchema(db) && iDb!=1) );
   194    194     assert( sqlite3_mutex_held(db->mutex) );
   195    195     assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
   196    196   
   197    197     pDb = &db->aDb[iDb];
          198  +  assert( pDb->pSPool==0 || IsReuseSchema(db) );
   198    199     if( pDb->pSPool ){
   199         -    assert( IsReuseSchema(db) );
   200    200       /* See if there is a free schema object in the schema-pool. If not,
   201    201       ** disconnect from said schema pool and continue. This function will
   202    202       ** connect to a (possibly different) schema-pool before returning. */
   203         -    if( (pDb->pSchema = sqlite3SchemaExtract(pDb->pSPool)) ){
          203  +    Schema *pNew = sqlite3SchemaExtract(pDb->pSPool);
          204  +    if( pNew ){
          205  +      pDb->pSchema = pNew;
   204    206         return SQLITE_OK;
   205    207       }
   206    208       rc = sqlite3SchemaDisconnect(db, iDb, 1);
   207    209       if( rc!=SQLITE_OK ) goto error_out;
   208    210       assert( pDb->pSchema && pDb->pSPool==0 );
   209    211     }
   210    212   

Changes to src/sqliteInt.h.

  4319   4319   void sqlite3DeleteIndexSamples(sqlite3*,Index*);
  4320   4320   void sqlite3DefaultRowEst(Index*);
  4321   4321   void sqlite3RegisterLikeFunctions(sqlite3*, int);
  4322   4322   int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
  4323   4323   void sqlite3SchemaClear(void *);
  4324   4324   int sqlite3SchemaConnect(sqlite3*, int, u64);
  4325   4325   int sqlite3SchemaDisconnect(sqlite3 *, int, int);
  4326         -void sqlite3SchemaZero(sqlite3*, int);
         4326  +void sqlite3SchemaClearOrDisconnect(sqlite3*, int);
  4327   4327   Schema *sqlite3SchemaExtract(SchemaPool*);
  4328         -void sqlite3SchemaReleaseAll(sqlite3 *);
         4328  +void sqlite3SchemaReleaseAll(sqlite3*);
         4329  +void sqlite3SchemaRelease(sqlite3*, int);
  4329   4330   void sqlite3SchemaWritable(Parse*, int);
  4330   4331   Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
  4331   4332   int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
  4332   4333   KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
  4333   4334   void sqlite3KeyInfoUnref(KeyInfo*);
  4334   4335   KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
  4335   4336   KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);

Changes to src/vdbe.c.

  5775   5775   
  5776   5776     iDb = pOp->p1;
  5777   5777     assert( iDb>=0 && iDb<db->nDb );
  5778   5778     assert( DbHasProperty(db, iDb, DB_SchemaLoaded) );
  5779   5779   
  5780   5780   #ifndef SQLITE_OMIT_ALTERTABLE
  5781   5781     if( pOp->p4.z==0 ){
         5782  +    assert( !IsReuseSchema(db) || iDb==1 );
  5782   5783       sqlite3SchemaClear(db->aDb[iDb].pSchema);
  5783   5784       db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
  5784   5785       rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
  5785   5786       db->mDbFlags |= DBFLAG_SchemaChange;
  5786   5787       p->expired = 0;
  5787   5788     }else
  5788   5789   #endif

Changes to test/reuse1.test.

   109    109     sqlite3 db  test.db -reuse-schema 1
   110    110   
   111    111     do_execsql_test 2.1 {
   112    112       ATTACH 'test.db2' AS aux;
   113    113       SELECT * FROM main.ft;
   114    114     } {one two three}
   115    115   
          116  +breakpoint
   116    117     do_execsql_test 2.2 {
   117    118       SELECT * FROM aux.ft;
   118    119     } {aux1 aux2 aux3}
   119    120   
   120    121     do_execsql_test 2.2 {
   121    122       SELECT * FROM aux.ft_content;
   122    123     } {1 aux1 2 aux2 3 aux3}
................................................................................
   296    297     CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
   297    298     CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
   298    299     CREATE INDEX i1 ON t1(b);
   299    300     INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
   300    301   }
   301    302   db close
   302    303   sqlite3 db test.db -reuse-schema 1
   303         -breakpoint
   304    304   foreach {tn sql} {
   305    305     1 { ANALYZE }
   306    306     2 { ANALYZE t1 }
   307    307     3 { ANALYZE i1 }
   308    308     4 { ANALYZE main }
   309    309     5 { ANALYZE main.t1 }
   310    310     6 { ANALYZE main.i1 }

Changes to test/reuse2.test.

   108    108   } {nref=1 nschema=1 nref=2 nschema=1}
   109    109   
   110    110   do_execsql_test -db db3 3.11.1 { SELECT * FROM t1 }
   111    111   do_execsql_test 3.11.2 { 
   112    112     SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
   113    113   } {nref=3 nschema=1}
   114    114   
          115  +#--------------------------------------------------------------------------
          116  +catch {db1 close}
          117  +catch {db2 close}
          118  +catch {db3 close}
          119  +reset_db
          120  +do_execsql_test 4.0.1 {
          121  +  CREATE TABLE x1(a, b, c);
          122  +  CREATE INDEX x1a ON x1(a);
          123  +  CREATE INDEX x1b ON x1(b);
          124  +}
          125  +do_test 4.0.2 {
          126  +  db close
          127  +  for {set i 1} {$i < 6} {incr i} {
          128  +    forcedelete test.db${i}-journal test.db${i}-wal test.db${i}-wal2 
          129  +    forcecopy test.db test.db${i}
          130  +  }
          131  +  sqlite3 db  test.db
          132  +  sqlite3 db2 test.db -reuse-schema 1
          133  +} {}
          134  +
          135  +register_schemapool_module db
          136  +do_execsql_test 4.0.3 {
          137  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          138  +} {}
          139  +
          140  +do_test 4.1.1 {
          141  +  execsql { 
          142  +    ATTACH 'test.db1' AS db1; 
          143  +    ATTACH 'test.db2' AS db2;
          144  +    ATTACH 'test.db3' AS db3;
          145  +    ATTACH 'test.db4' AS db4;
          146  +    ATTACH 'test.db5' AS db5;
          147  +  } db2
          148  +} {}
          149  +do_execsql_test 4.1.2 {
          150  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          151  +} {}
          152  +do_execsql_test -db db2 4.1.3 {
          153  +  SELECT * FROM db3.x1
          154  +}
          155  +do_execsql_test 4.1.4 {
          156  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          157  +} {nref=1 nschema=1}
          158  +do_execsql_test -db db2 4.1.5 {
          159  +  SELECT * FROM db2.x1
          160  +}
          161  +do_execsql_test 4.1.6 {
          162  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          163  +} {nref=2 nschema=1}
          164  +do_execsql_test -db db2 4.1.7 {
          165  +  SELECT * FROM x1
          166  +}
          167  +do_execsql_test 4.1.8 {
          168  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          169  +} {nref=3 nschema=1}
          170  +
          171  +do_test 4.2.1 {
          172  +  catchsql { SELECT * FROM abc } db2
          173  +} {1 {no such table: abc}}
          174  +do_execsql_test 4.2.2 {
          175  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          176  +} {nref=6 nschema=1}
          177  +
          178  +register_schemapool_module db2
          179  +do_execsql_test -db db2 4.3.1 {
          180  +  INSERT INTO x1 VALUES(1, 2, 3);
          181  +  INSERT INTO db1.x1 VALUES(4, 5, 6);
          182  +  INSERT INTO db2.x1 VALUES(7, 8, 9);
          183  +  INSERT INTO db3.x1 VALUES(10, 11, 12);
          184  +  INSERT INTO db4.x1 VALUES(13, 14, 15);
          185  +  INSERT INTO db5.x1 VALUES(16, 17, 18);
          186  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          187  +} {nref=6 nschema=1}
          188  +
          189  +do_execsql_test -db db2 4.3.2 {
          190  +  SELECT * FROM db5.x1;
          191  +  SELECT * FROM db4.x1;
          192  +  SELECT * FROM db3.x1;
          193  +  SELECT * FROM db2.x1;
          194  +  SELECT * FROM db1.x1;
          195  +  SELECT * FROM x1;
          196  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          197  +} {
          198  +  16 17 18  13 14 15  10 11 12  7 8 9  4 5 6  1 2 3
          199  +  nref=6 nschema=1
          200  +}
          201  +
          202  +do_execsql_test -db db2 4.3.3 {
          203  +  UPDATE x1 SET a=a+10;
          204  +  UPDATE db5.x1 SET a=a+10;
          205  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          206  +} {
          207  +  nref=6 nschema=1
          208  +}
          209  +
          210  +do_execsql_test -db db2 4.3.4 {
          211  +  SELECT * FROM db5.x1;
          212  +  SELECT * FROM db4.x1;
          213  +  SELECT * FROM db3.x1;
          214  +  SELECT * FROM db2.x1;
          215  +  SELECT * FROM db1.x1;
          216  +  SELECT * FROM x1;
          217  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          218  +} {
          219  +  26 17 18  13 14 15  10 11 12  7 8 9  4 5 6  11 2 3
          220  +  nref=6 nschema=1
          221  +}
          222  +
          223  +do_execsql_test -db db2 4.3.5 {
          224  +  DELETE FROM db3.x1;
          225  +  DELETE FROM x1;
          226  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          227  +} {
          228  +  nref=6 nschema=1
          229  +}
          230  +
          231  +do_execsql_test -db db2 4.3.6 {
          232  +  SELECT * FROM db5.x1;
          233  +  SELECT * FROM db4.x1;
          234  +  SELECT * FROM db3.x1;
          235  +  SELECT * FROM db2.x1;
          236  +  SELECT * FROM db1.x1;
          237  +  SELECT * FROM x1;
          238  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          239  +} {
          240  +  26 17 18  13 14 15  7 8 9  4 5 6 
          241  +  nref=6 nschema=1
          242  +}
          243  +
          244  +do_execsql_test -db db2 4.3.6 {
          245  +  SELECT * FROM db5.x1, db4.x1, db1.x1;
          246  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool ORDER BY 1;
          247  +} {26 17 18 13 14 15 4 5 6 nref=6 nschema=3}
          248  +
   115    249   finish_test
          250  +
   116    251