]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental integration of schemalint functionality with the shell tool. Does not...
authordan <dan@noemail.net>
Thu, 11 Feb 2016 21:01:16 +0000 (21:01 +0000)
committerdan <dan@noemail.net>
Thu, 11 Feb 2016 21:01:16 +0000 (21:01 +0000)
FossilOrigin-Name: ed49f297bcee86674ed673e195610b8cc1d35647

main.mk
manifest
manifest.uuid
src/main.c
src/pragma.c
src/shell.c
src/shell_indexes.c [new file with mode: 0644]
src/sqlite.h.in
src/sqliteInt.h
src/where.c

diff --git a/main.mk b/main.mk
index 22d40fa258fe5cdc7465f2870f473abd602c23ce..a10f043695d0fb1e57d3b538eae12b9e821623f9 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -472,7 +472,7 @@ libsqlite3.a:       $(LIBOBJ)
        $(AR) libsqlite3.a $(LIBOBJ)
        $(RANLIB) libsqlite3.a
 
-sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
+sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TOP)/src/shell_indexes.c
        $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \
                $(TOP)/src/shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
 
index 78c07a5223c95628589973352ac6353b88924a00..7da4cdeb1d6235f45fd5b38013a310d3d4389579 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\slatest\strunk\schanges\swith\sthis\sbranch.
-D 2016-02-09T15:10:56.225
+C Experimental\sintegration\sof\sschemalint\sfunctionality\swith\sthe\sshell\stool.\sDoes\snot\swork\syet.
+D 2016-02-11T21:01:16.253
 F Makefile.in dac2776c84e0d533b158a9af6e57e05c4a6b19f3
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc b0493f10caddb8adf992a4e6f1943141fc7c6816
@@ -13,7 +13,7 @@ F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
 F autoconf/Makefile.am 1c1657650775960804945dc392e14d9e43c5ed84
 F autoconf/Makefile.msc a35b2aab24d1603f3f0ae65cf01686c2578d319c
 F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
-F autoconf/README.txt e9757a381e5ce2553dbaa6247bb8ad00eb8d87aa w autoconf/README
+F autoconf/README.txt e9757a381e5ce2553dbaa6247bb8ad00eb8d87aa
 F autoconf/configure.ac 72a5e42beb090b32bca580285dc0ab3c4670adb8
 F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd
 F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
@@ -272,7 +272,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk cd48a5d8a6dc59229f4f3fe40771104f2918f789
+F main.mk eeff3d12ebe5945dac88147c641407c22a731131
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -313,7 +313,7 @@ F src/insert.c 046199e085e69e05af7bef197d53c5b4b402b6fa
 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
 F src/legacy.c b1b0880fc474abfab89e737b0ecfde0bd7a60902
 F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b
-F src/main.c b67a45397b93b7ba8fbd6bfcb03423d245baed05
+F src/main.c 816b9a98a6aca0fd643e77f3610d6a4a1a4c7e24
 F src/malloc.c 337e9808b5231855fe28857950f4f60ae42c417f
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
@@ -341,7 +341,7 @@ F src/parse.y d7bff41d460f2df96fb890f36700e85cb0fc5634
 F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23
 F src/pcache.h 4d0ccaad264d360981ec5e6a2b596d6e85242545
 F src/pcache1.c 72f644dc9e1468c72922eff5904048427b817051
-F src/pragma.c 80ee77226d0008d9188356a6cbbe6010866e1bee
+F src/pragma.c cfd521558fccd3864ec664af09a061e9e692583f
 F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
 F src/prepare.c c12b786713df3e8270c0f85f988c5359d8b4d87c
 F src/printf.c 63e6fb12bbe702dd664dc3703776c090383a5a26
@@ -349,11 +349,12 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
 F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c
 F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e
 F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d
-F src/shell.c dcd7a83645ef2a58ee9c6d0ea4714d877d7835c4
-F src/sqlite.h.in cf22ad1d52dca2c9862d63833e581028119aab7e
+F src/shell.c 2cde87e03712204231167c4a6c61b0eb5129e105
+F src/shell_indexes.c 3cff393ee86d15fbfbe31f30cd752b46d7779b52
+F src/sqlite.h.in c7db059d3b810b70b83d9ed1436fa813eba22462
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
-F src/sqliteInt.h 3aeaff9611acd790c8e76719b33db09ab885d537
+F src/sqliteInt.h a1d0d9613ed7da3657396795e44991fef188c8ee
 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
 F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
 F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
@@ -427,7 +428,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c d21b99fd1458159d0b1ecdccc8ee6ada4fdc4c54
 F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
 F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
-F src/where.c 438b89caa0cbe17cd32703a8f93baca9789d0474
+F src/where.c 89d5845353fe6d2e77bce52a2c8bea0781c69dad
 F src/whereInt.h 78b6b4de94db84aecbdc07fe3e38f648eb391e9a
 F src/wherecode.c 791a784bbf8749d560fdb0b990b607bc4f44a38d
 F src/whereexpr.c de117970b29471177a6901d60ad83a194671dc03
@@ -980,7 +981,7 @@ F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
 F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
 F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
 F test/savepoint7.test db3db281486c925095f305aad09fe806e5188ff3
-F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2 w test/savepoint3.test
+F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
 F test/scanstatus.test 5253c219e331318a437f436268e0e82345700285
 F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
 F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
@@ -1346,7 +1347,7 @@ F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
 F test/whereI.test eab5b226bbc344ac70d7dc09b963a064860ae6d7
 F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
 F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
-F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864 w test/where8m.test
+F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
 F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
@@ -1429,7 +1430,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 9341491c3a11d5a66e4f88d2af9b0d3799b4f27a ca72be8618e5d466d6f35819ca8bbd2b84269959
-R c3affb84a750d99cc1d1198e66d07e76
+P 1a4182eedd0143c3f71b3d97f1d1bb25adeba617
+R 6253a80a5a7097c3b24b24ce68900613
 U dan
-Z dbcdec085ed65802fe686b0a9369fd51
+Z bddf3d3a7cd183a6a2362ed54e10358b
index b2c40d21e220ca1848dc1f3bb56117aad26ed21d..38f9cc7372d98cc6467ab5985874b064a9f7ed32 100644 (file)
@@ -1 +1 @@
-1a4182eedd0143c3f71b3d97f1d1bb25adeba617
\ No newline at end of file
+ed49f297bcee86674ed673e195610b8cc1d35647
\ No newline at end of file
index 922af1315a35a3720ecca4be1ec5ee768ca82df5..72f12d2ac2bbceb3790bba1e62d8558b089e63b7 100644 (file)
@@ -792,6 +792,13 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
       rc = setupLookaside(db, pBuf, sz, cnt);
       break;
     }
+#ifdef SQLITE_SCHEMA_LINT
+    case SQLITE_DBCONFIG_WHEREINFO: {
+      db->xWhereInfo = va_arg(ap, void(*)(void*, int, const char*, int, i64));
+      db->pWhereInfoCtx = va_arg(ap, void*);
+      break;
+    }
+#endif
     default: {
       static const struct {
         int op;      /* The opcode */
index c34d5421c2ef62f93118803f1263096660cae772..2b4058ca162c3706b016e22ede5c6ba19c974099 100644 (file)
@@ -1048,6 +1048,7 @@ void sqlite3Pragma(
   ** type:       Column declaration type.
   ** notnull:    True if 'NOT NULL' is part of column declaration
   ** dflt_value: The default value for the column, if any.
+  ** pk:         Non-zero for PK fields.
   */
   case PragTyp_TABLE_INFO: if( zRight ){
     Table *pTab;
index 3f8b22f4fa26465193b8e59d5ae694bb90a6fb0a..e2fe41291d16a26203d8c4f1d46f96fcff169ccc 100644 (file)
@@ -156,6 +156,7 @@ static void setTextMode(FILE *out){
 # define setTextMode(X)
 #endif
 
+#include "shell_indexes.c"
 
 /* True if the timer is enabled */
 static int enableTimer = 0;
@@ -592,7 +593,8 @@ typedef struct ShellState ShellState;
 struct ShellState {
   sqlite3 *db;           /* The database */
   int echoOn;            /* True to echo input commands */
-  int autoEQP;           /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
+  int autoEQP;           /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */
+  int bRecommend;        /* Instead of sqlite3_exec(), recommend indexes */
   int statsOn;           /* True to display memory stats before each finalize */
   int scanstatsOn;       /* True to display scan stats before each finalize */
   int countChanges;      /* True to display change counts */
@@ -1544,6 +1546,19 @@ static void explain_data_delete(ShellState *p){
   p->iIndent = 0;
 }
 
+typedef struct RecCommandCtx RecCommandCtx;
+struct RecCommandCtx {
+  int (*xCallback)(void*,int,char**,char**,int*);
+  ShellState *pArg;
+};
+
+static void recCommandOut(void *pCtx, const char *zLine){
+  const char *zCol = "output";
+  RecCommandCtx *p = (RecCommandCtx*)pCtx;
+  int t = SQLITE_TEXT;
+  p->xCallback(p->pArg, 1, (char**)&zLine, (char**)&zCol, &t);
+}
+
 /*
 ** Execute a statement or set of statements.  Print 
 ** any result rows/columns depending on the current mode 
@@ -1570,6 +1585,13 @@ static int shell_exec(
     *pzErrMsg = NULL;
   }
 
+  if( pArg->bRecommend ){
+    RecCommandCtx ctx;
+    ctx.xCallback = xCallback;
+    ctx.pArg = pArg;
+    rc = shellIndexesCommand(db, zSql, recCommandOut, &ctx, pzErrMsg);
+  }else
+
   while( zSql[0] && (SQLITE_OK == rc) ){
     rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
     if( SQLITE_OK != rc ){
@@ -3609,6 +3631,15 @@ static int do_meta_command(char *zLine, ShellState *p){
     sqlite3_close(pSrc);
   }else
 
+  if( c=='r' && n>=2 && strncmp(azArg[0], "recommend", n)==0 ){
+    if( nArg==2 ){
+      p->bRecommend = booleanValue(azArg[1]);
+    }else{
+      raw_printf(stderr, "Usage: .recommend on|off\n");
+      rc = 1;
+    }
+  }else
+
 
   if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
     if( nArg==2 ){
@@ -4903,6 +4934,9 @@ int SQLITE_CDECL main(int argc, char **argv){
           if( bail_on_error ) return rc;
         }
       }
+
+    }else if( strcmp(z, "-recommend") ){
+      data.bRecommend = 1;
     }else{
       utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
       raw_printf(stderr,"Use -help for a list of options.\n");
diff --git a/src/shell_indexes.c b/src/shell_indexes.c
new file mode 100644 (file)
index 0000000..8e105fe
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+** 2016 February 10
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+
+typedef sqlite3_int64 i64;
+
+typedef struct IdxConstraint IdxConstraint;
+typedef struct IdxContext IdxContext;
+typedef struct IdxScan IdxScan;
+typedef struct IdxWhere IdxWhere;
+
+/*
+** A single constraint. Equivalent to either "col = ?" or "col < ?".
+**
+** pLink:
+**   ... todo ...
+*/
+struct IdxConstraint {
+  char *zColl;                    /* Collation sequence */
+  int bRange;                     /* True for range, false for eq */
+  int iCol;                       /* Constrained table column */
+  i64 depmask;                    /* Dependency mask */
+  IdxConstraint *pNext;           /* Next constraint in pEq or pRange list */
+  IdxConstraint *pLink;           /* See above */
+};
+
+/*
+** A WHERE clause. Made up of IdxConstraint objects.
+**
+**   a=? AND b=? AND (c=? OR d=?) AND (e=? OR f=?)
+**
+*/
+struct IdxWhere {
+  IdxConstraint *pEq;             /* List of == constraints */
+  IdxConstraint *pRange;          /* List of < constraints */
+  IdxWhere **apOr;                /* Array of OR branches (joined by pNextOr) */
+  IdxWhere *pNextOr;              /* Next in OR'd terms */
+  IdxWhere *pParent;              /* Parent object (or NULL) */
+};
+
+/*
+** A single scan of a single table.
+*/
+struct IdxScan {
+  char *zTable;                   /* Name of table to scan */
+  int iDb;                        /* Database containing table zTable */
+  i64 covering;                   /* Mask of columns required for cov. index */
+  IdxConstraint *pOrder;          /* ORDER BY columns */
+  IdxWhere where;                 /* WHERE Constraints */
+  IdxScan *pNextScan;             /* Next IdxScan object for same query */
+};
+
+/*
+** Context object passed to idxWhereInfo()
+*/
+struct IdxContext {
+  IdxWhere *pCurrent;             /* Current where clause */
+  IdxScan *pScan;                 /* List of scan objects */
+  sqlite3 *dbm;                   /* In-memory db for this analysis */
+  int rc;                         /* Error code (if error has occurred) */
+};
+
+typedef struct PragmaTable PragmaTable;
+typedef struct PragmaCursor PragmaCursor;
+
+struct PragmaTable {
+  sqlite3_vtab base;
+  sqlite3 *db;
+};
+
+struct PragmaCursor {
+  sqlite3_vtab_cursor base;
+  sqlite3_stmt *pStmt;
+  i64 iRowid;
+};
+
+/*
+** Connect to or create a pragma virtual table.
+*/
+static int pragmaConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVtab,
+  char **pzErr
+){
+  const char *zSchema = 
+    "CREATE TABLE a(tbl HIDDEN, cid, name, type, notnull, dflt_value, pk)";
+  PragmaTable *pTab = 0;
+  int rc = SQLITE_OK;
+
+  rc = sqlite3_declare_vtab(db, zSchema);
+  if( rc==SQLITE_OK ){
+    pTab = (PragmaTable *)sqlite3_malloc64(sizeof(PragmaTable));
+    if( pTab==0 ) rc = SQLITE_NOMEM;
+  }
+
+  assert( rc==SQLITE_OK || pTab==0 );
+  if( rc==SQLITE_OK ){
+    memset(pTab, 0, sizeof(PragmaTable));
+    pTab->db = db;
+  }
+
+  *ppVtab = (sqlite3_vtab*)pTab;
+  return rc;
+}
+
+/*
+** Disconnect from or destroy a pragma virtual table.
+*/
+static int pragmaDisconnect(sqlite3_vtab *pVtab){
+  sqlite3_free(pVtab);
+  return SQLITE_OK;
+}
+
+/*
+** xBestIndex method for pragma virtual tables.
+*/
+static int pragmaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+  int i;
+
+  pIdxInfo->estimatedCost = 1.0e6;  /* Initial cost estimate */
+
+  /* Look for a valid tbl=? constraint. */
+  for(i=0; i<pIdxInfo->nConstraint; i++){
+    if( pIdxInfo->aConstraint[i].usable==0 ) continue;
+    if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+    if( pIdxInfo->aConstraint[i].iColumn!=0 ) continue;
+    pIdxInfo->idxNum = 1;
+    pIdxInfo->estimatedCost = 1.0;
+    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+    pIdxInfo->aConstraintUsage[i].omit = 1;
+    break;
+  }
+  if( i==pIdxInfo->nConstraint ){
+    tab->zErrMsg = sqlite3_mprintf("missing required tbl=? constraint");
+    return SQLITE_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Open a new pragma cursor.
+*/
+static int pragmaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+  PragmaTable *pTab = (PragmaTable *)pVTab;
+  PragmaCursor *pCsr;
+
+  pCsr = (PragmaCursor*)sqlite3_malloc64(sizeof(PragmaCursor));
+  if( pCsr==0 ){
+    return SQLITE_NOMEM;
+  }else{
+    memset(pCsr, 0, sizeof(PragmaCursor));
+    pCsr->base.pVtab = pVTab;
+  }
+
+  *ppCursor = (sqlite3_vtab_cursor*)pCsr;
+  return SQLITE_OK;
+}
+
+/*
+** Move a statvfs cursor to the next entry in the file.
+*/
+static int pragmaNext(sqlite3_vtab_cursor *pCursor){
+  PragmaCursor *pCsr = (PragmaCursor*)pCursor;
+  int rc = SQLITE_OK;
+
+  if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
+    rc = sqlite3_finalize(pCsr->pStmt);
+    pCsr->pStmt = 0;
+  }
+  pCsr->iRowid++;
+  return rc;
+}
+
+static int pragmaEof(sqlite3_vtab_cursor *pCursor){
+  PragmaCursor *pCsr = (PragmaCursor*)pCursor;
+  return pCsr->pStmt==0;
+}
+
+static int pragmaFilter(
+  sqlite3_vtab_cursor *pCursor, 
+  int idxNum, const char *idxStr,
+  int argc, sqlite3_value **argv
+){
+  PragmaCursor *pCsr = (PragmaCursor*)pCursor;
+  PragmaTable *pTab = (PragmaTable*)(pCursor->pVtab);
+  char *zSql;
+  const char *zTbl;
+  int rc = SQLITE_OK;
+
+  if( pCsr->pStmt ){
+    sqlite3_finalize(pCsr->pStmt);
+    pCsr->pStmt = 0;
+  }
+  pCsr->iRowid = 0;
+
+  assert( argc==1 );
+  zTbl = (const char*)sqlite3_value_text(argv[0]);
+  zSql = sqlite3_mprintf("PRAGMA table_info(%Q)", zTbl);
+  if( zSql==0 ){
+    rc = SQLITE_NOMEM;
+  }else{
+    rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
+  }
+  return pragmaNext(pCursor);;
+}
+
+/*
+** xColumn method.
+*/
+static int pragmaColumn(
+  sqlite3_vtab_cursor *pCursor, 
+  sqlite3_context *ctx, 
+  int iCol
+){
+  PragmaCursor *pCsr = (PragmaCursor *)pCursor;
+  if( iCol>0 ){
+    sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, iCol-1));
+  }
+  return SQLITE_OK;
+}
+
+static int pragmaRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+  PragmaCursor *pCsr = (PragmaCursor *)pCursor;
+  *pRowid = pCsr->iRowid;
+  return SQLITE_OK;
+}
+
+static int registerPragmaVtabs(sqlite3 *db){
+  static sqlite3_module pragma_module = {
+    0,                            /* iVersion */
+    pragmaConnect,                /* xCreate */
+    pragmaConnect,                /* xConnect */
+    pragmaBestIndex,              /* xBestIndex */
+    pragmaDisconnect,             /* xDisconnect */
+    pragmaDisconnect,             /* xDestroy */
+    pragmaOpen,                   /* xOpen - open a cursor */
+    pragmaClose,                  /* xClose - close a cursor */
+    pragmaFilter,                 /* xFilter - configure scan constraints */
+    pragmaNext,                   /* xNext - advance a cursor */
+    pragmaEof,                    /* xEof - check for end of scan */
+    pragmaColumn,                 /* xColumn - read data */
+    pragmaRowid,                  /* xRowid - read data */
+    0,                            /* xUpdate */
+    0,                            /* xBegin */
+    0,                            /* xSync */
+    0,                            /* xCommit */
+    0,                            /* xRollback */
+    0,                            /* xFindMethod */
+    0,                            /* xRename */
+  };
+  return sqlite3_create_module(db, "pragma_table_info", &pragma_module, 0);
+}
+
+/*
+** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc(). 
+** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL.
+*/
+static void *idxMalloc(int *pRc, int nByte){
+  void *pRet;
+  assert( *pRc==SQLITE_OK );
+  assert( nByte>0 );
+  pRet = sqlite3_malloc(nByte);
+  if( pRet ){
+    memset(pRet, 0, nByte);
+  }else{
+    *pRc = SQLITE_NOMEM;
+  }
+  return pRet;
+}
+
+/*
+** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl
+** variable to point to a copy of nul-terminated string zColl.
+*/
+static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){
+  IdxConstraint *pNew;
+  int nColl = strlen(zColl);
+
+  assert( *pRc==SQLITE_OK );
+  pNew = (IdxConstraint*)idxMalloc(pRc, sizeof(IdxConstraint) * nColl + 1);
+  if( pNew ){
+    pNew->zColl = (char*)&pNew[1];
+    memcpy(pNew->zColl, zColl, nColl+1);
+  }
+  return pNew;
+}
+
+/*
+** SQLITE_DBCONFIG_WHEREINFO callback.
+*/
+static void idxWhereInfo(
+  void *pCtx,                     /* Pointer to IdxContext structure */
+  int eOp, 
+  const char *zVal, 
+  int iVal, 
+  i64 mask
+){
+  IdxContext *p = (IdxContext*)pCtx;
+
+#if 1
+  const char *zOp = 
+    eOp==SQLITE_WHEREINFO_TABLE ? "TABLE" :
+    eOp==SQLITE_WHEREINFO_EQUALS ? "EQUALS" :
+    eOp==SQLITE_WHEREINFO_RANGE ? "RANGE" :
+    eOp==SQLITE_WHEREINFO_ORDERBY ? "ORDERBY" :
+    eOp==SQLITE_WHEREINFO_NEXTOR ? "NEXTOR" :
+    eOp==SQLITE_WHEREINFO_ENDOR ? "ENDOR" :
+    eOp==SQLITE_WHEREINFO_BEGINOR ? "BEGINOR" :
+    "!error!";
+  printf("op=%s zVal=%s iVal=%d mask=%llx\n", zOp, zVal, iVal, mask);
+#endif
+
+  if( p->rc==SQLITE_OK ){
+    assert( eOp==SQLITE_WHEREINFO_TABLE || p->pScan!=0 );
+    switch( eOp ){
+      case SQLITE_WHEREINFO_TABLE: {
+        int nVal = strlen(zVal);
+        IdxScan *pNew = (IdxScan*)idxMalloc(&p->rc, sizeof(IdxScan) + nVal + 1);
+        if( !pNew ) return;
+        pNew->zTable = (char*)&pNew[1];
+        memcpy(pNew->zTable, zVal, nVal+1);
+        pNew->pNextScan = p->pScan;
+        pNew->covering = mask;
+        p->pScan = pNew;
+        p->pCurrent = &pNew->where;
+        break;
+      }
+
+      case SQLITE_WHEREINFO_ORDERBY: {
+        IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal);
+        IdxConstraint **pp;
+        if( pNew==0 ) return;
+        pNew->iCol = iVal;
+        for(pp=&p->pScan->pOrder; *pp; pp=&(*pp)->pNext);
+        *pp = pNew;
+        break;
+      }
+
+      case SQLITE_WHEREINFO_EQUALS:
+      case SQLITE_WHEREINFO_RANGE: {
+        IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal);
+        if( pNew==0 ) return;
+        pNew->iCol = iVal;
+        pNew->depmask = mask;
+
+        if( eOp==SQLITE_WHEREINFO_RANGE ){
+          pNew->pNext = p->pCurrent->pRange;
+          p->pCurrent->pRange = pNew;
+        }else{
+          pNew->pNext = p->pCurrent->pEq;
+          p->pCurrent->pEq = pNew;
+        }
+        break;
+      }
+
+      case SQLITE_WHEREINFO_BEGINOR: {
+        assert( 0 );
+        break;
+      }
+      case SQLITE_WHEREINFO_ENDOR: {
+        assert( 0 );
+        break;
+      }
+      case SQLITE_WHEREINFO_NEXTOR: {
+        assert( 0 );
+        break;
+      }
+    }
+  }
+}
+
+/*
+** An error associated with database handle db has just occurred. Pass
+** the error message to callback function xOut.
+*/
+static void idxDatabaseError(
+  sqlite3 *db,                    /* Database handle */
+  char **pzErrmsg                 /* Write error here */
+){
+  *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+}
+
+static int idxCreateTables(sqlite3 *db, sqlite3 *dbm, IdxScan *pScan){
+  int rc = SQLITE_OK;
+  IdxScan *pIter;
+  for(pIter=pScan; pIter; pIter=pIter->pNextScan){
+  }
+}
+
+static void idxScanFree(IdxScan *pScan){
+}
+
+/*
+** The xOut callback is invoked to return command output to the user. The
+** second argument is always a nul-terminated string. The first argument is
+** passed zero if the string contains normal output or non-zero if it is an
+** error message.
+*/
+int shellIndexesCommand(
+  sqlite3 *db,                         /* Database handle */
+  const char *zSql,                    /* SQL to find indexes for */
+  void (*xOut)(void*, const char*),    /* Output callback */
+  void *pOutCtx,                       /* Context for xOut() */
+  char **pzErrmsg                      /* OUT: Error message (sqlite3_malloc) */
+){
+  int rc = SQLITE_OK;
+  sqlite3 *dbm = 0;
+  IdxContext ctx;
+  sqlite3_stmt *pStmt = 0;        /* Statement compiled from zSql */
+
+  memset(&ctx, 0, sizeof(IdxContext));
+
+  /* Open an in-memory database to work with. The main in-memory 
+  ** database schema contains tables similar to those in the users 
+  ** database (handle db). The attached in-memory db (aux) contains
+  ** application tables used by the code in this file.  */
+  rc = sqlite3_open(":memory:", &dbm);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_exec(dbm, 
+        "ATTACH ':memory:' AS aux;"
+        "CREATE TABLE aux.depmask(mask PRIMARY KEY) WITHOUT ROWID;"
+        , 0, 0, 0
+    );
+  }
+  if( rc!=SQLITE_OK ){
+    idxDatabaseError(dbm, pzErrmsg);
+    goto indexes_out;
+  }
+
+  /* Analyze the SELECT statement in zSql. */
+  ctx.dbm = dbm;
+  sqlite3_db_config(db, SQLITE_DBCONFIG_WHEREINFO, idxWhereInfo, (void*)&ctx);
+  rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+  sqlite3_db_config(db, SQLITE_DBCONFIG_WHEREINFO, (void*)0, (void*)0);
+  if( rc!=SQLITE_OK ){
+    idxDatabaseError(db, pzErrmsg);
+    goto indexes_out;
+  }
+
+  /* Create tables within the main in-memory database. These tables
+  ** have the same names, columns and declared types as the tables in
+  ** the user database. All constraints except for PRIMARY KEY are
+  ** removed. */
+  rc = idxCreateTables(db, dbm, ctx.pScan);
+  if( rc!=SQLITE_OK ){
+    goto indexes_out;
+  }
+
+  /* Create candidate indexes within the in-memory database file */
+
+ indexes_out:
+  idxScanFree(ctx.pScan);
+  sqlite3_close(dbm);
+  return rc;
+}
+
+
index fce396c0f63d57e5fe5bf8be409ef448006ab5ef..03d3d72f6f56f156547d5b60af9428ddaa04fd23 100644 (file)
@@ -1909,6 +1909,15 @@ struct sqlite3_mem_methods {
 #define SQLITE_DBCONFIG_LOOKASIDE       1001  /* void* int int */
 #define SQLITE_DBCONFIG_ENABLE_FKEY     1002  /* int int* */
 #define SQLITE_DBCONFIG_ENABLE_TRIGGER  1003  /* int int* */
+#define SQLITE_DBCONFIG_WHEREINFO       1004  /* xWhereInfo void* */
+
+#define SQLITE_WHEREINFO_TABLE   1
+#define SQLITE_WHEREINFO_EQUALS  2
+#define SQLITE_WHEREINFO_RANGE   3
+#define SQLITE_WHEREINFO_ORDERBY 4
+#define SQLITE_WHEREINFO_BEGINOR 5
+#define SQLITE_WHEREINFO_ENDOR   6
+#define SQLITE_WHEREINFO_NEXTOR  7
 
 
 /*
index 760c1f4d219e6d060f8d1dde7802504d317f835a..57c58195e01da5425717dbce8df60bed4f623356 100644 (file)
@@ -1274,6 +1274,10 @@ struct sqlite3 {
 #ifdef SQLITE_USER_AUTHENTICATION
   sqlite3_userauth auth;        /* User authentication information */
 #endif
+#ifdef SQLITE_SCHEMA_LINT
+  void (*xWhereInfo)(void*, int, const char*, int, i64);
+  void *pWhereInfoCtx;
+#endif
 };
 
 /*
index af6bb20384f0747841c80aaf8a7e885ee60e24c4..3724fa33e0ec3c841be0464e2b84e8b9a9cdd86e 100644 (file)
@@ -3906,176 +3906,103 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
 }
 
 #ifdef SQLITE_SCHEMA_LINT
-static char *whereAppendPrintf(sqlite3 *db, const char *zFmt, ...){
-  va_list ap;
-  char *zRes = 0;
-  va_start(ap, zFmt);
-  zRes = sqlite3_vmprintf(zFmt, ap);
-  if( zRes==0 ){
-    db->mallocFailed = 1;
-  }else if( db->mallocFailed ){
-    sqlite3_free(zRes);
-    zRes = 0;
-  }
-  va_end(ap);
-  return zRes;
-}
-
-/*
-** Append a representation of term pTerm to the string in zIn and return
-** the result. Or, if an OOM occurs, free zIn and return a NULL pointer.
-*/
-static char *whereAppendSingleTerm(
-  Parse *pParse,
-  Table *pTab,
-  int iCol,
-  int bOr,
-  char *zIn,
-  WhereTerm *pTerm
-){
-  char *zBuf;
-  sqlite3 *db = pParse->db;
-  Expr *pX = pTerm->pExpr;
-  CollSeq *pColl;
-  const char *zOp = 0;
-
-  if( pTerm->eOperator & (WO_IS|WO_EQ|WO_IN) ){
-    zOp = "eq";
-  }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GE|WO_GT) ){
-    zOp = "range";
-  }
-  pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
-
-  if( zOp ){
-    const char *zFmt = bOr ? "%z{{%s \"%w\" \"%w\" %lld}}" :
-                             "%z{%s \"%w\" \"%w\" %lld}";
-    zBuf = whereAppendPrintf(db, zFmt, zIn, 
-        zOp, pTab->aCol[iCol].zName, 
-        (pColl ? pColl->zName : "BINARY"),
-        pTerm->prereqRight
-    );
-  }else{
-    zBuf = zIn;
-  }
-
-  return zBuf;
-}
-
-static char *whereTraceWC(
+static void whereTraceWC(
   Parse *pParse, 
-  int bInitialSpace,
   struct SrcList_item *pItem,
-  char *zIn,
-  WhereClause *pWC
+  WhereClause *pWC,
+  int bOr
 ){
   sqlite3 *db = pParse->db;
   Table *pTab = pItem->pTab;
-  char *zBuf = zIn;
-  int iCol;
+  void (*x)(void*, int, const char*, int, i64) = db->xWhereInfo;
+  void *pCtx = db->pWhereInfoCtx;
+  int bFirst = 1;                 /* True until first callback is made */
   int ii;
-  int bFirst = !bInitialSpace;
-  int bOr = (pWC->op==TK_OR);
 
-  /* List of WO_SINGLE constraints */
-  for(iCol=0; iCol<pTab->nCol; iCol++){
+  /* Issue callbacks for WO_SINGLE constraints */
+  for(ii=0; ii<pTab->nCol; ii++){
     int opMask = WO_SINGLE; 
     WhereScan scan;
     WhereTerm *pTerm;
-    for(pTerm=whereScanInit(&scan, pWC, pItem->iCursor, iCol, opMask, 0);
+    for(pTerm=whereScanInit(&scan, pWC, pItem->iCursor, ii, opMask, 0);
         pTerm;
         pTerm=whereScanNext(&scan)
     ){
-      /* assert( iCol==pTerm->u.leftColumn ); */
-      if( bFirst==0 ) zBuf = whereAppendPrintf(db, "%z ", zBuf);
-      zBuf = whereAppendSingleTerm(pParse, pTab, iCol, bOr, zBuf, pTerm);
+      int eOp;
+      Expr *pX = pTerm->pExpr;
+      CollSeq *pC = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
+      if( pTerm->eOperator & (WO_IS|WO_EQ|WO_IN) ){
+        eOp = SQLITE_WHEREINFO_EQUALS;
+      }else{
+        eOp = SQLITE_WHEREINFO_RANGE;
+      }
+      if( bOr && !bFirst ) x(pCtx, SQLITE_WHEREINFO_NEXTOR, 0, 0, 0);
+      x(pCtx, eOp, (pC ? pC->zName : "BINARY"), ii, pTerm->prereqRight);
       bFirst = 0;
     }
   }
 
-  /* Add composite - (WO_OR|WO_AND) - constraints */
+  /* Callbacks for composite - (WO_OR|WO_AND) - constraints */
   for(ii=0; ii<pWC->nTerm; ii++){
     WhereTerm *pTerm = &pWC->a[ii];
-    if( pTerm->eOperator & (WO_OR|WO_AND) ){
-      const char *zFmt = ((pTerm->eOperator&WO_OR) ? "%z%s{or " : "%z%s{");
-      zBuf = whereAppendPrintf(db, zFmt, zBuf, bFirst ? "" : " ");
-      zBuf = whereTraceWC(pParse, 0, pItem, zBuf, &pTerm->u.pOrInfo->wc);
-      zBuf = whereAppendPrintf(db, "%z}", zBuf);
+    if( pTerm->eOperator & WO_OR ){
+      assert( bOr==0 );
+      x(pCtx, SQLITE_WHEREINFO_BEGINOR, 0, 0, 0);
+      whereTraceWC(pParse, pItem, &pTerm->u.pOrInfo->wc, 1);
+      x(pCtx, SQLITE_WHEREINFO_ENDOR, 0, 0, 0);
+    }
+    if( pTerm->eOperator & WO_AND ){
+      if( bOr && !bFirst ) x(pCtx, SQLITE_WHEREINFO_NEXTOR, 0, 0, 0);
+      whereTraceWC(pParse, pItem, &pTerm->u.pAndInfo->wc, 0);
       bFirst = 0;
     }
   }
-
-  return zBuf;
 }
 
+
 static void whereTraceBuilder(
   Parse *pParse,
   WhereLoopBuilder *p
 ){
   sqlite3 *db = pParse->db;
-  if( db->xTrace ){
-    ExprList *pOrderBy = p->pOrderBy;
-    WhereInfo *pWInfo = p->pWInfo;
-    int nTablist = pWInfo->pTabList->nSrc;
+  if( db->xWhereInfo && db->init.busy==0 ){
+    void (*x)(void*, int, const char*, int, i64) = db->xWhereInfo;
+    void *pCtx = db->pWhereInfoCtx;
     int ii;
+    int nTab = p->pWInfo->pTabList->nSrc;
 
     /* Loop through each element of the FROM clause. Ignore any sub-selects
-    ** or views. Invoke the xTrace() callback once for each real table. */
-    for(ii=0; ii<nTablist; ii++){
-      char *zBuf = 0;
-      int iCol;
-      int nCol;
-      Table *pTab;
-
-      struct SrcList_item *pItem = &pWInfo->pTabList->a[ii];
-      if( pItem->pSelect ) continue;
-      pTab = pItem->pTab;
-      nCol = pTab->nCol;
-
-      /* Append the table name to the buffer. */
-      zBuf = whereAppendPrintf(db, "\"%w\"", pTab->zName);
-
-      /* Append the list of columns required to create a covering index */
-      zBuf = whereAppendPrintf(db, "%z {cols", zBuf);
-      if( 0==(pItem->colUsed & ((u64)1 << (sizeof(Bitmask)*8-1))) ){
-        for(iCol=0; iCol<nCol; iCol++){
-          if( iCol==(sizeof(Bitmask)*8-1) ) break;
-          if( pItem->colUsed & ((u64)1 << iCol) ){
-            const char *zName = pTab->aCol[iCol].zName;
-            zBuf = whereAppendPrintf(db, "%z \"%w\"", zBuf, zName);
-          }
-        }
-      }
-      zBuf = whereAppendPrintf(db, "%z}",zBuf);
-
-      /* Append the contents of WHERE clause */
-      zBuf = whereTraceWC(pParse, 1, pItem, zBuf, p->pWC);
-
-      /* Append the ORDER BY clause, if any */
-      if( pOrderBy ){
-        int i;
-        int bFirst = 1;
-        for(i=0; i<pOrderBy->nExpr; i++){
-          Expr *pExpr = pOrderBy->a[i].pExpr; 
-          CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr);
-
-          pExpr = sqlite3ExprSkipCollate(pExpr);
-          if( pExpr->op==TK_COLUMN && pExpr->iTable==pItem->iCursor ){
-            if( pExpr->iColumn>=0 ){
-              const char *zName = pTab->aCol[pExpr->iColumn].zName;
-              zBuf = whereAppendPrintf(db, "%z%s\"%w\" \"%w\" %s", zBuf,
-                  bFirst ? " {orderby " : " ", zName, pColl->zName,
-                  (pOrderBy->a[i].sortOrder ? "DESC" : "ASC")
-              );
-              bFirst = 0;
+    ** or views. Invoke the xWhereInfo() callback multiple times for each
+    ** real table.  */
+    for(ii=0; ii<p->pWInfo->pTabList->nSrc; ii++){
+      struct SrcList_item *pItem = &p->pWInfo->pTabList->a[ii];
+      if( pItem->pSelect==0 ){
+        Table *pTab = pItem->pTab;
+        int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+
+        /* Table name callback */
+        x(pCtx, SQLITE_WHEREINFO_TABLE, pTab->zName, iDb, pItem->colUsed);
+
+        /* ORDER BY callbacks */
+        if( p->pOrderBy ){
+          int i;
+          int bFirst = 1;
+          for(i=0; i<p->pOrderBy->nExpr; i++){
+            Expr *pExpr = p->pOrderBy->a[i].pExpr; 
+            CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr);
+            pExpr = sqlite3ExprSkipCollate(pExpr);
+            if( pExpr->op==TK_COLUMN && pExpr->iTable==pItem->iCursor ){
+              int iCol = pExpr->iColumn;
+              if( iCol>=0 ){
+                x(pCtx, SQLITE_WHEREINFO_ORDERBY, pColl->zName, iCol, 0); 
+              }
             }
           }
         }
-        if( bFirst==0 ) zBuf = whereAppendPrintf(db, "%z}", zBuf);
-      }
 
-      /* Pass the buffer to the xTrace() callback, then free it */
-      db->xTrace(db->pTraceArg, zBuf);
-      sqlite3DbFree(db, zBuf);
+        /* WHERE callbacks */
+        whereTraceWC(pParse, pItem, p->pWC, 0);
+      }
     }
   }
 }