mirror of https://github.com/sqlite/sqlite
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:
parent
cc285c5ab0
commit
18bf807689
20
manifest
20
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
|
||||
|
|
|
@ -1 +1 @@
|
|||
a2715b049a86555990abccc7aa363c524ddb9982
|
||||
b7f1fc26d24012e1e7c7f6b3cc0b84ad2b02b8ad
|
|
@ -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;
|
||||
|
|
126
src/vdbemem.c
126
src/vdbemem.c
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue