]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Have ota use imposter tables to write to indexes instead of the sqlite3_index_writer...
authordan <dan@noemail.net>
Sat, 31 Jan 2015 20:42:04 +0000 (20:42 +0000)
committerdan <dan@noemail.net>
Sat, 31 Jan 2015 20:42:04 +0000 (20:42 +0000)
FossilOrigin-Name: cdaeab467f6aa3217be161377a9b78a4eec37093

ext/ota/otafault.test
ext/ota/sqlite3ota.c
manifest
manifest.uuid
src/main.c

index 915d6448d2137e6336724ba614ebdf2dc2df6dcc..5c641dc675139991f30275ceffdce9178aa4276f 100644 (file)
@@ -64,7 +64,6 @@ do_faultsim_test 2 -faults oom-trans* -prep {
                        {1 SQLITE_NOMEM} \
                        {1 SQLITE_IOERR_NOMEM} \
                        {1 {SQLITE_NOMEM - unable to open a temporary database file for storing temporary tables}}
-
   if {$testrc==0} {
     sqlite3 db test.db
     faultsim_integrity_check
index a3d73ae67b69a04d45b2a5da363d9400ce672386..1727e51f1fbdcc1bb255d720687306c4d9687e53 100644 (file)
@@ -96,24 +96,23 @@ struct OtaState {
 **
 **     * the table itself, 
 **     * each index of the table (zero or more points to visit), and
-**     * a special "cleanup table" point.
+**     * a special "cleanup table" state.
 */
 struct OtaObjIter {
   sqlite3_stmt *pTblIter;         /* Iterate through tables */
   sqlite3_stmt *pIdxIter;         /* Index iterator */
   int nTblCol;                    /* Size of azTblCol[] array */
   char **azTblCol;                /* Array of quoted column names */
+  char **azTblType;               /* Array of column types */
   unsigned char *abTblPk;         /* Array of flags - true for PK columns */
   int eType;
-#if 0
-  unsigned char bRowid;           /* True for implicit IPK tables */
-  unsigned char bVtab;            /* True for a virtual table */
-#endif
 
   /* Output variables. zTbl==0 implies EOF. */
   int bCleanup;                   /* True in "cleanup" state */
   const char *zTbl;               /* Name of target db table */
   const char *zIdx;               /* Name of target db index (or null) */
+  int tnum;                       /* Root page of index (not table) */
+  int bUnique;                    /* Current index is unique */
   int iVisit;                     /* Number of points visited, incl. current */
 
   /* Statements created by otaObjIterPrepareAll() */
@@ -232,9 +231,11 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
   int i;
   for(i=0; i<pIter->nTblCol; i++){
     sqlite3_free(pIter->azTblCol[i]);
+    sqlite3_free(pIter->azTblType[i]);
   }
   sqlite3_free(pIter->azTblCol);
   pIter->azTblCol = 0;
+  pIter->azTblType = 0;
   pIter->abTblPk = 0;
   pIter->nTblCol = 0;
   sqlite3_free(pIter->zMask);
@@ -307,6 +308,8 @@ static int otaObjIterNext(sqlite3ota *p, OtaObjIter *pIter){
         pIter->zIdx = 0;
       }else{
         pIter->zIdx = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
+        pIter->tnum = sqlite3_column_int(pIter->pIdxIter, 1);
+        pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
         rc = SQLITE_OK;
       }
     }
@@ -339,8 +342,9 @@ static int otaObjIterFirst(sqlite3ota *p, OtaObjIter *pIter){
 
   if( rc==SQLITE_OK ){
     rc = prepareAndCollectError(p->db, &pIter->pIdxIter, &p->zErrmsg,
-        "SELECT name FROM main.sqlite_master "
-        "WHERE type='index' AND tbl_name = ?"
+        "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
+        "  FROM main.sqlite_master "
+        "  WHERE type='index' AND tbl_name = ?"
     );
   }
 
@@ -428,7 +432,7 @@ static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
 ** error code in the OTA handle passed as the first argument.
 */
 static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
-  int nByte = sizeof(char*) * nCol + sizeof(unsigned char*) * nCol;
+  int nByte = sizeof(char*) * nCol * 2 + sizeof(unsigned char*) * nCol;
   char **azNew;
 
   assert( p->rc==SQLITE_OK );
@@ -436,12 +440,32 @@ static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
   if( azNew ){
     memset(azNew, 0, nByte);
     pIter->azTblCol = azNew;
-    pIter->abTblPk = (unsigned char*)&pIter->azTblCol[nCol];
+    pIter->azTblType = &azNew[nCol];
+    pIter->abTblPk = (unsigned char*)&pIter->azTblType[nCol];
   }else{
     p->rc = SQLITE_NOMEM;
   }
 }
 
+static char *otaStrndup(const char *zStr, int nStr, int *pRc){
+  char *zRet = 0;
+  assert( *pRc==SQLITE_OK );
+
+  if( zStr ){
+    int nCopy = nStr;
+    if( nCopy<0 ) nCopy = strlen(zStr) + 1;
+    zRet = (char*)sqlite3_malloc(nCopy);
+    if( zRet ){
+      memcpy(zRet, zStr, nCopy);
+    }else{
+      *pRc = SQLITE_NOMEM;
+    }
+  }
+
+  return zRet;
+}
+
+
 /*
 ** Return true if zTab is the name of a virtual table within the target
 ** database.
@@ -531,6 +555,8 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
         );
       }else{
         int iPk = sqlite3_column_int(pStmt, 5);
+        const char *zType = (const char*)sqlite3_column_text(pStmt, 2);
+        pIter->azTblType[i] = otaStrndup(zType, -1, &p->rc);
         pIter->abTblPk[i] = (iPk!=0);
         if( iPk ){
           pIter->eType = (iPk<0) ? OTA_PK_EXTERNAL : OTA_PK_REAL;
@@ -644,6 +670,115 @@ static char *otaObjIterGetCollist(
   return zList;
 }
 
+/*
+** This function is used to create a SELECT list (the list of SQL 
+** expressions that follows a SELECT keyword) for a SELECT statement 
+** used to read from an ota_xxx table while updating the index object
+** currently indicated by the iterator object passed as the second 
+** argument. A "PRAGMA index_xinfo = <idxname>" statement is used to
+** obtain the required information.
+**
+** If the index is of the following form:
+**
+**   CREATE INDEX i1 ON t1(c, b COLLATE nocase);
+**
+** and "t1" is a table with an explicit INTEGER PRIMARY KEY column 
+** "ipk", the returned string is:
+**
+**   "`c` COLLATE 'BINARY', `b` COLLATE 'NOCASE', `ipk` COLLATE 'BINARY'"
+**
+** As well as the returned string, three other malloc'd strings are 
+** returned via output parameters. As follows:
+**
+**   pzImposterCols: ...
+**   pzImposterPk: ...
+**   pzWhere: ...
+*/
+static char *otaObjIterGetIndexCols(
+  sqlite3ota *p,                  /* OTA object */
+  OtaObjIter *pIter,              /* Object iterator for column names */
+  char **pzImposterCols,          /* OUT: Columns for imposter table */
+  char **pzImposterPk,            /* OUT: Imposter PK clause */
+  char **pzWhere,                 /* OUT: WHERE clause */
+  int *pnBind                     /* OUT: Total number of columns */
+){
+  int rc = p->rc;                 /* Error code */
+  int rc2;                        /* sqlite3_finalize() return code */
+  char *zRet = 0;                 /* String to return */
+  char *zImpCols = 0;             /* String to return via *pzImposterCols */
+  char *zImpPK = 0;               /* String to return via *pzImposterPK */
+  char *zWhere = 0;               /* String to return via *pzWhere */
+  int nBind = 0;                  /* Value to return via *pnBind */
+  const char *zComma = "";        /* Set to ", " later on */
+  const char *zAnd = "";          /* Set to " AND " later on */
+  sqlite3_stmt *pXInfo = 0;       /* PRAGMA index_xinfo = ? */
+
+  if( rc==SQLITE_OK ){
+    assert( p->zErrmsg==0 );
+    rc = prepareFreeAndCollectError(p->db, &pXInfo, &p->zErrmsg,
+        sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
+    );
+  }
+
+  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+    const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
+    int iCid = sqlite3_column_int(pXInfo, 1);
+    const char *zCol;
+    const char *zType;
+
+    if( iCid<0 ){
+      /* An integer primary key. If the table has an explicit IPK, use
+      ** its name. Otherwise, use "ota_rowid".  */
+      if( pIter->eType==OTA_PK_REAL ){
+        int i;
+        for(i=0; i<pIter->nTblCol && pIter->abTblPk[i]==0; i++);
+        assert( i<pIter->nTblCol );
+        zCol = pIter->azTblCol[i];
+      }else{
+        zCol = "ota_rowid";
+      }
+      zType = "INTEGER";
+    }else{
+      zCol = pIter->azTblCol[iCid];
+      zType = pIter->azTblType[iCid];
+    }
+
+    zRet = sqlite3_mprintf("%z%s%s COLLATE %Q", zRet, zComma, zCol, zCollate);
+    if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
+      zImpPK = sqlite3_mprintf("%z%sc%d", zImpPK, zComma, nBind);
+    }
+    zImpCols = sqlite3_mprintf(
+        "%z%sc%d %s COLLATE %Q", zImpCols, zComma, nBind, zType, zCollate
+    );
+    zWhere = sqlite3_mprintf("%z%sc%d IS ?", zWhere, zAnd, nBind);
+    if( zRet==0 || zImpPK==0 || zImpCols==0 || zWhere==0 ) rc = SQLITE_NOMEM;
+    zComma = ", ";
+    zAnd = " AND ";
+    nBind++;
+  }
+
+  rc2 = sqlite3_finalize(pXInfo);
+  if( rc==SQLITE_OK ) rc = rc2;
+
+  if( rc!=SQLITE_OK ){
+    sqlite3_free(zRet);
+    sqlite3_free(zImpCols);
+    sqlite3_free(zImpPK);
+    sqlite3_free(zWhere);
+    zRet = 0;
+    zImpCols = 0;
+    zImpPK = 0;
+    zWhere = 0;
+    p->rc = rc;
+  }
+
+  *pzImposterCols = zImpCols;
+  *pzImposterPk = zImpPK;
+  *pzWhere = zWhere;
+  *pnBind = nBind;
+  return zRet;
+}
+
 /*
 ** Assuming the current table columns are "a", "b" and "c", and the zObj
 ** paramter is passed "old", return a string of the form:
@@ -792,6 +927,7 @@ static int otaObjIterPrepareAll(
 ){
   assert( pIter->bCleanup==0 );
   if( pIter->pSelect==0 && otaObjIterGetCols(p, pIter)==SQLITE_OK ){
+    const int tnum = pIter->tnum;
     char *zCollist = 0;           /* List of indexed columns */
     char **pz = &p->zErrmsg;
     const char *zIdx = pIter->zIdx;
@@ -803,25 +939,43 @@ static int otaObjIterPrepareAll(
     }
 
     if( zIdx ){
-      int *aiCol;                 /* Column map */
-      const char **azColl;        /* Collation sequences */
+      char *zImposterCols = 0;
+      char *zImposterPK = 0;
+      char *zWhere = 0;
+      char *zBind = 0;
+      int nBind = 0;
 
       assert( pIter->eType!=OTA_PK_VTAB );
+      zCollist = otaObjIterGetIndexCols(
+          p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
+      );
+      zBind = otaObjIterGetBindlist(p, nBind);
+
+      /* Create the imposter table used to write to this index. */
+      sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1);
+      sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
+      otaMPrintfExec(p, 
+          "CREATE TABLE ota_imposter( %s, PRIMARY KEY( %s ) ) WITHOUT ROWID",
+          zImposterCols, zImposterPK
+      );
+      sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
 
-      /* Create the index writers */
+      /* Create the statement to insert index entries */
+      pIter->nCol = nBind;
       if( p->rc==SQLITE_OK ){
-        p->rc = sqlite3_index_writer(
-            p->db, 0, zIdx, &pIter->pInsert, &azColl, &aiCol, &pIter->nCol
+        p->rc = prepareFreeAndCollectError(p->db, &pIter->pInsert, &p->zErrmsg,
+          sqlite3_mprintf("INSERT INTO ota_imposter VALUES(%s)", zBind)
         );
       }
+
+      /* And to delete index entries */
       if( p->rc==SQLITE_OK ){
-        p->rc = sqlite3_index_writer(
-            p->db, 1, zIdx, &pIter->pDelete, &azColl, &aiCol, &pIter->nCol
+        p->rc = prepareFreeAndCollectError(p->db, &pIter->pDelete, &p->zErrmsg,
+          sqlite3_mprintf("DELETE FROM ota_imposter WHERE %s", zWhere)
         );
       }
 
       /* Create the SELECT statement to read keys in sorted order */
-      zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol, azColl);
       if( p->rc==SQLITE_OK ){
         char *zSql;
         if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
@@ -844,6 +998,11 @@ static int otaObjIterPrepareAll(
         }
         p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz, zSql);
       }
+
+      sqlite3_free(zImposterCols);
+      sqlite3_free(zImposterPK);
+      sqlite3_free(zWhere);
+      sqlite3_free(zBind);
     }else{
       int bOtaRowid = (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE);
       const char *zTbl = pIter->zTbl;
@@ -1205,7 +1364,7 @@ static int otaStep(sqlite3ota *p){
       sqlite3_stmt *pUpdate = 0;
       otaGetUpdateStmt(p, pIter, zMask, &pUpdate);
       if( pUpdate ){
-        for(i=0; i<pIter->nCol; i++){
+        for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
           pVal = sqlite3_column_value(pIter->pSelect, i);
           sqlite3_bind_value(pUpdate, i+1, pVal);
         }
@@ -1375,24 +1534,6 @@ static void otaSaveTransactionState(sqlite3ota *p){
   }
 }
 
-static char *otaStrndup(char *zStr, int nStr, int *pRc){
-  char *zRet = 0;
-  assert( *pRc==SQLITE_OK );
-
-  if( zStr ){
-    int nCopy = nStr;
-    if( nCopy<0 ) nCopy = strlen(zStr) + 1;
-    zRet = (char*)sqlite3_malloc(nCopy);
-    if( zRet ){
-      memcpy(zRet, zStr, nCopy);
-    }else{
-      *pRc = SQLITE_NOMEM;
-    }
-  }
-
-  return zRet;
-}
-
 static void otaFreeState(OtaState *p){
   if( p ){
     sqlite3_free(p->zTbl);
@@ -1740,6 +1881,7 @@ static int test_sqlite3ota_cmd(
           db, "ota_delta", -1, SQLITE_UTF8, (void*)interp, test_ota_delta, 0, 0
       );
       Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
+      sqlite3_exec(db, "PRAGMA vdbe_trace = 1", 0, 0, 0);
       ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
       break;
     }
index 109bbd65377ca31e16bb1bc49f10a54f9c85abae..3c7bd57c8046e5f02a6c9f0160987d914e543840 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sin\ssupport\sfor\sthe\sindex_xinfo\spragma.
-D 2015-01-31T02:34:23.039
+C Have\sota\suse\simposter\stables\sto\swrite\sto\sindexes\sinstead\sof\sthe\ssqlite3_index_writer()\sinterface.\sThe\serror\shandling\sin\sthis\sversion\sis\sbroken\sin\sa\sfew\ssmall\sways.
+D 2015-01-31T20:42:04.027
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -135,8 +135,8 @@ F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
 F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
 F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
 F ext/ota/ota9.test d3eee95dd836824d07a22e5efcdb7bf6e869358b
-F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b
-F ext/ota/sqlite3ota.c 84cab0f965144772068ec0183252ae5e5278f0be
+F ext/ota/otafault.test 508ba87c83d632670ac0f94371a465d4bb4d49dd
+F ext/ota/sqlite3ota.c 975ccfe032ee81ee39368ed5e9cb33cbb6edc603
 F ext/ota/sqlite3ota.h ce378c0c503f625611713133f9c79704ea4ee7a4
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
 F ext/rtree/rtree.c 14e6239434d4e3f65d3e90320713f26aa24e167f
@@ -210,7 +210,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
 F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
 F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
 F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660
-F src/main.c d6c5fd51a719fcdcf1cc9ef08349dbd4454cf2f3
+F src/main.c c4cb192ebf0bcc975648ae05ac40bc1f40018c52
 F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
@@ -1254,7 +1254,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 3ed6eb2fab5d95709ef392170339e6dd5ba13971 30f51d7b3b292191e8351223242e708bb7f3dfa6
-R 50758dde054ce12d446bde117108ee7b
-U drh
-Z ec35f224c29e962e36a2b69233e35843
+P f9b6dc77021ee421bffd5697d5d337d3bbd07eb9
+R 907483269529ba471a525b7f8d05bc2e
+U dan
+Z 87f206a3330dd276f74650f9e68af8ed
index 6038dc4c5d858d61530fb3bfd66dd19a9ceadfb5..ecf8a3c6bb7eaa06d8f5117c7f054a64067b7fcf 100644 (file)
@@ -1 +1 @@
-f9b6dc77021ee421bffd5697d5d337d3bbd07eb9
\ No newline at end of file
+cdaeab467f6aa3217be161377a9b78a4eec37093
\ No newline at end of file
index 6ccc6d68f649c694ba5a1019693d47aba54a90c2..7bca1e57773be4d5bc59a8b2f173e6e112e40fad 100644 (file)
@@ -3644,12 +3644,14 @@ int sqlite3_test_control(int op, ...){
     */
     case SQLITE_TESTCTRL_IMPOSTER: {
       sqlite3 *db = va_arg(ap, sqlite3*);
+      sqlite3_mutex_enter(db->mutex);
       db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
       db->init.busy = db->init.imposterTable = va_arg(ap,int);
       db->init.newTnum = va_arg(ap,int);
       if( db->init.busy==0 && db->init.newTnum>0 ){
         sqlite3ResetAllSchemasOfConnection(db);
       }
+      sqlite3_mutex_leave(db->mutex);
       break;
     }
   }