From: drh Date: Tue, 13 Jun 2006 17:38:59 +0000 (+0000) Subject: The optimizer recognizes MATCH operators and allows virtual-tables to make X-Git-Tag: version-3.6.10~2935 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7f3759015a1f56fa22b8cf6105ac8793fc2363f1;p=thirdparty%2Fsqlite.git The optimizer recognizes MATCH operators and allows virtual-tables to make use of them. (CVS 3232) FossilOrigin-Name: 136bed496b89943522310ec511199b78198d0844 --- diff --git a/manifest b/manifest index 551994b3fb..fe2d455fe5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\sthe\sMATCH\soperator.\s(CVS\s3231) -D 2006-06-13T15:37:26 +C The\soptimizer\srecognizes\sMATCH\soperators\sand\sallows\svirtual-tables\sto\smake\nuse\sof\sthem.\s(CVS\s3232) +D 2006-06-13T17:39:00 F Makefile.in 56fd6261e83f60724e6dcd764e06ab68cbd53909 F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -43,7 +43,7 @@ F src/date.c cd2bd5d1ebc6fa12d6312f69789ae5b0a2766f2e F src/delete.c f9a8c7837adb4bb4810a698a041a88d5ec7bfa9a F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b F src/expr.c 8c873e05436ca8ee0ac4c7825d35ff898abb9c89 -F src/func.c acbbf533b55221f26760798d99b37de3ac5678fe +F src/func.c 16eaca47b4d5c66f750e0b2d8814387d70f0d5b2 F src/hash.c 449f3d6620193aa557f5d86cbc5cc6b87702b185 F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564 F src/insert.c 2c3eeb4bcde13c1006824ef14953c2fdad31cf36 @@ -102,7 +102,7 @@ F src/vdbeaux.c 0168d770d03f9815511780a49cd8360d9a5f1ec5 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 5f0afe3b92bb2c037f8d5d697f7c151fa50783a3 F src/vtab.c 12d83f7de893d06592d6d37c285defefebbd2d48 -F src/where.c 0b1fcf590040175e20c6d8f2e13485b683d3d03c +F src/where.c 7e614b0278c688aec94c79d42f602b24e9e4119f F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/all.test 5df90d015ca63fcef2a4b62c24f7316b66c4bfd4 @@ -171,7 +171,7 @@ F test/enc3.test 890508efff6677345e93bf2a8adb0489b30df030 F test/expr.test 7b4b349abdb05ab1862c1cfcf7607e3731efc5d2 F test/fkey1.test 153004438d51e6769fb1ce165f6313972d6263ce F test/format4.test 9f31d41d4f926cab97b2ebe6be00a6ab12dece87 -F test/func.test 27d02fd00b7c2a6b5c8c302d02f9f20876ce5cc8 +F test/func.test 7d8dd2e7d92fb17974d84c0cf02dfb7f9885b5b2 F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d F test/index.test e65df12bed94b2903ee89987115e1578687e9266 @@ -363,7 +363,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P ea4bc5a0be6cfc81ef1e9405f396c43205fe9cd8 -R 3c35da63c52751c585cf9cb6b2473575 +P 815b84d5273b42978edcee0d4afe7f91a7933f4e +R fa210e05061370c7e0ab1d80526752c9 U drh -Z a4bc00b43637d2bd30e6651aa4a937df +Z 0393cac6dbaee2c659fa623848ca0a40 diff --git a/manifest.uuid b/manifest.uuid index c37238b498..94d6966610 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -815b84d5273b42978edcee0d4afe7f91a7933f4e \ No newline at end of file +136bed496b89943522310ec511199b78198d0844 \ No newline at end of file diff --git a/src/func.c b/src/func.c index 5927e8ba0d..2affd84800 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.128 2006/05/11 13:25:39 drh Exp $ +** $Id: func.c,v 1.129 2006/06/13 17:39:00 drh Exp $ */ #include "sqliteInt.h" #include @@ -548,6 +548,19 @@ static void versionFunc( sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); } +/* +** The MATCH() function is unimplemented. If anybody tries to use it, +** return an error. +*/ +static void matchStub( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + static const char zErr[] = "MATCH is not implemented"; + sqlite3_result_error(context, zErr, sizeof(zErr)-1); +} + /* ** EXPERIMENTAL - This is not an official function. The interface may @@ -1003,6 +1016,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ { "last_insert_rowid", 0, 1, SQLITE_UTF8, 0, last_insert_rowid }, { "changes", 0, 1, SQLITE_UTF8, 0, changes }, { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, + { "match", 2, 0, SQLITE_UTF8, 0, matchStub }, #ifdef SQLITE_SOUNDEX { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, #endif diff --git a/src/where.c b/src/where.c index 99edb3f5c0..2bfb21cbf5 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.214 2006/06/13 15:00:55 danielk1977 Exp $ +** $Id: where.c,v 1.215 2006/06/13 17:39:01 drh Exp $ */ #include "sqliteInt.h" @@ -156,6 +156,7 @@ struct ExprMaskSet { #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) +#define WO_MATCH 64 /* ** Value for flags returned by bestIndex() @@ -523,6 +524,34 @@ static int isLikeOrGlob( } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ +/* +** Check to see if the given expression is of the form +** +** column MATCH expr +** +** If it is then return TRUE. If not, return FALSE. +*/ +static int isMatchOfColumn( + Expr *pExpr /* Test this expression */ +){ + ExprList *pList; + + if( pExpr->op!=TK_FUNCTION ){ + return 0; + } + if( pExpr->token.n!=5 || sqlite3StrNICmp(pExpr->token.z,"match",5)!=0 ){ + return 0; + } + pList = pExpr->pList; + if( pList->nExpr!=2 ){ + return 0; + } + if( pList->a[1].pExpr->op != TK_COLUMN ){ + return 0; + } + return 1; +} + /* ** If the pBase expression originated in the ON or USING clause of ** a join, then transfer the appropriate markings over to derived. @@ -747,6 +776,39 @@ or_not_possible: } } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ + +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* Add a WO_MATCH auxiliary term to the constraint set if the + ** current expression is of the form: column MATCH expr. + ** This information is used by the xBestIndex methods of + ** virtual tables. The native query optimizer does not attempt + ** to do anything with MATCH functions. + */ + if( isMatchOfColumn(pExpr) ){ + int idxNew; + Expr *pRight, *pLeft; + WhereTerm *pNewTerm; + Bitmask prereqColumn, prereqExpr; + + pRight = pExpr->pList->a[0].pExpr; + pLeft = pExpr->pList->a[1].pExpr; + prereqExpr = exprTableUsage(pMaskSet, pRight); + prereqColumn = exprTableUsage(pMaskSet, pLeft); + if( (prereqExpr & prereqColumn)==0 ){ + idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL); + pNewTerm = &pWC->a[idxNew]; + pNewTerm->prereqRight = prereqExpr; + pNewTerm->leftCursor = pLeft->iTable; + pNewTerm->leftColumn = pLeft->iColumn; + pNewTerm->eOperator = WO_MATCH; + pNewTerm->iParent = idxTerm; + pTerm = &pWC->a[idxNew]; + pTerm->nChild = 1; + pTerm->flags |= TERM_COPIED; + pNewTerm->prereqAll = pTerm->prereqAll; + } + } +#endif /* SQLITE_OMIT_VIRTUALTABLE */ } @@ -887,7 +949,20 @@ static double estLog(double N){ #ifndef SQLITE_OMIT_VIRTUALTABLE /* -** Compute the best index for a virtual table +** Compute the best index for a virtual table. +** +** The best index is computed by the xBestIndex method of the virtual +** table module. This routine is really just a wrapper that sets up +** the sqlite3_index_info structure that is used to communicate with +** xBestIndex. +** +** In a join, this routine might be called multiple times for the +** same virtual table. The sqlite3_index_info structure is created +** and initialized on the first invocation and reused on all subsequent +** invocations. The sqlite3_index_info structure is also used when +** code is generated to access the virtual table. The whereInfoDelete() +** routine takes care of freeing the sqlite3_index_info structure after +** everybody has finished with it. */ static double bestVirtualIndex( Parse *pParse, /* The parsing context */ @@ -971,12 +1046,16 @@ static double bestVirtualIndex( pIdxCons[j].iColumn = pTerm->leftColumn; pIdxCons[j].iTermOffset = i; pIdxCons[j].op = pTerm->eOperator; + /* The direct assignment in the previous line is possible only because + ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The + ** following asserts verify this fact. */ assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); - assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE) ); + assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH ); + assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); j++; } for(i=0; iazModuleArg && pTab->azModuleArg[0] ); if( pTab->pVtab==0 ){ @@ -995,7 +1081,24 @@ static double bestVirtualIndex( } /* Set the aConstraint[].usable fields and initialize all - ** output variables + ** output variables to zero. + ** + ** aConstraint[].usable is true for constraints where the right-hand + ** side contains only references to tables to the left of the current + ** table. In other words, if the constraint is of the form: + ** + ** column = expr + ** + ** and we are evaluating a join, then the constraint on column is + ** only valid if all tables referenced in expr occur to the left + ** of the table containing column. + ** + ** The aConstraints[] array contains entries for all constraints + ** on the current table. That way we only have to compute it once + ** even though we might try to pick the best index multiple times. + ** For each attempt at picking an index, the order of tables in the + ** join might be different so we have to recompute the usable flag + ** each time. */ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; pUsage = pIdxInfo->aConstraintUsage; @@ -1846,10 +1949,11 @@ WhereInfo *sqlite3WhereBegin( #ifndef SQLITE_OMIT_VIRTUALTABLE if( pLevel->pIdxInfo ){ + /* Case 0: The table is a virtual-table. Use the VFilter and VNext + ** to access the data. + */ char *zSpace; /* Space for OP_VFilter to marshall it's arguments */ - /* Case 0: That table is a virtual-table. Use the VFilter and VNext. - */ sqlite3_index_info *pIdxInfo = pLevel->pIdxInfo; for(i=1; i<=pIdxInfo->nConstraint; i++){ int j; diff --git a/test/func.test b/test/func.test index 5637750a6f..6a87f1283c 100644 --- a/test/func.test +++ b/test/func.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing built-in functions. # -# $Id: func.test,v 1.51 2006/03/26 01:21:23 drh Exp $ +# $Id: func.test,v 1.52 2006/06/13 17:39:01 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -643,6 +643,27 @@ do_test func-18.32 { } } {1 {integer overflow}} - +# The MATCH function exists but is only a stub and always throws an error. +# +do_test func-19.1 { + execsql { + SELECT match(a,b) FROM t1 WHERE 0; + } +} {} +do_test func-19.2 { + catchsql { + SELECT 'abc' MATCH 'xyz'; + } +} {1 {MATCH is not implemented}} +do_test func-19.3 { + catchsql { + SELECT 'abc' NOT MATCH 'xyz'; + } +} {1 {MATCH is not implemented}} +do_test func-19.4 { + catchsql { + SELECT match(1,2,3); + } +} {1 {wrong number of arguments to function match()}} finish_test