]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The optimizer recognizes MATCH operators and allows virtual-tables to make
authordrh <drh@noemail.net>
Tue, 13 Jun 2006 17:38:59 +0000 (17:38 +0000)
committerdrh <drh@noemail.net>
Tue, 13 Jun 2006 17:38:59 +0000 (17:38 +0000)
use of them. (CVS 3232)

FossilOrigin-Name: 136bed496b89943522310ec511199b78198d0844

manifest
manifest.uuid
src/func.c
src/where.c
test/func.test

index 551994b3fb0b1d6af44e79618941c85cb4141fb8..fe2d455fe561ccd381f9cbb2c84ed4a27747b450 100644 (file)
--- 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
index c37238b498971786f2d41c01d5d13d8a7030b80e..94d69666107511fcfbbe1cf607d7da76c0b1af82 100644 (file)
@@ -1 +1 @@
-815b84d5273b42978edcee0d4afe7f91a7933f4e
\ No newline at end of file
+136bed496b89943522310ec511199b78198d0844
\ No newline at end of file
index 5927e8ba0d91a0bfb8710001a325c18b6536a8ca..2affd84800838e3a0f584d8fcbcc2f46f562793c 100644 (file)
@@ -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
index 99edb3f5c04a20419104cba4e1c85ed752b9088e..2bfb21cbf5ded6fede8ccae5be87b8059419ca72 100644 (file)
@@ -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;
index 5637750a6f747e3d84ba6afe13e7a5f7de453a1e..6a87f1283cd577f3c606ab6bb4528858aec2e713 100644 (file)
@@ -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