diff options
author | drh <drh@noemail.net> | 2020-06-08 11:34:40 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2020-06-08 11:34:40 +0000 |
commit | e6463a717a15ec0c7958e1bda45bd08da28188ab (patch) | |
tree | 2c9346ec7fee7e74498eda401732203ad25e0e70 /src/expr.c | |
parent | a0365c487c1267af6d3ab101399c6fa35f5184a3 (diff) | |
parent | 2f82acc036c5f286fda6ef815bfc3469acd402e2 (diff) | |
download | sqlite-e6463a717a15ec0c7958e1bda45bd08da28188ab.tar.gz sqlite-e6463a717a15ec0c7958e1bda45bd08da28188ab.zip |
When an Expr object is changed and that Expr is referenced by an AggInfo, then
also update the AggInfo. Also, persist all AggInfo objects until the Parse
object is destroyed. This is a new fix for ticket [c8d3b9f0a750a529] that
avoids the follow-on problems identified by tickets
[0899cf62f597d7e7], [1f6f353b684fc708], [e5504e987e419fb0], and
[f7d890858f361402].
FossilOrigin-Name: 6e6b3729e0549de028f6c5bf494b2d69d621c81b61a1dc0a329d3950039342fb
Diffstat (limited to 'src/expr.c')
-rw-r--r-- | src/expr.c | 67 |
1 files changed, 65 insertions, 2 deletions
diff --git a/src/expr.c b/src/expr.c index 496177a5c..6fbc8bb49 100644 --- a/src/expr.c +++ b/src/expr.c @@ -52,7 +52,12 @@ char sqlite3ExprAffinity(const Expr *pExpr){ op = pExpr->op; if( op==TK_SELECT ){ assert( pExpr->flags&EP_xIsSelect ); - return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); + if( ALWAYS(pExpr->x.pSelect) + && pExpr->x.pSelect->pEList + && ALWAYS(pExpr->x.pSelect->pEList->a[0].pExpr) + ){ + return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); + } } if( op==TK_REGISTER ) op = pExpr->op2; #ifndef SQLITE_OMIT_CAST @@ -5710,6 +5715,64 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ } /* +** This is a Walker expression node callback. +** +** For Expr nodes that contain pAggInfo pointers, make sure the AggInfo +** object that is referenced does not refer directly to the Expr. If +** it does, make a copy. This is done because the pExpr argument is +** subject to change. +** +** The copy is stored on pParse->pConstExpr with a register number of 0. +** This will cause the expression to be deleted automatically when the +** Parse object is destroyed, but the zero register number means that it +** will not generate any code in the preamble. +*/ +static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ + if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) + && pExpr->pAggInfo!=0 + ){ + AggInfo *pAggInfo = pExpr->pAggInfo; + int iAgg = pExpr->iAgg; + Parse *pParse = pWalker->pParse; + sqlite3 *db = pParse->db; + assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION ); + if( pExpr->op==TK_AGG_COLUMN ){ + assert( iAgg>=0 && iAgg<pAggInfo->nColumn ); + if( pAggInfo->aCol[iAgg].pExpr==pExpr ){ + pExpr = sqlite3ExprDup(db, pExpr, 0); + if( pExpr ){ + pAggInfo->aCol[iAgg].pExpr = pExpr; + pParse->pConstExpr = + sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + } + } + }else{ + assert( iAgg>=0 && iAgg<pAggInfo->nFunc ); + if( pAggInfo->aFunc[iAgg].pExpr==pExpr ){ + pExpr = sqlite3ExprDup(db, pExpr, 0); + if( pExpr ){ + pAggInfo->aFunc[iAgg].pExpr = pExpr; + pParse->pConstExpr = + sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + } + } + } + } + return WRC_Continue; +} + +/* +** Initialize a Walker object so that will persist AggInfo entries referenced +** by the tree that is walked. +*/ +void sqlite3AggInfoPersistWalkerInit(Walker *pWalker, Parse *pParse){ + memset(pWalker, 0, sizeof(*pWalker)); + pWalker->pParse = pParse; + pWalker->xExprCallback = agginfoPersistExprCb; + pWalker->xSelectCallback = sqlite3SelectWalkNoop; +} + +/* ** Add a new element to the pAggInfo->aCol[] array. Return the index of ** the new element. Return a negative number if malloc fails. */ @@ -5739,7 +5802,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){ &i ); return i; -} +} /* ** This is the xExprCallback for a tree walker. It is used to |