Skip to content

Commit ffd335b

Browse files
author
D. Richard Hipp
committed
Add the AS MATERIALIZED and AS NOT MATERIALIZED syntax that works like it
does in PostgreSQL.
1 parent 4b74cc6 commit ffd335b

File tree

5 files changed

+41
-14
lines changed

5 files changed

+41
-14
lines changed

src/build.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5211,7 +5211,8 @@ Cte *sqlite3CteNew(
52115211
Parse *pParse, /* Parsing context */
52125212
Token *pName, /* Name of the common-table */
52135213
ExprList *pArglist, /* Optional column name list for the table */
5214-
Select *pQuery /* Query used to initialize the table */
5214+
Select *pQuery, /* Query used to initialize the table */
5215+
u8 eM10d /* The MATERIALIZED flag */
52155216
){
52165217
Cte *pNew;
52175218
sqlite3 *db = pParse->db;
@@ -5226,6 +5227,7 @@ Cte *sqlite3CteNew(
52265227
pNew->pSelect = pQuery;
52275228
pNew->pCols = pArglist;
52285229
pNew->zName = sqlite3NameFromToken(pParse->db, pName);
5230+
pNew->eM10d = eM10d;
52295231
}
52305232
return pNew;
52315233
}

src/parse.y

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
250250
%ifndef SQLITE_OMIT_GENERATED_COLUMNS
251251
GENERATED ALWAYS
252252
%endif
253+
MATERIALIZED
253254
REINDEX RENAME CTIME_KW IF
254255
.
255256
%wildcard ANY.
@@ -1666,8 +1667,12 @@ with ::= .
16661667
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
16671668
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
16681669

1669-
wqitem(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
1670-
A = sqlite3CteNew(pParse, &X, Y, Z); /*A-overwrites-X*/
1670+
%type wqas {u8}
1671+
wqas(A) ::= AS. {A = M10d_Any;}
1672+
wqas(A) ::= AS MATERIALIZED. {A = M10d_Yes;}
1673+
wqas(A) ::= AS NOT MATERIALIZED. {A = M10d_No;}
1674+
wqitem(A) ::= nm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. {
1675+
A = sqlite3CteNew(pParse, &X, Y, Z, M); /*A-overwrites-X*/
16711676
}
16721677
wqlist(A) ::= wqitem(X). {
16731678
A = sqlite3WithAdd(pParse, 0, X); /*A-overwrites-X*/

src/select.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4540,6 +4540,10 @@ static int propagateConstants(
45404540
** changes to the WHERE clause of the inner query could change the
45414541
** window over which window functions are calculated).
45424542
**
4543+
** (7) The inner query is a Common Table Expression (CTE) that should
4544+
** be materialized. (This restriction is implemented in the calling
4545+
** routine.)
4546+
**
45434547
** Return 0 if no changes are made and non-zero if one or more WHERE clause
45444548
** terms are duplicated into the subquery.
45454549
*/
@@ -4923,6 +4927,7 @@ static int resolveFromTermToCte(
49234927
int bMayRecursive; /* True if compound joined by UNION [ALL] */
49244928
With *pSavedWith; /* Initial value of pParse->pWith */
49254929
int iRecTab = -1; /* Cursor for recursive table */
4930+
CteUse *pCteUse;
49264931

49274932
/* If pCte->zCteErr is non-NULL at this point, then this is an illegal
49284933
** recursive reference to CTE pCte. Leave an error in pParse and return
@@ -4937,14 +4942,16 @@ static int resolveFromTermToCte(
49374942
assert( pFrom->pTab==0 );
49384943
pTab = sqlite3DbMallocZero(db, sizeof(Table));
49394944
if( pTab==0 ) return 2;
4940-
if( pCte->pUse==0 ){
4941-
pCte->pUse = sqlite3DbMallocZero(db, sizeof(pCte->pUse[0]));
4942-
if( pCte->pUse==0
4943-
|| sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCte->pUse)==0
4945+
pCteUse = pCte->pUse;
4946+
if( pCteUse==0 ){
4947+
pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0]));
4948+
if( pCteUse==0
4949+
|| sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0
49444950
){
49454951
sqlite3DbFree(db, pTab);
49464952
return 2;
49474953
}
4954+
pCteUse->eM10d = pCte->eM10d;
49484955
}
49494956
pFrom->pTab = pTab;
49504957
pTab->nTabRef = 1;
@@ -4956,8 +4963,11 @@ static int resolveFromTermToCte(
49564963
if( db->mallocFailed ) return 2;
49574964
assert( pFrom->pSelect );
49584965
pFrom->fg.isCte = 1;
4959-
pFrom->u2.pCteUse = pCte->pUse;
4960-
pCte->pUse->nUse++;
4966+
pFrom->u2.pCteUse = pCteUse;
4967+
pCteUse->nUse++;
4968+
if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){
4969+
pCteUse->eM10d = M10d_Yes;
4970+
}
49614971

49624972
/* Check if this is a recursive CTE. */
49634973
pRecTerm = pSel = pFrom->pSelect;
@@ -6226,7 +6236,7 @@ int sqlite3Select(
62266236
** inside the subquery. This can help the subquery to run more efficiently.
62276237
*/
62286238
if( OptimizationEnabled(db, SQLITE_PushDown)
6229-
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->nUse<=1)
6239+
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d==M10d_Yes)
62306240
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
62316241
(pItem->fg.jointype & JT_OUTER)!=0)
62326242
){
@@ -6250,15 +6260,15 @@ int sqlite3Select(
62506260
** The subquery is implemented as a co-routine if:
62516261
** (1) the subquery is guaranteed to be the outer loop (so that
62526262
** it does not need to be computed more than once), and
6253-
** (2) the subquery is not a CTE that is used more then once.
6263+
** (2) the subquery is not a CTE that should be materialized
62546264
**
62556265
** TODO: Are there other reasons beside (1) and (2) to use a co-routine
62566266
** implementation?
62576267
*/
62586268
if( i==0
62596269
&& (pTabList->nSrc==1
62606270
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
6261-
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->nUse<2) /* (2) */
6271+
&& (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
62626272
){
62636273
/* Implement a co-routine that will return a single row of the result
62646274
** set on each invocation.

src/sqliteInt.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3895,8 +3895,16 @@ struct Cte {
38953895
Select *pSelect; /* The definition of this CTE */
38963896
const char *zCteErr; /* Error message for circular references */
38973897
CteUse *pUse; /* Usage information for this CTE */
3898+
u8 eM10d; /* The MATERIALIZED flag */
38983899
};
38993900

3901+
/*
3902+
** Allowed values for the materialized flag (eM10d):
3903+
*/
3904+
#define M10d_Yes 0 /* AS MATERIALIZED */
3905+
#define M10d_Any 1 /* Not specified. Query planner's choice */
3906+
#define M10d_No 2 /* AS NOT MATERIALIZED */
3907+
39003908
/*
39013909
** An instance of the With object represents a WITH clause containing
39023910
** one or more CTEs (common table expressions).
@@ -3924,6 +3932,7 @@ struct CteUse {
39243932
int regRtn; /* Return address register for addrM9e subroutine */
39253933
int iCur; /* Ephemeral table holding the materialization */
39263934
LogEst nRowEst; /* Estimated number of rows in the table */
3935+
u8 eM10d; /* The MATERIALIZED flag */
39273936
};
39283937

39293938

@@ -4930,7 +4939,7 @@ const char *sqlite3JournalModename(int);
49304939
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
49314940
#endif
49324941
#ifndef SQLITE_OMIT_CTE
4933-
Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*);
4942+
Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
49344943
void sqlite3CteDelete(sqlite3*,Cte*);
49354944
With *sqlite3WithAdd(Parse*,With*,Cte*);
49364945
void sqlite3WithDelete(sqlite3*,With*);

tool/mkkeywordhash.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ static Keyword aKeywordTable[] = {
229229
{ "FOREIGN", "TK_FOREIGN", FKEY, 1 },
230230
{ "FROM", "TK_FROM", ALWAYS, 10 },
231231
{ "FULL", "TK_JOIN_KW", ALWAYS, 3 },
232-
{ "GENERATED", "TK_GENERATED", GENCOL, 1 },
232+
{ "GENERATED", "TK_GENERATED", ALWAYS, 1 },
233233
{ "GLOB", "TK_LIKE_KW", ALWAYS, 3 },
234234
{ "GROUP", "TK_GROUP", ALWAYS, 5 },
235235
{ "GROUPS", "TK_GROUPS", WINDOWFUNC, 2 },
@@ -255,6 +255,7 @@ static Keyword aKeywordTable[] = {
255255
{ "LIKE", "TK_LIKE_KW", ALWAYS, 5 },
256256
{ "LIMIT", "TK_LIMIT", ALWAYS, 3 },
257257
{ "MATCH", "TK_MATCH", ALWAYS, 2 },
258+
{ "MATERIALIZED", "TK_MATERIALIZED", CTE, 12 },
258259
{ "NATURAL", "TK_JOIN_KW", ALWAYS, 3 },
259260
{ "NO", "TK_NO", FKEY|WINDOWFUNC, 2 },
260261
{ "NOT", "TK_NOT", ALWAYS, 10 },

0 commit comments

Comments
 (0)