mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-18 10:21:03 +03:00
Break out the Cte object from the With object. This will make it simpler
to add new kinds of Cte objects (ex: DML statements) and/or MATERIALIZED keywords in the future. It brings trunk into closer alignment with the experimental as-materialize branch. FossilOrigin-Name: f03efe905d7b40fb25f9f78b874bb56c6d6ccacb60f86b3b199d430d5eade8d2
This commit is contained in:
76
src/build.c
76
src/build.c
@@ -5204,24 +5204,74 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
/*
|
||||
** Create a new CTE object
|
||||
*/
|
||||
Cte *sqlite3CteNew(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Token *pName, /* Name of the common-table */
|
||||
ExprList *pArglist, /* Optional column name list for the table */
|
||||
Select *pQuery /* Query used to initialize the table */
|
||||
){
|
||||
Cte *pNew;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
pNew = sqlite3DbMallocZero(db, sizeof(*pNew));
|
||||
assert( pNew!=0 || db->mallocFailed );
|
||||
|
||||
if( db->mallocFailed ){
|
||||
sqlite3ExprListDelete(db, pArglist);
|
||||
sqlite3SelectDelete(db, pQuery);
|
||||
}else{
|
||||
pNew->pSelect = pQuery;
|
||||
pNew->pCols = pArglist;
|
||||
pNew->zName = sqlite3NameFromToken(pParse->db, pName);
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear information from a Cte object, but do not deallocate storage
|
||||
** for the object itself.
|
||||
*/
|
||||
static void cteClear(sqlite3 *db, Cte *pCte){
|
||||
assert( pCte!=0 );
|
||||
sqlite3ExprListDelete(db, pCte->pCols);
|
||||
sqlite3SelectDelete(db, pCte->pSelect);
|
||||
sqlite3DbFree(db, pCte->zName);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the contents of the CTE object passed as the second argument.
|
||||
*/
|
||||
void sqlite3CteDelete(sqlite3 *db, Cte *pCte){
|
||||
assert( pCte!=0 );
|
||||
cteClear(db, pCte);
|
||||
sqlite3DbFree(db, pCte);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is invoked once per CTE by the parser while parsing a
|
||||
** WITH clause.
|
||||
** WITH clause. The CTE described by teh third argument is added to
|
||||
** the WITH clause of the second argument. If the second argument is
|
||||
** NULL, then a new WITH argument is created.
|
||||
*/
|
||||
With *sqlite3WithAdd(
|
||||
Parse *pParse, /* Parsing context */
|
||||
With *pWith, /* Existing WITH clause, or NULL */
|
||||
Token *pName, /* Name of the common-table */
|
||||
ExprList *pArglist, /* Optional column name list for the table */
|
||||
Select *pQuery /* Query used to initialize the table */
|
||||
Cte *pCte /* CTE to add to the WITH clause */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
With *pNew;
|
||||
char *zName;
|
||||
|
||||
if( pCte==0 ){
|
||||
return pWith;
|
||||
}
|
||||
|
||||
/* Check that the CTE name is unique within this WITH clause. If
|
||||
** not, store an error in the Parse structure. */
|
||||
zName = sqlite3NameFromToken(pParse->db, pName);
|
||||
zName = pCte->zName;
|
||||
if( zName && pWith ){
|
||||
int i;
|
||||
for(i=0; i<pWith->nCte; i++){
|
||||
@@ -5240,16 +5290,11 @@ With *sqlite3WithAdd(
|
||||
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
|
||||
|
||||
if( db->mallocFailed ){
|
||||
sqlite3ExprListDelete(db, pArglist);
|
||||
sqlite3SelectDelete(db, pQuery);
|
||||
sqlite3DbFree(db, zName);
|
||||
sqlite3CteDelete(db, pCte);
|
||||
pNew = pWith;
|
||||
}else{
|
||||
pNew->a[pNew->nCte].pSelect = pQuery;
|
||||
pNew->a[pNew->nCte].pCols = pArglist;
|
||||
pNew->a[pNew->nCte].zName = zName;
|
||||
pNew->a[pNew->nCte].zCteErr = 0;
|
||||
pNew->nCte++;
|
||||
pNew->a[pNew->nCte++] = *pCte;
|
||||
sqlite3DbFree(db, pCte);
|
||||
}
|
||||
|
||||
return pNew;
|
||||
@@ -5262,10 +5307,7 @@ void sqlite3WithDelete(sqlite3 *db, With *pWith){
|
||||
if( pWith ){
|
||||
int i;
|
||||
for(i=0; i<pWith->nCte; i++){
|
||||
struct Cte *pCte = &pWith->a[i];
|
||||
sqlite3ExprListDelete(db, pCte->pCols);
|
||||
sqlite3SelectDelete(db, pCte->pSelect);
|
||||
sqlite3DbFree(db, pCte->zName);
|
||||
cteClear(db, &pWith->a[i]);
|
||||
}
|
||||
sqlite3DbFree(db, pWith);
|
||||
}
|
||||
|
||||
49
src/parse.y
49
src/parse.y
@@ -510,29 +510,25 @@ cmd ::= select(X). {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Attach a With object describing the WITH clause to a Select
|
||||
** object describing the query for which the WITH clause is a prefix.
|
||||
*/
|
||||
static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){
|
||||
if( pSelect ){
|
||||
pSelect->pWith = pWith;
|
||||
parserDoubleLinkSelect(pParse, pSelect);
|
||||
}else{
|
||||
sqlite3WithDelete(pParse->db, pWith);
|
||||
}
|
||||
return pSelect;
|
||||
}
|
||||
}
|
||||
|
||||
%ifndef SQLITE_OMIT_CTE
|
||||
select(A) ::= WITH wqlist(W) selectnowith(X). {
|
||||
Select *p = X;
|
||||
if( p ){
|
||||
p->pWith = W;
|
||||
parserDoubleLinkSelect(pParse, p);
|
||||
}else{
|
||||
sqlite3WithDelete(pParse->db, W);
|
||||
}
|
||||
A = p;
|
||||
}
|
||||
select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {
|
||||
Select *p = X;
|
||||
if( p ){
|
||||
p->pWith = W;
|
||||
parserDoubleLinkSelect(pParse, p);
|
||||
}else{
|
||||
sqlite3WithDelete(pParse->db, W);
|
||||
}
|
||||
A = p;
|
||||
}
|
||||
select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);}
|
||||
select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X).
|
||||
{A = attachWithToSelect(pParse,X,W);}
|
||||
%endif /* SQLITE_OMIT_CTE */
|
||||
select(A) ::= selectnowith(X). {
|
||||
Select *p = X;
|
||||
@@ -1662,17 +1658,22 @@ anylist ::= anylist ANY.
|
||||
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
|
||||
%type wqlist {With*}
|
||||
%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
|
||||
%type wqitem {Cte*}
|
||||
// %destructor wqitem {sqlite3CteDelete(pParse->db, $$);} // not reachable
|
||||
|
||||
with ::= .
|
||||
%ifndef SQLITE_OMIT_CTE
|
||||
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
|
||||
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
|
||||
|
||||
wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
|
||||
A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/
|
||||
wqitem(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
|
||||
A = sqlite3CteNew(pParse, &X, Y, Z); /*A-overwrites-X*/
|
||||
}
|
||||
wqlist(A) ::= wqlist(A) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
|
||||
A = sqlite3WithAdd(pParse, A, &X, Y, Z);
|
||||
wqlist(A) ::= wqitem(X). {
|
||||
A = sqlite3WithAdd(pParse, 0, X); /*A-overwrites-X*/
|
||||
}
|
||||
wqlist(A) ::= wqlist(A) COMMA wqitem(X). {
|
||||
A = sqlite3WithAdd(pParse, A, X);
|
||||
}
|
||||
%endif SQLITE_OMIT_CTE
|
||||
|
||||
|
||||
@@ -1136,6 +1136,7 @@ typedef struct AutoincInfo AutoincInfo;
|
||||
typedef struct Bitvec Bitvec;
|
||||
typedef struct CollSeq CollSeq;
|
||||
typedef struct Column Column;
|
||||
typedef struct Cte Cte;
|
||||
typedef struct Db Db;
|
||||
typedef struct DbFixer DbFixer;
|
||||
typedef struct Schema Schema;
|
||||
@@ -3874,18 +3875,23 @@ void sqlite3SelectWalkAssert2(Walker*, Select*);
|
||||
#define WRC_Abort 2 /* Abandon the tree walk */
|
||||
|
||||
/*
|
||||
** An instance of this structure represents a set of one or more CTEs
|
||||
** (common table expressions) created by a single WITH clause.
|
||||
** A single common table expression
|
||||
*/
|
||||
struct Cte {
|
||||
char *zName; /* Name of this CTE */
|
||||
ExprList *pCols; /* List of explicit column names, or NULL */
|
||||
Select *pSelect; /* The definition of this CTE */
|
||||
const char *zCteErr; /* Error message for circular references */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the With object represents a WITH clause containing
|
||||
** one or more CTEs (common table expressions).
|
||||
*/
|
||||
struct With {
|
||||
int nCte; /* Number of CTEs in the WITH clause */
|
||||
With *pOuter; /* Containing WITH clause, or NULL */
|
||||
struct Cte { /* For each CTE in the WITH clause.... */
|
||||
char *zName; /* Name of this CTE */
|
||||
ExprList *pCols; /* List of explicit column names, or NULL */
|
||||
Select *pSelect; /* The definition of this CTE */
|
||||
const char *zCteErr; /* Error message for circular references */
|
||||
} a[1];
|
||||
int nCte; /* Number of CTEs in the WITH clause */
|
||||
With *pOuter; /* Containing WITH clause, or NULL */
|
||||
Cte a[1]; /* For each CTE in the WITH clause.... */
|
||||
};
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -4891,12 +4897,17 @@ const char *sqlite3JournalModename(int);
|
||||
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
|
||||
Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*);
|
||||
void sqlite3CteDelete(sqlite3*,Cte*);
|
||||
With *sqlite3WithAdd(Parse*,With*,Cte*);
|
||||
void sqlite3WithDelete(sqlite3*,With*);
|
||||
void sqlite3WithPush(Parse*, With*, u8);
|
||||
#else
|
||||
#define sqlite3WithPush(x,y,z)
|
||||
#define sqlite3WithDelete(x,y)
|
||||
# define sqlite3CteNew(P,T,E,S) ((void*)0)
|
||||
# define sqlite3CteDelete(D,C)
|
||||
# define sqlite3CteWithAdd(P,W,C) ((void*)0)
|
||||
# define sqlite3WithDelete(x,y)
|
||||
# define sqlite3WithPush(x,y,z)
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
|
||||
|
||||
Reference in New Issue
Block a user