SQLite

Check-in [3ca8856a7b]
Login

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

Overview
Comment:Fix virtual table support for SQLITE_OPEN_REUSABLE_SCHEMA connections.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: 3ca8856a7b1c36885cea007b8fb05b59f1fdc9d4b54436819193f498519a23c7
User & Date: dan 2019-02-09 17:47:14.573
Context
2019-02-11
19:34
Add eponymous virtual table "schemapool". For inspecting the current contents of the schema-pool. (check-in: 2ebeb74783 user: dan tags: reuse-schema)
2019-02-09
17:47
Fix virtual table support for SQLITE_OPEN_REUSABLE_SCHEMA connections. (check-in: 3ca8856a7b user: dan tags: reuse-schema)
2019-02-08
19:30
Add test cases and fix problems on this branch. (check-in: 2b2e9f81cd user: dan tags: reuse-schema)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/callback.c.
627
628
629
630
631
632
633

634
635
636
637
638







639
640
641
642
643
644
645
  if( IsReuseSchema(db) && iDb!=1 ){
    Db *pDb = &db->aDb[iDb];
    SchemaPool *pSPool = pDb->pSPool;
    assert_schema_state_ok(db);
    assert( pDb->pSchema );

    if( pSPool==0 ){

      if( bNew==0 ){
        schemaDelete(pDb->pSchema);
        pDb->pSchema = 0;
      }
    }else{







      sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
      if( DbHasProperty(db, iDb, DB_SchemaLoaded) ){
        schemaRelease(pDb);
      }
      if( bNew ){
        Schema *pNew = sqlite3SchemaGet(db, 0);
        if( pNew==0 ){







>





>
>
>
>
>
>
>







627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  if( IsReuseSchema(db) && iDb!=1 ){
    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;
      }
    }else{
      VTable *p;
      VTable *pNext;
      for(p=pDb->pVTable; p; p=pNext){
        pNext = p->pNext;
        sqlite3VtabUnlock(p);
      }
      pDb->pVTable = 0;
      sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
      if( DbHasProperty(db, iDb, DB_SchemaLoaded) ){
        schemaRelease(pDb);
      }
      if( bNew ){
        Schema *pNew = sqlite3SchemaGet(db, 0);
        if( pNew==0 ){
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
}

/*
** Find and return the schema associated with a BTree.  Create
** a new one if necessary.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
  Schema * p;
  if( pBt && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA)==0 ){
    p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
  }else{
    p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
  }
  if( !p ){
    sqlite3OomFault(db);







|







714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
}

/*
** Find and return the schema associated with a BTree.  Create
** a new one if necessary.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
  Schema *p;
  if( pBt && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA)==0 ){
    p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
  }else{
    p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
  }
  if( !p ){
    sqlite3OomFault(db);
Changes to src/main.c.
1049
1050
1051
1052
1053
1054
1055









1056
1057
1058
1059
1060
1061
1062
  for(i=0; i<db->nDb; i++){
    Schema *pSchema = db->aDb[i].pSchema;
    if( pSchema ){
      for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
        Table *pTab = (Table *)sqliteHashData(p);
        if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
      }









    }
  }
  for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){
    Module *pMod = (Module *)sqliteHashData(p);
    if( pMod->pEpoTab ){
      sqlite3VtabDisconnect(db, pMod->pEpoTab);
    }







>
>
>
>
>
>
>
>
>







1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
  for(i=0; i<db->nDb; i++){
    Schema *pSchema = db->aDb[i].pSchema;
    if( pSchema ){
      for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
        Table *pTab = (Table *)sqliteHashData(p);
        if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
      }
    }
    if( i!=1 && IsReuseSchema(db) ){
      VTable *pVTable;
      VTable *pNext;
      for(pVTable=db->aDb[i].pVTable; pVTable; pVTable=pNext){
        pNext = pVTable->pNext;
        sqlite3VtabUnlock(pVTable);
      }
      db->aDb[i].pVTable = 0;
    }
  }
  for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){
    Module *pMod = (Module *)sqliteHashData(p);
    if( pMod->pEpoTab ){
      sqlite3VtabDisconnect(db, pMod->pEpoTab);
    }
Changes to src/sqliteInt.h.
1200
1201
1202
1203
1204
1205
1206

1207
1208
1209
1210
1211
1212
1213
struct Db {
  char *zDbSName;      /* Name of this database. (schema name, not filename) */
  Btree *pBt;          /* The B*Tree structure for this database file */
  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 */

};

/*
** An instance of the following structure stores a database schema.
**
** Most Schema objects are associated with a Btree.  The exception is
** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing.







>







1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
struct Db {
  char *zDbSName;      /* Name of this database. (schema name, not filename) */
  Btree *pBt;          /* The B*Tree structure for this database file */
  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 */
  VTable *pVTable;     /* List of all VTable objects (REUSE_SCHEMA mode only) */
};

/*
** An instance of the following structure stores a database schema.
**
** Most Schema objects are associated with a Btree.  The exception is
** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing.
1952
1953
1954
1955
1956
1957
1958

1959
1960
1961
1962
1963
1964
1965
  sqlite3 *db;              /* Database connection associated with this table */
  Module *pMod;             /* Pointer to module implementation */
  sqlite3_vtab *pVtab;      /* Pointer to vtab instance */
  int nRef;                 /* Number of pointers to this structure */
  u8 bConstraint;           /* True if constraints are supported */
  int iSavepoint;           /* Depth of the SAVEPOINT stack */
  VTable *pNext;            /* Next in linked list (see above) */

};

/*
** The schema for each SQL table and view is represented in memory
** by an instance of the following structure.
*/
struct Table {







>







1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
  sqlite3 *db;              /* Database connection associated with this table */
  Module *pMod;             /* Pointer to module implementation */
  sqlite3_vtab *pVtab;      /* Pointer to vtab instance */
  int nRef;                 /* Number of pointers to this structure */
  u8 bConstraint;           /* True if constraints are supported */
  int iSavepoint;           /* Depth of the SAVEPOINT stack */
  VTable *pNext;            /* Next in linked list (see above) */
  char *zName;              /* Table name (REUSE_SCHEMA mode) */
};

/*
** The schema for each SQL table and view is represented in memory
** by an instance of the following structure.
*/
struct Table {
Changes to src/vtab.c.
140
141
142
143
144
145
146









147
148
149
150
151
152
153
** pTab is a pointer to a Table structure representing a virtual-table.
** Return a pointer to the VTable object used by connection db to access 
** this virtual-table, if one has been created, or NULL otherwise.
*/
VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
  VTable *pVtab;
  assert( IsVirtual(pTab) );









  for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
  return pVtab;
}

/*
** Decrement the ref-count on a virtual table object. When the ref-count
** reaches zero, call the xDisconnect() method to delete the object.







>
>
>
>
>
>
>
>
>







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
** pTab is a pointer to a Table structure representing a virtual-table.
** Return a pointer to the VTable object used by connection db to access 
** this virtual-table, if one has been created, or NULL otherwise.
*/
VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
  VTable *pVtab;
  assert( IsVirtual(pTab) );
  if( IsReuseSchema(db) ){
    int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
    if( iDb!=1 ){
      for(pVtab=db->aDb[iDb].pVTable; pVtab; pVtab=pVtab->pNext){
        if( sqlite3StrICmp(pTab->zName, pVtab->zName)==0 ) break;
      }
      return pVtab;
    }
  }
  for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
  return pVtab;
}

/*
** Decrement the ref-count on a virtual table object. When the ref-count
** reaches zero, call the xDisconnect() method to delete the object.
504
505
506
507
508
509
510

511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526

527
528
529
530
531
532
533
534


535
536
537
538
539
540
541
  int rc;
  const char *const*azArg = (const char *const*)pTab->azModuleArg;
  int nArg = pTab->nModuleArg;
  char *zErr = 0;
  char *zModuleName;
  int iDb;
  VtabCtx *pCtx;


  /* Check that the virtual-table is not already being initialized */
  for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
    if( pCtx->pTab==pTab ){
      *pzErr = sqlite3MPrintf(db, 
          "vtable constructor called recursively: %s", pTab->zName
      );
      return SQLITE_LOCKED;
    }
  }

  zModuleName = sqlite3DbStrDup(db, pTab->zName);
  if( !zModuleName ){
    return SQLITE_NOMEM_BKPT;
  }


  pVTable = sqlite3MallocZero(sizeof(VTable));
  if( !pVTable ){
    sqlite3OomFault(db);
    sqlite3DbFree(db, zModuleName);
    return SQLITE_NOMEM_BKPT;
  }
  pVTable->db = db;
  pVTable->pMod = pMod;



  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;

  /* Invoke the virtual table constructor */
  assert( &db->pVtabCtx );
  assert( xConstruct );







>
















>
|







>
>







513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
  int rc;
  const char *const*azArg = (const char *const*)pTab->azModuleArg;
  int nArg = pTab->nModuleArg;
  char *zErr = 0;
  char *zModuleName;
  int iDb;
  VtabCtx *pCtx;
  int nByte;                      /* Bytes of space to allocate */

  /* Check that the virtual-table is not already being initialized */
  for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
    if( pCtx->pTab==pTab ){
      *pzErr = sqlite3MPrintf(db, 
          "vtable constructor called recursively: %s", pTab->zName
      );
      return SQLITE_LOCKED;
    }
  }

  zModuleName = sqlite3DbStrDup(db, pTab->zName);
  if( !zModuleName ){
    return SQLITE_NOMEM_BKPT;
  }

  nByte = sizeof(VTable) + sqlite3Strlen30(pTab->zName) + 1;
  pVTable = (VTable*)sqlite3MallocZero(nByte);
  if( !pVTable ){
    sqlite3OomFault(db);
    sqlite3DbFree(db, zModuleName);
    return SQLITE_NOMEM_BKPT;
  }
  pVTable->db = db;
  pVTable->pMod = pMod;
  pVTable->zName = (char*)&pVTable[1];
  memcpy(pVTable->zName, pTab->zName, nByte-sizeof(VTable));

  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;

  /* Invoke the virtual table constructor */
  assert( &db->pVtabCtx );
  assert( xConstruct );
568
569
570
571
572
573
574
575


576
577
578




579
580

581
582
583
584
585
586
587
      *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
      sqlite3VtabUnlock(pVTable);
      rc = SQLITE_ERROR;
    }else{
      int iCol;
      u8 oooHidden = 0;
      /* If everything went according to plan, link the new VTable structure
      ** into the linked list headed by pTab->pVTable. Then loop through the 


      ** columns of the table to see if any of them contain the token "hidden".
      ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from
      ** the type string.  */




      pVTable->pNext = pTab->pVTable;
      pTab->pVTable = pVTable;


      for(iCol=0; iCol<pTab->nCol; iCol++){
        char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
        int nType;
        int i = 0;
        nType = sqlite3Strlen30(zType);
        for(i=0; i<nType; i++){







|
>
>
|
|
|
>
>
>
>
|
|
>







581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
      *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
      sqlite3VtabUnlock(pVTable);
      rc = SQLITE_ERROR;
    }else{
      int iCol;
      u8 oooHidden = 0;
      /* If everything went according to plan, link the new VTable structure
      ** into the linked list headed by pTab->pVTable. Or, if this is a
      ** reusable schema, into the linked list headed by Db.pVTable.
      **
      ** Then loop through the columns of the table to see if any of them
      ** contain the token "hidden". If so, set the Column COLFLAG_HIDDEN flag
      ** and remove the token from the type string.  */
      if( IsReuseSchema(db) && iDb!=1 ){
        pVTable->pNext = db->aDb[iDb].pVTable;
        db->aDb[iDb].pVTable = pVTable;
      }else{
        pVTable->pNext = pTab->pVTable;
        pTab->pVTable = pVTable;
      }

      for(iCol=0; iCol<pTab->nCol; iCol++){
        char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
        int nType;
        int i = 0;
        nType = sqlite3Strlen30(zType);
        for(i=0; i<nType; i++){
Changes to test/reuse2.test.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#


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 {







<







12
13
14
15
16
17
18

19
20
21
22
23
24
25
#


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 {
37
38
39
40
41
42
43
44















45
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








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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}

#--------------------------------------------------------------------------
reset_db
ifcapable fts5 {
  do_execsql_test 2.0 {
    CREATE VIRTUAL TABLE ft USING fts5(c);
    INSERT INTO ft VALUES('one two three');
  }
  db close
  sqlite3 db test.db -reuse-schema 1

  do_execsql_test 2.1 {
    SELECT * FROM ft
  } {{one two three}}
}

finish_test