aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2025-07-18 12:10:15 +0000
committerdrh <>2025-07-18 12:10:15 +0000
commit74cc109780ee33138db6e7ed076426604131b8c6 (patch)
tree3ecd297d28c2aa81e60d437c03219386e9b51ad2 /src
parent01a2953350395ca3b0962c9a0245db847f271b82 (diff)
downloadsqlite-74cc109780ee33138db6e7ed076426604131b8c6.tar.gz
sqlite-74cc109780ee33138db6e7ed076426604131b8c6.zip
Ensure that the accumulator for an aggregate always gets initialized,
even when the aggregate is on the right side of a LEFT JOIN and never gets evaluated. This fixes a problem introduced by [663f5dd32d9db832] and found by dbsqlfuzz. Test cases in TH3. FossilOrigin-Name: 235cf6586b9ac914a32bd8adfd460daae998687f02f0998a7aa3c6bfc857d1c9
Diffstat (limited to 'src')
-rw-r--r--src/expr.c25
-rw-r--r--src/select.c1
-rw-r--r--src/sqliteInt.h2
3 files changed, 28 insertions, 0 deletions
diff --git a/src/expr.c b/src/expr.c
index 3f040309a..dba0aa2de 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -4989,6 +4989,12 @@ expr_code_doover:
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
return target;
}
+ case TK_NULLS: {
+ /* Set a range of registers to NULL. pExpr->y.nReg registers starting
+ ** with target */
+ sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
+ return target;
+ }
default: {
/* Make NULL the default case so that if a bug causes an illegal
** Expr node to be passed into this function, it will be handled
@@ -5699,6 +5705,25 @@ int sqlite3ExprCodeRunJustOnce(
}
/*
+** Make arrangements to invoke OP_Null on a range of registers
+** during initialization.
+*/
+SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
+ Parse *pParse, /* Parsing context */
+ int iReg, /* First register to set to NULL */
+ int nReg /* Number of sequential registers to NULL out */
+){
+ u8 okConstFactor = pParse->okConstFactor;
+ Expr t;
+ memset(&t, 0, sizeof(t));
+ t.op = TK_NULLS;
+ t.y.nReg = nReg;
+ pParse->okConstFactor = 1;
+ sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
+ pParse->okConstFactor = okConstFactor;
+}
+
+/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
** are stored.
diff --git a/src/select.c b/src/select.c
index 1b1266313..665c30933 100644
--- a/src/select.c
+++ b/src/select.c
@@ -8482,6 +8482,7 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
+ sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index df0e1c3ba..a09c94ae6 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3082,6 +3082,7 @@ struct Expr {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
+ int nReg; /* TK_NULLS: Number of registers to NULL out */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@@ -5119,6 +5120,7 @@ void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
void sqlite3ExprCodeCopy(Parse*, Expr*, int);
void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
+void sqlite3ExprNullRegisterRange(Parse*, int, int);
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);