/ Check-in [3ed6eb2f]
Login

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

Overview
Comment:Merge the SQLITE_TESTCTRL_IMPOSTER changes from trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ota-update
Files: files | file ages | folders
SHA1: 3ed6eb2fab5d95709ef392170339e6dd5ba13971
User & Date: drh 2015-01-30 21:00:10
Context
2015-01-31
02:34
Merge in support for the index_xinfo pragma. check-in: f9b6dc77 user: drh tags: ota-update
2015-01-30
21:00
Merge the SQLITE_TESTCTRL_IMPOSTER changes from trunk. check-in: 3ed6eb2f user: drh tags: ota-update
20:59
Change SQLITE_TESTCTRL_INITMODE to SQLITE_TESTCTRL_IMPOSTER. Revise the order of parameters. Give it the ability to reset the schema parse table so that imposter tables can be erased. check-in: 42d56017 user: drh tags: trunk
16:36
Merge all recent trunk changes, and especially the SQLITE_TESTCTRL_INITMODE enhancement. check-in: 36436dde user: drh tags: ota-update
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

   172    172     ** written. For index b-trees, it is the root page of the associated
   173    173     ** table.  */
   174    174     if( isIndex ){
   175    175       HashElem *p;
   176    176       for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){
   177    177         Index *pIdx = (Index *)sqliteHashData(p);
   178    178         if( pIdx->tnum==(int)iRoot ){
          179  +        if( iTab ){
          180  +          /* Two or more indexes share the same root page.  There must
          181  +          ** be imposter tables.  So just return true.  The assert is not
          182  +          ** useful in that case. */
          183  +          return 1;
          184  +        }
   179    185           iTab = pIdx->pTable->tnum;
   180    186         }
   181    187       }
   182    188     }else{
   183    189       iTab = iRoot;
   184    190     }
   185    191   

Changes to src/build.c.

  1727   1727       }
  1728   1728       pPk->nKeyCol = j;
  1729   1729     }
  1730   1730     pPk->isCovering = 1;
  1731   1731     assert( pPk!=0 );
  1732   1732     nPk = pPk->nKeyCol;
  1733   1733   
  1734         -  /* Make sure every column of the PRIMARY KEY is NOT NULL */
  1735         -  for(i=0; i<nPk; i++){
  1736         -    pTab->aCol[pPk->aiColumn[i]].notNull = 1;
         1734  +  /* Make sure every column of the PRIMARY KEY is NOT NULL.  (Except,
         1735  +  ** do not enforce this for imposter tables.) */
         1736  +  if( !db->init.imposterTable ){
         1737  +    for(i=0; i<nPk; i++){
         1738  +      pTab->aCol[pPk->aiColumn[i]].notNull = 1;
         1739  +    }
         1740  +    pPk->uniqNotNull = 1;
  1737   1741     }
  1738         -  pPk->uniqNotNull = 1;
  1739   1742   
  1740   1743     /* The root page of the PRIMARY KEY is the table root page */
  1741   1744     pPk->tnum = pTab->tnum;
  1742   1745   
  1743   1746     /* Update the in-memory representation of all UNIQUE indices by converting
  1744   1747     ** the final rowid column into one or more columns of the PRIMARY KEY.
  1745   1748     */

Changes to src/main.c.

  3622   3622       ** not.
  3623   3623       */
  3624   3624       case SQLITE_TESTCTRL_ISINIT: {
  3625   3625         if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
  3626   3626         break;
  3627   3627       }
  3628   3628   
  3629         -    /*   sqlite3_test_control(SQLITE_TESTCTRL_INITMODE, db, busy, iDb, newTnum);
         3629  +    /*   sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum);
         3630  +    **
         3631  +    ** This test control is used to create imposter tables.  "db" is a pointer
         3632  +    ** to the database connection.  dbName is the database name (ex: "main" or
         3633  +    ** "temp") which will receive the imposter.  "onOff" turns imposter mode on
         3634  +    ** or off.  "tnum" is the root page of the b-tree to which the imposter
         3635  +    ** table should connect.
         3636  +    **
         3637  +    ** Enable imposter mode only when the schema has already been parsed.  Then
         3638  +    ** run a single CREATE TABLE statement to construct the imposter table in the
         3639  +    ** parsed schema.  Then turn imposter mode back off again.
  3630   3640       **
  3631         -    ** Set the db->init.busy, db->init.iDb, and db->init.tnum fields.
         3641  +    ** If onOff==0 and tnum>0 then reset the schema for all databases, causing
         3642  +    ** the schema to be reparsed the next time it is needed.  This has the
         3643  +    ** effect of erasing all imposter tables.
  3632   3644       */
  3633         -    case SQLITE_TESTCTRL_INITMODE: {
         3645  +    case SQLITE_TESTCTRL_IMPOSTER: {
  3634   3646         sqlite3 *db = va_arg(ap, sqlite3*);
  3635         -      db->init.busy = va_arg(ap,int);
  3636         -      db->init.iDb = va_arg(ap,int);
         3647  +      db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
         3648  +      db->init.busy = db->init.imposterTable = va_arg(ap,int);
  3637   3649         db->init.newTnum = va_arg(ap,int);
         3650  +      if( db->init.busy==0 && db->init.newTnum>0 ){
         3651  +        sqlite3ResetAllSchemasOfConnection(db);
         3652  +      }
  3638   3653         break;
  3639   3654       }
  3640   3655     }
  3641   3656     va_end(ap);
  3642   3657   #endif /* SQLITE_OMIT_BUILTIN_TEST */
  3643   3658     return rc;
  3644   3659   }

Changes to src/shell.c.

  3532   3532         { "always",                SQLITE_TESTCTRL_ALWAYS                 },
  3533   3533         { "reserve",               SQLITE_TESTCTRL_RESERVE                },
  3534   3534         { "optimizations",         SQLITE_TESTCTRL_OPTIMIZATIONS          },
  3535   3535         { "iskeyword",             SQLITE_TESTCTRL_ISKEYWORD              },
  3536   3536         { "scratchmalloc",         SQLITE_TESTCTRL_SCRATCHMALLOC          },
  3537   3537         { "byteorder",             SQLITE_TESTCTRL_BYTEORDER              },
  3538   3538         { "never_corrupt",         SQLITE_TESTCTRL_NEVER_CORRUPT          },
  3539         -      { "initmode",              SQLITE_TESTCTRL_INITMODE               },
         3539  +      { "imposter",              SQLITE_TESTCTRL_IMPOSTER               },
  3540   3540       };
  3541   3541       int testctrl = -1;
  3542   3542       int rc = 0;
  3543   3543       int i, n;
  3544   3544       open_db(p, 0);
  3545   3545   
  3546   3546       /* convert testctrl text option to value. allow any unique prefix
................................................................................
  3625   3625             } else {
  3626   3626               fprintf(stderr,"Error: testctrl %s takes a single char * option\n",
  3627   3627                               azArg[1]);
  3628   3628             }
  3629   3629             break;
  3630   3630   #endif
  3631   3631   
  3632         -        case SQLITE_TESTCTRL_INITMODE:
         3632  +        case SQLITE_TESTCTRL_IMPOSTER:
  3633   3633             if( nArg==5 ){
  3634   3634               rc = sqlite3_test_control(testctrl, p->db, 
  3635         -                          integerValue(azArg[2]),
         3635  +                          azArg[2],
  3636   3636                             integerValue(azArg[3]),
  3637   3637                             integerValue(azArg[4]));
  3638   3638             }else{
  3639         -            fprintf(stderr,"Usage: .testctrl initmode fBusy iDb newTnum\n");
         3639  +            fprintf(stderr,"Usage: .testctrl initmode dbName onoff tnum\n");
  3640   3640               rc = 1;
  3641   3641             }
  3642   3642             break;
  3643   3643   
  3644   3644           case SQLITE_TESTCTRL_BITVEC_TEST:         
  3645   3645           case SQLITE_TESTCTRL_FAULT_INSTALL:       
  3646   3646           case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: 

Changes to src/sqlite.h.in.

  6261   6261   #define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
  6262   6262   #define SQLITE_TESTCTRL_EXPLAIN_STMT            19  /* NOT USED */
  6263   6263   #define SQLITE_TESTCTRL_NEVER_CORRUPT           20
  6264   6264   #define SQLITE_TESTCTRL_VDBE_COVERAGE           21
  6265   6265   #define SQLITE_TESTCTRL_BYTEORDER               22
  6266   6266   #define SQLITE_TESTCTRL_ISINIT                  23
  6267   6267   #define SQLITE_TESTCTRL_SORTER_MMAP             24
  6268         -#define SQLITE_TESTCTRL_INITMODE                25
         6268  +#define SQLITE_TESTCTRL_IMPOSTER                25
  6269   6269   #define SQLITE_TESTCTRL_LAST                    25
  6270   6270   
  6271   6271   /*
  6272   6272   ** CAPI3REF: SQLite Runtime Status
  6273   6273   **
  6274   6274   ** ^This interface is used to retrieve runtime status information
  6275   6275   ** about the performance of SQLite, and optionally to reset various

Changes to src/sqliteInt.h.

  1083   1083     int aLimit[SQLITE_N_LIMIT];   /* Limits */
  1084   1084     int nMaxSorterMmap;           /* Maximum size of regions mapped by sorter */
  1085   1085     struct sqlite3InitInfo {      /* Information used during initialization */
  1086   1086       int newTnum;                /* Rootpage of table being initialized */
  1087   1087       u8 iDb;                     /* Which db file is being initialized */
  1088   1088       u8 busy;                    /* TRUE if currently initializing */
  1089   1089       u8 orphanTrigger;           /* Last statement is orphaned TEMP trigger */
         1090  +    u8 imposterTable;           /* Building an imposter table */
  1090   1091     } init;
  1091   1092     int nVdbeActive;              /* Number of VDBEs currently running */
  1092   1093     int nVdbeRead;                /* Number of active VDBEs that read or write */
  1093   1094     int nVdbeWrite;               /* Number of active VDBEs that read and write */
  1094   1095     int nVdbeExec;                /* Number of nested calls to VdbeExec() */
  1095   1096     int nExtension;               /* Number of loaded extensions */
  1096   1097     void **aExtension;            /* Array of shared library handles */

Changes to src/test1.c.

  5911   5911   ){
  5912   5912     struct Verb {
  5913   5913       const char *zName;
  5914   5914       int i;
  5915   5915     } aVerb[] = {
  5916   5916       { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT }, 
  5917   5917       { "SQLITE_TESTCTRL_SORTER_MMAP",     SQLITE_TESTCTRL_SORTER_MMAP     }, 
  5918         -    { "SQLITE_TESTCTRL_INITMODE",        SQLITE_TESTCTRL_INITMODE        },
         5918  +    { "SQLITE_TESTCTRL_IMPOSTER",        SQLITE_TESTCTRL_IMPOSTER        },
  5919   5919     };
  5920   5920     int iVerb;
  5921   5921     int iFlag;
  5922   5922     int rc;
  5923   5923   
  5924   5924     if( objc<2 ){
  5925   5925       Tcl_WrongNumArgs(interp, 1, objv, "VERB ARGS...");
................................................................................
  5953   5953         }
  5954   5954         if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR;
  5955   5955         if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR;
  5956   5956         sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, val);
  5957   5957         break;
  5958   5958       }
  5959   5959   
  5960         -    case SQLITE_TESTCTRL_INITMODE: {
  5961         -      int fBusy, iDb, newTnum;
         5960  +    case SQLITE_TESTCTRL_IMPOSTER: {
         5961  +      int onOff, tnum;
         5962  +      const char *zDbName;
  5962   5963         sqlite3 *db;
  5963   5964         if( objc!=6 ){
  5964         -        Tcl_WrongNumArgs(interp, 2, objv, "DB fBusy iDb newTnum");
         5965  +        Tcl_WrongNumArgs(interp, 2, objv, "DB dbName onOff tnum");
  5965   5966           return TCL_ERROR;
  5966   5967         }
  5967   5968         if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR;
  5968         -      if( Tcl_GetIntFromObj(interp, objv[3], &fBusy) ) return TCL_ERROR;
  5969         -      if( Tcl_GetIntFromObj(interp, objv[4], &iDb) ) return TCL_ERROR;
  5970         -      if( Tcl_GetIntFromObj(interp, objv[5], &newTnum) ) return TCL_ERROR;
  5971         -      sqlite3_test_control(SQLITE_TESTCTRL_INITMODE, db, fBusy, iDb, newTnum);
         5969  +      zDbName = Tcl_GetString(objv[3]);
         5970  +      if( Tcl_GetIntFromObj(interp, objv[4], &onOff) ) return TCL_ERROR;
         5971  +      if( Tcl_GetIntFromObj(interp, objv[5], &tnum) ) return TCL_ERROR;
         5972  +      sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zDbName, onOff, tnum);
  5972   5973         break;
  5973   5974       }
  5974   5975     }
  5975   5976   
  5976   5977     Tcl_ResetResult(interp);
  5977   5978     return TCL_OK;
  5978   5979   }

Name change from test/initmode.test to test/imposter1.test.

     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12     12   # This file implements tests for SQLite library.
    13     13   #
    14     14   # The focus of this file is adding extra entries in the symbol table
    15         -# using sqlite3_test_control(SQLITE_TESTCTRL_INITMODE) and verifying that
           15  +# using sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER) and verifying that
    16     16   # SQLite handles those as expected.
    17     17   #
    18     18   
    19     19   set testdir [file dirname $argv0]
    20     20   source $testdir/tester.tcl
    21         -set testprefix initmode
           21  +set testprefix imposter
    22     22   
    23     23   # Create a bunch of data to sort against
    24     24   #
    25         -do_test initmode-1.0 {
           25  +do_test imposter-1.0 {
    26     26     execsql {
    27     27       CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d NOT NULL);
    28     28       CREATE INDEX t1b ON t1(b);
    29     29       CREATE UNIQUE INDEX t1c ON t1(c);
    30     30       WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
    31     31         INSERT INTO t1(a,b,c,d) SELECT i,1000+i,2000+i,3000+i FROM c;
    32     32     }
    33     33     set t1_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1'}]
    34         -  set t1a_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1a'}]
    35     34     set t1b_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1b'}]
           35  +  set t1c_root [db one {SELECT rootpage FROM sqlite_master WHERE name='t1c'}]
    36     36   
    37         -  # Create a shadow table that uses the same b-tree as t1 but which does
           37  +  # Create an imposter table that uses the same b-tree as t1 but which does
    38     38     # not have the indexes
    39     39     #
    40         -  sqlite3_test_control SQLITE_TESTCTRL_INITMODE db 1 0 $t1_root
           40  +  sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1_root
    41     41     db eval {CREATE TABLE xt1(a,b,c,d)}
    42         -  sqlite3_test_control SQLITE_TESTCTRL_INITMODE db 0 0 0
           42  +
           43  +  # And create an imposter table for the t1c index.
           44  +  sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $t1c_root
           45  +  db eval {CREATE TABLE xt1c(c,rowid,PRIMARY KEY(c,rowid))WITHOUT ROWID;}
           46  +
           47  +  # Go out of imposter mode for now.
           48  +  sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0
    43     49   
    44     50     # Create triggers to record changes to xt1.
    45     51     #
    46     52     db eval {
    47     53       CREATE TEMP TABLE chnglog(desc TEXT);
    48     54       CREATE TEMP TRIGGER xt1_del AFTER DELETE ON xt1 BEGIN
    49     55         INSERT INTO chnglog VALUES(
................................................................................
    60     66     }
    61     67   } {}
    62     68   
    63     69   # The xt1 table has separate xt1.rowid and xt1.a columns.  The xt1.rowid
    64     70   # column corresponds to t1.rowid and t1.a, but the xt1.a column is always
    65     71   # NULL
    66     72   #
    67         -do_execsql_test initmode-1.1 {
           73  +do_execsql_test imposter-1.1 {
    68     74     SELECT rowid FROM xt1 WHERE a IS NOT NULL;
    69     75   } {}
    70         -do_execsql_test initmode-1.2 {
           76  +do_execsql_test imposter-1.2 {
    71     77     SELECT a,b,c,d FROM t1 EXCEPT SELECT rowid,b,c,d FROM xt1;
    72     78     SELECT rowid,b,c,d FROM xt1 EXCEPT SELECT a,b,c,d FROM t1;
    73     79   } {}
    74     80   
    75     81   
    76     82   # Make changes via the xt1 shadow table.  This will not update the
    77     83   # indexes on t1 nor check the uniqueness constraint on t1.c nor check
    78     84   # the NOT NULL constraint on t1.d, resulting in a logically inconsistent
    79     85   # database.
    80     86   #
    81         -do_execsql_test initmode-1.3 {
           87  +do_execsql_test imposter-1.3 {
    82     88     DELETE FROM xt1 WHERE rowid=5;
    83     89     INSERT INTO xt1(rowid,a,b,c,d) VALUES(99,'hello',1099,2022,NULL);
    84     90     SELECT * FROM chnglog ORDER BY rowid;
    85     91   } [list \
    86     92     {DELETE t1: rowid=5, a=NULL, b=1005, c=2005, d=3005} \
    87     93     {INSERT t1:  rowid=99, a='hello', b=1099, c=2022, d=NULL} \
    88     94   ]
    89     95   
    90         -do_execsql_test initmode-1.4a {
           96  +do_execsql_test imposter-1.4a {
    91     97     PRAGMA integrity_check;
    92     98   } {/NULL value in t1.d/}
    93         -do_execsql_test initmode-1.4b {
           99  +do_execsql_test imposter-1.4b {
    94    100     PRAGMA integrity_check;
    95    101   } {/row # missing from index t1b/}
    96         -do_execsql_test initmode-1.4c {
          102  +do_execsql_test imposter-1.4c {
          103  +  PRAGMA integrity_check;
          104  +} {/row # missing from index t1c/}
          105  +
          106  +# Cleanup the corruption.
          107  +# Then demonstrate that the xt1c imposter table can insert non-unique
          108  +# and NULL values into the UNIQUE index.
          109  +#
          110  +do_execsql_test imposter-2.0 {
          111  +  DELETE FROM t1;
          112  +  WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10)
          113  +   INSERT INTO t1(a,b,c,d) SELECT i,i,i,i FROM c;
          114  +  UPDATE xt1c SET c=NULL WHERE rowid=5;
    97    115     PRAGMA integrity_check;
    98    116   } {/row # missing from index t1c/}
          117  +
          118  +do_execsql_test imposter-2.1 {
          119  +  DELETE FROM t1;
          120  +  WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10)
          121  +   INSERT INTO t1(a,b,c,d) SELECT i,i,i,i FROM c;
          122  +  UPDATE xt1c SET c=99 WHERE rowid IN (5,7,9);
          123  +  SELECT c FROM t1 ORDER BY c;
          124  +} {1 2 3 4 6 8 10 99 99 99}
          125  +do_execsql_test imposter-2.2 {
          126  +  UPDATE xt1 SET c=99 WHERE rowid IN (5,7,9);
          127  +  PRAGMA integrity_check;
          128  +} {/non-unique entry in index t1c/}
          129  +
          130  +# Erase the imposter tables
          131  +#
          132  +do_test imposter-3.1 {
          133  +  sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 1
          134  +  db eval {
          135  +    DELETE FROM t1 WHERE rowid IN (5,7,9);
          136  +    PRAGMA integrity_check;
          137  +  }
          138  +} {ok}
          139  +
    99    140   
   100    141   finish_test