Progress towards integrating schemalint into the shell tool. Some cases work now.
FossilOrigin-Name: 58d4cf26e15f90463148bec63d6ab514ffbbae60
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
C Experimental\sintegration\sof\sschemalint\sfunctionality\swith\sthe\sshell\stool.\sDoes\snot\swork\syet.
|
||||
D 2016-02-11T21:01:16.253
|
||||
C Progress\stowards\sintegrating\sschemalint\sinto\sthe\sshell\stool.\sSome\scases\swork\snow.
|
||||
D 2016-02-15T20:12:16.368
|
||||
F Makefile.in dac2776c84e0d533b158a9af6e57e05c4a6b19f3
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc b0493f10caddb8adf992a4e6f1943141fc7c6816
|
||||
@@ -350,7 +350,7 @@ F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c
|
||||
F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e
|
||||
F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d
|
||||
F src/shell.c 2cde87e03712204231167c4a6c61b0eb5129e105
|
||||
F src/shell_indexes.c 3cff393ee86d15fbfbe31f30cd752b46d7779b52
|
||||
F src/shell_indexes.c eefa3e9b7d992d10d1f71e27a2e12024a838b365
|
||||
F src/sqlite.h.in c7db059d3b810b70b83d9ed1436fa813eba22462
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
|
||||
@@ -1430,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 1a4182eedd0143c3f71b3d97f1d1bb25adeba617
|
||||
R 6253a80a5a7097c3b24b24ce68900613
|
||||
P ed49f297bcee86674ed673e195610b8cc1d35647
|
||||
R 6a53f87d207675e944487abae6392564
|
||||
U dan
|
||||
Z bddf3d3a7cd183a6a2362ed54e10358b
|
||||
Z dd362fd8eca18359c7fdd88fbcc31963
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ed49f297bcee86674ed673e195610b8cc1d35647
|
||||
58d4cf26e15f90463148bec63d6ab514ffbbae60
|
||||
+489
-16
@@ -18,6 +18,9 @@ typedef struct IdxContext IdxContext;
|
||||
typedef struct IdxScan IdxScan;
|
||||
typedef struct IdxWhere IdxWhere;
|
||||
|
||||
typedef struct IdxColumn IdxColumn;
|
||||
typedef struct IdxTable IdxTable;
|
||||
|
||||
/*
|
||||
** A single constraint. Equivalent to either "col = ?" or "col < ?".
|
||||
**
|
||||
@@ -34,16 +37,22 @@ struct IdxConstraint {
|
||||
};
|
||||
|
||||
/*
|
||||
** A WHERE clause. Made up of IdxConstraint objects.
|
||||
** A WHERE clause. Made up of IdxConstraint objects. Example WHERE clause:
|
||||
**
|
||||
** a=? AND b=? AND (c=? OR d=?) AND (e=? OR f=?)
|
||||
**
|
||||
** The above
|
||||
**
|
||||
**
|
||||
**
|
||||
*/
|
||||
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 *pOr; /* List of OR constraints */
|
||||
|
||||
IdxWhere *pNextOr; /* Next in OR constraints of same IdxWhere */
|
||||
IdxWhere *pSibling; /* Next branch in single OR constraint */
|
||||
IdxWhere *pParent; /* Parent object (or NULL) */
|
||||
};
|
||||
|
||||
@@ -51,6 +60,7 @@ struct IdxWhere {
|
||||
** A single scan of a single table.
|
||||
*/
|
||||
struct IdxScan {
|
||||
IdxTable *pTable; /* Table-info */
|
||||
char *zTable; /* Name of table to scan */
|
||||
int iDb; /* Database containing table zTable */
|
||||
i64 covering; /* Mask of columns required for cov. index */
|
||||
@@ -64,11 +74,27 @@ struct IdxScan {
|
||||
*/
|
||||
struct IdxContext {
|
||||
IdxWhere *pCurrent; /* Current where clause */
|
||||
int rc; /* Error code (if error has occurred) */
|
||||
IdxScan *pScan; /* List of scan objects */
|
||||
sqlite3 *dbm; /* In-memory db for this analysis */
|
||||
int rc; /* Error code (if error has occurred) */
|
||||
sqlite3 *db; /* User database under analysis */
|
||||
sqlite3_stmt *pInsertMask; /* To write to aux.depmask */
|
||||
};
|
||||
|
||||
/*
|
||||
** Data regarding a database table. Extracted from "PRAGMA table_info"
|
||||
*/
|
||||
struct IdxColumn {
|
||||
char *zName;
|
||||
char *zColl;
|
||||
int iPk;
|
||||
};
|
||||
struct IdxTable {
|
||||
int nCol;
|
||||
IdxColumn *aCol;
|
||||
};
|
||||
|
||||
|
||||
typedef struct PragmaTable PragmaTable;
|
||||
typedef struct PragmaCursor PragmaCursor;
|
||||
|
||||
@@ -94,7 +120,7 @@ static int pragmaConnect(
|
||||
char **pzErr
|
||||
){
|
||||
const char *zSchema =
|
||||
"CREATE TABLE a(tbl HIDDEN, cid, name, type, notnull, dflt_value, pk)";
|
||||
"CREATE TABLE a(tbl HIDDEN, cid, name, type, isnotnull, dflt_value, pk)";
|
||||
PragmaTable *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
@@ -102,6 +128,8 @@ static int pragmaConnect(
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (PragmaTable *)sqlite3_malloc64(sizeof(PragmaTable));
|
||||
if( pTab==0 ) rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
assert( rc==SQLITE_OK || pTab==0 );
|
||||
@@ -152,7 +180,6 @@ static int pragmaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
** 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));
|
||||
@@ -167,6 +194,13 @@ static int pragmaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int pragmaClose(sqlite3_vtab_cursor *pCursor){
|
||||
PragmaCursor *pCsr = (PragmaCursor*)pCursor;
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a statvfs cursor to the next entry in the file.
|
||||
*/
|
||||
@@ -212,6 +246,7 @@ static int pragmaFilter(
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
|
||||
}
|
||||
if( rc ) return rc;
|
||||
return pragmaNext(pCursor);;
|
||||
}
|
||||
|
||||
@@ -308,7 +343,7 @@ static void idxWhereInfo(
|
||||
){
|
||||
IdxContext *p = (IdxContext*)pCtx;
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
const char *zOp =
|
||||
eOp==SQLITE_WHEREINFO_TABLE ? "TABLE" :
|
||||
eOp==SQLITE_WHEREINFO_EQUALS ? "EQUALS" :
|
||||
@@ -361,19 +396,36 @@ static void idxWhereInfo(
|
||||
pNew->pNext = p->pCurrent->pEq;
|
||||
p->pCurrent->pEq = pNew;
|
||||
}
|
||||
|
||||
sqlite3_bind_int64(p->pInsertMask, 1, mask);
|
||||
sqlite3_step(p->pInsertMask);
|
||||
p->rc = sqlite3_reset(p->pInsertMask);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_WHEREINFO_BEGINOR: {
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
case SQLITE_WHEREINFO_ENDOR: {
|
||||
assert( 0 );
|
||||
IdxWhere *pNew = (IdxWhere*)idxMalloc(&p->rc, sizeof(IdxWhere));
|
||||
if( pNew==0 ) return;
|
||||
pNew->pParent = p->pCurrent;
|
||||
pNew->pNextOr = p->pCurrent->pOr;
|
||||
p->pCurrent->pOr = pNew;
|
||||
p->pCurrent = pNew;
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_WHEREINFO_NEXTOR: {
|
||||
assert( 0 );
|
||||
IdxWhere *pNew = (IdxWhere*)idxMalloc(&p->rc, sizeof(IdxWhere));
|
||||
if( pNew==0 ) return;
|
||||
pNew->pParent = p->pCurrent->pParent;
|
||||
assert( p->pCurrent->pSibling==0 );
|
||||
p->pCurrent->pSibling = pNew;
|
||||
p->pCurrent = pNew;
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_WHEREINFO_ENDOR: {
|
||||
assert( p->pCurrent->pParent );
|
||||
p->pCurrent = p->pCurrent->pParent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -391,16 +443,418 @@ static void idxDatabaseError(
|
||||
*pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
static int idxCreateTables(sqlite3 *db, sqlite3 *dbm, IdxScan *pScan){
|
||||
static char *idxQueryToList(
|
||||
sqlite3 *db,
|
||||
const char *zBind,
|
||||
int *pRc,
|
||||
char **pzErrmsg,
|
||||
const char *zSql
|
||||
){
|
||||
char *zRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pStmt, 1, zBind, -1, SQLITE_TRANSIENT);
|
||||
while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *z = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
zRet = sqlite3_mprintf("%z%s%Q", zRet, zRet?", ":"", z);
|
||||
if( zRet==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
if( rc ){
|
||||
idxDatabaseError(db, pzErrmsg);
|
||||
sqlite3_free(zRet);
|
||||
zRet = 0;
|
||||
}
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
return zRet;
|
||||
}
|
||||
|
||||
static int idxGetTableInfo(
|
||||
sqlite3 *db,
|
||||
IdxScan *pScan,
|
||||
char **pzErrmsg
|
||||
){
|
||||
const char *zSql = "SELECT name, pk FROM pragma_table_info(?)";
|
||||
sqlite3_stmt *p1 = 0;
|
||||
int nCol = 0;
|
||||
int nByte = sizeof(IdxTable);
|
||||
IdxTable *pNew = 0;
|
||||
int rc, rc2;
|
||||
char *pCsr;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &p1, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
idxDatabaseError(db, pzErrmsg);
|
||||
return rc;
|
||||
}
|
||||
sqlite3_bind_text(p1, 1, pScan->zTable, -1, SQLITE_TRANSIENT);
|
||||
while( SQLITE_ROW==sqlite3_step(p1) ){
|
||||
const char *zCol = sqlite3_column_text(p1, 0);
|
||||
nByte += 1 + strlen(zCol);
|
||||
rc = sqlite3_table_column_metadata(
|
||||
db, "main", pScan->zTable, zCol, 0, &zCol, 0, 0, 0
|
||||
);
|
||||
nByte += 1 + strlen(zCol);
|
||||
nCol++;
|
||||
}
|
||||
rc2 = sqlite3_reset(p1);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
|
||||
nByte += sizeof(IdxColumn) * nCol;
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = idxMalloc(&rc, nByte);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew->aCol = (IdxColumn*)&pNew[1];
|
||||
pNew->nCol = nCol;
|
||||
pCsr = (char*)&pNew->aCol[nCol];
|
||||
}
|
||||
|
||||
nCol = 0;
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
|
||||
const char *zCol = sqlite3_column_text(p1, 0);
|
||||
int nCopy = strlen(zCol) + 1;
|
||||
pNew->aCol[nCol].zName = pCsr;
|
||||
pNew->aCol[nCol].iPk = sqlite3_column_int(p1, 1);
|
||||
memcpy(pCsr, zCol, nCopy);
|
||||
pCsr += nCopy;
|
||||
|
||||
rc = sqlite3_table_column_metadata(
|
||||
db, "main", pScan->zTable, zCol, 0, &zCol, 0, 0, 0
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
nCopy = strlen(zCol) + 1;
|
||||
pNew->aCol[nCol].zColl = pCsr;
|
||||
memcpy(pCsr, zCol, nCopy);
|
||||
pCsr += nCopy;
|
||||
}
|
||||
|
||||
nCol++;
|
||||
}
|
||||
rc2 = sqlite3_finalize(p1);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pScan->pTable = pNew;
|
||||
}else{
|
||||
sqlite3_free(pNew);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int idxCreateTables(
|
||||
sqlite3 *db, /* User database */
|
||||
sqlite3 *dbm, /* In-memory database to create tables in */
|
||||
IdxScan *pScan, /* List of scans */
|
||||
char **pzErrmsg /* OUT: Error message */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
IdxScan *pIter;
|
||||
for(pIter=pScan; pIter; pIter=pIter->pNextScan){
|
||||
for(pIter=pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
|
||||
int nPk = 0;
|
||||
char *zCols = 0;
|
||||
char *zPk = 0;
|
||||
char *zCreate = 0;
|
||||
int iCol;
|
||||
|
||||
rc = idxGetTableInfo(db, pIter, pzErrmsg);
|
||||
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<pIter->pTable->nCol; iCol++){
|
||||
IdxColumn *pCol = &pIter->pTable->aCol[iCol];
|
||||
if( pCol->iPk>nPk ) nPk = pCol->iPk;
|
||||
zCols = sqlite3_mprintf("%z%s%Q", zCols, (zCols?", ":""), pCol->zName);
|
||||
if( zCols==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=nPk; iCol++){
|
||||
int j;
|
||||
for(j=0; j<pIter->pTable->nCol; j++){
|
||||
IdxColumn *pCol = &pIter->pTable->aCol[j];
|
||||
if( pCol->iPk==iCol ){
|
||||
zPk = sqlite3_mprintf("%z%s%Q", zPk, (zPk?", ":""), pCol->zName);
|
||||
if( zPk==0 ) rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( zPk ){
|
||||
zCreate = sqlite3_mprintf("CREATE TABLE %Q(%s, PRIMARY KEY(%s))",
|
||||
pIter->zTable, zCols, zPk
|
||||
);
|
||||
}else{
|
||||
zCreate = sqlite3_mprintf("CREATE TABLE %Q(%s)", pIter->zTable, zCols);
|
||||
}
|
||||
if( zCreate==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
#if 1
|
||||
printf("/* %s */\n", zCreate);
|
||||
#endif
|
||||
rc = sqlite3_exec(dbm, zCreate, 0, 0, pzErrmsg);
|
||||
}
|
||||
sqlite3_free(zCols);
|
||||
sqlite3_free(zPk);
|
||||
sqlite3_free(zCreate);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is set to anything other than
|
||||
** SQLITE_OK when it is called.
|
||||
**
|
||||
** If *pRc is initially set to SQLITE_OK, then the text specified by
|
||||
** the printf() style arguments is appended to zIn and the result returned
|
||||
** in a buffer allocated by sqlite3_malloc(). sqlite3_free() is called on
|
||||
** zIn before returning.
|
||||
*/
|
||||
static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
char *zAppend = 0;
|
||||
char *zRet = 0;
|
||||
int nIn = zIn ? strlen(zIn) : 0;
|
||||
int nAppend = 0;
|
||||
va_start(ap, zFmt);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
zAppend = sqlite3_vmprintf(zFmt, ap);
|
||||
if( zAppend ){
|
||||
nAppend = strlen(zAppend);
|
||||
zRet = (char*)sqlite3_malloc(nIn + nAppend);
|
||||
}
|
||||
if( zAppend && zRet ){
|
||||
memcpy(zRet, zIn, nIn);
|
||||
memcpy(&zRet[nIn], zAppend, nAppend+1);
|
||||
}else{
|
||||
sqlite3_free(zRet);
|
||||
zRet = 0;
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3_free(zAppend);
|
||||
sqlite3_free(zIn);
|
||||
}
|
||||
va_end(ap);
|
||||
return zRet;
|
||||
}
|
||||
|
||||
static char *idxAppendColDefn(
|
||||
int *pRc,
|
||||
char *zIn,
|
||||
IdxTable *pTab,
|
||||
IdxConstraint *pCons
|
||||
){
|
||||
char *zRet = zIn;
|
||||
IdxColumn *p = &pTab->aCol[pCons->iCol];
|
||||
if( zRet ) zRet = idxAppendText(pRc, zRet, ", ");
|
||||
zRet = idxAppendText(pRc, zRet, "%Q", p->zName);
|
||||
if( sqlite3_stricmp(p->zColl, pCons->zColl) ){
|
||||
zRet = idxAppendText(pRc, zRet, " COLLATE %Q", pCons->zColl);
|
||||
}
|
||||
return zRet;
|
||||
}
|
||||
|
||||
static int idxCreateFromCons(
|
||||
sqlite3 *dbm,
|
||||
IdxScan *pScan,
|
||||
IdxConstraint *pEq,
|
||||
IdxConstraint *pTail
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
if( pEq || pTail ){
|
||||
IdxTable *pTab = pScan->pTable;
|
||||
char *zCols = 0;
|
||||
char *zIdx = 0;
|
||||
IdxConstraint *pCons;
|
||||
int h = 0;
|
||||
|
||||
for(pCons=pEq; pCons; pCons=pCons->pLink){
|
||||
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
|
||||
}
|
||||
for(pCons=pTail; pCons; pCons=pCons->pLink){
|
||||
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
|
||||
}
|
||||
|
||||
/* Hash the list of columns to come up with a name for the index */
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
for(i=0; zCols[i]; i++){
|
||||
h += ((h<<3) + zCols[i]);
|
||||
}
|
||||
|
||||
zIdx = sqlite3_mprintf("CREATE INDEX IF NOT EXISTS "
|
||||
"'%q_idx_%08x' ON %Q(%s)", pScan->zTable, h, pScan->zTable, zCols
|
||||
);
|
||||
if( !zIdx ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_exec(dbm, zIdx, 0, 0, 0);
|
||||
printf("/* %s */\n", zIdx);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(zIdx);
|
||||
sqlite3_free(zCols);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int idxCreateFromWhere(
|
||||
sqlite3*, i64, IdxScan*, IdxWhere*, IdxConstraint*, IdxConstraint*
|
||||
);
|
||||
|
||||
static int idxCreateForeachOr(
|
||||
sqlite3 *dbm,
|
||||
i64 mask, /* Consider only these constraints */
|
||||
IdxScan *pScan, /* Create indexes for this scan */
|
||||
IdxWhere *pWhere, /* Read constraints from here */
|
||||
IdxConstraint *pEq, /* == constraints for inclusion */
|
||||
IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
IdxWhere *p1;
|
||||
IdxWhere *p2;
|
||||
for(p1=pWhere->pOr; p1 && rc==SQLITE_OK; p1=p1->pNextOr){
|
||||
rc = idxCreateFromWhere(dbm, mask, pScan, p1, pEq, pTail);
|
||||
for(p2=p1->pSibling; p2 && rc==SQLITE_OK; p2=p2->pSibling){
|
||||
rc = idxCreateFromWhere(dbm, mask, pScan, p2, pEq, pTail);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int idxCreateFromWhere(
|
||||
sqlite3 *dbm,
|
||||
i64 mask, /* Consider only these constraints */
|
||||
IdxScan *pScan, /* Create indexes for this scan */
|
||||
IdxWhere *pWhere, /* Read constraints from here */
|
||||
IdxConstraint *pEq, /* == constraints for inclusion */
|
||||
IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */
|
||||
){
|
||||
IdxConstraint *p1 = pEq;
|
||||
IdxConstraint *pCon;
|
||||
int rc;
|
||||
|
||||
/* Gather up all the == constraints that match the mask. */
|
||||
for(pCon=pWhere->pEq; pCon; pCon=pCon->pNext){
|
||||
if( (mask & pCon->depmask)==pCon->depmask ){
|
||||
pCon->pLink = p1;
|
||||
p1 = pCon;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create an index using the == constraints collected above. And the
|
||||
** range constraint/ORDER BY terms passed in by the caller, if any. */
|
||||
rc = idxCreateFromCons(dbm, pScan, p1, pTail);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = idxCreateForeachOr(dbm, mask, pScan, pWhere, p1, pTail);
|
||||
}
|
||||
|
||||
/* If no range/ORDER BY passed by the caller, create a version of the
|
||||
** index for each range constraint that matches the mask. */
|
||||
if( pTail==0 ){
|
||||
for(pCon=pWhere->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){
|
||||
assert( pCon->pLink==0 );
|
||||
if( (mask & pCon->depmask)==pCon->depmask ){
|
||||
rc = idxCreateFromCons(dbm, pScan, p1, pCon);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = idxCreateForeachOr(dbm, mask, pScan, pWhere, p1, pCon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int idxPrepareStmt(
|
||||
sqlite3 *db, /* Database handle to compile against */
|
||||
const char *zSql, /* SQL statement to compile */
|
||||
sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
|
||||
char **pzErrmsg /* OUT: sqlite3_malloc()ed error message */
|
||||
){
|
||||
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*ppStmt = 0;
|
||||
idxDatabaseError(db, pzErrmsg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create candidate indexes in database [dbm] based on the data in
|
||||
** linked-list pScan.
|
||||
*/
|
||||
static int idxCreateCandidates(
|
||||
sqlite3 *dbm,
|
||||
IdxScan *pScan,
|
||||
char **pzErrmsg
|
||||
){
|
||||
int rc2;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pDepmask; /* Foreach depmask */
|
||||
IdxScan *pIter;
|
||||
|
||||
rc = idxPrepareStmt(dbm, "SELECT mask FROM depmask", &pDepmask, pzErrmsg);
|
||||
|
||||
for(pIter=pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
|
||||
IdxWhere *pWhere = &pIter->where;
|
||||
while( SQLITE_ROW==sqlite3_step(pDepmask) && rc==SQLITE_OK ){
|
||||
i64 mask = sqlite3_column_int64(pDepmask, 0);
|
||||
rc = idxCreateFromWhere(dbm, mask, pIter, pWhere, 0, 0);
|
||||
if( rc==SQLITE_OK && pIter->pOrder ){
|
||||
rc = idxCreateFromWhere(dbm, mask, pIter, pWhere, 0, pIter->pOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc2 = sqlite3_finalize(pDepmask);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void idxScanFree(IdxScan *pScan){
|
||||
}
|
||||
|
||||
int idxFindIndexes(
|
||||
sqlite3 *dbm, /* 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) */
|
||||
){
|
||||
char *zExplain;
|
||||
sqlite3_stmt *pExplain;
|
||||
int rc;
|
||||
|
||||
zExplain = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zSql);
|
||||
if( zExplain==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = idxPrepareStmt(dbm, zExplain, &pExplain, pzErrmsg);
|
||||
sqlite3_free(zExplain);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
while( sqlite3_step(pExplain)==SQLITE_ROW ){
|
||||
int iCol;
|
||||
// for(iCol=0; iCol<sqlite3_column_count(pExplain); iCol++){ }
|
||||
xOut(pOutCtx, sqlite3_column_text(pExplain, 3));
|
||||
}
|
||||
rc = sqlite3_finalize(pExplain);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
@@ -419,6 +873,8 @@ int shellIndexesCommand(
|
||||
IdxContext ctx;
|
||||
sqlite3_stmt *pStmt = 0; /* Statement compiled from zSql */
|
||||
|
||||
rc = registerPragmaVtabs(db);
|
||||
if( rc ) return rc;
|
||||
memset(&ctx, 0, sizeof(IdxContext));
|
||||
|
||||
/* Open an in-memory database to work with. The main in-memory
|
||||
@@ -430,9 +886,19 @@ int shellIndexesCommand(
|
||||
rc = sqlite3_exec(dbm,
|
||||
"ATTACH ':memory:' AS aux;"
|
||||
"CREATE TABLE aux.depmask(mask PRIMARY KEY) WITHOUT ROWID;"
|
||||
"INSERT INTO aux.depmask VALUES(0);"
|
||||
, 0, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
/* Prepare an INSERT statement for writing to aux.depmask */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_prepare_v2(dbm,
|
||||
"INSERT OR IGNORE INTO depmask SELECT mask | ?1 FROM depmask;", -1,
|
||||
&ctx.pInsertMask, 0
|
||||
);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
idxDatabaseError(dbm, pzErrmsg);
|
||||
goto indexes_out;
|
||||
@@ -443,6 +909,7 @@ int shellIndexesCommand(
|
||||
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;
|
||||
@@ -452,12 +919,18 @@ int shellIndexesCommand(
|
||||
** 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);
|
||||
rc = idxCreateTables(db, dbm, ctx.pScan, pzErrmsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto indexes_out;
|
||||
}
|
||||
|
||||
/* Create candidate indexes within the in-memory database file */
|
||||
rc = idxCreateCandidates(dbm, ctx.pScan, pzErrmsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto indexes_out;
|
||||
}
|
||||
|
||||
rc = idxFindIndexes(dbm, zSql, xOut, pOutCtx, pzErrmsg);
|
||||
|
||||
indexes_out:
|
||||
idxScanFree(ctx.pScan);
|
||||
|
||||
Reference in New Issue
Block a user