Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch lateral-join Excluding Merge-Ins
This is equivalent to a diff from c4da7fa279 to e7f27bbdf4
2024-10-25
| ||
22:44 | Rename VERSION_XYZ to the more conventional PACKAGE_VERSION and remove the unused VERSION_XY. (check-in: 2d2f6dfdeb user: stephan tags: trunk) | |
20:21 | Merge the latest trunk enhancements into the reuse-schema branch. (check-in: 12fc5fd5e0 user: drh tags: reuse-schema) | |
19:40 | Merge the latest trunk enhancements and especially the autosetup changes into the wal2 branch. (check-in: 4ddea432bb user: drh tags: wal2) | |
19:32 | Merge the latest trunk enhancements and especially the new autosetup changes from trunk into the begin-concurrent branch. (check-in: af43791d47 user: drh tags: begin-concurrent) | |
16:20 | Merge the latest trunk enhancements into the lateral-join branch. (Leaf check-in: e7f27bbdf4 user: drh tags: lateral-join) | |
15:28 | Update the compile-for-*.md documents. (check-in: c4da7fa279 user: drh tags: trunk) | |
14:39 | Rework the configure+make system to use autosetup rather than autoconf. Autosetup runs faster and is easier to maintain, and it allows for common targets (such as "sqlite3" and "sqlite3.c") to be built within having to install "tclsh". (check-in: d8c0e01842 user: drh tags: trunk) | |
2024-10-05
| ||
22:28 | Fix minor problem with error recovery after OOM in the parser. (check-in: aafab58453 user: drh tags: lateral-join) | |
Changes to src/build.c.
︙ | ︙ | |||
5184 5185 5186 5187 5188 5189 5190 | ** ** Example: Suppose the join is like this: ** ** A natural cross join B ** ** The operator is "natural cross join". The A and B operands are stored ** in p->a[0] and p->a[1], respectively. The parser initially stores the | | > > > > | 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 | ** ** Example: Suppose the join is like this: ** ** A natural cross join B ** ** The operator is "natural cross join". The A and B operands are stored ** in p->a[0] and p->a[1], respectively. The parser initially stores the ** operator with A in p->a[0]. This routine shifts that operator over to ** B in p->a[1]. After this shift, the left-most term (p->a[0].fg.jointype) ** is not used except as a placeholder for JT_LTORJ and/or JT_LATERAL to ** indicate that at least one RIGHT JOIN or LATERAL JOIN (respectively) ** occurs somewhere in the FROM. ** ** Additional changes: ** ** * All tables to the left of the right-most RIGHT JOIN are tagged with ** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the ** code generator can easily tell that the table is part of ** the left operand of at least one RIGHT JOIN. |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
730 731 732 733 734 735 736 737 738 739 740 741 742 743 | seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N). { A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N); sqlite3SrcListFuncArgs(pParse, A, E); } %ifndef SQLITE_OMIT_SUBQUERY seltablist(A) ::= stl_prefix(A) LP select(S) RP as(Z) on_using(N). { A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,&N); } seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP as(Z) on_using(N). { if( A==0 && Z.n==0 && N.pOn==0 && N.pUsing==0 ){ A = F; }else if( ALWAYS(F!=0) && F->nSrc==1 ){ A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,&N); if( A ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > | 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 | seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N). { A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,&N); sqlite3SrcListFuncArgs(pParse, A, E); } %ifndef SQLITE_OMIT_SUBQUERY seltablist(A) ::= stl_prefix(A) LP select(S) RP as(Z) on_using(N). { A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,&N); } seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP select(S) RP as(Z) on_using(N). { /* \___________/ ** | ** This must be a single identifier token "LATERAL". We cannot make ** LATERAL a keyword, since there might be legacy databases that ** use "lateral" as a table name and a table name is valid syntax ** in this position. */ SrcList *pSrc = A; if( Y.n!=7 || sqlite3StrNICmp(Y.z,"lateral",7)!=0 || D.z!=0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &Y); }else if( pSrc && ALWAYS(pSrc->nSrc>0) && (pSrc->a[pSrc->nSrc-1].fg.jointype & JT_RIGHT)!=0 ){ sqlite3ErrorMsg(pParse, "join must be INNER or LEFT for a LATERAL reference"); pParse->db->errByteOffset = (int)(Y.z - pParse->zTail); } pSrc = A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,&N); if( pParse->nErr==0 && ALWAYS(pSrc!=0) && pSrc->nSrc>1 ){ pSrc->a[pSrc->nSrc-1].fg.isLateral = 1; pSrc->a[pSrc->nSrc-2].fg.jointype |= JT_LATERAL; if( ALWAYS(S!=0) ) S->selFlags |= SF_Lateral; } } seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP as(Z) on_using(N). { if( A==0 && Z.n==0 && N.pOn==0 && N.pUsing==0 ){ A = F; }else if( ALWAYS(F!=0) && F->nSrc==1 ){ A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,&N); if( A ){ |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 | return pParse->nErr ? WRC_Abort : WRC_Prune; } isCompound = p->pPrior!=0; nCompound = 0; pLeftmost = p; while( p ){ assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); p->selFlags |= SF_Resolved; | > | < < | | 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 | return pParse->nErr ? WRC_Abort : WRC_Prune; } isCompound = p->pPrior!=0; nCompound = 0; pLeftmost = p; while( p ){ int nSrc; assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); p->selFlags |= SF_Resolved; /* Resolve the expressions in the LIMIT and OFFSET clauses. */ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pWinSelect = p; if( p->pLimit!=0 && sqlite3ResolveExprNames(&sNC, p->pLimit) ){ return WRC_Abort; } /* If the SF_Converted flags is set, then this Select object was ** was created by the convertCompoundSelectToSubquery() function. ** In this case the ORDER BY clause (p->pOrderBy) should be resolved ** as if it were part of the sub-query, not the parent. This block |
︙ | ︙ | |||
1914 1915 1916 1917 1918 1919 1920 | pSub->pOrderBy = p->pOrderBy; p->pOrderBy = 0; } /* Recursively resolve names in all subqueries in the FROM clause */ if( pOuterNC ) pOuterNC->nNestedSelect++; | | > > > > > > > > > > > > > > > > | | > > > > > > | > > > > | < | | 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 | pSub->pOrderBy = p->pOrderBy; p->pOrderBy = 0; } /* Recursively resolve names in all subqueries in the FROM clause */ if( pOuterNC ) pOuterNC->nNestedSelect++; nSrc = p->pSrc->nSrc; for(i=0; i<nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; assert( pItem->zName!=0 || pItem->fg.isSubquery ); /* Test of tag-20240424-1*/ if( pItem->fg.isSubquery && (pItem->u4.pSubq->pSelect->selFlags & SF_Resolved)==0 ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; int nRef2 = sNC.nRef; NameContext *pSubNC; const char *zSavedContext = pParse->zAuthContext; if( pItem->zName ) pParse->zAuthContext = pItem->zName; if( pItem->fg.isLateral ){ assert( i>0 ); /* Because p->pSub->a[0] is never marked LATERAL */ assert( pItem->u4.pSubq->pSelect!=0 ); assert( pItem->u4.pSubq->pSelect->selFlags & SF_Lateral ); p->pSrc->nSrc = i; sNC.pSrcList = p->pSrc; if( pItem->u4.pSubq->pSelect->pLimit ){ /* If a LIMIT/OFFSET clause exists on a LATERAL subquery, allow ** variables from other FROM clause terms to the left of the ** subquery to be used in the LIMIT/OFFSET clause. */ sNC.pNext = 0; (void)sqlite3ResolveExprNames(&sNC, pItem->u4.pSubq->pSelect->pLimit); } sNC.pNext = pOuterNC; pSubNC = &sNC; }else{ sNC.pSrcList = 0; sNC.pNext = 0; pSubNC = pOuterNC; } sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pSubNC); p->pSrc->nSrc = nSrc; if( sNC.nRef>nRef2 ){ pItem->fg.isCorrelated = 1; } pParse->zAuthContext = zSavedContext; if( pParse->nErr ) return WRC_Abort; assert( db->mallocFailed==0 ); /* If the number of references to the outer context changed when ** expressions in the sub-select were resolved, the sub-select ** is correlated. It is not required to check the refcount on any ** but the innermost outer context object, as lookupName() increments ** the refcount on all contexts between the current one and the ** context containing the column when it resolves a name. */ if( pOuterNC && pOuterNC->nRef>nRef ){ pItem->fg.isCorrelated = 1; } } } if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){ pOuterNC->nNestedSelect--; } |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
289 290 291 292 293 294 295 | && sqlite3StrNICmp((char*)p->z, &zKeyText[aKeyword[j].i], p->n)==0 ){ jointype |= aKeyword[j].code; break; } } testcase( j==0 || j==1 || j==2 || j==3 || j==4 || j==5 || j==6 ); if( j>=ArraySize(aKeyword) ){ | | < | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | && sqlite3StrNICmp((char*)p->z, &zKeyText[aKeyword[j].i], p->n)==0 ){ jointype |= aKeyword[j].code; break; } } testcase( j==0 || j==1 || j==2 || j==3 || j==4 || j==5 || j==6 ); if( j>=ArraySize(aKeyword) ){ jointype = JT_OUTER; /* Triggers error report below */ break; } } if( (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || (jointype & (JT_OUTER|JT_LEFT|JT_RIGHT))==JT_OUTER ){ const char *zSp1 = " "; const char *zSp2 = " "; if( pB==0 ){ zSp1++; } if( pC==0 ){ zSp2++; } sqlite3ErrorMsg(pParse, "unknown join type: " |
︙ | ︙ | |||
3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 | SrcItem *pItem; int i; if( !p ) return; do{ substExprList(pSubst, p->pEList); substExprList(pSubst, p->pGroupBy); substExprList(pSubst, p->pOrderBy); p->pHaving = substExpr(pSubst, p->pHaving); p->pWhere = substExpr(pSubst, p->pWhere); pSrc = p->pSrc; assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->fg.isSubquery ){ substSelect(pSubst, pItem->u4.pSubq->pSelect, 1); | > | 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 | SrcItem *pItem; int i; if( !p ) return; do{ substExprList(pSubst, p->pEList); substExprList(pSubst, p->pGroupBy); substExprList(pSubst, p->pOrderBy); p->pLimit = substExpr(pSubst, p->pLimit); p->pHaving = substExpr(pSubst, p->pHaving); p->pWhere = substExpr(pSubst, p->pWhere); pSrc = p->pSrc; assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->fg.isSubquery ){ substSelect(pSubst, pItem->u4.pSubq->pSelect, 1); |
︙ | ︙ | |||
7297 7298 7299 7300 7301 7302 7303 | ** being used as the outer loop if the sqlite3WhereBegin() ** routine nominates it to that position. ** (iii) The query is not a UPDATE ... FROM ** (2) The subquery is not a CTE that should be materialized because ** (a) the AS MATERIALIZED keyword is used, or ** (b) the CTE is used multiple times and does not have the ** NOT MATERIALIZED keyword | | | 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 | ** being used as the outer loop if the sqlite3WhereBegin() ** routine nominates it to that position. ** (iii) The query is not a UPDATE ... FROM ** (2) The subquery is not a CTE that should be materialized because ** (a) the AS MATERIALIZED keyword is used, or ** (b) the CTE is used multiple times and does not have the ** NOT MATERIALIZED keyword ** (3) The FROM clause does not contain a RIGHT JOIN ** (4) The SQLITE_Coroutine optimization disable flag is not set ** (5) The subquery is not self-joined */ static int fromClauseTermCanBeCoroutine( Parse *pParse, /* Parsing context */ SrcList *pTabList, /* FROM clause */ int i, /* Which term of the FROM clause holds the subquery */ |
︙ | ︙ | |||
7326 7327 7328 7329 7330 7331 7332 | if( pTabList->nSrc==1 ) return 1; /* (1a) */ if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ return 1; } if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ while( 1 /*exit-by-break*/ ){ | | > > > > > | 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 | if( pTabList->nSrc==1 ) return 1; /* (1a) */ if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ return 1; } if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ while( 1 /*exit-by-break*/ ){ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LATERAL) ){ testcase( pItem->fg.jointype & JT_OUTER ); testcase( pItem->fg.jointype & JT_CROSS ); testcase( pItem->fg.jointype & JT_LATERAL ); return 0; /* (1c-ii) */ } if( i==0 ) break; i--; pItem--; if( pItem->fg.isSubquery ) return 0; /* (1c-i) */ } return 1; } |
︙ | ︙ | |||
7848 7849 7850 7851 7852 7853 7854 | */ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. tag-select-0482 */ int addrTop = sqlite3VdbeCurrentAddr(v)+1; | > | | 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 | */ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. tag-select-0482 */ int addrTop = sqlite3VdbeCurrentAddr(v)+1; assert( pItem->fg.isLateral==0 ); pItem->u4.pSubq->regReturn = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop); VdbeComment((v, "%!S", pItem)); pSubq->addrFillSub = addrTop; sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pSTab->nRowLogEst = pSub->nSelectRow; |
︙ | ︙ | |||
7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 | VdbeNoopComment((v, "materialize %!S", pItem)); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pSTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); sqlite3VdbeAddOp2(v, OP_Return, pSubq->regReturn, topAddr+1); VdbeComment((v, "end %!S", pItem)); sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); sqlite3VdbeJumpHere(v, topAddr); sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ | > > > > > > > > > | 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 | VdbeNoopComment((v, "materialize %!S", pItem)); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pSTab->nRowLogEst = pSub->nSelectRow; if( pItem->fg.isLateral && pItem->fg.isCorrelated ){ int kk; for(kk=0; kk<i; kk++){ SrcItem *pX = &pTabList->a[kk]; if( pX->fg.viaCoroutine==0 ) continue; sqlite3TranslateColumnToCopy(pParse, topAddr+1, pX->iCursor, pX->u4.pSubq->regResult, 0); } } if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); sqlite3VdbeAddOp2(v, OP_Return, pSubq->regReturn, topAddr+1); VdbeComment((v, "end %!S", pItem)); sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); sqlite3VdbeJumpHere(v, topAddr); sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
3285 3286 3287 3288 3289 3290 3291 | int regResult; /* Registers holding results of a co-routine */ }; /* ** The SrcItem object represents a single term in the FROM clause of a query. ** The SrcList object is mostly an array of SrcItems. ** | | | | | 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 | int regResult; /* Registers holding results of a co-routine */ }; /* ** The SrcItem object represents a single term in the FROM clause of a query. ** The SrcList object is mostly an array of SrcItems. ** ** The jointype starts out showing the join type between the current term ** and the next term on the list. The parser builds the list this way. ** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each ** jointype expresses the join between the current term and the previous. ** ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. ** ** Aggressive use of "union" helps keep the size of the object small. This ** has been shown to boost performance, in addition to saving memory. ** Access to union elements is gated by the following rules which should |
︙ | ︙ | |||
3323 3324 3325 3326 3327 3328 3329 | ** inside the fg struct. */ struct SrcItem { char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */ struct { | | > | 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 | ** inside the fg struct. */ struct SrcItem { char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */ struct { u8 jointype; /* Type of join. See above for more detail. */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isSubquery :1; /* True if this term is a subquery */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned isLateral :1; /* True if sub-query is LATERAL */ unsigned isMaterialized:1; /* This is a materialized view */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ |
︙ | ︙ | |||
3398 3399 3400 3401 3402 3403 3404 | #define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */ #define JT_NATURAL 0x04 /* True for a "natural" join */ #define JT_LEFT 0x08 /* Left outer join */ #define JT_RIGHT 0x10 /* Right outer join */ #define JT_OUTER 0x20 /* The "OUTER" keyword is present */ #define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN ** Mnemonic: Left Table Of Right Join */ | | > > | 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 | #define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */ #define JT_NATURAL 0x04 /* True for a "natural" join */ #define JT_LEFT 0x08 /* Left outer join */ #define JT_RIGHT 0x10 /* Right outer join */ #define JT_OUTER 0x20 /* The "OUTER" keyword is present */ #define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN ** Mnemonic: Left Table Of Right Join */ #define JT_LATERAL 0x80 /* A LATERAL join. Or, if on the left-most ** SrcItem, an indication that at least one ** correlated LATERAL join exists in the FROM */ /* ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin() ** and the WhereInfo.wctrlFlags member. ** ** Value constraints (enforced via assert()): ** WHERE_USE_LIMIT == SF_FixedLimit |
︙ | ︙ | |||
3606 3607 3608 3609 3610 3611 3612 | #define SF_Compound 0x0000100 /* Part of a compound query */ #define SF_Values 0x0000200 /* Synthesized from VALUES clause */ #define SF_MultiValue 0x0000400 /* Single VALUES term with multiple rows */ #define SF_NestedFrom 0x0000800 /* Part of a parenthesized FROM clause */ #define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */ #define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */ #define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */ | | | 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 | #define SF_Compound 0x0000100 /* Part of a compound query */ #define SF_Values 0x0000200 /* Synthesized from VALUES clause */ #define SF_MultiValue 0x0000400 /* Single VALUES term with multiple rows */ #define SF_NestedFrom 0x0000800 /* Part of a parenthesized FROM clause */ #define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */ #define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */ #define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */ #define SF_Lateral 0x0008000 /* Is of the form: LATERAL(SELECT ...) */ #define SF_Converted 0x0010000 /* By convertCompoundSelectToSubquery() */ #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ |
︙ | ︙ | |||
5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 | void sqlite3CodeChangeCount(Vdbe*,int,const char*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, Upsert*); WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, ExprList*,Select*,u16,int); void sqlite3WhereEnd(WhereInfo*); LogEst sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); int sqlite3WhereOrderByLimitOptLabel(WhereInfo*); void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*); int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); | > > > > > > > | 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 | void sqlite3CodeChangeCount(Vdbe*,int,const char*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, Upsert*); WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, ExprList*,Select*,u16,int); void sqlite3WhereEnd(WhereInfo*); void sqlite3TranslateColumnToCopy( Parse *pParse, /* Parsing context */ int iStart, /* Translate from this opcode to the end */ int iTabCur, /* OP_Column/OP_Rowid references to this table */ int iRegister, /* The first column is in this register */ int iAutoidxCur /* If non-zero, cursor of autoindex being generated */ ); LogEst sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); int sqlite3WhereOrderByLimitOptLabel(WhereInfo*); void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*); int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); |
︙ | ︙ |
Changes to src/treeview.c.
︙ | ︙ | |||
207 208 209 210 211 212 213 214 215 216 217 218 219 220 | sqlite3_str_appendf(&x, " RIGHT-JOIN"); }else if( pItem->fg.jointype & JT_CROSS ){ sqlite3_str_appendf(&x, " CROSS-JOIN"); } if( pItem->fg.jointype & JT_LTORJ ){ sqlite3_str_appendf(&x, " LTORJ"); } if( pItem->fg.fromDDL ){ sqlite3_str_appendf(&x, " DDL"); } if( pItem->fg.isCte ){ sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); } if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ | > > > | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | sqlite3_str_appendf(&x, " RIGHT-JOIN"); }else if( pItem->fg.jointype & JT_CROSS ){ sqlite3_str_appendf(&x, " CROSS-JOIN"); } if( pItem->fg.jointype & JT_LTORJ ){ sqlite3_str_appendf(&x, " LTORJ"); } if( pItem->fg.isLateral ){ sqlite3_str_appendf(&x, " LATERAL"); } if( pItem->fg.fromDDL ){ sqlite3_str_appendf(&x, " DDL"); } if( pItem->fg.isCte ){ sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); } if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 | rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ goto jump_to_p2; } } } VdbeBranchTaken(0, 2); MemSetTypeFlag(pIn1, MEM_Int); break; } #ifndef SQLITE_OMIT_FLOATING_POINT /* Opcode: RealAffinity P1 * * * * | > > > | 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 | rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ goto jump_to_p2; } } } #ifdef SQLITE_DEBUG pIn1->pScopyFrom = 0; #endif VdbeBranchTaken(0, 2); MemSetTypeFlag(pIn1, MEM_Int); break; } #ifndef SQLITE_OMIT_FLOATING_POINT /* Opcode: RealAffinity P1 * * * * |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
704 705 706 707 708 709 710 | ** instead of via table lookup. ** ** If the iAutoidxCur is not zero, then any OP_Rowid instructions on ** cursor iTabCur are transformed into OP_Sequence opcode for the ** iAutoidxCur cursor, in order to generate unique rowids for the ** automatic index being generated. */ | | | 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 | ** instead of via table lookup. ** ** If the iAutoidxCur is not zero, then any OP_Rowid instructions on ** cursor iTabCur are transformed into OP_Sequence opcode for the ** iAutoidxCur cursor, in order to generate unique rowids for the ** automatic index being generated. */ void sqlite3TranslateColumnToCopy( Parse *pParse, /* Parsing context */ int iStart, /* Translate from this opcode to the end */ int iTabCur, /* OP_Column/OP_Rowid references to this table */ int iRegister, /* The first column is in this register */ int iAutoidxCur /* If non-zero, cursor of autoindex being generated */ ){ Vdbe *v = pParse->pVdbe; |
︙ | ︙ | |||
1193 1194 1195 1196 1197 1198 1199 | sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); if( pSrc->fg.viaCoroutine ){ assert( pSrc->fg.isSubquery && pSrc->u4.pSubq!=0 ); sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); assert( pLevel->iIdxCur>0 ); | | | | 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 | sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); if( pSrc->fg.viaCoroutine ){ assert( pSrc->fg.isSubquery && pSrc->u4.pSubq!=0 ); sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); assert( pLevel->iIdxCur>0 ); sqlite3TranslateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pSrc->u4.pSubq->regResult, pLevel->iIdxCur); sqlite3VdbeGoto(v, addrTop); pSrc->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); } sqlite3VdbeJumpHere(v, addrTop); |
︙ | ︙ | |||
4802 4803 4804 4805 4806 4807 4808 | pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){ Bitmask mUnusable = 0; pNew->iTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); if( bFirstPastRJ | | > | | | > > > | > > > | 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 | pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){ Bitmask mUnusable = 0; pNew->iTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); if( bFirstPastRJ || (pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LTORJ|JT_LATERAL))!=0 ){ /* Add prerequisites to prevent reordering of FROM clause terms ** across CROSS joins, outer joins, and lateral joins. ** The bFirstPastRJ boolean prevents the right operand of a ** RIGHT JOIN from being swapped with other elements even further ** to the right. ** ** The JT_LTORJ case and the hasRightJoin flag work together to ** prevent FROM-clause terms from moving from the right side of ** a LEFT JOIN over to the left side of that join if the LEFT JOIN ** is itself on the left side of a RIGHT JOIN. */ if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; testcase( pItem->fg.jointype & JT_CROSS ); testcase( pItem->fg.jointype & JT_OUTER ); testcase( pItem->fg.jointype & JT_LATERAL ); mPrereq |= mPrior; bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; }else if( !hasRightJoin ){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pSTab) ){ SrcItem *p; for(p=&pItem[1]; p<pEnd; p++){ if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS|JT_LATERAL)) ){ testcase( p->fg.jointype & JT_CROSS ); testcase( p->fg.jointype & JT_OUTER ); testcase( p->fg.jointype & JT_LATERAL ); mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable); }else #endif /* SQLITE_OMIT_VIRTUALTABLE */ { |
︙ | ︙ | |||
6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 | || NEVER(pItem->fg.isSubquery==0) || pItem->u4.pSubq->pSelect->pOrderBy==0 ){ pWInfo->revMask |= MASKBIT(ii); } } } /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains ** information needed to terminate the loop. Later, the calling routine ** should invoke sqlite3WhereEnd() with the return value of this function ** in order to complete the WHERE clause processing. | > > > > > > > > > > > > > > > > > > > > > > > > | 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 | || NEVER(pItem->fg.isSubquery==0) || pItem->u4.pSubq->pSelect->pOrderBy==0 ){ pWInfo->revMask |= MASKBIT(ii); } } } /* ** Adjust the addrBody of all WHERE_IDX_ONLY WhereLoops prior to ii ** so that the addrBody covers the subroutine that computes a LATERAL ** subquery. ** ** This routine is broken out into a separate no-inline subroutine because ** it runs rarely, and by breaking it out it reduces register contention ** in the main sqlite3WhereBegin() routine, helping sqlite3WhereBegin() ** to run faster. */ static SQLITE_NOINLINE void whereAdjustAddrBodyForLateral( WhereInfo *pWInfo, /* There WHERE loop info */ SrcItem *pSrc, /* The LATERAL subquery */ int ii /* Index of pSrc in the FROM clause */ ){ while( --ii >= 0 ){ if( pWInfo->a[ii].pWLoop->wsFlags & WHERE_IDX_ONLY && pWInfo->a[ii].addrBody > pSrc->u4.pSubq->addrFillSub ){ pWInfo->a[ii].addrBody = pSrc->u4.pSubq->addrFillSub; } } } /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains ** information needed to terminate the loop. Later, the calling routine ** should invoke sqlite3WhereEnd() with the return value of this function ** in order to complete the WHERE clause processing. |
︙ | ︙ | |||
7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 | int iOnce = 0; assert( pSrc->fg.isSubquery ); pSubq = pSrc->u4.pSubq; if( pSrc->fg.isCorrelated==0 ){ iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); }else{ iOnce = 0; } sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub); VdbeComment((v, "materialize %!S", pSrc)); if( iOnce ) sqlite3VdbeJumpHere(v, iOnce); } assert( pTabList == pWInfo->pTabList ); if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ | > > > | 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 | int iOnce = 0; assert( pSrc->fg.isSubquery ); pSubq = pSrc->u4.pSubq; if( pSrc->fg.isCorrelated==0 ){ iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); }else{ iOnce = 0; if( pSrc->fg.isLateral ){ whereAdjustAddrBodyForLateral(pWInfo, pSrc, ii); } } sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub); VdbeComment((v, "materialize %!S", pSrc)); if( iOnce ) sqlite3VdbeJumpHere(v, iOnce); } assert( pTabList == pWInfo->pTabList ); if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ |
︙ | ︙ | |||
7348 7349 7350 7351 7352 7353 7354 | ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); assert( pTabItem->fg.isSubquery ); assert( pTabItem->u4.pSubq->regResult>=0 ); | | | | 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 | ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); assert( pTabItem->fg.isSubquery ); assert( pTabItem->u4.pSubq->regResult>=0 ); sqlite3TranslateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->u4.pSubq->regResult, 0); continue; } /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can ** yield a significant performance boost. |
︙ | ︙ |
Changes to src/wherecode.c.
︙ | ︙ | |||
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 | return 0; } } return 1; } return 0; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ Bitmask sqlite3WhereCodeOneLoopStart( Parse *pParse, /* Parsing context */ | > > > > > > > > > > > > > > > > > > > > > > > > > > | 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 | return 0; } } return 1; } return 0; } /* ** Find an appropriate label for iLevel loop to jump to if it the table ** for that loop is empty. ** ** For a simple query, we might as well jump to the break-address of the ** outermost loop, halting the query, since if one of the joined tables ** is empty, the result set will be empty. But that does not work if ** there are outer joins. Nor does it work if the empty table is a ** correlated subquery (with the LATERAL keyword). */ static SQLITE_NOINLINE int haltAddress( WhereInfo *pWInfo, int iLevel, SrcItem *pTabItem ){ if( pTabItem->fg.isLateral==0 || pTabItem->fg.isCorrelated==0 ){ while( 1 /*exit-by-break*/ ){ if( pWInfo->a[iLevel].iLeftJoin ) break; if( pWInfo->a[iLevel].pRJ ) break; if( iLevel==0 ) break; iLevel--; }; } return pWInfo->a[iLevel].addrBrk; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ Bitmask sqlite3WhereCodeOneLoopStart( Parse *pParse, /* Parsing context */ |
︙ | ︙ | |||
1480 1481 1482 1483 1484 1485 1486 | int bRev; /* True if we need to scan in reverse order */ WhereLoop *pLoop; /* The WhereLoop object being coded */ WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ sqlite3 *db; /* Database connection */ SrcItem *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ | < | 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 | int bRev; /* True if we need to scan in reverse order */ WhereLoop *pLoop; /* The WhereLoop object being coded */ WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ sqlite3 *db; /* Database connection */ SrcItem *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrCont; /* Jump here to continue with next cycle */ int iRowidReg = 0; /* Rowid is stored in this register, if not zero */ int iReleaseReg = 0; /* Temp register to free before returning */ Index *pIdx = 0; /* Index used by loop (if any) */ int iLoop; /* Iteration of constraint generator loop */ pWC = &pWInfo->sWC; |
︙ | ︙ | |||
1540 1541 1542 1543 1544 1545 1546 | ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN match flag")); } | < < < < < < < < | 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 | ); if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN match flag")); } /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ int regYield; Subquery *pSubq; assert( pTabItem->fg.isSubquery && pTabItem->u4.pSubq!=0 ); pSubq = pTabItem->u4.pSubq; regYield = pSubq->regReturn; |
︙ | ︙ | |||
1783 1784 1785 1786 1787 1788 1789 | VdbeComment((v, "pk")); VdbeCoverageIf(v, pX->op==TK_GT); VdbeCoverageIf(v, pX->op==TK_LE); VdbeCoverageIf(v, pX->op==TK_LT); VdbeCoverageIf(v, pX->op==TK_GE); sqlite3ReleaseTempReg(pParse, rTemp); }else{ | | > | 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 | VdbeComment((v, "pk")); VdbeCoverageIf(v, pX->op==TK_GT); VdbeCoverageIf(v, pX->op==TK_LE); VdbeCoverageIf(v, pX->op==TK_LT); VdbeCoverageIf(v, pX->op==TK_GE); sqlite3ReleaseTempReg(pParse, rTemp); }else{ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, haltAddress(pWInfo, iLevel, pTabItem)); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); } if( pEnd ){ Expr *pX; pX = pEnd->pExpr; assert( pX!=0 ); |
︙ | ︙ | |||
2578 2579 2580 2581 2582 2583 2584 | /* Tables marked isRecursive have only a single row that is stored in ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ codeCursorHint(pTabItem, pWInfo, pLevel, 0); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; | | > | 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 | /* Tables marked isRecursive have only a single row that is stored in ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ codeCursorHint(pTabItem, pWInfo, pLevel, 0); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, haltAddress(pWInfo, iLevel, pTabItem)); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } } #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
︙ | ︙ |
Added test/joinL.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 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 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | # 2024-07-20 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # Tests for LATERAL JOIN # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix joinL db null NULL do_execsql_test 1.0 { CREATE TABLE product(product_id INT, price REAL, product TEXT); INSERT INTO product VALUES (1, 6.76, 'apple'), (2, 5.28, 'banana'), (3, 28.28, 'cherry'), (4, 13.54, 'dates'), (5, 30.58, 'elderberry'), (6, 26.57, 'figs'), (7, 64.85, 'grapes'), (8, 21.55, 'huckleberry'), (9, 29.00, 'jakefruit'), (10, 17.31, 'kiwi'); CREATE TABLE wishlist(wishlist_id int, username text, desired_price real); INSERT INTO wishlist VALUES (1, 'alice', 45.0), (2, 'bob', 30.0), (3, 'cindy', 15.0), (4, 'david', 4.0); SELECT * FROM wishlist AS w LEFT JOIN LATERAL ( SELECT * FROM product AS p WHERE p.price < w.desired_price ORDER BY p.price DESC LIMIT 3 ) AS x ORDER BY wishlist_id, price DESC; } { 1 alice 45.0 5 30.58 elderberry 1 alice 45.0 9 29.0 jakefruit 1 alice 45.0 3 28.28 cherry 2 bob 30.0 9 29.0 jakefruit 2 bob 30.0 3 28.28 cherry 2 bob 30.0 6 26.57 figs 3 cindy 15.0 4 13.54 dates 3 cindy 15.0 1 6.76 apple 3 cindy 15.0 2 5.28 banana 4 david 4.0 NULL NULL NULL } do_execsql_test 1.1 { SELECT * FROM wishlist AS w JOIN LATERAL ( SELECT * FROM product AS p WHERE p.price < w.desired_price ORDER BY p.price DESC LIMIT 3 ) AS x ORDER BY wishlist_id, price DESC; } { 1 alice 45.0 5 30.58 elderberry 1 alice 45.0 9 29.0 jakefruit 1 alice 45.0 3 28.28 cherry 2 bob 30.0 9 29.0 jakefruit 2 bob 30.0 3 28.28 cherry 2 bob 30.0 6 26.57 figs 3 cindy 15.0 4 13.54 dates 3 cindy 15.0 1 6.76 apple 3 cindy 15.0 2 5.28 banana } do_catchsql_test 1.2 { SELECT * FROM wishlist AS w RIGHT JOIN LATERAL ( SELECT * FROM product AS p WHERE p.price < w.desired_price ORDER BY p.price DESC LIMIT 3 ) AS x ON true ORDER BY wishlist_id, price DESC; } {1 {join must be INNER or LEFT for a LATERAL reference}} do_catchsql_test 1.3 { SELECT * FROM wishlist AS w FULL OUTER JOIN LATERAL ( SELECT * FROM product AS p WHERE p.price < w.desired_price ORDER BY p.price DESC LIMIT 3 ) AS x ON true ORDER BY wishlist_id, price DESC; } {1 {join must be INNER or LEFT for a LATERAL reference}} do_catchsql_test 1.4 { SELECT * FROM wishlist AS w FULL OUTER JOIN LATERALx ( SELECT * FROM product AS p WHERE p.price < w.desired_price ORDER BY p.price DESC LIMIT 3 ) AS x ON true ORDER BY wishlist_id, price DESC; } {1 {near "LATERALx": syntax error}} do_catchsql_test 1.5 { SELECT * FROM wishlist AS w FULL OUTER JOIN LATERAL.xyz ( SELECT * FROM product AS p WHERE p.price < w.desired_price ORDER BY p.price DESC LIMIT 3 ) AS x ON true ORDER BY wishlist_id, price DESC; } {1 {near "LATERAL": syntax error}} do_execsql_test 2.0 { CREATE TABLE orders(id INTEGER PRIMARY KEY, user_id INT, created_at TEXT); INSERT INTO orders VALUES (1,1,'2024-07-20T01:35:03'), (2,2,'2024-07-20T01:35:07'), (3,3,'2024-07-20T01:35:10'), (4,1,'2024-07-20T01:58:10'), (5,3,'2024-07-20T01:58:17'), (6,3,'2024-07-20T01:58:25'); SELECT user_id, first_order_time, next_order_time, id FROM (SELECT user_id, min(created_at) AS first_order_time FROM orders GROUP BY user_id) AS o1 LEFT JOIN LATERAL (SELECT id, created_at AS next_order_time FROM orders WHERE user_id = o1.user_id AND created_at > o1.first_order_time ORDER BY created_at ASC LIMIT 1) AS o2 ON true; } { 1 2024-07-20T01:35:03 2024-07-20T01:58:10 4 2 2024-07-20T01:35:07 NULL NULL 3 2024-07-20T01:35:10 2024-07-20T01:58:17 5 } do_execsql_test 2.1 { SELECT user_id, first_order_time, next_order_time, id FROM LATERAL (SELECT user_id, min(created_at) AS first_order_time FROM orders GROUP BY user_id) AS o1 JOIN LATERAL (SELECT id, created_at AS next_order_time FROM orders WHERE user_id = o1.user_id AND created_at > o1.first_order_time ORDER BY created_at ASC LIMIT 1) AS o2 ON true; } { 1 2024-07-20T01:35:03 2024-07-20T01:58:10 4 3 2024-07-20T01:35:10 2024-07-20T01:58:17 5 } # "LATERAL" is a not actually a keyword. It can be used as an indentifier for # historical compatibility. # do_execsql_test 3.0 { CREATE TABLE lateral(a,lateral lateral); INSERT INTO lateral VALUES(1,2); SELECT * FROM lateral; ATTACH ':memory:' AS lateral; CREATE TABLE lateral.t2(x,y); INSERT INTO t2 VALUES(98,99); SELECT * FROM t2; } {1 2 98 99} # https://sqlite.org/forum/forumpost/dfe2cd37ca3a9a80 # do_execsql_test 4.0 { SELECT * FROM (VALUES (1), (2)) JOIN LATERAL (select COUNT(*), column1); } {1 1 1 2 1 2} do_execsql_test 4.1 { CREATE TABLE t3(x INTEGER PRIMARY KEY); WITH RECURSIVE c(n) AS (VALUES(0) UNION ALL SELECT n+1 FROM c WHERE n<20) INSERT INTO t3(x) SELECT n FROM c; CREATE TABLE t4(y,z); INSERT INTO t4 VALUES(1,0),(3,2),(5,7); SELECT * FROM t4, LATERAL(SELECT x FROM t3 ORDER BY x LIMIT y OFFSET z); } { 1 0 0 3 2 2 3 2 3 3 2 4 5 7 7 5 7 8 5 7 9 5 7 10 5 7 11 } do_execsql_test 4.2 { SELECT * FROM (VALUES(1,0),(3,2),(5,7)), LATERAL(SELECT x FROM t3 ORDER BY x LIMIT column1 OFFSET column2); } { 1 0 0 3 2 2 3 2 3 3 2 4 5 7 7 5 7 8 5 7 9 5 7 10 5 7 11 } # https://sqlite.org/forum/forumpost/fc29fa4f14 # do_execsql_test 5.0 { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); CREATE TABLE t2(c,d,e,f); INSERT INTO t2 VALUES(3,4,5,6); CREATE INDEX t2cd ON t2(c,d); CREATE TABLE dual(dummy TEXT); INSERT INTO dual VALUES('X'); SELECT c, a, sb FROM t2, LATERAL( SELECT a, sum(b) AS sb FROM t1 GROUP BY a HAVING sum(b)<d ) AS lx ORDER BY a, c; } {3 1 2} do_execsql_test 5.1 { SELECT c, a, sb FROM dual, t2, LATERAL( SELECT a, sum(b) AS sb FROM t1 GROUP BY a HAVING sum(b)<d ) AS lx ORDER BY a, c; } {3 1 2} do_execsql_test 5.2 { SELECT c, a, sb FROM t2, dual, LATERAL( SELECT a, sum(b) AS sb FROM t1 GROUP BY a HAVING sum(b)<d ) AS lx ORDER BY a, c; } {3 1 2} finish_test |