Experimental integration of schemalint functionality with the shell tool. Does not work yet.
FossilOrigin-Name: ed49f297bcee86674ed673e195610b8cc1d35647
This commit is contained in:
parent
02e4f27146
commit
3e6ac1643c
2
main.mk
2
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)
|
||||
|
||||
|
31
manifest
31
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
|
||||
|
@ -1 +1 @@
|
||||
1a4182eedd0143c3f71b3d97f1d1bb25adeba617
|
||||
ed49f297bcee86674ed673e195610b8cc1d35647
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
36
src/shell.c
36
src/shell.c
@ -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
468
src/shell_indexes.c
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
191
src/where.c
191
src/where.c
@ -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(
|
||||
static void whereTraceWC(
|
||||
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(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user