Experimental integration of schemalint functionality with the shell tool. Does not work yet.

FossilOrigin-Name: ed49f297bcee86674ed673e195610b8cc1d35647
This commit is contained in:
dan 2016-02-11 21:01:16 +00:00
parent 02e4f27146
commit 3e6ac1643c
10 changed files with 601 additions and 150 deletions

View File

@ -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)

View File

@ -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

View File

@ -1 +1 @@
1a4182eedd0143c3f71b3d97f1d1bb25adeba617
ed49f297bcee86674ed673e195610b8cc1d35647

View 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 */

View 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;

View 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");

468
src/shell_indexes.c Normal file
View File

@ -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;
}

View 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
/*

View 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
};
/*

View 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;
** 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);
struct SrcList_item *pItem = &pWInfo->pTabList->a[ii];
if( pItem->pSelect ) continue;
pTab = pItem->pTab;
nCol = pTab->nCol;
/* Table name callback */
x(pCtx, SQLITE_WHEREINFO_TABLE, pTab->zName, iDb, pItem->colUsed);
/* 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;
/* 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);
}
}
}
}