From: drh Date: Tue, 1 Mar 2016 22:48:00 +0000 (+0000) Subject: New test cases and infrastructure for testing the xBestIndex method of X-Git-Tag: version-3.12.0~118 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7434b1dbf4e8407618f38c0a08271ce59a24a13b;p=thirdparty%2Fsqlite.git New test cases and infrastructure for testing the xBestIndex method of virtual tables. FossilOrigin-Name: 1d41c161165006d6c2af47e476f05fb13039f8b8 --- 7434b1dbf4e8407618f38c0a08271ce59a24a13b diff --cc manifest index e8fac4d6a2,775d6d0fdb..7784cb3c77 --- a/manifest +++ b/manifest @@@ -1,5 -1,5 +1,5 @@@ - C Improved\sdebugging\soutput\swith\swheretrace.\s\sFix\ssome\stypos\sin\stest\sscript\ncomments. - D 2016-03-01T22:41:27.874 -C Fix\sa\smemory\sleak\sin\sthe\stest\scode\son\sthis\sbranch. -D 2016-03-01T18:35:55.112 ++C New\stest\scases\sand\sinfrastructure\sfor\stesting\sthe\sxBestIndex\smethod\sof\s\nvirtual\stables. ++D 2016-03-01T22:48:00.766 F Makefile.in 4e90dc1521879022aa9479268a4cd141d1771142 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 4f319afb7c049d40aff7af6e8c4e7cc2ba18e079 @@@ -371,6 -371,7 +371,7 @@@ F src/test9.c bea1e8cf52aa93695487baded F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 -F src/test_bestindex.c 31ac82f4b21395572e26c6693bf47fdd032d6e61 ++F src/test_bestindex.c cd2eb53d7abf082665bca161e6762c4ba395f17a F src/test_blob.c b2551a9b5573232db5f66f292307c37067937239 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f F src/test_config.c 0dee90328e3dedf8ba002ee94b6a7e7ea7726fe4 @@@ -492,6 -493,7 +493,7 @@@ F test/backup_malloc.test 7162d604ec2b4 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f F test/bc_common.tcl 3eda41ef9cda7d5f6c205462c96228b301da4191 -F test/bestindex1.test 8d8ae7e96a98079f43518cae536535cc52aee49b ++F test/bestindex1.test 41c763428e403663b3e15b2acba7f07b40b22592 F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc @@@ -1451,7 -1453,7 +1453,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 - P f2c16094a536e7ef62444d0fe38cbee2a4999426 - R 8bf9dc83caf7a5f48b09e10ce3dd712c -P 759b9d5b22aa60cc1d6b606f81eb7366c28cbcbe -R e9150850334c910fa82ad79b36b013cf -U dan -Z bc3fb098cb9eaf89cb42b29f1b53a09a ++P 13a37fd487ce7d4f98a12f7a67a9c05dadc66557 7a1add56341f43dc41adc7b370e58860f4dd50a3 ++R 0985007e1a98dc617470e125c83c71f2 ++T +closed 7a1add56341f43dc41adc7b370e58860f4dd50a3 +U drh - Z cbae5a3ad8eb0cbd1b6534802cfd9e9b ++Z 971ab7ec7b38808209f8a28c1ffc2c5d diff --cc manifest.uuid index 1fa5c8f4d5,0849f0a941..ab5413d9af --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 13a37fd487ce7d4f98a12f7a67a9c05dadc66557 -7a1add56341f43dc41adc7b370e58860f4dd50a3 ++1d41c161165006d6c2af47e476f05fb13039f8b8 diff --cc src/test_bestindex.c index 0000000000,4fbf63bf19..c0e67299f4 mode 000000,100644..100644 --- a/src/test_bestindex.c +++ b/src/test_bestindex.c @@@ -1,0 -1,442 +1,442 @@@ + /* -** 2013 Jan 11 ++** 2016-03-01 + ** + ** 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. + ** + ************************************************************************* + ** Code for testing the virtual table xBestIndex method and the query + ** planner. + */ + + + /* + ** INSTRUCTIONS + ** + ** This module exports a single tcl command - [register_tcl_module]. When + ** invoked, it registers a special virtual table module with a database + ** connection. + ** + ** The virtual table is currently read-only. And always returns zero rows. + ** It is created with a single argument - the name of a Tcl command - as + ** follows: + ** + ** CREATE VIRTUAL TABLE x1 USING tcl(tcl_command); + ** + ** The command [tcl_command] is invoked when the table is first created (or + ** connected) and when the xBestIndex() method is invoked. When it is created + ** (or connected), it is invoked as follows: + ** + ** tcl_command xConnect + ** + ** In this case the return value of the script is passed to the + ** sqlite3_declare_vtab() function to create the virtual table schema. + ** + ** When the xBestIndex() method is called by SQLite, the Tcl command is + ** invoked as: + ** + ** tcl_command xBestIndex CONSTRAINTS ORDERBY MASK + ** + ** where CONSTRAINTS is a tcl representation of the aConstraints[] array, + ** ORDERBY is a representation of the contents of the aOrderBy[] array and + ** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual + ** table is declared as: + ** + ** CREATE TABLE x1(a, b, c) + ** + ** and the query is: + ** + ** SELECT * FROM x1 WHERE a=? AND c module name ("fs") + ** argv[1] -> database name + ** argv[2] -> table name + ** argv[...] -> other module argument fields. + */ + static int tclConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr + ){ + Tcl_Interp *interp = (Tcl_Interp*)pAux; + tcl_vtab *pTab; + const char *zCmd; + Tcl_Obj *pScript = 0; + int rc; + + if( argc!=4 ){ + *pzErr = sqlite3_mprintf("wrong number of arguments"); + return SQLITE_ERROR; + } + zCmd = argv[3]; + + pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab)); + if( pTab==0 ) return SQLITE_NOMEM; + memset(pTab, 0, sizeof(tcl_vtab)); + + pTab->pCmd = Tcl_NewStringObj(zCmd, -1); + pTab->interp = interp; + Tcl_IncrRefCount(pTab->pCmd); + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1)); + + rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); + if( rc!=TCL_OK ){ + *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp)); + rc = SQLITE_ERROR; + }else{ + rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp)); + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(pTab); + pTab = 0; + } + + *ppVtab = &pTab->base; + return rc; + } + + /* The xDisconnect and xDestroy methods are also the same */ + static int tclDisconnect(sqlite3_vtab *pVtab){ + tcl_vtab *pTab = (tcl_vtab*)pVtab; + Tcl_DecrRefCount(pTab->pCmd); + sqlite3_free(pTab); + return SQLITE_OK; + } + + /* + ** Open a new tcl cursor. + */ + static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + tcl_cursor *pCur; + pCur = sqlite3_malloc(sizeof(tcl_cursor)); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(tcl_cursor)); + *ppCursor = &pCur->base; + return SQLITE_OK; + } + + /* + ** Close a tcl cursor. + */ + static int tclClose(sqlite3_vtab_cursor *cur){ + tcl_cursor *pCur = (tcl_cursor *)cur; + sqlite3_free(pCur); + return SQLITE_OK; + } + + static int tclNext(sqlite3_vtab_cursor *cur){ + return SQLITE_OK; + } + + static int tclFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv + ){ + return SQLITE_OK; + } + + static int tclColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + return SQLITE_OK; + } + + static int tclRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + return SQLITE_OK; + } + + static int tclEof(sqlite3_vtab_cursor *cur){ + return 1; + } + + static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + tcl_vtab *pTab = (tcl_vtab*)tab; + Tcl_Interp *interp = pTab->interp; + Tcl_Obj *pArg; + Tcl_Obj *pScript; + int ii; + int rc = SQLITE_OK; + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1)); + + pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + for(ii=0; iinConstraint; ii++){ + struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; + Tcl_Obj *pElem = Tcl_NewObj(); + const char *zOp = "?"; + + Tcl_IncrRefCount(pElem); + + switch( pCons->op ){ + case SQLITE_INDEX_CONSTRAINT_EQ: + zOp = "eq"; break; + case SQLITE_INDEX_CONSTRAINT_GT: + zOp = "gt"; break; + case SQLITE_INDEX_CONSTRAINT_LE: + zOp = "le"; break; + case SQLITE_INDEX_CONSTRAINT_LT: + zOp = "lt"; break; + case SQLITE_INDEX_CONSTRAINT_GE: + zOp = "ge"; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: + zOp = "match"; break; + case SQLITE_INDEX_CONSTRAINT_LIKE: + zOp = "like"; break; + case SQLITE_INDEX_CONSTRAINT_GLOB: + zOp = "glob"; break; + case SQLITE_INDEX_CONSTRAINT_REGEXP: + zOp = "regexp"; break; + } + + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable)); + + Tcl_ListObjAppendElement(0, pArg, pElem); + Tcl_DecrRefCount(pElem); + } + + Tcl_ListObjAppendElement(0, pScript, pArg); + Tcl_DecrRefCount(pArg); + + pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + for(ii=0; iinOrderBy; ii++){ + struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii]; + Tcl_Obj *pElem = Tcl_NewObj(); + Tcl_IncrRefCount(pElem); + + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc)); + + Tcl_ListObjAppendElement(0, pArg, pElem); + Tcl_DecrRefCount(pElem); + } + + Tcl_ListObjAppendElement(0, pScript, pArg); + Tcl_DecrRefCount(pArg); + + Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed)); + + rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); + Tcl_DecrRefCount(pScript); + if( rc!=TCL_OK ){ + const char *zErr = Tcl_GetStringResult(interp); + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + }else{ + /* Analyze the scripts return value. The return value should be a tcl + ** list object with an even number of elements. The first element of each + ** pair must be one of: + ** + ** "orderby" (value of orderByConsumed flag) + ** "cost" (value of estimatedCost field) + ** "rows" (value of estimatedRows field) + ** "use" (index of used constraint in aConstraint[]) + ** "idxnum" (value of idxNum field) + ** "idxstr" (value of idxStr field) + ** "omit" (index of omitted constraint in aConstraint[]) + */ + Tcl_Obj *pRes = Tcl_GetObjResult(interp); + Tcl_Obj **apElem = 0; + int nElem; + rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem); + if( rc!=TCL_OK ){ + const char *zErr = Tcl_GetStringResult(interp); + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + }else{ + int iArgv = 1; + for(ii=0; rc==SQLITE_OK && iiestimatedCost); + }else + if( sqlite3_stricmp("orderby", zCmd)==0 ){ + rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed); + }else + if( sqlite3_stricmp("idxnum", zCmd)==0 ){ + rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum); + }else + if( sqlite3_stricmp("idxstr", zCmd)==0 ){ + sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p)); + pIdxInfo->needToFreeIdxStr = 1; + }else + if( sqlite3_stricmp("rows", zCmd)==0 ){ + rc = Tcl_GetWideIntFromObj(interp, p, &pIdxInfo->estimatedRows); + }else + if( sqlite3_stricmp("use", zCmd)==0 + || sqlite3_stricmp("omit", zCmd)==0 + ){ + int iCons; + rc = Tcl_GetIntFromObj(interp, p, &iCons); + if( rc==SQLITE_OK ){ + if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons); + }else{ + int bOmit = (zCmd[0]=='o' || zCmd[0]=='O'); + pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++; + pIdxInfo->aConstraintUsage[iCons].omit = bOmit; + } + } + }else{ + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd); + } + if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){ + const char *zErr = Tcl_GetStringResult(interp); + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + } + } + } + } + + return rc; + } + + /* + ** A virtual table module that provides read-only access to a + ** Tcl global variable namespace. + */ + static sqlite3_module tclModule = { + 0, /* iVersion */ + tclConnect, + tclConnect, + tclBestIndex, + tclDisconnect, + tclDisconnect, + tclOpen, /* xOpen - open a cursor */ + tclClose, /* xClose - close a cursor */ + tclFilter, /* xFilter - configure scan constraints */ + tclNext, /* xNext - advance a cursor */ + tclEof, /* xEof - check for end of scan */ + tclColumn, /* xColumn - read data */ + tclRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + }; + + /* + ** Decode a pointer to an sqlite3 object. + */ + extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + + /* + ** Register the echo virtual table module. + */ + static int register_tcl_module( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ + ){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + #ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3_create_module(db, "tcl", &tclModule, (void *)interp); + #endif + return TCL_OK; + } + + #endif + + + /* + ** Register commands with the TCL interpreter. + */ + int Sqlitetesttcl_Init(Tcl_Interp *interp){ + #ifndef SQLITE_OMIT_VIRTUALTABLE + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_tcl_module", register_tcl_module, 0 }, + }; + int i; + for(i=0; i