The optimizer recognizes MATCH operators and allows virtual-tables to make
use of them. (CVS 3232) FossilOrigin-Name: 136bed496b89943522310ec511199b78198d0844
This commit is contained in:
parent
03bea70cd8
commit
7f3759015a
16
manifest
16
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
|
||||
|
@ -1 +1 @@
|
||||
815b84d5273b42978edcee0d4afe7f91a7933f4e
|
||||
136bed496b89943522310ec511199b78198d0844
|
16
src/func.c
16
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 <ctype.h>
|
||||
@ -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
|
||||
|
116
src/where.c
116
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; i<nOrderBy; i++){
|
||||
@ -986,6 +1065,13 @@ static double bestVirtualIndex(
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, the sqlite3_index_info structure that pIdxInfo points
|
||||
** to will have been initialized, either during the current invocation or
|
||||
** during some prior invocation. Now we just have to customize the
|
||||
** details of pIdxInfo for the current invocation and pass it to
|
||||
** xBestIndex.
|
||||
*/
|
||||
|
||||
/* The module name must be defined */
|
||||
assert( pTab->azModuleArg && 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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user