mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-10 01:02:56 +03:00
Refactor the code that figures out which SELECT in a cascade of nested queries
a particular aggregate function belongs to. This fixes the problem reported by [forum:/forumpost/c7cc2aa3546e39c1|forum post c7cc2aa3546e39c1]. New test cases in dbsqlfuzz and th3. FossilOrigin-Name: 74aec5dd1df95b5635f4da1f13753f113ea1d61de3dc3a1523ba51089c1900e4
This commit is contained in:
141
src/expr.c
141
src/expr.c
@@ -5879,81 +5879,109 @@ int sqlite3ExprCoveredByIndex(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** An instance of the following structure is used by the tree walker
|
||||
** to count references to table columns in the arguments of an
|
||||
** aggregate function, in order to implement the
|
||||
** sqlite3FunctionThisSrc() routine.
|
||||
/* Structure used to pass information throught the Walker in order to
|
||||
** implement sqlite3ReferencesSrcList().
|
||||
*/
|
||||
struct SrcCount {
|
||||
SrcList *pSrc; /* One particular FROM clause in a nested query */
|
||||
int iSrcInner; /* Smallest cursor number in this context */
|
||||
int nThis; /* Number of references to columns in pSrcList */
|
||||
int nOther; /* Number of references to columns in other FROM clauses */
|
||||
struct RefSrcList {
|
||||
sqlite3 *db; /* Database connection used for sqlite3DbRealloc() */
|
||||
SrcList *pRef; /* Looking for references to these tables */
|
||||
int nExclude; /* Number of tables to exclude from the search */
|
||||
int *aiExclude; /* Cursor IDs for tables to exclude from the search */
|
||||
};
|
||||
|
||||
/*
|
||||
** xSelect callback for sqlite3FunctionUsesThisSrc(). If this is the first
|
||||
** SELECT with a FROM clause encountered during this iteration, set
|
||||
** SrcCount.iSrcInner to the cursor number of the leftmost object in
|
||||
** the FROM cause.
|
||||
** Walker SELECT callbacks for sqlite3ReferencesSrcList().
|
||||
**
|
||||
** When entering a new subquery on the pExpr argument, add all FROM clause
|
||||
** entries for that subquery to the exclude list.
|
||||
**
|
||||
** When leaving the subquery, remove those entries from the exclude list.
|
||||
*/
|
||||
static int selectSrcCount(Walker *pWalker, Select *pSel){
|
||||
struct SrcCount *p = pWalker->u.pSrcCount;
|
||||
if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){
|
||||
pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor;
|
||||
static int selectRefEnter(Walker *pWalker, Select *pSelect){
|
||||
struct RefSrcList *p = pWalker->u.pRefSrcList;
|
||||
SrcList *pSrc = pSelect->pSrc;
|
||||
int i, j, *piNew;
|
||||
if( pSrc->nSrc==0 ) return WRC_Continue;
|
||||
j = p->nExclude;
|
||||
p->nExclude += pSrc->nSrc;
|
||||
piNew = sqlite3DbRealloc(p->db, p->aiExclude, p->nExclude*sizeof(int));
|
||||
if( piNew==0 ){
|
||||
p->nExclude = 0;
|
||||
return WRC_Abort;
|
||||
}else{
|
||||
p->aiExclude = piNew;
|
||||
}
|
||||
for(i=0; i<pSrc->nSrc; i++, j++){
|
||||
p->aiExclude[j] = pSrc->a[i].iCursor;
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
static void selectRefLeave(Walker *pWalker, Select *pSelect){
|
||||
struct RefSrcList *p = pWalker->u.pRefSrcList;
|
||||
SrcList *pSrc = pSelect->pSrc;
|
||||
if( p->nExclude ){
|
||||
assert( p->nExclude>=pSrc->nSrc );
|
||||
p->nExclude -= pSrc->nSrc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Count the number of references to columns.
|
||||
/* This is the Walker EXPR callback for sqlite3ReferencesSrcList().
|
||||
**
|
||||
** Set the 0x01 bit of pWalker->eCode if there is a reference to any
|
||||
** of the tables shown in RefSrcList.pRef.
|
||||
**
|
||||
** Set the 0x02 bit of pWalker->eCode if there is a reference to a
|
||||
** table is in neither RefSrcList.pRef nor RefSrcList.aiExclude.
|
||||
*/
|
||||
static int exprSrcCount(Walker *pWalker, Expr *pExpr){
|
||||
/* There was once a NEVER() on the second term on the grounds that
|
||||
** sqlite3FunctionUsesThisSrc() was always called before
|
||||
** sqlite3ExprAnalyzeAggregates() and so the TK_COLUMNs have not yet
|
||||
** been converted into TK_AGG_COLUMN. But this is no longer true due
|
||||
** to window functions - sqlite3WindowRewrite() may now indirectly call
|
||||
** FunctionUsesThisSrc() when creating a new sub-select. */
|
||||
if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){
|
||||
static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){
|
||||
if( pExpr->op==TK_COLUMN
|
||||
|| pExpr->op==TK_AGG_COLUMN
|
||||
){
|
||||
int i;
|
||||
struct SrcCount *p = pWalker->u.pSrcCount;
|
||||
SrcList *pSrc = p->pSrc;
|
||||
struct RefSrcList *p = pWalker->u.pRefSrcList;
|
||||
SrcList *pSrc = p->pRef;
|
||||
int nSrc = pSrc ? pSrc->nSrc : 0;
|
||||
for(i=0; i<nSrc; i++){
|
||||
if( pExpr->iTable==pSrc->a[i].iCursor ) break;
|
||||
if( pExpr->iTable==pSrc->a[i].iCursor ){
|
||||
pWalker->eCode |= 1;
|
||||
return WRC_Continue;
|
||||
}
|
||||
}
|
||||
if( i<nSrc ){
|
||||
p->nThis++;
|
||||
}else if( pExpr->iTable<p->iSrcInner ){
|
||||
/* In a well-formed parse tree (no name resolution errors),
|
||||
** TK_COLUMN nodes with smaller Expr.iTable values are in an
|
||||
** outer context. Those are the only ones to count as "other" */
|
||||
p->nOther++;
|
||||
for(i=0; i<p->nExclude && p->aiExclude[i]!=pExpr->iTable; i++){}
|
||||
if( i>=p->nExclude ){
|
||||
pWalker->eCode |= 2;
|
||||
}
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Determine if any of the arguments to the pExpr Function reference
|
||||
** pSrcList. Return true if they do. Also return true if the function
|
||||
** has no arguments or has only constant arguments. Return false if pExpr
|
||||
** references columns but not columns of tables found in pSrcList.
|
||||
** Check to see if pExpr references any tables in pSrcList.
|
||||
** Possible return values:
|
||||
**
|
||||
** 1 pExpr does references a table in pSrcList.
|
||||
**
|
||||
** 0 pExpr references some table that is not defined in either
|
||||
** pSrcList or in subqueries of pExpr itself.
|
||||
**
|
||||
** -1 pExpr only references no tables at all, or it only
|
||||
** references tables defined in subqueries of pExpr itself.
|
||||
**
|
||||
** As currently used, pExpr is always an aggregate function call. That
|
||||
** fact is exploited for efficiency.
|
||||
*/
|
||||
int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
|
||||
int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){
|
||||
Walker w;
|
||||
struct SrcCount cnt;
|
||||
assert( pExpr->op==TK_AGG_FUNCTION );
|
||||
struct RefSrcList x;
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = exprSrcCount;
|
||||
w.xSelectCallback = selectSrcCount;
|
||||
w.u.pSrcCount = &cnt;
|
||||
cnt.pSrc = pSrcList;
|
||||
cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF;
|
||||
cnt.nThis = 0;
|
||||
cnt.nOther = 0;
|
||||
memset(&x, 0, sizeof(x));
|
||||
w.xExprCallback = exprRefToSrcList;
|
||||
w.xSelectCallback = selectRefEnter;
|
||||
w.xSelectCallback2 = selectRefLeave;
|
||||
w.u.pRefSrcList = &x;
|
||||
x.db = pParse->db;
|
||||
x.pRef = pSrcList;
|
||||
assert( pExpr->op==TK_AGG_FUNCTION );
|
||||
assert( ExprUseXList(pExpr) );
|
||||
sqlite3WalkExprList(&w, pExpr->x.pList);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
@@ -5961,7 +5989,14 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
|
||||
sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter);
|
||||
}
|
||||
#endif
|
||||
return cnt.nThis>0 || cnt.nOther==0;
|
||||
sqlite3DbFree(pParse->db, x.aiExclude);
|
||||
if( w.eCode & 0x01 ){
|
||||
return 1;
|
||||
}else if( w.eCode ){
|
||||
return 0;
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user