1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add ext/expert/README.md.

FossilOrigin-Name: 9318f1b9ed2d8da3a82ea69179e2d56a99d326c7721642665f87f6a4534e7bf0
This commit is contained in:
dan
2017-04-10 20:00:26 +00:00
parent cd84474ece
commit 96d43a05ec
5 changed files with 228 additions and 98 deletions

68
ext/expert/README.md Normal file
View File

@ -0,0 +1,68 @@
## SQLite Expert Extension
This folder contains code for a simple system to propose useful indexes
given a database and a set of SQL queries. It works as follows:
1. The user database schema is copied to a temporary database.
1. All SQL queries are prepared against the temporary database. The
**sqlite3\_whereinfo\_hook()** API is used to record information regarding
the WHERE and ORDER BY clauses attached to each query.
1. The information gathered in step 2 is used to create (possibly a large
number of) candidate indexes.
1. The SQL queries are prepared a second time. If the planner uses any
of the indexes created in step 3, they are recommended to the user.
No ANALYZE data is available to the planner in step 4 above. This can lead to sub-optimal results.
This extension requires that SQLite be built with the
SQLITE\_ENABLE\_WHEREINFO\_HOOK pre-processor symbol defined.
# C API
The SQLite expert C API is defined in sqlite3expert.h. Most uses will proceed
as follows:
1. An sqlite3expert object is created by calling **sqlite3\_expert\_new()**.
A database handle opened by the user is passed as an argument.
1. The sqlite3expert object is configured with one or more SQL statements
by making one or more calls to **sqlite3\_expert\_sql()**. Each call may
specify a single SQL statement, or multiple statements separated by
semi-colons.
1. **sqlite3\_expert\_analyze()** is called to run the analysis.
1. One or more calls are made to **sqlite3\_expert\_report()** to extract
components of the results of the analysis.
1. **sqlite3\_expert\_destroy()** is called to free all resources.
Refer to comments in sqlite3expert.h for further details.
# sqlite3_expert application
The file "expert.c" contains the code for a command line application that
uses the API described above. It can be compiled with (for example):
<pre>
gcc -O2 -DSQLITE_ENABLE_WHEREINFO_HOOK sqlite3.c expert.c sqlite3expert.c -o sqlite3_expert
</pre>
Assuming the database is "test.db", it can then be run to analyze a single query:
<pre>
./sqlite3_expert -sql &lt;sql-query&gt; test.db
</pre>
Or an entire text file worth of queries with:
<pre>
./sqlite3_expert -file &lt;text-file&gt; test.db
</pre>

View File

@ -17,7 +17,9 @@
# Test plan: # Test plan:
# #
# #
set testdir [file dirname $argv0] if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl source $testdir/tester.tcl
set testprefix expert1 set testprefix expert1

View File

@ -1,5 +1,5 @@
/* /*
** 2016 February 10 ** 2017 April 09
** **
** The author disclaims copyright to this source code. In place of ** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing: ** a legal notice, here is a blessing:
@ -21,16 +21,15 @@
typedef sqlite3_int64 i64; typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64; typedef sqlite3_uint64 u64;
typedef struct IdxColumn IdxColumn;
typedef struct IdxConstraint IdxConstraint; typedef struct IdxConstraint IdxConstraint;
typedef struct IdxScan IdxScan; typedef struct IdxScan IdxScan;
typedef struct IdxStatement IdxStatement; typedef struct IdxStatement IdxStatement;
typedef struct IdxWhere IdxWhere;
typedef struct IdxColumn IdxColumn;
typedef struct IdxTable IdxTable; typedef struct IdxTable IdxTable;
/* /*
** A single constraint. Equivalent to either "col = ?" or "col < ?". ** A single constraint. Equivalent to either "col = ?" or "col < ?" (or
** any other type of single-ended range constraint on a column).
** **
** pLink: ** pLink:
** Used to temporarily link IdxConstraint objects into lists while ** Used to temporarily link IdxConstraint objects into lists while
@ -47,38 +46,22 @@ struct IdxConstraint {
IdxConstraint *pLink; /* See above */ IdxConstraint *pLink; /* See above */
}; };
/*
** A WHERE clause. Made up of IdxConstraint objects. Example WHERE clause:
**
** a=? AND b=? AND c=? AND d=? AND e>? AND f<?
**
** The above is decomposed into 6 AND connected clauses. The first four are
** added to the IdxWhere.pEq linked list, the following two into
** IdxWhere.pRange.
**
** IdxWhere.pEq and IdxWhere.pRange are simple linked lists of IdxConstraint
** objects linked by the IdxConstraint.pNext field.
*/
struct IdxWhere {
IdxConstraint *pEq; /* List of == constraints */
IdxConstraint *pRange; /* List of < constraints */
};
/* /*
** A single scan of a single table. ** A single scan of a single table.
*/ */
struct IdxScan { struct IdxScan {
IdxTable *pTable; /* Table-info */
char *zTable; /* Name of table to scan */ char *zTable; /* Name of table to scan */
int iDb; /* Database containing table zTable */ int iDb; /* Database containing table zTable */
i64 covering; /* Mask of columns required for cov. index */ i64 covering; /* Mask of columns required for cov. index */
IdxConstraint *pOrder; /* ORDER BY columns */ IdxConstraint *pOrder; /* ORDER BY columns */
IdxWhere where; /* WHERE Constraints */ IdxConstraint *pEq; /* List of == constraints */
IdxScan *pNextScan; /* Next IdxScan object for same query */ IdxConstraint *pRange; /* List of < constraints */
IdxScan *pNextScan; /* Next IdxScan object for same analysis */
}; };
/* /*
** Data regarding a database table. Extracted from "PRAGMA table_info" ** Information regarding a single database table. Extracted from
** "PRAGMA table_info" by function idxGetTableInfo().
*/ */
struct IdxColumn { struct IdxColumn {
char *zName; char *zName;
@ -90,6 +73,10 @@ struct IdxTable {
IdxColumn *aCol; IdxColumn *aCol;
}; };
/*
** Each statement being analyzed is represented by an instance of this
** structure.
*/
struct IdxStatement { struct IdxStatement {
int iId; /* Statement number */ int iId; /* Statement number */
char *zSql; /* SQL statement */ char *zSql; /* SQL statement */
@ -99,6 +86,15 @@ struct IdxStatement {
}; };
/*
** A hash table for storing strings. With space for a payload string
** with each entry. Methods are:
**
** idxHashInit()
** idxHashClear()
** idxHashAdd()
** idxHashSearch()
*/
#define IDX_HASH_SIZE 1023 #define IDX_HASH_SIZE 1023
typedef struct IdxHashEntry IdxHashEntry; typedef struct IdxHashEntry IdxHashEntry;
typedef struct IdxHash IdxHash; typedef struct IdxHash IdxHash;
@ -113,20 +109,36 @@ struct IdxHash {
IdxHashEntry *aHash[IDX_HASH_SIZE]; IdxHashEntry *aHash[IDX_HASH_SIZE];
}; };
/*
** A hash table for storing a set of 64-bit values. Methods are:
**
** idxHash64Init()
** idxHash64Clear()
** idxHash64Add()
*/
typedef struct IdxHash64Entry IdxHash64Entry;
typedef struct IdxHash64 IdxHash64;
struct IdxHash64Entry {
u64 iVal;
IdxHash64Entry *pNext; /* Next entry in hash table */
IdxHash64Entry *pHashNext; /* Next entry in same hash bucket */
};
struct IdxHash64 {
IdxHash64Entry *pFirst; /* Most recently added entry in hash table */
IdxHash64Entry *aHash[IDX_HASH_SIZE];
};
/* /*
** sqlite3expert object. ** sqlite3expert object.
*/ */
struct sqlite3expert { struct sqlite3expert {
sqlite3 *db; /* Users database */ sqlite3 *db; /* User database */
sqlite3 *dbm; /* In-memory db for this analysis */ sqlite3 *dbm; /* In-memory db for this analysis */
int bRun; /* True once analysis has run */ int bRun; /* True once analysis has run */
char **pzErrmsg; char **pzErrmsg;
IdxScan *pScan; /* List of scan objects */ IdxScan *pScan; /* List of scan objects */
IdxStatement *pStatement; /* List of IdxStatement objects */ IdxStatement *pStatement; /* List of IdxStatement objects */
int rc; /* Error code from whereinfo hook */ int rc; /* Error code from whereinfo hook */
IdxHash hIdx; /* Hash containing all candidate indexes */ IdxHash hIdx; /* Hash containing all candidate indexes */
}; };
@ -148,24 +160,16 @@ static void *idxMalloc(int *pRc, int nByte){
return pRet; return pRet;
} }
/************************************************************************* /*
** Start of hash table implementations. ** Initialize an IdxHash64 hash table.
*/ */
typedef struct IdxHash64Entry IdxHash64Entry;
typedef struct IdxHash64 IdxHash64;
struct IdxHash64Entry {
u64 iVal;
IdxHash64Entry *pNext; /* Next entry in hash table */
IdxHash64Entry *pHashNext; /* Next entry in same hash bucket */
};
struct IdxHash64 {
IdxHash64Entry *pFirst; /* Most recently added entry in hash table */
IdxHash64Entry *aHash[IDX_HASH_SIZE];
};
static void idxHash64Init(IdxHash64 *pHash){ static void idxHash64Init(IdxHash64 *pHash){
memset(pHash, 0, sizeof(IdxHash64)); memset(pHash, 0, sizeof(IdxHash64));
} }
/*
** Reset an IdxHash64 hash table.
*/
static void idxHash64Clear(IdxHash64 *pHash){ static void idxHash64Clear(IdxHash64 *pHash){
IdxHash64Entry *pEntry; IdxHash64Entry *pEntry;
IdxHash64Entry *pNext; IdxHash64Entry *pNext;
@ -175,6 +179,11 @@ static void idxHash64Clear(IdxHash64 *pHash){
} }
memset(pHash, 0, sizeof(IdxHash64)); memset(pHash, 0, sizeof(IdxHash64));
} }
/*
** Add iVal to the IdxHash64 hash table passed as the second argument. This
** function is a no-op if iVal is already present in the hash table.
*/
static void idxHash64Add(int *pRc, IdxHash64 *pHash, u64 iVal){ static void idxHash64Add(int *pRc, IdxHash64 *pHash, u64 iVal){
int iHash = (int)((iVal*7) % IDX_HASH_SIZE); int iHash = (int)((iVal*7) % IDX_HASH_SIZE);
IdxHash64Entry *pEntry; IdxHash64Entry *pEntry;
@ -193,9 +202,16 @@ static void idxHash64Add(int *pRc, IdxHash64 *pHash, u64 iVal){
} }
} }
/*
** Initialize an IdxHash hash table.
*/
static void idxHashInit(IdxHash *pHash){ static void idxHashInit(IdxHash *pHash){
memset(pHash, 0, sizeof(IdxHash)); memset(pHash, 0, sizeof(IdxHash));
} }
/*
** Reset an IdxHash hash table.
*/
static void idxHashClear(IdxHash *pHash){ static void idxHashClear(IdxHash *pHash){
int i; int i;
for(i=0; i<IDX_HASH_SIZE; i++){ for(i=0; i<IDX_HASH_SIZE; i++){
@ -208,6 +224,11 @@ static void idxHashClear(IdxHash *pHash){
} }
memset(pHash, 0, sizeof(IdxHash)); memset(pHash, 0, sizeof(IdxHash));
} }
/*
** Return the index of the hash bucket that the string specified by the
** arguments to this function belongs.
*/
static int idxHashString(const char *z, int n){ static int idxHashString(const char *z, int n){
unsigned int ret = 0; unsigned int ret = 0;
int i; int i;
@ -217,6 +238,11 @@ static int idxHashString(const char *z, int n){
return (int)(ret % IDX_HASH_SIZE); return (int)(ret % IDX_HASH_SIZE);
} }
/*
** If zKey is already present in the hash table, return non-zero and do
** nothing. Otherwise, add an entry with key zKey and payload string zVal to
** the hash table passed as the second argument.
*/
static int idxHashAdd( static int idxHashAdd(
int *pRc, int *pRc,
IdxHash *pHash, IdxHash *pHash,
@ -250,6 +276,12 @@ static int idxHashAdd(
return 0; return 0;
} }
/*
** If the hash table contains an entry with a key equal to the string
** passed as the final two arguments to this function, return a pointer
** to the payload string. Otherwise, if zKey/nKey is not present in the
** hash table, return NULL.
*/
static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){ static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
int iHash; int iHash;
IdxHashEntry *pEntry; IdxHashEntry *pEntry;
@ -264,10 +296,6 @@ static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
return 0; return 0;
} }
/*
** End of hash table implementations.
**************************************************************************/
/* /*
** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl ** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl
** variable to point to a copy of nul-terminated string zColl. ** variable to point to a copy of nul-terminated string zColl.
@ -346,11 +374,11 @@ static void idxWhereInfo(
pNew->depmask = mask; pNew->depmask = mask;
if( eOp==SQLITE_WHEREINFO_RANGE ){ if( eOp==SQLITE_WHEREINFO_RANGE ){
pNew->pNext = p->pScan->where.pRange; pNew->pNext = p->pScan->pRange;
p->pScan->where.pRange = pNew; p->pScan->pRange = pNew;
}else{ }else{
pNew->pNext = p->pScan->where.pEq; pNew->pNext = p->pScan->pEq;
p->pScan->where.pEq = pNew; p->pScan->pEq = pNew;
} }
break; break;
} }
@ -369,6 +397,9 @@ static void idxDatabaseError(
*pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
} }
/*
** Prepare an SQL statement.
*/
static int idxPrepareStmt( static int idxPrepareStmt(
sqlite3 *db, /* Database handle to compile against */ sqlite3 *db, /* Database handle to compile against */
sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */ sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
@ -383,6 +414,9 @@ static int idxPrepareStmt(
return rc; return rc;
} }
/*
** Prepare an SQL statement using the results of a printf() formatting.
*/
static int idxPrintfPrepareStmt( static int idxPrintfPrepareStmt(
sqlite3 *db, /* Database handle to compile against */ sqlite3 *db, /* Database handle to compile against */
sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */ sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
@ -405,17 +439,32 @@ static int idxPrintfPrepareStmt(
return rc; return rc;
} }
/*
** Finalize SQL statement pStmt. If (*pRc) is SQLITE_OK when this function
** is called, set it to the return value of sqlite3_finalize() before
** returning. Otherwise, discard the sqlite3_finalize() return value.
*/
static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){ static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt); int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ) *pRc = rc; if( *pRc==SQLITE_OK ) *pRc = rc;
} }
/*
** Attempt to allocate an IdxTable structure corresponding to table zTab
** in the main database of connection db. If successful, set (*ppOut) to
** point to the new object and return SQLITE_OK. Otherwise, return an
** SQLite error code and set (*ppOut) to NULL. In this case *pzErrmsg may be
** set to point to an error string.
**
** It is the responsibility of the caller to eventually free either the
** IdxTable object or error message using sqlite3_free().
*/
static int idxGetTableInfo( static int idxGetTableInfo(
sqlite3 *db, sqlite3 *db, /* Database connection to read details from */
IdxScan *pScan, const char *zTab, /* Table name */
char **pzErrmsg IdxTable **ppOut, /* OUT: New object (if successful) */
char **pzErrmsg /* OUT: Error message (if not) */
){ ){
const char *zTbl = pScan->zTable;
sqlite3_stmt *p1 = 0; sqlite3_stmt *p1 = 0;
int nCol = 0; int nCol = 0;
int nByte = sizeof(IdxTable); int nByte = sizeof(IdxTable);
@ -423,12 +472,12 @@ static int idxGetTableInfo(
int rc, rc2; int rc, rc2;
char *pCsr; char *pCsr;
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTbl); rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1); const char *zCol = (const char*)sqlite3_column_text(p1, 1);
nByte += 1 + strlen(zCol); nByte += 1 + strlen(zCol);
rc = sqlite3_table_column_metadata( rc = sqlite3_table_column_metadata(
db, "main", zTbl, zCol, 0, &zCol, 0, 0, 0 db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
); );
nByte += 1 + strlen(zCol); nByte += 1 + strlen(zCol);
nCol++; nCol++;
@ -456,7 +505,7 @@ static int idxGetTableInfo(
pCsr += nCopy; pCsr += nCopy;
rc = sqlite3_table_column_metadata( rc = sqlite3_table_column_metadata(
db, "main", zTbl, zCol, 0, &zCol, 0, 0, 0 db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
nCopy = strlen(zCol) + 1; nCopy = strlen(zCol) + 1;
@ -469,12 +518,12 @@ static int idxGetTableInfo(
} }
idxFinalize(&rc, p1); idxFinalize(&rc, p1);
if( rc==SQLITE_OK ){ if( rc!=SQLITE_OK ){
pScan->pTable = pNew;
}else{
sqlite3_free(pNew); sqlite3_free(pNew);
pNew = 0;
} }
*ppOut = pNew;
return rc; return rc;
} }
@ -515,6 +564,10 @@ static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
return zRet; return zRet;
} }
/*
** Return true if zId must be quoted in order to use it as an SQL
** identifier, or false otherwise.
*/
static int idxIdentifierRequiresQuotes(const char *zId){ static int idxIdentifierRequiresQuotes(const char *zId){
int i; int i;
for(i=0; zId[i]; i++){ for(i=0; zId[i]; i++){
@ -529,10 +582,14 @@ static int idxIdentifierRequiresQuotes(const char *zId){
return 0; return 0;
} }
/*
** This function appends an index column definition suitable for constraint
** pCons to the string passed as zIn and returns the result.
*/
static char *idxAppendColDefn( static char *idxAppendColDefn(
int *pRc, int *pRc, /* IN/OUT: Error code */
char *zIn, char *zIn, /* Column defn accumulated so far */
IdxTable *pTab, IdxTable *pTab, /* Table index will be created on */
IdxConstraint *pCons IdxConstraint *pCons
){ ){
char *zRet = zIn; char *zRet = zIn;
@ -636,6 +693,7 @@ static int idxFindCompatible(
static int idxCreateFromCons( static int idxCreateFromCons(
sqlite3expert *p, sqlite3expert *p,
IdxTable *pTab,
IdxScan *pScan, IdxScan *pScan,
IdxConstraint *pEq, IdxConstraint *pEq,
IdxConstraint *pTail IdxConstraint *pTail
@ -643,7 +701,6 @@ static int idxCreateFromCons(
sqlite3 *dbm = p->dbm; sqlite3 *dbm = p->dbm;
int rc = SQLITE_OK; int rc = SQLITE_OK;
if( (pEq || pTail) && 0==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){ if( (pEq || pTail) && 0==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){
IdxTable *pTab = pScan->pTable;
char *zCols = 0; char *zCols = 0;
char *zIdx = 0; char *zIdx = 0;
IdxConstraint *pCons; IdxConstraint *pCons;
@ -704,9 +761,9 @@ static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){
static int idxCreateFromWhere( static int idxCreateFromWhere(
sqlite3expert *p, sqlite3expert *p,
IdxTable *pTab,
i64 mask, /* Consider only these constraints */ i64 mask, /* Consider only these constraints */
IdxScan *pScan, /* Create indexes for this scan */ IdxScan *pScan, /* Create indexes for this scan */
IdxWhere *pWhere, /* Read constraints from here */
IdxConstraint *pEq, /* == constraints for inclusion */ IdxConstraint *pEq, /* == constraints for inclusion */
IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */ IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */
){ ){
@ -715,7 +772,7 @@ static int idxCreateFromWhere(
int rc; int rc;
/* Gather up all the == constraints that match the mask. */ /* Gather up all the == constraints that match the mask. */
for(pCon=pWhere->pEq; pCon; pCon=pCon->pNext){ for(pCon=pScan->pEq; pCon; pCon=pCon->pNext){
if( (mask & pCon->depmask)==pCon->depmask if( (mask & pCon->depmask)==pCon->depmask
&& idxFindConstraint(p1, pCon)==0 && idxFindConstraint(p1, pCon)==0
&& idxFindConstraint(pTail, pCon)==0 && idxFindConstraint(pTail, pCon)==0
@ -727,18 +784,18 @@ static int idxCreateFromWhere(
/* Create an index using the == constraints collected above. And the /* Create an index using the == constraints collected above. And the
** range constraint/ORDER BY terms passed in by the caller, if any. */ ** range constraint/ORDER BY terms passed in by the caller, if any. */
rc = idxCreateFromCons(p, pScan, p1, pTail); rc = idxCreateFromCons(p, pTab, pScan, p1, pTail);
/* If no range/ORDER BY passed by the caller, create a version of the /* If no range/ORDER BY passed by the caller, create a version of the
** index for each range constraint that matches the mask. */ ** index for each range constraint that matches the mask. */
if( pTail==0 ){ if( pTail==0 ){
for(pCon=pWhere->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){ for(pCon=pScan->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){
assert( pCon->pLink==0 ); assert( pCon->pLink==0 );
if( (mask & pCon->depmask)==pCon->depmask if( (mask & pCon->depmask)==pCon->depmask
&& idxFindConstraint(pEq, pCon)==0 && idxFindConstraint(pEq, pCon)==0
&& idxFindConstraint(pTail, pCon)==0 && idxFindConstraint(pTail, pCon)==0
){ ){
rc = idxCreateFromCons(p, pScan, p1, pCon); rc = idxCreateFromCons(p, pTab, pScan, p1, pCon);
} }
} }
} }
@ -758,30 +815,36 @@ static int idxCreateCandidates(sqlite3expert *p, char **pzErr){
for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){ for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
IdxHash64Entry *pEntry; IdxHash64Entry *pEntry;
IdxWhere *pWhere = &pIter->where;
IdxConstraint *pCons; IdxConstraint *pCons;
IdxTable *pTab = 0;
rc = idxGetTableInfo(p->dbm, pIter->zTable, &pTab, pzErr);
idxHash64Add(&rc, &hMask, 0); idxHash64Add(&rc, &hMask, 0);
for(pCons=pIter->where.pEq; pCons; pCons=pCons->pNext){ for(pCons=pIter->pEq; pCons; pCons=pCons->pNext){
for(pEntry=hMask.pFirst; pEntry; pEntry=pEntry->pNext){ for(pEntry=hMask.pFirst; pEntry; pEntry=pEntry->pNext){
idxHash64Add(&rc, &hMask, pEntry->iVal | (u64)pCons->depmask); idxHash64Add(&rc, &hMask, pEntry->iVal | (u64)pCons->depmask);
} }
} }
for(pEntry=hMask.pFirst; pEntry; pEntry=pEntry->pNext){ for(pEntry=hMask.pFirst; rc==SQLITE_OK && pEntry; pEntry=pEntry->pNext){
i64 mask = (i64)pEntry->iVal; i64 mask = (i64)pEntry->iVal;
rc = idxCreateFromWhere(p, mask, pIter, pWhere, 0, 0); rc = idxCreateFromWhere(p, pTab, mask, pIter, 0, 0);
if( rc==SQLITE_OK && pIter->pOrder ){ if( rc==SQLITE_OK && pIter->pOrder ){
rc = idxCreateFromWhere(p, mask, pIter, pWhere, 0, pIter->pOrder); rc = idxCreateFromWhere(p, pTab, mask, pIter, 0, pIter->pOrder);
} }
} }
sqlite3_free(pTab);
idxHash64Clear(&hMask); idxHash64Clear(&hMask);
} }
return rc; return rc;
} }
/*
** Free all elements of the linked list starting at pConstraint.
*/
static void idxConstraintFree(IdxConstraint *pConstraint){ static void idxConstraintFree(IdxConstraint *pConstraint){
IdxConstraint *pNext; IdxConstraint *pNext;
IdxConstraint *p; IdxConstraint *p;
@ -802,9 +865,8 @@ static void idxScanFree(IdxScan *pScan, IdxScan *pLast){
for(p=pScan; p!=pLast; p=pNext){ for(p=pScan; p!=pLast; p=pNext){
pNext = p->pNextScan; pNext = p->pNextScan;
idxConstraintFree(p->pOrder); idxConstraintFree(p->pOrder);
idxConstraintFree(p->where.pEq); idxConstraintFree(p->pEq);
idxConstraintFree(p->where.pRange); idxConstraintFree(p->pRange);
sqlite3_free(p->pTable);
sqlite3_free(p); sqlite3_free(p);
} }
} }
@ -825,6 +887,11 @@ static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){
} }
/*
** This function is called after candidate indexes have been created. It
** runs all the queries to see which indexes they prefer, and populates
** IdxStatement.zIdx and IdxStatement.zEQP with the results.
*/
int idxFindIndexes( int idxFindIndexes(
sqlite3expert *p, sqlite3expert *p,
char **pzErr /* OUT: Error message (sqlite3_malloc) */ char **pzErr /* OUT: Error message (sqlite3_malloc) */
@ -981,18 +1048,10 @@ int sqlite3_expert_sql(
} }
int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){ int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){
int rc = SQLITE_OK; int rc;
IdxScan *pIter;
/* Load IdxTable objects */
for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
rc = idxGetTableInfo(p->dbm, pIter, pzErr);
}
/* Create candidate indexes within the in-memory database file */ /* Create candidate indexes within the in-memory database file */
if( rc==SQLITE_OK ){ rc = idxCreateCandidates(p, pzErr);
rc = idxCreateCandidates(p, pzErr);
}
/* Figure out which of the candidate indexes are preferred by the query /* Figure out which of the candidate indexes are preferred by the query
** planner and report the results to the user. */ ** planner and report the results to the user. */

View File

@ -1,5 +1,5 @@
C Changes\sto\sallow\sthe\scode\sin\ssqlite3expert.c\sto\sbe\stested\sdirectly\s(via\sthe\nAPI\sin\ssqlite3expert.h)\sinstead\sof\sby\sinvoking\sthe\ssqlite3_expert\sapplication.\nFix\smemory\sleaks\sand\sother\sproblems. C Add\sext/expert/README.md.
D 2017-04-10T16:13:20.707 D 2017-04-10T20:00:26.414
F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc a4c0613a18663bda56d8cf76079ab6590a7c3602e54befb4bbdef76bcaa38b6a F Makefile.msc a4c0613a18663bda56d8cf76079ab6590a7c3602e54befb4bbdef76bcaa38b6a
@ -40,9 +40,10 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91
F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74 F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/expert/README.md 9f15075ec5ad772808eff55ef044c31140fd1146aa0a3c47eafd155e71851b01
F ext/expert/expert.c bf0fd71921cb7b807cda9a76fb380e3d6e6b980d6167093b2952b41ec9ad8f46 F ext/expert/expert.c bf0fd71921cb7b807cda9a76fb380e3d6e6b980d6167093b2952b41ec9ad8f46
F ext/expert/expert1.test c1b1405f3ac20e9f71dacdf7bd68ff22e273b249a419260b123ebe385daf2db5 w test/expert1.test F ext/expert/expert1.test d4451ccdca910ec3652a7a1994210fe2b7bc8f56431ed0cf2ff3dee798c89084
F ext/expert/sqlite3expert.c b87f13e90b999b5b10c4ec004b6a935150c00d3af1a16944e262172b9b831b8c F ext/expert/sqlite3expert.c 8bcb83b3723239dc6a2a199e7a030741b7ecf47814828a1da7ea559aa42f094a
F ext/expert/sqlite3expert.h feeaee4ab73ba52426329781bbb28032ce18cf5abd2bf6221bac2df4c32b3013 F ext/expert/sqlite3expert.h feeaee4ab73ba52426329781bbb28032ce18cf5abd2bf6221bac2df4c32b3013
F ext/expert/test_expert.c bad0611732d07180d586bd589cbb7713dc3ab0338c52bff29680eb2007678c05 F ext/expert/test_expert.c bad0611732d07180d586bd589cbb7713dc3ab0338c52bff29680eb2007678c05
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@ -1576,7 +1577,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 0857c48e02a76490fc623364f77363165dea94ec254f93d8f0fd0bac2968c572 P 5dd9831721b70a89a26728adcd49c7f6103ef8266891a79c2db34d913702709e
R edcb3d0fdfbb918bffbc8d72b5429335 R 119e273740189bcdef018d8609f3a756
U dan U dan
Z 0d7d47356215948792ad81449f48b82b Z 664c10ad907cc8fb129a4f4feaf498a1

View File

@ -1 +1 @@
5dd9831721b70a89a26728adcd49c7f6103ef8266891a79c2db34d913702709e 9318f1b9ed2d8da3a82ea69179e2d56a99d326c7721642665f87f6a4534e7bf0