aboutsummaryrefslogtreecommitdiff
path: root/src/expr.c
diff options
context:
space:
mode:
authordan <Dan Kennedy>2024-10-05 17:37:19 +0000
committerdan <Dan Kennedy>2024-10-05 17:37:19 +0000
commitc857b9eb5d9cfab1366fb80e5d1c306e340b6d65 (patch)
treeec8b3498483cef7b6587a9bc5409e81b24225700 /src/expr.c
parent706fdeebb834a11fedc5e9473f951af79e6f2d65 (diff)
downloadsqlite-c857b9eb5d9cfab1366fb80e5d1c306e340b6d65.tar.gz
sqlite-c857b9eb5d9cfab1366fb80e5d1c306e340b6d65.zip
Experimental change to allow expressions with subtypes to be read from indexes in situations where they are not used as function parameters.
FossilOrigin-Name: ac63f98ad85a4dd1e49cc64b41f0ca0044153972c15d71c669f4bc3ec590e268
Diffstat (limited to 'src/expr.c')
-rw-r--r--src/expr.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/expr.c b/src/expr.c
index 1b18828dd..83b1ff56c 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1164,6 +1164,7 @@ Expr *sqlite3ExprFunction(
){
Expr *pNew;
sqlite3 *db = pParse->db;
+ int ii;
assert( pToken );
pNew = sqlite3ExprAlloc(db, TK_FUNCTION, pToken, 1);
if( pNew==0 ){
@@ -1178,6 +1179,11 @@ Expr *sqlite3ExprFunction(
){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken);
}
+ if( pList && pParse->nErr==0 ){
+ for(ii=0; ii<pList->nExpr; ii++){
+ ExprSetProperty(pList->a[ii].pExpr, EP_FuncArg);
+ }
+ }
pNew->x.pList = pList;
ExprSetProperty(pNew, EP_HasFunc);
assert( ExprUseXList(pNew) );
@@ -4555,6 +4561,59 @@ static int exprCodeInlineFunction(
}
/*
+** Expression Node callback for sqlite3ExprCanReturnSubtype().
+**
+** Only a function call is able to return a subtype. So if the node
+** is not a function call, return WRC_Prune immediately.
+**
+** A function call is able to return a subtype if it has the
+** SQLITE_RESULT_SUBTYPE property.
+**
+** Assume that every function is able to pass-through a subtype from
+** one of its argument (using sqlite3_result_value()). Most functions
+** are not this way, but we don't have a mechanism to distinguish those
+** that are from those that are not, so assume they all work this way.
+** That means that if one of its arguments is another function and that
+** other function is able to return a subtype, then this function is
+** able to return a subtype.
+*/
+static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){
+ int n;
+ FuncDef *pDef;
+ sqlite3 *db;
+ if( pExpr->op!=TK_FUNCTION ){
+ return WRC_Prune;
+ }
+ assert( ExprUseXList(pExpr) );
+ db = pWalker->pParse->db;
+ n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
+ pWalker->eCode = 1;
+ return WRC_Prune;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Return TRUE if expression pExpr is able to return a subtype.
+**
+** A TRUE return does not guarantee that a subtype will be returned.
+** It only indicates that a subtype return is possible. False positives
+** are acceptable as they only disable an optimization. False negatives,
+** on the other hand, can lead to incorrect answers.
+*/
+static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.pParse = pParse;
+ w.xExprCallback = exprNodeCanReturnSubtype;
+ sqlite3WalkExpr(&w, pExpr);
+ return w.eCode;
+}
+
+
+/*
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
** If it is, then resolve the expression by reading from the index and
** return the register into which the value has been read. If pExpr is
@@ -4586,6 +4645,17 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
continue;
}
+
+ /* Functions that might set a subtype should not be replaced by the
+ ** value taken from an expression index if they are themselves an
+ ** argument to another scalar function or aggregate.
+ ** https://sqlite.org/forum/forumpost/68d284c86b082c3e */
+ if( ExprHasProperty(pExpr, EP_FuncArg)
+ && sqlite3ExprCanReturnSubtype(pParse, pExpr)
+ ){
+ continue;
+ }
+
v = pParse->pVdbe;
assert( v!=0 );
if( p->bMaybeNullRow ){