]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the checkindex.c virtual table so that it will output the
authordrh <drh@noemail.net>
Tue, 7 Nov 2017 16:23:24 +0000 (16:23 +0000)
committerdrh <drh@noemail.net>
Tue, 7 Nov 2017 16:23:24 +0000 (16:23 +0000)
index_name and after_key parameters.  Also add a new diagnostic output
column named scanner_sql which shows the SQL statement used to implement
the current index scan.

FossilOrigin-Name: 32e2520ce91351acceda845d81c9567f7a634257dc2b5b90fe6fb6583d8c0f87

ext/repair/checkindex.c
ext/repair/test/checkindex01.test
manifest
manifest.uuid

index fa8c713f7c3f56bf69e4967f4f694ae4431fc2b2..46ecc12fc43ce899a85c3fef75b794ba703731a0 100644 (file)
 #include "sqlite3ext.h"
 SQLITE_EXTENSION_INIT1
 
+/*
+** Stuff that is available inside the amalgamation, but which we need to
+** declare ourselves if this module is compiled separately.
+*/
 #ifndef SQLITE_AMALGAMATION
 # include <string.h>
 # include <stdio.h>
 # include <stdlib.h>
 # include <assert.h>
-# define ALWAYS(X)  1
-# define NEVER(X)   0
-  typedef unsigned char u8;
-  typedef unsigned short u16;
-  typedef unsigned int u32;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
 #define get4byte(x) (        \
     ((u32)((x)[0])<<24) +    \
     ((u32)((x)[1])<<16) +    \
@@ -42,8 +44,10 @@ struct CidxTable {
 
 struct CidxCursor {
   sqlite3_vtab_cursor base;       /* Base class.  Must be first */
-  sqlite3_int64 iRowid;
-  sqlite3_stmt *pStmt;
+  sqlite3_int64 iRowid;           /* Row number of the output */
+  char *zIdxName;                 /* Copy of the index_name parameter */
+  char *zAfterKey;                /* Copy of the after_key parameter */
+  sqlite3_stmt *pStmt;            /* SQL statement that generates the output */
 };
 
 typedef struct CidxColumn CidxColumn;
@@ -82,7 +86,7 @@ static void cidxCursorError(CidxCursor *pCsr, const char *zFmt, ...){
 }
 
 /*
-** Connect to then incremental_index_check virtual table.
+** Connect to the incremental_index_check virtual table.
 */
 static int cidxConnect(
   sqlite3 *db,
@@ -98,10 +102,14 @@ static int cidxConnect(
 #define IIC_CURRENT_KEY   1
 #define IIC_INDEX_NAME    2
 #define IIC_AFTER_KEY     3
+#define IIC_SCANNER_SQL   4
   rc = sqlite3_declare_vtab(db,
       "CREATE TABLE xyz("
-      " errmsg TEXT, current_key TEXT,"
-      " index_name HIDDEN, after_key HIDDEN"
+      " errmsg TEXT,"            /* Error message or NULL if everything is ok */
+      " current_key TEXT,"       /* SQLite quote() text of key values */
+      " index_name HIDDEN,"      /* IN: name of the index being scanned */
+      " after_key HIDDEN,"       /* IN: Start scanning after this key */
+      " scanner_sql HIDDEN"      /* debuggingn info: SQL used for scanner */
       ")"
   );
   pRet = cidxMalloc(&rc, sizeof(CidxTable));
@@ -123,7 +131,15 @@ static int cidxDisconnect(sqlite3_vtab *pVtab){
 }
 
 /*
-** xBestIndex method.
+** idxNum and idxStr are not used.  There are only three possible plans,
+** which are all distinguished by the number of parameters.
+**
+**   No parameters:         A degenerate plan.  The result is zero rows.
+**   1 Parameter:           Scan all of the index starting with first entry
+**   2 parameters:          Scan the index starting after the "after_key".    
+**
+** Provide successively smaller costs for each of these plans to encourage
+** the query planner to select the one with the most parameters.
 */
 static int cidxBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
   int iIdxName = -1;
@@ -179,7 +195,8 @@ static int cidxOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
 static int cidxClose(sqlite3_vtab_cursor *pCursor){
   CidxCursor *pCsr = (CidxCursor*)pCursor;
   sqlite3_finalize(pCsr->pStmt);
-  pCsr->pStmt = 0;
+  sqlite3_free(pCsr->zIdxName);
+  sqlite3_free(pCsr->zAfterKey);
   sqlite3_free(pCsr);
   return SQLITE_OK;
 }
@@ -650,6 +667,83 @@ static char *cidxColumnList(
   return zRet;
 }
 
+/*
+** Generate SQL (in memory obtained from sqlite3_malloc()) that will
+** continue the index scan for zIdxName starting after zAfterKey.
+*/
+int cidxGenerateScanSql(
+  CidxCursor *pCsr,           /* The cursor which needs the new statement */
+  const char *zIdxName,       /* index to be scanned */
+  const char *zAfterKey,      /* start after this key, if not NULL */
+  char **pzSqlOut             /* OUT: Write the generated SQL here */
+){
+  int rc;
+  char *zTab = 0;
+  char *zCurrentKey = 0;
+  char *zOrderBy = 0;
+  char *zSubWhere = 0;
+  char *zSubExpr = 0;
+  char *zSrcList = 0;
+  char **azAfter = 0;
+  CidxIndex *pIdx = 0;
+
+  *pzSqlOut = 0;
+  rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);
+
+  zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
+  zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
+  zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
+  zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
+  zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);
+
+  if( rc==SQLITE_OK && zAfterKey ){
+    rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
+  }
+
+  if( rc || zAfterKey==0 ){
+    *pzSqlOut = cidxMprintf(&rc,
+        "SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
+        "FROM (SELECT %s FROM %Q ORDER BY %s) AS i",
+        zSubExpr, zTab, zSubWhere, zCurrentKey, 
+        zSrcList, zTab, zOrderBy
+    );
+  }else{
+    const char *zSep = "";
+    char *zSql;
+    int i;
+
+    zSql = cidxMprintf(&rc, 
+        "SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
+        zSubExpr, zTab, zSubWhere, zCurrentKey
+    );
+    for(i=pIdx->nCol-1; i>=0; i--){
+      int j;
+      if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
+      for(j=0; j<2; j++){
+        char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
+        zSql = cidxMprintf(&rc, "%z"
+            "%sSELECT * FROM (SELECT %s FROM %Q WHERE %z ORDER BY %s)",
+            zSql, zSep, zSrcList, zTab, zWhere, zOrderBy
+        );
+        zSep = " UNION ALL ";
+        if( pIdx->aCol[i].bDesc==0 ) break;
+      }
+    }
+    *pzSqlOut = cidxMprintf(&rc, "%z) AS i", zSql);
+  }
+
+  sqlite3_free(zTab);
+  sqlite3_free(zCurrentKey);
+  sqlite3_free(zOrderBy);
+  sqlite3_free(zSubWhere);
+  sqlite3_free(zSubExpr);
+  sqlite3_free(zSrcList);
+  cidxFreeIndex(pIdx);
+  sqlite3_free(azAfter);
+  return rc;
+}
+
+
 /* 
 ** Position a cursor back to the beginning.
 */
@@ -663,6 +757,13 @@ static int cidxFilter(
   const char *zIdxName = 0;
   const char *zAfterKey = 0;
 
+  sqlite3_free(pCsr->zIdxName);
+  pCsr->zIdxName = 0;
+  sqlite3_free(pCsr->zAfterKey);
+  pCsr->zAfterKey = 0;
+  sqlite3_finalize(pCsr->pStmt);
+  pCsr->pStmt = 0;
+
   if( argc>0 ){
     zIdxName = (const char*)sqlite3_value_text(argv[0]);
     if( argc>1 ){
@@ -671,72 +772,13 @@ static int cidxFilter(
   }
 
   if( zIdxName ){
-    char *zTab = 0;
-    char *zCurrentKey = 0;
-    char *zOrderBy = 0;
-    char *zSubWhere = 0;
-    char *zSubExpr = 0;
-    char *zSrcList = 0;
-
-    char **azAfter = 0;
-    CidxIndex *pIdx = 0;
-
-    rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);
-
-    zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
-    zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
-    zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
-    zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
-    zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);
-
-    if( rc==SQLITE_OK && zAfterKey ){
-      rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
-    }
-
-    if( rc || zAfterKey==0 ){
-      pCsr->pStmt = cidxPrepare(&rc, pCsr, 
-          "SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
-          "FROM (SELECT %s FROM %Q ORDER BY %s) AS i",
-          zSubExpr, zTab, zSubWhere, zCurrentKey, 
-          zSrcList, zTab, zOrderBy
-      );
-      /* printf("SQL: %s\n", sqlite3_sql(pCsr->pStmt)); */
-    }else{
-      const char *zSep = "";
-      char *zSql;
-      int i;
-
-      zSql = cidxMprintf(&rc, 
-          "SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
-          zSubExpr, zTab, zSubWhere, zCurrentKey
-      );
-      for(i=pIdx->nCol-1; i>=0; i--){
-        int j;
-        if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
-        for(j=0; j<2; j++){
-          char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
-          zSql = cidxMprintf(&rc, "%z"
-              "%sSELECT * FROM (SELECT %s FROM %Q WHERE %z ORDER BY %s)",
-              zSql, zSep, zSrcList, zTab, zWhere, zOrderBy
-          );
-          zSep = " UNION ALL ";
-          if( pIdx->aCol[i].bDesc==0 ) break;
-        }
-      }
-      zSql = cidxMprintf(&rc, "%z) AS i", zSql);
-
-      /* printf("SQL: %s\n", zSql); */
+    char *zSql = 0;
+    pCsr->zIdxName = sqlite3_mprintf("%s", zIdxName);
+    pCsr->zAfterKey = zAfterKey ? sqlite3_mprintf("%s", zAfterKey) : 0;
+    rc = cidxGenerateScanSql(pCsr, zIdxName, zAfterKey, &zSql);
+    if( zSql ){
       pCsr->pStmt = cidxPrepare(&rc, pCsr, "%z", zSql);
     }
-
-    sqlite3_free(zTab);
-    sqlite3_free(zCurrentKey);
-    sqlite3_free(zOrderBy);
-    sqlite3_free(zSubWhere);
-    sqlite3_free(zSubExpr);
-    sqlite3_free(zSrcList);
-    cidxFreeIndex(pIdx);
-    sqlite3_free(azAfter);
   }
 
   if( pCsr->pStmt ){
@@ -756,26 +798,46 @@ static int cidxColumn(
   int iCol
 ){
   CidxCursor *pCsr = (CidxCursor*)pCursor;
-  assert( iCol>=IIC_ERRMSG && iCol<=IIC_AFTER_KEY );
-  if( iCol==IIC_ERRMSG ){
-    const char *zVal = 0;
-    if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
-      if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
-        zVal = "row data mismatch";
+  assert( iCol>=IIC_ERRMSG && iCol<=IIC_SCANNER_SQL );
+  switch( iCol ){
+    case IIC_ERRMSG: {
+      const char *zVal = 0;
+      if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
+        if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
+          zVal = "row data mismatch";
+        }
+      }else{
+        zVal = "row missing";
       }
-    }else{
-      zVal = "row missing";
+      sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);
+      break;
+    }
+    case IIC_CURRENT_KEY: {
+      sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));
+      break;
+    }
+    case IIC_INDEX_NAME: {
+      sqlite3_result_text(ctx, pCsr->zIdxName, -1, SQLITE_TRANSIENT);
+      break;
+    }
+    case IIC_AFTER_KEY: {
+      sqlite3_result_text(ctx, pCsr->zAfterKey, -1, SQLITE_TRANSIENT);
+      break;
+    }
+    case IIC_SCANNER_SQL: {
+      char *zSql = 0;
+      cidxGenerateScanSql(pCsr, pCsr->zIdxName, pCsr->zAfterKey, &zSql);
+      sqlite3_result_text(ctx, zSql, -1, sqlite3_free);
+      break;
     }
-    sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);
-  }else if( iCol==IIC_CURRENT_KEY ){
-    sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));
   }
   return SQLITE_OK;
 }
 
 /* Return the ROWID for the sqlite_btreeinfo table */
 static int cidxRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
-  *pRowid = 0;
+  CidxCursor *pCsr = (CidxCursor*)pCursor;
+  *pRowid = pCsr->iRowid;
   return SQLITE_OK;
 }
 
index 744a21c69e707069d6668c8135fd88339feb15b0..d4abdcea7ec62ad7ad5d65137ff441b3e3b92412 100644 (file)
@@ -43,14 +43,24 @@ proc do_index_check_test {tn idx res} {
 }
 
 
-do_execsql_test 1.2 {
-  SELECT errmsg IS NULL, current_key FROM incremental_index_check('i1');
+do_execsql_test 1.2.1 {
+  SELECT rowid, errmsg IS NULL, current_key FROM incremental_index_check('i1');
 } {
-  1 'five',5
-  1 'four',4
-  1 'one',1
-  1 'three',3
-  1 'two',2
+  1 1 'five',5
+  2 1 'four',4
+  3 1 'one',1
+  4 1 'three',3
+  5 1 'two',2
+}
+do_execsql_test 1.2.2 {
+  SELECT errmsg IS NULL, current_key, index_name, after_key, scanner_sql
+    FROM incremental_index_check('i1') LIMIT 1;
+} {
+  1
+  'five',5
+  i1
+  {}
+  {SELECT (SELECT a IS i.i0 FROM 't1' AS t WHERE "rowid" COLLATE BINARY IS i.i1), quote(i0)||','||quote(i1) FROM (SELECT (a) AS i0, ("rowid" COLLATE BINARY) AS i1 FROM 't1' ORDER BY 1,2) AS i}
 }
 
 do_index_check_test 1.3 i1 {
index f1f80d7a12b06f44f2c05ca99b163e256a168bed..d1fed8a3863aaa0d6d06f252edaa2285f680fb33 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sharmless\scompiler\swarning\sfrom\sXcode\s9.1.
-D 2017-11-06T09:34:45.112
+C Enhance\sthe\scheckindex.c\svirtual\stable\sso\sthat\sit\swill\soutput\sthe\nindex_name\sand\safter_key\sparameters.\s\sAlso\sadd\sa\snew\sdiagnostic\soutput\ncolumn\snamed\sscanner_sql\swhich\sshows\sthe\sSQL\sstatement\sused\sto\simplement\nthe\scurrent\sindex\sscan.
+D 2017-11-07T16:23:24.445
 F Makefile.in b142eb20482922153ebc77b261cdfd0a560ed05a81e9f6d9a2b0e8192922a1d2
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc a55372a22454e742ba7c8f6edf05b83213ec01125166ad7dcee0567e2f7fc81b
@@ -328,12 +328,12 @@ F ext/rbu/sqlite3rbu.h b42bcd4d8357268c6c39ab2a60b29c091e89328fa8cc49c8fac5ab8d0
 F ext/rbu/test_rbu.c 7073979b9cc80912bb03599ac8d85ab5d3bf03cfacd3463f2dcdd7822997533a
 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
 F ext/repair/checkfreelist.c 0dbae18c1b552f58d64f8969e4fb1e7f11930c60a8c2a9a8d50b7f15bdfd54bd
-F ext/repair/checkindex.c 73f26fc1e2e17d68ede5db5b0aaf4869f2d6182f45f3d3624befc503c0f04a70
+F ext/repair/checkindex.c 50264729469ae44abe9ce6c78944ec577e50faf26e362f3036142425807a8b67
 F ext/repair/sqlite3_checker.c.in 4a5a3af3f450fe503e5a2985e98516dc2a6b9ad247449e284c1cf140fc91720f
 F ext/repair/sqlite3_checker.tcl 4820d7f58428d47336874b5a148a95b4dad38fe5da72286c01a861590b8f8337
 F ext/repair/test/README.md 34b2f542cf5be7bffe479242b33ee3492cea30711e447cc4a1a86cb5915f419e
 F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc78249442da72ff3f8297398a69
-F ext/repair/test/checkindex01.test 98bfac50822da9681d75570087aac92a905290ffdaddf95ab6f69212fb4c7b14
+F ext/repair/test/checkindex01.test c4c9e62b1068ddb2d8b5c75580c3036713fc27ec105bbc579114da4ab29d53f4
 F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
 F ext/rtree/rtree.c cc91b6905bf55512c6ebc7dfdd37ac81c86f1753db8cfa6d62f0ee864464044f
@@ -1673,7 +1673,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 51ee5188b03c4b9508e94afaee4bf1f224aef28875efabda8ce09a5ab641d99e
-R ca8ae1ded89ca445023d13a31194e801
+P 66d98310b91c69fd01c6a9a958ef1eabda14ec6cd0e4b6612f877f2dfe486c54
+R 9707d9dec21c954b720b6c37687c0a3c
 U drh
-Z c36ef70e8d55fddaef51df49c2bfea5f
+Z a2de00e6bfce7f49b15935d74b6b30ce
index 0cd36e32cba68c9e853087c20b0592df43932ed0..938db32b6b43655ec090a0603e73eb03e411fa75 100644 (file)
@@ -1 +1 @@
-66d98310b91c69fd01c6a9a958ef1eabda14ec6cd0e4b6612f877f2dfe486c54
\ No newline at end of file
+32e2520ce91351acceda845d81c9567f7a634257dc2b5b90fe6fb6583d8c0f87
\ No newline at end of file