Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | When flattening UNION ALL subqueries into a join query, ensure that separate cursor numbers are used for each segment of the newly flattened query. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | union-all-flattener |
Files: | files | file ages | folders |
SHA3-256: |
c510377b0b052e400f2ee4f20220b61c |
User & Date: | dan 2020-12-18 16:13:39 |
Context
2020-12-18
| ||
18:04 | Fix for the previous fix in the case where a UNION ALL sub-query is joined against some other compound query. (Closed-Leaf check-in: 63c5cfb9 user: dan tags: union-all-flattener) | |
16:13 | When flattening UNION ALL subqueries into a join query, ensure that separate cursor numbers are used for each segment of the newly flattened query. (check-in: c510377b user: dan tags: union-all-flattener) | |
2020-12-17
| ||
17:17 | Fix a part of the header comment for flattenSubquery(). (check-in: dc0937ce user: dan tags: union-all-flattener) | |
Changes
Changes to src/select.c.
︙ | ︙ | |||
3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 | w.xSelectCallback = sqlite3SelectWalkNoop; w.u.pSrcItem = pSrcItem; pSrcItem->colUsed = 0; sqlite3WalkSelect(&w, pSelect); } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** This routine attempts to flatten subqueries as a performance optimization. ** This routine returns 1 if it makes changes and 0 if no flattening occurs. ** ** To understand the concept of flattening, consider the following ** query: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 | w.xSelectCallback = sqlite3SelectWalkNoop; w.u.pSrcItem = pSrcItem; pSrcItem->colUsed = 0; sqlite3WalkSelect(&w, pSelect); } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** Assign new cursor numbers to each of the items in pSrc. For each ** new cursor number assigned, set an entry in the aCsrMap[] array ** to map the old cursor number to the new: ** ** aCsrMap[iOld] = iNew; ** ** The array is guaranteed by the caller to be large enough for all ** existing cursor numbers in pSrc. ** ** If pSrc contains any sub-selects, call this routine recursively ** on the FROM clause of each such sub-select, with iExcept set to -1. */ static void srclistRenumberCursors( Parse *pParse, /* Parse context */ int *aCsrMap, /* Array to store cursor mappings in */ SrcList *pSrc, /* FROM clause to renumber */ int iExcept /* FROM clause item to skip */ ){ int i; struct SrcList_item *pItem; for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){ if( i!=iExcept ){ int iNew = pParse->nTab++; aCsrMap[pItem->iCursor] = iNew; pItem->iCursor = iNew; if( pItem->pSelect ){ srclistRenumberCursors(pParse, aCsrMap, pItem->pSelect->pSrc, -1); } } } } /* ** Expression walker callback used by renumberCursors() to update ** Expr objects to match newly assigned cursor numbers. */ static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ int *aCsrMap = pWalker->u.aiCol; if( pExpr->op==TK_COLUMN && aCsrMap[pExpr->iTable] ){ pExpr->iTable = aCsrMap[pExpr->iTable]; } return WRC_Continue; } /* ** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc) ** of the SELECT statement passed as the second argument, and to each ** cursor in the FROM clause of any FROM clause sub-selects, recursively. ** Except, do not assign a new cursor number to the iExcept'th element in ** the FROM clause of (*p). Update all expressions and other references ** to refer to the new cursor numbers. ** ** Argument aCsrMap is an array that may be used for temporary working ** space. Two guarantees are made by the caller: ** ** * the array is larger than the largest cursor number used within the ** select statement passed as an argument, and ** ** * the array entries for all cursor numbers that do *not* appear in ** FROM clauses of the select statement as described above are ** initialized to zero. */ static void renumberCursors( Parse *pParse, /* Parse context */ Select *p, /* Select to renumber cursors within */ int iExcept, /* FROM clause item to skip */ int *aCsrMap /* Working space */ ){ Walker w; srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept); memset(&w, 0, sizeof(w)); w.u.aiCol = aCsrMap; w.xExprCallback = renumberCursorsCb; w.xSelectCallback = sqlite3SelectWalkNoop; sqlite3WalkSelect(&w, p); } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** This routine attempts to flatten subqueries as a performance optimization. ** This routine returns 1 if it makes changes and 0 if no flattening occurs. ** ** To understand the concept of flattening, consider the following ** query: |
︙ | ︙ | |||
3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 | int iNewParent = -1;/* Replacement table for iParent */ int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ int i; /* Loop counter */ Expr *pWhere; /* The WHERE clause */ struct SrcList_item *pSubitem; /* The subquery */ sqlite3 *db = pParse->db; Walker w; /* Walker to persist agginfo data */ /* Check to see if flattening is permitted. Return 0 if not. */ assert( p!=0 ); assert( p->pPrior==0 ); if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0; pSrc = p->pSrc; | > | 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 | int iNewParent = -1;/* Replacement table for iParent */ int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */ int i; /* Loop counter */ Expr *pWhere; /* The WHERE clause */ struct SrcList_item *pSubitem; /* The subquery */ sqlite3 *db = pParse->db; Walker w; /* Walker to persist agginfo data */ int *aCsrMap = 0; /* Check to see if flattening is permitted. Return 0 if not. */ assert( p!=0 ); assert( p->pPrior==0 ); if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0; pSrc = p->pSrc; |
︙ | ︙ | |||
3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 | for(ii=0; ii<p->pOrderBy->nExpr; ii++){ if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; } } /* Restriction (23) */ if( (p->selFlags & SF_Recursive) ) return 0; } /***** If we reach this point, flattening is permitted. *****/ SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n", pSub->selId, pSub, iFrom)); /* Authorize the subquery */ | > > > > | 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 | for(ii=0; ii<p->pOrderBy->nExpr; ii++){ if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; } } /* Restriction (23) */ if( (p->selFlags & SF_Recursive) ) return 0; if( pSrc->nSrc>1 ){ aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int)); } } /***** If we reach this point, flattening is permitted. *****/ SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n", pSub->selId, pSub, iFrom)); /* Authorize the subquery */ |
︙ | ︙ | |||
4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 | p->pLimit = pLimit; p->pOrderBy = pOrderBy; p->op = TK_ALL; pSubitem->pTab = pItemTab; if( pNew==0 ){ p->pPrior = pPrior; }else{ pNew->pPrior = pPrior; if( pPrior ) pPrior->pNext = pNew; pNew->pNext = p; p->pPrior = pNew; SELECTTRACE(2,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } assert( pSubitem->pSelect==0 ); | > > > > > | | | < | 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 | p->pLimit = pLimit; p->pOrderBy = pOrderBy; p->op = TK_ALL; pSubitem->pTab = pItemTab; if( pNew==0 ){ p->pPrior = pPrior; }else{ if( aCsrMap && db->mallocFailed==0 ){ renumberCursors(pParse, pNew, iFrom, aCsrMap); } pNew->pPrior = pPrior; if( pPrior ) pPrior->pNext = pNew; pNew->pNext = p; p->pPrior = pNew; SELECTTRACE(2,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } assert( pSubitem->pSelect==0 ); } sqlite3DbFree(db, aCsrMap); if( db->mallocFailed ){ pSubitem->pSelect = pSub1; return 1; } /* Defer deleting the Table object associated with the ** subquery until code generation is ** complete, since there may still exist Expr.pTab entries that ** refer to the subquery even after flattening. Ticket #3346. ** |
︙ | ︙ |
Changes to test/unionall.test.
︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 | SELECT * FROM t1, t2 WHERE x=k ORDER BY k,v||'' } { 2 two 2 ii 2 TWO 2 ii 3 three 3 iii 3 THREE 3 iii } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | SELECT * FROM t1, t2 WHERE x=k ORDER BY k,v||'' } { 2 two 2 ii 2 TWO 2 ii 3 three 3 iii 3 THREE 3 iii } do_execsql_test 2.2.9a { SELECT * FROM t1, t2 ORDER BY +k } { 1 one 2 ii 1 ONE 2 ii 2 two 2 ii 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii 1 one 3 iii 1 ONE 3 iii 2 two 3 iii 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii 1 one 4 iv 1 ONE 4 iv 2 two 4 iv 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv 1 one 5 v 1 ONE 5 v 2 two 5 v 2 TWO 5 v 3 three 5 v 3 THREE 5 v } do_execsql_test 2.2.9b { SELECT * FROM t1, t2 ORDER BY k } { 1 one 2 ii 1 ONE 2 ii 2 two 2 ii 2 TWO 2 ii 3 three 2 ii 3 THREE 2 ii 1 one 3 iii 1 ONE 3 iii 2 two 3 iii 2 TWO 3 iii 3 three 3 iii 3 THREE 3 iii 1 one 4 iv 1 ONE 4 iv 2 two 4 iv 2 TWO 4 iv 3 three 4 iv 3 THREE 4 iv 1 one 5 v 1 ONE 5 v 2 two 5 v 2 TWO 5 v 3 three 5 v 3 THREE 5 v } finish_test |
Changes to test/unionallfault.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 | faultsim_save_and_close do_faultsim_test 1 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | faultsim_save_and_close do_faultsim_test 1 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { SELECT * FROM t1, ( SELECT x FROM t1 UNION ALL SELECT y FROM t1 ), t3 } } -test { faultsim_test_result {0 {}} } finish_test |