} else {
set CMD ".././sqlite3_expert"
}
-if {![file executable $CMD]} {
- finish_test
- return
-}
-
proc squish {txt} {
regsub -all {[[:space:]]+} $txt { }
}
-proc do_rec_test {tn sql res} {
- set res [squish [string trim $res]]
- set tst [subst -nocommands {
- squish [string trim [exec $::CMD -verbose 0 -sql {$sql;} test.db]]
- }]
- uplevel [list do_test $tn $tst $res]
-}
-
proc do_setup_rec_test {tn setup sql res} {
reset_db
db eval $setup
uplevel [list do_rec_test $tn $sql $res]
}
+foreach {tn setup} {
+ 1 {
+ if {![file executable $CMD]} { continue }
+
+ proc do_rec_test {tn sql res} {
+ set res [squish [string trim $res]]
+ set tst [subst -nocommands {
+ squish [string trim [exec $::CMD -verbose 0 -sql {$sql;} test.db]]
+ }]
+ uplevel [list do_test $tn $tst $res]
+ }
+ }
+ 2 {
+ if {[info commands sqlite3_expert_new]==""} { continue }
+
+ proc do_rec_test {tn sql res} {
+ set expert [sqlite3_expert_new db]
+ $expert sql $sql
+ $expert analyze
+
+ set result [list]
+ for {set i 0} {$i < [$expert count]} {incr i} {
+ lappend result [string trim [$expert report $i indexes]]
+ lappend result [string trim [$expert report $i plan]]
+ }
-do_setup_rec_test 1.1 { CREATE TABLE t1(a, b, c) } {
+ $expert destroy
+
+ set tst [subst -nocommands {set {} [squish [join {$result}]]}]
+ uplevel [list do_test $tn $tst [string trim [squish $res]]]
+ }
+ }
+} {
+
+ eval $setup
+
+
+do_setup_rec_test $tn.1.1 { CREATE TABLE t1(a, b, c) } {
SELECT * FROM t1
} {
(no new indexes)
0|0|0|SCAN TABLE t1
}
-do_setup_rec_test 1.2 {
+do_setup_rec_test $tn.1.2 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 WHERE b>?;
0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_00000062 (b>?)
}
-do_setup_rec_test 1.3 {
+do_setup_rec_test $tn.1.3 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 WHERE b COLLATE nocase BETWEEN ? AND ?
0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_3e094c27 (b>? AND b<?)
}
-do_setup_rec_test 1.4 {
+do_setup_rec_test $tn.1.4 {
CREATE TABLE t1(a, b, c);
} {
SELECT a FROM t1 ORDER BY b;
0|0|0|SCAN TABLE t1 USING INDEX t1_idx_00000062
}
-do_setup_rec_test 1.5 {
+do_setup_rec_test $tn.1.5 {
CREATE TABLE t1(a, b, c);
} {
SELECT a FROM t1 WHERE a=? ORDER BY b;
0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
}
-do_setup_rec_test 1.6 {
+do_setup_rec_test $tn.1.6 {
CREATE TABLE t1(a, b, c);
} {
SELECT min(a) FROM t1
0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1_idx_00000061
}
-do_setup_rec_test 1.7 {
+do_setup_rec_test $tn.1.7 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 ORDER BY a, b, c;
0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_033e95fe
}
-do_setup_rec_test 1.8 {
+do_setup_rec_test $tn.1.8 {
CREATE TABLE t1(a, b, c);
} {
SELECT * FROM t1 ORDER BY a ASC, b COLLATE nocase DESC, c ASC;
0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5be6e222
}
-do_setup_rec_test 1.9 {
+do_setup_rec_test $tn.1.9 {
CREATE TABLE t1(a COLLATE NOCase, b, c);
} {
SELECT * FROM t1 WHERE a=?
# Tables with names that require quotes.
#
-do_setup_rec_test 8.1 {
+do_setup_rec_test $tn.8.1 {
CREATE TABLE "t t"(a, b, c);
} {
SELECT * FROM "t t" WHERE a=?
0|0|0|SEARCH TABLE t t USING INDEX t t_idx_00000061 (a=?)
}
-do_setup_rec_test 8.2 {
+do_setup_rec_test $tn.8.2 {
CREATE TABLE "t t"(a, b, c);
} {
SELECT * FROM "t t" WHERE b BETWEEN ? AND ?
# Columns with names that require quotes.
#
-do_setup_rec_test 9.1 {
+do_setup_rec_test $tn.9.1 {
CREATE TABLE t3(a, "b b", c);
} {
SELECT * FROM t3 WHERE "b b" = ?
0|0|0|SEARCH TABLE t3 USING INDEX t3_idx_00050c52 (b b=?)
}
-do_setup_rec_test 9.2 {
+do_setup_rec_test $tn.9.2 {
CREATE TABLE t3(a, "b b", c);
} {
SELECT * FROM t3 ORDER BY "b b"
# Transitive constraints
#
-do_setup_rec_test 10.1 {
+do_setup_rec_test $tn.10.1 {
CREATE TABLE t5(a, b);
CREATE TABLE t6(c, d);
} {
# OR terms.
#
-do_setup_rec_test 11.1 {
+do_setup_rec_test $tn.11.1 {
CREATE TABLE t7(a, b);
} {
SELECT * FROM t7 WHERE a=? OR b=?
0|0|0|SEARCH TABLE t7 USING INDEX t7_idx_00000062 (b=?)
}
+}
+
finish_test
*************************************************************************
*/
+#if !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHEREINFO_HOOK)
+
#include "sqlite3expert.h"
#include <assert.h>
#include <string.h>
typedef sqlite3_uint64 u64;
typedef struct IdxConstraint IdxConstraint;
-typedef struct IdxContext IdxContext;
typedef struct IdxScan IdxScan;
typedef struct IdxStatement IdxStatement;
typedef struct IdxWhere IdxWhere;
IdxColumn *aCol;
};
-/*
-** Context object passed to idxWhereInfo() and other functions.
-*/
-struct IdxContext {
- char **pzErrmsg;
- 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 */
- sqlite3 *db; /* User database under analysis */
- sqlite3_stmt *pInsertMask; /* To write to aux.depmask */
-};
-
struct IdxStatement {
int iId; /* Statement number */
char *zSql; /* SQL statement */
** sqlite3_whereinfo_hook() callback.
*/
static void idxWhereInfo(
- void *pCtx, /* Pointer to IdxContext structure */
+ void *pCtx, /* Pointer to sqlite3expert structure */
int eOp,
const char *zVal,
int iVal,
return rc;
}
+static void idxConstraintFree(IdxConstraint *pConstraint){
+ IdxConstraint *pNext;
+ IdxConstraint *p;
+
+ for(p=pConstraint; p; p=pNext){
+ pNext = p->pNext;
+ sqlite3_free(p);
+ }
+}
+
/*
** Free all elements of the linked list starting from pScan up until pLast
** (pLast is not freed).
*/
static void idxScanFree(IdxScan *pScan, IdxScan *pLast){
- /* TODO! */
+ IdxScan *p;
+ IdxScan *pNext;
+ for(p=pScan; p!=pLast; p=pNext){
+ pNext = p->pNextScan;
+ idxConstraintFree(p->pOrder);
+ idxConstraintFree(p->where.pEq);
+ idxConstraintFree(p->where.pRange);
+ sqlite3_free(p->pTable);
+ sqlite3_free(p);
+ }
}
/*
** until pLast (pLast is not freed).
*/
static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){
- /* TODO! */
+ IdxStatement *p;
+ IdxStatement *pNext;
+ for(p=pStatement; p!=pLast; p=pNext){
+ pNext = p->pNext;
+ sqlite3_free(p->zEQP);
+ sqlite3_free(p->zIdx);
+ sqlite3_free(p);
+ }
}
}
find_indexes_out:
+ idxHashClear(&hIdx);
return rc;
}
rc = idxGetTableInfo(p->dbm, pIter, pzErr);
}
-
/* Create candidate indexes within the in-memory database file */
if( rc==SQLITE_OK ){
rc = idxCreateCandidates(p, pzErr);
return rc;
}
+/*
+** Return the total number of statements that have been added to this
+** sqlite3expert using sqlite3_expert_sql().
+*/
int sqlite3_expert_count(sqlite3expert *p){
int nRet = 0;
if( p->pStatement ) nRet = p->pStatement->iId+1;
return nRet;
}
+/*
+** Return a component of the report.
+*/
const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){
const char *zRet = 0;
IdxStatement *pStmt;
sqlite3_close(p->dbm);
idxScanFree(p->pScan, 0);
idxStatementFree(p->pStatement, 0);
+ idxHashClear(&p->hIdx);
sqlite3_free(p);
}
+#endif /* !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHEREINFO_HOOK) */
+
--- /dev/null
+/*
+** 2017 April 07
+**
+** 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.
+**
+*************************************************************************
+*/
+
+#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_WHEREINFO_HOOK)
+
+#include "sqlite3expert.h"
+#include <assert.h>
+#include <string.h>
+
+#if defined(INCLUDE_SQLITE_TCL_H)
+# include "sqlite_tcl.h"
+#else
+# include "tcl.h"
+# ifndef SQLITE_TCLAPI
+# define SQLITE_TCLAPI
+# endif
+#endif
+
+/*
+** Extract an sqlite3* db handle from the object passed as the second
+** argument. If successful, set *pDb to point to the db handle and return
+** TCL_OK. Otherwise, return TCL_ERROR.
+*/
+static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
+ Tcl_CmdInfo info;
+ if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
+ Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
+ return TCL_ERROR;
+ }
+
+ *pDb = *(sqlite3 **)info.objClientData;
+ return TCL_OK;
+}
+
+
+/*
+** Tclcmd: $expert sql SQL
+** $expert analyze
+** $expert count
+** $expert report STMT EREPORT
+** $expert destroy
+*/
+static int SQLITE_TCLAPI testExpertCmd(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3expert *pExpert = (sqlite3expert*)clientData;
+ struct Subcmd {
+ const char *zSub;
+ int nArg;
+ const char *zMsg;
+ } aSub[] = {
+ { "sql", 1, "TABLE", }, /* 0 */
+ { "analyze", 0, "", }, /* 1 */
+ { "count", 0, "", }, /* 2 */
+ { "report", 2, "STMT EREPORT", }, /* 3 */
+ { "destroy", 0, "", }, /* 4 */
+ { 0 }
+ };
+ int iSub;
+ int rc = TCL_OK;
+ char *zErr = 0;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
+ return TCL_ERROR;
+ }
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
+ );
+ if( rc!=TCL_OK ) return rc;
+ if( objc!=2+aSub[iSub].nArg ){
+ Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
+ return TCL_ERROR;
+ }
+
+ switch( iSub ){
+ case 0: { /* sql */
+ char *zArg = Tcl_GetString(objv[2]);
+ rc = sqlite3_expert_sql(pExpert, zArg, &zErr);
+ break;
+ }
+
+ case 1: { /* analyze */
+ rc = sqlite3_expert_analyze(pExpert, &zErr);
+ break;
+ }
+
+ case 2: { /* count */
+ int n = sqlite3_expert_count(pExpert);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
+ break;
+ }
+
+ case 3: { /* report */
+ const char *aEnum[] = {
+ "sql", "indexes", "plan", 0
+ };
+ int iEnum;
+ int iStmt;
+ const char *zReport;
+
+ if( Tcl_GetIntFromObj(interp, objv[2], &iStmt)
+ || Tcl_GetIndexFromObj(interp, objv[3], aEnum, "report", 0, &iEnum)
+ ){
+ return TCL_ERROR;
+ }
+
+ assert( EXPERT_REPORT_SQL==1 );
+ assert( EXPERT_REPORT_INDEXES==2 );
+ assert( EXPERT_REPORT_PLAN==3 );
+ zReport = sqlite3_expert_report(pExpert, iStmt, 1+iEnum);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zReport, -1));
+ break;
+ }
+
+ default: /* destroy */
+ assert( iSub==4 );
+ Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
+ break;
+ }
+
+ if( rc!=TCL_OK ){
+ if( zErr ){
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
+ }else{
+ extern const char *sqlite3ErrName(int);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
+ }
+ }
+ sqlite3_free(zErr);
+ return rc;
+}
+
+static void SQLITE_TCLAPI testExpertDel(void *clientData){
+ sqlite3expert *pExpert = (sqlite3expert*)clientData;
+ sqlite3_expert_destroy(pExpert);
+}
+
+/*
+** sqlite3_expert_new DB
+*/
+static int SQLITE_TCLAPI test_sqlite3_expert_new(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static int iCmd = 0;
+ sqlite3 *db;
+ char *zCmd = 0;
+ char *zErr = 0;
+ sqlite3expert *pExpert;
+ int rc = TCL_OK;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( dbHandleFromObj(interp, objv[1], &db) ){
+ return TCL_ERROR;
+ }
+
+ zCmd = sqlite3_mprintf("sqlite3expert%d", ++iCmd);
+ if( zCmd==0 ){
+ Tcl_AppendResult(interp, "out of memory", (char*)0);
+ return TCL_ERROR;
+ }
+
+ pExpert = sqlite3_expert_new(db, &zErr);
+ if( pExpert==0 ){
+ Tcl_AppendResult(interp, zErr, (char*)0);
+ rc = TCL_ERROR;
+ }else{
+ void *p = (void*)pExpert;
+ Tcl_CreateObjCommand(interp, zCmd, testExpertCmd, p, testExpertDel);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
+ }
+
+ sqlite3_free(zCmd);
+ sqlite3_free(zErr);
+ return rc;
+}
+
+int TestExpert_Init(Tcl_Interp *interp){
+ struct Cmd {
+ const char *zCmd;
+ Tcl_ObjCmdProc *xProc;
+ } aCmd[] = {
+ { "sqlite3_expert_new", test_sqlite3_expert_new },
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
+ struct Cmd *p = &aCmd[i];
+ Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
+ }
+
+ return TCL_OK;
+}
+
+#else /* defined(SQLITE_TEST) && defined(SQLITE_ENABLE_WHEREINFO_HOOK) */
+int TestExpert_Init(Tcl_Interp *interp){
+ return TCL_OK;
+}
+#endif
# Source code to the test files.
#
TESTSRC = \
+ $(TOP)/ext/expert/sqlite3expert.c \
+ $(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/rbu/test_rbu.c \
-C Fix\sthe\s-file\soption\son\sthe\ssqlite3_expert\sprogram.
-D 2017-04-09T08:38:37.326
+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.
+D 2017-04-10T16:13:20.707
F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc a4c0613a18663bda56d8cf76079ab6590a7c3602e54befb4bbdef76bcaa38b6a
F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/expert/expert.c bf0fd71921cb7b807cda9a76fb380e3d6e6b980d6167093b2952b41ec9ad8f46
-F ext/expert/sqlite3expert.c 864ebebab5fd12cad8fb5debd26ff083dffe87ade15c1b6dcc7a7c3ae0afdd1b
+F ext/expert/expert1.test c1b1405f3ac20e9f71dacdf7bd68ff22e273b249a419260b123ebe385daf2db5 w test/expert1.test
+F ext/expert/sqlite3expert.c b87f13e90b999b5b10c4ec004b6a935150c00d3af1a16944e262172b9b831b8c
F ext/expert/sqlite3expert.h feeaee4ab73ba52426329781bbb28032ce18cf5abd2bf6221bac2df4c32b3013
+F ext/expert/test_expert.c bad0611732d07180d586bd589cbb7713dc3ab0338c52bff29680eb2007678c05
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 6d4f1f1f78a6ac453d35f18c4e696fdefbe65dfec9530a41c5579ef8ec076072
+F main.mk ef818c7b1bb21f657e3bfb363cc7167264d688ca404a666e6ddda6029e94c43b
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
-F src/tclsqlite.c 6c2151b6d8d98e183a04466d40df8889c0574d79
+F src/tclsqlite.c 2e0f7f63de8329526fbcb14fa5261d3574b2a06dd330f4df680120a3e6156133
F src/test1.c 8a98191a1da8e100f77cdb5cc716df67d405028d
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c d03f5b5da9a2410b7a91c64b0d3306ed28ab6fee
F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
-F test/expert1.test 4108ab79ee454679e3d46e3703943ac49676b6174703a13ed81f9ee8cd794e68
F test/expr.test 66a2c9ac34f74f036faa4092f5402c7d3162fc93
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test af720e7d139e7e5417341d0f0eef2b911c0b067852138dc2f5b6a451b5725118
+F test/permutations.test 9c0da2079fa37e7509957c9efbbdc282dea4ed0e732d19e6f216d53ae431a67d
F test/pragma.test 1e94755164a3a3264cd39836de4bebcb7809e5f8
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P e051e8f21b909c5e89a7203eef6501fecd64f82a634c4e10398b5ef7d716eb37
-R c2522cb6c6416b4473f74ba522229374
+P 0857c48e02a76490fc623364f77363165dea94ec254f93d8f0fd0bac2968c572
+R edcb3d0fdfbb918bffbc8d72b5429335
U dan
-Z 7f4db390eaebfa3a6aedc09b4bf9a775
+Z 0d7d47356215948792ad81449f48b82b
-0857c48e02a76490fc623364f77363165dea94ec254f93d8f0fd0bac2968c572
\ No newline at end of file
+5dd9831721b70a89a26728adcd49c7f6103ef8266891a79c2db34d913702709e
\ No newline at end of file
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
extern int TestSession_Init(Tcl_Interp*);
#endif
+ extern int TestExpert_Init(Tcl_Interp*);
extern int Fts5tcl_Init(Tcl_Interp *);
extern int SqliteRbu_Init(Tcl_Interp*);
extern int Sqlitetesttcl_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
TestSession_Init(interp);
#endif
+ TestExpert_Init(interp);
Fts5tcl_Init(interp);
SqliteRbu_Init(interp);
Sqlitetesttcl_Init(interp);
foreach f [glob -nocomplain \
$testdir/../ext/rtree/*.test \
$testdir/../ext/fts5/test/*.test \
+ $testdir/../ext/expert/*.test \
] {
lappend alltests $f
}