Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improved optimizations of views as the right operand of a LEFT JOIN. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
41c27bc0ff1d3135cdb6273ede4595f5 |
User & Date: | drh 2017-04-18 11:20:19 |
References
2023-03-02
| ||
13:49 | When flattening the right operand of a LEFT JOIN (check-in [41c27bc0ff1d3135]), ensure that the OP_IfNullRow opcode does not NULL-out a subquery result that was computed within OP_Once. This fixes the problem problem reported by forum post 402f05296d. (check-in: 8fe13f7a user: drh tags: trunk) | |
2023-03-01
| ||
20:23 | When flattening a view that is the right operand of a LEFT JOIN, using the optimization of check-in [41c27bc0ff1d3135], always insert the TK_IF_NULL_ROW expression nodes, even for TK_COLUMN expressions, as the TK_COLUMN might be a column from an outer query and hence still need to be NULLed out. This fixes the problem described by forum post 26387ea7ef. (check-in: 198b3e33 user: drh tags: trunk) | |
2020-02-22
| ||
13:07 | • Fixed ticket [aa437869]: sqlite3.c:87338: int sqlite3VdbeExec(Vdbe *): Assertion `sqlite3BtreeCursorIsValid(pCrsr)' failed. plus 6 other changes (artifact: 9fee2067 user: drh) | |
2019-12-18
| ||
20:51 | Continue to back away from the LEFT JOIN optimization of check-in [41c27bc0ff1d3135] by disallowing query flattening if the outer query is DISTINCT. Without this fix, if an index scan is run on the table within the view on the right-hand side of the LEFT JOIN, stale result registers might be accessed yielding incorrect results, and/or an OP_IfNullRow opcode might be invoked on the un-opened table, resulting in a NULL-pointer dereference. This problem was found by the Yongheng and Rui fuzzer. (check-in: 86297431 user: drh tags: trunk) | |
2017-07-29
| ||
03:26 | • New ticket [de3403bf] Column name shift due to LEFT JOIN query flattening. (artifact: e6b7df69 user: drh) | |
2017-06-20
| ||
16:15 | • New ticket [892fc34f] Incorrect query result when a LEFT JOIN subquery is flattened. (artifact: d28d6544 user: drh) | |
Context
2017-04-18
| ||
13:50 | Use sqlite3_table_column_metadata() instead of a SELECT statement to check for the existence of a %_stat table in fts3. This leads to smaller and easier to test code. (check-in: dc2a4802 user: dan tags: trunk) | |
11:20 | Improved optimizations of views as the right operand of a LEFT JOIN. (check-in: 41c27bc0 user: drh tags: trunk) | |
05:49 | Refactor the fts3ColumnMethod() function so that all branches can be covered. (check-in: e47fdb49 user: dan tags: trunk) | |
2017-04-14
| ||
19:03 | Add the TK_IF_NULL_ROW opcode to deal with non-NULL result columns in the result set of a view or subquery on the RHS of a LEFT JOIN that gets flattened. (Closed-Leaf check-in: 3a5860d8 user: drh tags: left-join-view) | |
Changes
Changes to src/expr.c.
︙ | ︙ | |||
3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 | break; } case TK_VECTOR: { sqlite3ErrorMsg(pParse, "row value misused"); break; } /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** ** Form B: ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END | > > > > > > > > > > > | 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 | break; } case TK_VECTOR: { sqlite3ErrorMsg(pParse, "row value misused"); break; } case TK_IF_NULL_ROW: { int addrINR; addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable); sqlite3ExprCachePush(pParse); inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); sqlite3ExprCachePop(pParse); sqlite3VdbeJumpHere(v, addrINR); sqlite3VdbeChangeP3(v, addrINR, inReg); break; } /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** ** Form B: ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 197 198 199 200 201 | A = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); } } columnlist ::= columnlist COMMA columnname carglist. columnlist ::= columnname carglist. columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} // Define operator precedence early so that this is the first occurrence // of the operator tokens in the grammer. Keeping the operators together // causes them to be assigned integer values that are close together, // which keeps parser tables smaller. // // The token values assigned to these symbols is determined by the order | > > > > > > > > > > > > > > > > > | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | A = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); } } columnlist ::= columnlist COMMA columnname carglist. columnlist ::= columnname carglist. columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} // The following directive causes tokens ABORT, AFTER, ASC, etc. to // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT REINDEX RENAME CTIME_KW IF . %wildcard ANY. // Define operator precedence early so that this is the first occurrence // of the operator tokens in the grammer. Keeping the operators together // causes them to be assigned integer values that are close together, // which keeps parser tables smaller. // // The token values assigned to these symbols is determined by the order |
︙ | ︙ | |||
218 219 220 221 222 223 224 | %right BITNOT. // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. // %token_class id ID|INDEXED. | < < < < < < < < < < < < < < < < < | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | %right BITNOT. // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. // %token_class id ID|INDEXED. // And "ids" is an identifer-or-string. // %token_class ids ID|STRING. // The name of a column or table can be any of the following: // |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 | ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. */ typedef struct SubstContext { Parse *pParse; /* The parsing context */ int iTable; /* Replace references to this table */ ExprList *pEList; /* Replacement expressions */ } SubstContext; /* Forward Declarations */ static void substExprList(SubstContext*, ExprList*); static void substSelect(SubstContext*, Select*, int); | > > | 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 | ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. */ typedef struct SubstContext { Parse *pParse; /* The parsing context */ int iTable; /* Replace references to this table */ int iNewTable; /* New table number */ int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */ ExprList *pEList; /* Replacement expressions */ } SubstContext; /* Forward Declarations */ static void substExprList(SubstContext*, ExprList*); static void substSelect(SubstContext*, Select*, int); |
︙ | ︙ | |||
3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 | ** of the subquery rather the result set of the subquery. */ static Expr *substExpr( SubstContext *pSubst, /* Description of the substitution */ Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){ if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; }else{ Expr *pNew; Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr ); assert( pExpr->pLeft==0 && pExpr->pRight==0 ); if( sqlite3ExprIsVector(pCopy) ){ sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ sqlite3 *db = pSubst->pParse->db; pNew = sqlite3ExprDup(db, pCopy, 0); if( pNew && (pExpr->flags & EP_FromJoin) ){ pNew->iRightJoinTable = pExpr->iRightJoinTable; pNew->flags |= EP_FromJoin; } sqlite3ExprDelete(db, pExpr); pExpr = pNew; | > > > > > > > > > > > | 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 | ** of the subquery rather the result set of the subquery. */ static Expr *substExpr( SubstContext *pSubst, /* Description of the substitution */ Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; if( ExprHasProperty(pExpr, EP_FromJoin) && pExpr->iRightJoinTable==pSubst->iTable ){ pExpr->iRightJoinTable = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){ if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; }else{ Expr *pNew; Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; Expr ifNullRow; assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr ); assert( pExpr->pLeft==0 && pExpr->pRight==0 ); if( sqlite3ExprIsVector(pCopy) ){ sqlite3VectorErrorMsg(pSubst->pParse, pCopy); }else{ sqlite3 *db = pSubst->pParse->db; if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){ memset(&ifNullRow, 0, sizeof(ifNullRow)); ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; ifNullRow.iTable = pSubst->iNewTable; pCopy = &ifNullRow; } pNew = sqlite3ExprDup(db, pCopy, 0); if( pNew && (pExpr->flags & EP_FromJoin) ){ pNew->iRightJoinTable = pExpr->iRightJoinTable; pNew->flags |= EP_FromJoin; } sqlite3ExprDelete(db, pExpr); pExpr = pNew; |
︙ | ︙ | |||
3280 3281 3282 3283 3284 3285 3286 | ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or (2a) the outer query is not a join ** and (2b) the outer query does not use subqueries other than the one ** FROM-clause subquery that is a candidate for flattening. (2b is ** due to ticket [2f7170d73bf9abf80] from 2015-02-09.) ** | | | | | 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 | ** (1) The subquery and the outer query do not both use aggregates. ** ** (2) The subquery is not an aggregate or (2a) the outer query is not a join ** and (2b) the outer query does not use subqueries other than the one ** FROM-clause subquery that is a candidate for flattening. (2b is ** due to ticket [2f7170d73bf9abf80] from 2015-02-09.) ** ** (3) The subquery is not the right operand of a LEFT JOIN ** or the subquery is not itself a join. ** ** (4) The subquery is not DISTINCT. ** ** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT ** sub-queries that were excluded from this optimization. Restriction ** (4) has since been expanded to exclude all DISTINCT subqueries. ** ** (6) The subquery does not use aggregates or the outer query is not ** DISTINCT. ** ** (7) The subquery has a FROM clause. TODO: For subqueries without ** A FROM clause, consider adding a FROM clause with the special ** table sqlite_once that consists of a single row containing a ** single NULL. ** ** (8) The subquery does not use LIMIT or the outer query is not a join. ** ** (9) The subquery does not use LIMIT or the outer query does not use ** aggregates. |
︙ | ︙ | |||
3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 | Select *pParent; /* Current UNION ALL term of the other query */ Select *pSub; /* The inner query or "subquery" */ Select *pSub1; /* Pointer to the rightmost select in sub-query */ SrcList *pSrc; /* The FROM clause of the outer query */ SrcList *pSubSrc; /* The FROM clause of the subquery */ ExprList *pList; /* The result set of the outer query */ int iParent; /* VDBE cursor number of the pSub result set temp table */ int i; /* Loop counter */ Expr *pWhere; /* The WHERE clause */ struct SrcList_item *pSubitem; /* The subquery */ sqlite3 *db = pParse->db; /* Check to see if flattening is permitted. Return 0 if not. */ | > > | 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 | Select *pParent; /* Current UNION ALL term of the other query */ Select *pSub; /* The inner query or "subquery" */ Select *pSub1; /* Pointer to the rightmost select in sub-query */ SrcList *pSrc; /* The FROM clause of the outer query */ SrcList *pSubSrc; /* The FROM clause of the subquery */ ExprList *pList; /* The result set of the outer query */ int iParent; /* VDBE cursor number of the pSub result set temp table */ 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; /* Check to see if flattening is permitted. Return 0 if not. */ |
︙ | ︙ | |||
3425 3426 3427 3428 3429 3430 3431 | if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery)) || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0 || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0 ){ return 0; /* Restriction (2b) */ } } | | | 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 | if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery)) || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0 || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0 ){ return 0; /* Restriction (2b) */ } } pSubSrc = pSub->pSrc; assert( pSubSrc ); /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, ** not arbitrary expressions, we allowed some combining of LIMIT and OFFSET ** because they could be computed at compile-time. But when LIMIT and OFFSET ** became arbitrary expressions, we were forced to add restrictions (13) ** and (14). */ |
︙ | ︙ | |||
3463 3464 3465 3466 3467 3468 3469 | if( pSub->selFlags & (SF_Recursive|SF_MinMaxAgg) ){ return 0; /* Restrictions (22) and (24) */ } if( (p->selFlags & SF_Recursive) && pSub->pPrior ){ return 0; /* Restriction (23) */ } | | | | < | < < < < < < < < < < < < < < < < < > > | > | 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 | if( pSub->selFlags & (SF_Recursive|SF_MinMaxAgg) ){ return 0; /* Restrictions (22) and (24) */ } if( (p->selFlags & SF_Recursive) && pSub->pPrior ){ return 0; /* Restriction (23) */ } /* ** If the subquery is the right operand of a LEFT JOIN, then the ** subquery may not be a join itself. Example of why this is not allowed: ** ** t1 LEFT OUTER JOIN (t2 JOIN t3) ** ** If we flatten the above, we would get ** ** (t1 LEFT OUTER JOIN t2) JOIN t3 ** ** which is not at all the same thing. ** ** See also tickets #306, #350, and #3300. */ if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ isLeftJoin = 1; if( pSubSrc->nSrc>1 ){ return 0; /* Restriction (3) */ } } /* Restriction 17: If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries ** that make up the compound SELECT are allowed to be aggregate or distinct ** queries. */ |
︙ | ︙ | |||
3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 | /* Transfer the FROM clause terms from the subquery into the ** outer query. */ for(i=0; i<nSubSrc; i++){ sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing); assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); pSrc->a[i+iFrom] = pSubSrc->a[i]; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } pSrc->a[iFrom].fg.jointype = jointype; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. ** | > | 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 | /* Transfer the FROM clause terms from the subquery into the ** outer query. */ for(i=0; i<nSubSrc; i++){ sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing); assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); pSrc->a[i+iFrom] = pSubSrc->a[i]; iNewParent = pSubSrc->a[i].iCursor; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } pSrc->a[iFrom].fg.jointype = jointype; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. ** |
︙ | ︙ | |||
3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 | } assert( pParent->pOrderBy==0 ); assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); if( subqueryIsAgg ){ assert( pParent->pHaving==0 ); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; pParent->pHaving = sqlite3ExprAnd(db, sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving ); assert( pParent->pGroupBy==0 ); pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); }else{ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere); } if( db->mallocFailed==0 ){ SubstContext x; x.pParse = pParse; x.iTable = iParent; x.pEList = pSub->pEList; substSelect(&x, pParent, 0); } /* The flattened query is distinct if either the inner or the ** outer query is distinct. */ | > > > > > | 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 | } assert( pParent->pOrderBy==0 ); assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); if( isLeftJoin ){ setJoinExpr(pWhere, iNewParent); } if( subqueryIsAgg ){ assert( pParent->pHaving==0 ); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; pParent->pHaving = sqlite3ExprAnd(db, sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving ); assert( pParent->pGroupBy==0 ); pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); }else{ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere); } if( db->mallocFailed==0 ){ SubstContext x; x.pParse = pParse; x.iTable = iParent; x.iNewTable = iNewParent; x.isLeftJoin = isLeftJoin; x.pEList = pSub->pEList; substSelect(&x, pParent, 0); } /* The flattened query is distinct if either the inner or the ** outer query is distinct. */ |
︙ | ︙ | |||
3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 | if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ nChng++; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); x.pParse = pParse; x.iTable = iCursor; x.pEList = pSubq->pEList; pNew = substExpr(&x, pNew); pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew); pSubq = pSubq->pPrior; } } return nChng; | > > | 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 | if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){ nChng++; while( pSubq ){ SubstContext x; pNew = sqlite3ExprDup(pParse->db, pWhere, 0); x.pParse = pParse; x.iTable = iCursor; x.iNewTable = iCursor; x.isLeftJoin = 0; x.pEList = pSubq->pEList; pNew = substExpr(&x, pNew); pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew); pSubq = pSubq->pPrior; } } return nChng; |
︙ | ︙ |
Changes to src/treeview.c.
︙ | ︙ | |||
465 466 467 468 469 470 471 472 473 474 475 476 477 478 | sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR"); break; } case TK_SELECT_COLUMN: { sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); break; } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; } } if( zBinOp ){ | > > > > > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR"); break; } case TK_SELECT_COLUMN: { sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn); sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0); break; } case TK_IF_NULL_ROW: { sqlite3TreeViewLine(pView, "IF-NULL-ROW %d", pExpr->iTable); sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); break; } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; } } if( zBinOp ){ |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 | pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ goto jump_to_p2; } break; } /* Opcode: Column P1 P2 P3 P4 P5 ** Synopsis: r[P3]=PX ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional ** information about the format of the data.) Extract the P2-th column | > > > > > > > > > > > > > > > > > | 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 | pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ goto jump_to_p2; } break; } /* Opcode: IfNullRow P1 P2 P3 * * ** Synopsis: if P1.nullRow then r[P3]=NULL, goto P2 ** ** Check the cursor P1 to see if it is currently pointing at a NULL row. ** If it is, then set register P3 to NULL and jump immediately to P2. ** If P1 is not on a NULL row, then fall through without making any ** changes. */ case OP_IfNullRow: { /* jump */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); if( p->apCsr[pOp->p1]->nullRow ){ sqlite3VdbeMemSetNull(aMem + pOp->p3); goto jump_to_p2; } break; } /* Opcode: Column P1 P2 P3 P4 P5 ** Synopsis: r[P3]=PX ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional ** information about the format of the data.) Extract the P2-th column |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 | pOp->p1 = pLevel->iIdxCur; } assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 || pWInfo->eOnePass ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; } } } } /* Final cleanup */ | > > | 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 | pOp->p1 = pLevel->iIdxCur; } assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 || pWInfo->eOnePass ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; }else if( pOp->opcode==OP_IfNullRow ){ pOp->p1 = pLevel->iIdxCur; } } } } /* Final cleanup */ |
︙ | ︙ |
Changes to tool/addopcodes.tcl.
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 46 47 48 | AGG_FUNCTION AGG_COLUMN UMINUS UPLUS REGISTER VECTOR SELECT_COLUMN ASTERISK SPAN SPACE ILLEGAL } if {[lrange $extras end-1 end]!="SPACE ILLEGAL"} { error "SPACE and ILLEGAL must be the last two token codes and they\ | > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | AGG_FUNCTION AGG_COLUMN UMINUS UPLUS REGISTER VECTOR SELECT_COLUMN IF_NULL_ROW ASTERISK SPAN SPACE ILLEGAL } if {[lrange $extras end-1 end]!="SPACE ILLEGAL"} { error "SPACE and ILLEGAL must be the last two token codes and they\ |
︙ | ︙ |