]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow the query planner to evaluate deterministic scalar SQL functions used in WHERE...
authordan <dan@noemail.net>
Wed, 11 Mar 2015 20:06:40 +0000 (20:06 +0000)
committerdan <dan@noemail.net>
Wed, 11 Mar 2015 20:06:40 +0000 (20:06 +0000)
FossilOrigin-Name: b7f1fc26d24012e1e7c7f6b3cc0b84ad2b02b8ad

manifest
manifest.uuid
src/vdbeapi.c
src/vdbemem.c
test/analyzeF.test [new file with mode: 0644]

index abbbe02140cc5c367d8dc93aea5f4f761594cc4e..60c76c5cba670675a5fb397680407b8b7f5a371e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Expand\sthe\smulti-process\stest\scases\sto\srepeat\seach\scase\s20\stimes\sand\nto\srepeat\stests\susing\sdifferent\sjournal\smodes.
-D 2015-03-11T14:34:38.239
+C Allow\sthe\squery\splanner\sto\sevaluate\sdeterministic\sscalar\sSQL\sfunctions\sused\sin\sWHERE\sconstraints\sif\sall\sarguments\sare\sSQL\sliterals\sin\sorder\sto\scompare\sthe\sresults\swith\ssqlite_stat4\ssample\sdata.
+D 2015-03-11T20:06:40.904
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 88a3e6261286db378fdffa1124cad11b3c05f5bb
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -296,10 +296,10 @@ F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec
 F src/vdbe.c 94cbc2115075b1a562a2a702c29ba48e74f85d34
 F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
 F src/vdbeInt.h bb56fd199d8af1a2c1b9639ee2f70724b4338e3a
-F src/vdbeapi.c dac0d0d8009a8aa549cd77d9c29da44c0344f0c4
+F src/vdbeapi.c 5c207659c8a57c12c3f77a8fb97544e032fc2f14
 F src/vdbeaux.c 97911edb61074b871ec4aa2d6bb779071643dee5
 F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
-F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
+F src/vdbemem.c 8572106eb3b64ad6e02698c0fb312ccb47bb5c9e
 F src/vdbesort.c 6d64c5448b64851b99931ede980addc3af70d5e2
 F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010
 F src/vtab.c 699f2b8d509cfe379c33dde33827875d5b030e01
@@ -333,6 +333,7 @@ F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d
 F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93
 F test/analyzeD.test 08f9d0bee4e118a66fff3a32d02dbe0ee0a2b594
 F test/analyzeE.test 8684e8ac5722fb97c251887ad97e5d496a98af1d
+F test/analyzeF.test 299a47183c648d8ad92671f313def8fd7cb09875
 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
 F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
 F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
@@ -1241,7 +1242,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 8d0b11c96e15556dd65ced05708a832aef134e69
-R 2327229d01e481a57e92de598065db5c
-U drh
-Z cd1fd5866edba6a7b1ae8e17fb546b84
+P a2715b049a86555990abccc7aa363c524ddb9982
+R 8289f5b5a84378b5cf3294cc8646b1fb
+T *branch * stat4-function
+T *sym-stat4-function *
+T -sym-trunk *
+U dan
+Z a4cf4e61f3250d5413e8887a5d5cd094
index 57b677aa0846ca1bc3144a7a895ab32e2cb56859..28f0ab069bf17d1009ae8c84baf79f44060e33c0 100644 (file)
@@ -1 +1 @@
-a2715b049a86555990abccc7aa363c524ddb9982
\ No newline at end of file
+b7f1fc26d24012e1e7c7f6b3cc0b84ad2b02b8ad
\ No newline at end of file
index 8c0038e4e8469be07282b198c271026a5c182e96..fc3d60f20bae439d6be1d38c8d729cdc966c2124 100644 (file)
@@ -637,12 +637,13 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
 */
 sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
   Vdbe *v = p->pVdbe;
+  sqlite3_int64 iTime = 0;
   int rc;
-  if( v->iCurrentTime==0 ){
-    rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, &v->iCurrentTime);
-    if( rc ) v->iCurrentTime = 0;
-  }
-  return v->iCurrentTime;
+  if( v && v->iCurrentTime ) return v->iCurrentTime;
+  rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, &iTime);
+  if( rc ) return 0;
+  if( v ) v->iCurrentTime = iTime;
+  return iTime;
 }
 
 /*
@@ -712,6 +713,7 @@ void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
   AuxData *pAuxData;
 
   assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+  if( pCtx->pVdbe==0 ) return 0;
   for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
     if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
   }
@@ -735,6 +737,7 @@ void sqlite3_set_auxdata(
 
   assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
   if( iArg<0 ) goto failed;
+  if( pVdbe==0 ) goto failed;
 
   for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
     if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
index 870fb5bd891c2c83ae8c8683afa5ceca93b99649..ce7b73deace976781ae79711985b286eb34b57ba 100644 (file)
@@ -1134,6 +1134,128 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
   return sqlite3ValueNew(db);
 }
 
+/*
+** The expression object indicated by the second argument is guaranteed
+** to be a scalar SQL function. If
+**
+**   * all function arguments are SQL literals,
+**   * the SQLITE_FUNC_CONSTANT function flag is set,
+**   * the SQLITE_FUNC_NEEDCOLL function flag is not set, and
+**   * this routine is being invoked as part of examining stat4 data,
+**     not as part of handling a default value on a column created using
+**     ALTER TABLE ADD COLUMN, 
+**
+** then this routine attempts to invoke the SQL function. Assuming no
+** error occurs, output parameter (*ppVal) is set to point to a value 
+** object containing the result before returning SQLITE_OK.
+**
+** Affinity aff is applied to the result of the function before returning.
+** If the result is a text value, the sqlite3_value object uses encoding 
+** enc.
+**
+** If the conditions above are not met, this function returns SQLITE_OK
+** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to
+** NULL and an SQLite error code returned.
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static int valueFromFunction(
+  sqlite3 *db,                    /* The database connection */
+  Expr *p,                        /* The expression to evaluate */
+  u8 enc,                         /* Encoding to use */
+  u8 aff,                         /* Affinity to use */
+  sqlite3_value **ppVal,          /* Write the new value here */
+  struct ValueNewStat4Ctx *pCtx   /* Second argument for valueNew() */
+){
+  sqlite3_context ctx;            /* Context object for function invocation */
+  sqlite3_value **apVal = 0;      /* Function arguments */
+  int nVal = 0;                   /* Size of apVal[] array */
+  FuncDef *pFunc = 0;             /* Function definition */
+  sqlite3_value *pVal = 0;        /* New value */
+  int rc = SQLITE_OK;             /* Return code */
+  int nName;                      /* Size of function name in bytes */
+  ExprList *pList;                /* Function arguments */
+  int i;                          /* Iterator variable */
+
+  /* If pCtx==0, then this is probably being called to to obtain an 
+  ** sqlite3_value object for the default value of a column. In that case
+  ** function expressions are not supported. Function expressions are
+  ** only supported when extracting values to compare with sqlite_stat4 
+  ** records.
+  **
+  ** It may also be that this function expression is an argument passed
+  ** to another function expression. As in "f2(...)" within the query:
+  **
+  **   SELECT * FROM tbl WHERE tbl.c = f1(0, f2(...), 1);
+  **
+  ** For now, extracting the value of "f1(...)" is not supported either.
+  */
+  if( pCtx==0 ) return SQLITE_OK;
+  
+  assert( (p->flags & (EP_TokenOnly|EP_Reduced))==0 );
+  pList = p->x.pList;
+  if( pList ) nVal = pList->nExpr;
+  nName = sqlite3Strlen30(p->u.zToken);
+  pFunc = sqlite3FindFunction(db, p->u.zToken, nName, nVal, enc, 0);
+  assert( pFunc );
+  if( (pFunc->funcFlags & SQLITE_FUNC_CONSTANT)==0 
+   || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+  ){
+    return SQLITE_OK;
+  }
+
+  if( pList ){
+    apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal);
+    if( apVal==0 ){
+      rc = SQLITE_NOMEM;
+      goto value_from_function_out;
+    }
+    for(i=0; i<nVal; i++){
+      rc = sqlite3ValueFromExpr(db, pList->a[i].pExpr, enc, aff, &apVal[i]);
+      if( apVal[i]==0 ) goto value_from_function_out;
+      assert( rc==SQLITE_OK );
+    }
+  }
+
+  pVal = valueNew(db, pCtx);
+  if( pVal==0 ){
+    rc = SQLITE_NOMEM;
+    goto value_from_function_out;
+  }
+
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.pOut = pVal;
+  ctx.pFunc = pFunc;
+  pFunc->xFunc(&ctx, nVal, apVal);
+  if( ctx.isError ){
+    rc = ctx.isError;
+    sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal));
+  }else{
+    sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8);
+    if( rc==SQLITE_OK ){
+      rc = sqlite3VdbeChangeEncoding(pVal, enc);
+    }
+    if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){
+      rc = SQLITE_TOOBIG;
+    }
+  }
+
+ value_from_function_out:
+  if( rc!=SQLITE_OK ){
+    if( pCtx==0 ) sqlite3ValueFree(pVal);
+    pVal = 0;
+  }
+  for(i=0; i<nVal; i++){
+    sqlite3ValueFree(apVal[i]);
+  }
+  sqlite3DbFree(db, apVal);
+
+  *ppVal = pVal;
+  return rc;
+}
+#else
+# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK
+#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+
 /*
 ** Extract a value from the supplied expression in the manner described
 ** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
@@ -1242,6 +1364,10 @@ static int valueFromExpr(
   }
 #endif
 
+  else if( op==TK_FUNCTION ){
+    rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
+  }
+
   *ppVal = pVal;
   return rc;
 
diff --git a/test/analyzeF.test b/test/analyzeF.test
new file mode 100644 (file)
index 0000000..e96cd87
--- /dev/null
@@ -0,0 +1,78 @@
+# 2015-03-12
+#
+# 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.
+#
+#***********************************************************************
+# Test that deterministic scalar functions passed constant arguments
+# are used with stat4 data.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix analyzeF
+
+ifcapable {!stat4} {
+  finish_test
+  return
+}
+
+proc isqrt {i} { expr { int(sqrt($i)) } }
+db func isqrt isqrt
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(x INTEGER, y INTEGER);
+  WITH data(i) AS (
+    SELECT 1 UNION ALL SELECT i+1 FROM data
+  )
+  INSERT INTO t1 SELECT isqrt(i), isqrt(i) FROM data LIMIT 400;
+  CREATE INDEX t1x ON t1(x);
+  CREATE INDEX t1y ON t1(y);
+  ANALYZE;
+}
+
+proc str {a} { return $a }
+db func str str
+
+# Note: tests 7 to 12 might be unstable - as they assume SQLite will
+# prefer the expression to the right of the AND clause. Which of
+# course could change.
+#
+# Note 2: tests 9 and 10 depend on the tcl interface creating functions
+# without the SQLITE_DETERMINISTIC flag set.
+#
+foreach {tn where idx} {
+  1 "x = 4 AND y = 19"     {t1x (x=?)}
+  2 "x = 19 AND y = 4"     {t1y (y=?)}
+  3 "x = '4' AND y = '19'" {t1x (x=?)}
+  4 "x = '19' AND y = '4'" {t1y (y=?)}
+  5 "x = substr('5195', 2, 2) AND y = substr('145', 2, 1)" {t1y (y=?)}
+  6 "x = substr('145', 2, 1) AND y = substr('5195', 2, 2)" {t1x (x=?)}
+
+  7  "x = substr('5195', 2, 2+0) AND y = substr('145', 2, 1+0)" {t1y (y=?)}
+  8  "x = substr('145', 2, 1+0) AND y = substr('5195', 2, 2+0)" {t1y (y=?)}
+
+  9  "x = str('19') AND y = str('4')" {t1y (y=?)}
+  10 "x = str('4') AND y = str('19')" {t1y (y=?)}
+
+  11 "x = nullif('19', 0) AND y = nullif('4', 0)" {t1y (y=?)}
+  12 "x = nullif('4', 0) AND y = nullif('19', 0)" {t1y (y=?)}
+} {
+  set res "0 0 0 {SEARCH TABLE t1 USING INDEX $idx}"
+  do_eqp_test 1.$tn "SELECT * FROM t1 WHERE $where" $res
+}
+
+do_catchsql_test 2.1 {
+  SELECT * FROM t1 WHERE x = substr('145', 2, 1) AND y = func(1, 2, 3)
+} {1 {no such function: func}}
+do_catchsql_test 2.2 {
+  UPDATE t1 SET y=y+1 WHERE x = substr('145', 2, 1) AND y = func(1, 2, 3)
+} {1 {no such function: func}}
+
+
+finish_test
+