Allow the query planner to evaluate deterministic scalar SQL functions used in WHERE constraints if all arguments are SQL literals in order to compare the results with sqlite_stat4 sample data.

FossilOrigin-Name: b7f1fc26d24012e1e7c7f6b3cc0b84ad2b02b8ad
This commit is contained in:
dan 2015-03-11 20:06:40 +00:00
parent cc285c5ab0
commit 18bf807689
5 changed files with 225 additions and 14 deletions

View File

@ -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

View File

@ -1 +1 @@
a2715b049a86555990abccc7aa363c524ddb9982
b7f1fc26d24012e1e7c7f6b3cc0b84ad2b02b8ad

View 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;

View 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;

78
test/analyzeF.test Normal file
View File

@ -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