Compare commits

...

8 Commits

Author SHA1 Message Date
drh b745cccfd9 Merge changes from trunk.
FossilOrigin-Name: 7a90571e0a0e43017cf5b9636ff111e0d0f7f9e9a4678d39b0e4f9c176ae232e
2021-02-20 12:47:54 +00:00
drh e076d6bc14 Remove unnecessarily complexity from sqlite3WithReleaseByParse(). Improved
TreeView output for the enhanced CTE structures.  This code still does not
work 100%.  I'm saving my place in order to (maybe) come back to it later.

FossilOrigin-Name: 30bb18b45013bf2ac214863c97f64544511cae62029b9762c97cbb69ca658887
2021-02-16 19:29:44 +00:00
drh aee35d7509 Trying to get the new AS MATERIALIZE syntax of CTEs to work. There are
still performance and memory management issues.  This is a WIP check-in.

FossilOrigin-Name: bf0fd9b23a77a8fc1be5f8f8f9f7dce62b924d2f4eecaf13f0b21cc58bfd3a61
2021-02-16 16:32:07 +00:00
drh 68097b4e93 Change the syntax from "GENERATED AS" to "AS MATERIALIZED" so as to match
the syntax of PostgreSQL 12+.

FossilOrigin-Name: 78dcddd9697d95629c18131ab0842aa4d08bc3c7451cd0e7a8d83e4dde277bda
2021-02-16 00:48:51 +00:00
drh 1b6cc73096 Merge the LIKE operator fix from trunk.
FossilOrigin-Name: 8c8618780a2cb80c0c1f244fa8555ce36501ad7e435a2cfee1030bbdc8d644db
2021-02-15 17:51:32 +00:00
drh 478583bbd6 Merge minor fixes from trunk.
FossilOrigin-Name: d876b287e1d1fdc1db083fb9786889fb82117ec77defdaa078858e1092068166
2021-02-15 14:55:04 +00:00
drh bd4dfa9071 If the GENERATED keyword occurs before the AS keyword in a common table
expression (CTE) definition, then that CTE becomes an "optimization barrier".
For now, that means the CTE is always materialized.  It also means that
query flattener or pushdown optimizations that cross the CTE boundary are
omitted.

FossilOrigin-Name: 186ec18b24d16c5d4d7a5b5e98c7504892950a1018774cb7bb214fc011ec09f4
2021-02-13 23:46:26 +00:00
drh 879746ca63 Parsing of DML statements in a WITH clause. But at this point, it just
generates an error about "not yet supported".

FossilOrigin-Name: 964ff68d8fa4d72b1b4d510f1ec62a9674b75d4ebae4ba254bc2f99432857628
2021-02-12 21:07:58 +00:00
12 changed files with 359 additions and 133 deletions
+16 -16
View File
@@ -1,5 +1,5 @@
C Fix\sanother\sproblem\shandling\scorrupt\sdatabase\sfiles\sin\sthe\sALTER\sTABLE\sDROP\sCOLUMN\scode.
D 2021-02-19T18:39:32.279
C Merge\schanges\sfrom\strunk.
D 2021-02-20T12:47:54.421
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -485,15 +485,15 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c 694020ad8a3af3d79b09f74c8f1421272a419cdea42a13401e3b0f7dea6e9c3e
F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e
F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331
F src/build.c 3e1e1df6807c91c17b0e0cdf24d0e85176d7e006d9783ab3eb1afb154aa26822
F src/build.c 6bb7341559433152ac5bc082209cbb1f6d62d8d369ca2a1c10387c5c9e8450d5
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410
F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
F src/delete.c 352ea931218c45a3daf17472d4141b9c7fc026d85da3f1ade404ea5bb6d67f77
F src/expr.c 47c85263e6d179424e6b09e2c79db5704ab5b8cbc2fae2ee3285faa2566f2e74
F src/delete.c 8517d4d57dc12e4cb9adec1801249ce18a3be3e6ccd75a266f66499a57e5b062
F src/expr.c fe76d3b9f606347b1d84cb059264ed0ba0f33ecce57a864f1575bc3d8ae42ba9
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 73adaca988d0dd517d373b432dc9dfa2cd7fa3108b114260132a80832de19037
F src/func.c 479f6929be027eb0210cbdde9d3529c012facf082d64a6b854a9415940761e5e
@@ -530,23 +530,23 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
F src/parse.y cdc4e5efaf1618d82f6b241a30f1a586188bf37e9c55f967258ed15fe24c51a2
F src/parse.y a8102583778a6aedb602042463d0deb7811c913845561d74de9a9da400f79504
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
F src/pragma.c 6daaaecc26a4b09481d21722525b079ce756751a43a79cc1d8f122d686806193
F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
F src/prepare.c 7a534d100c556f45f10aee131f2e4244cb52547b7cf17e1c393f55d8abb62e97
F src/prepare.c f634a9e799a6b1c136d8ee12479cffa22862bfb807d307b1db406aa0cdb042a5
F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 52f81603cc40f78449f5b6aed96dbea9484b194771ecb1937e8c0f6547c186a0
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 371a5c95d47d4e969337548ed9bd344788f15518a1c75921507355bd93815205
F src/select.c 1078af8a2e416e83a5f7d05adbee974bce32d1e17f47acd592f18d542a14fa29
F src/shell.c.in 844417f84df1f6c4fce1c815629a888cfdcf219e86513e9c332bbcc38832f477
F src/sqlite.h.in 8855a19f37ade8dad189a9e48233a2ebe1b46faf469c7eb0906a654e252dcc57
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
F src/sqliteInt.h 4bd449478df0444ccb9f6a8e6f210b2ceacdd59562f52630edb0bb2d8cc25094
F src/sqliteInt.h 652ccfddec0d3df91439b09fda7a9c32a0af07e365f4a1377b5caff03888c2b1
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -606,7 +606,7 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
F src/treeview.c 734a65babc2a0bb725a5af33cf3e677f7a8b20ad5be6dc04e07c2ff6081c16e6
F src/trigger.c 861c3ec2c5b0fc830bdf82470454a9324fad70cbaa96d2e208fb54577c9e8d28
F src/update.c 0f5a61f0787199983530a33f6fffe4f52742f35fcdf6ccfad1078b1a8bc17723
F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
@@ -628,7 +628,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 69e770e96fd56cc21608992bf2c6f1f3dc5cf2572d0495c6a643b06c3a679f14
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/walker.c d9c4e454ebb9499e908aa62d55b8994c375cf5355ac78f60d45af17f7890701c
F src/where.c 6efc4a10bfe0ec908c4f3c9112afb7675c952204b4b0b255672f488860141fec
F src/where.c 228cb57b51ca02853bb932c4548ffaa3949f40f8398a0f393683dd4c08cdbea0
F src/whereInt.h ae03b5e3a4cca9bd9cb1b7d3c63faf8f1f177200fc8cecc87d3d0cab6ca338e6
F src/wherecode.c 43a63441f8662ddf86b15975683a502ec33f08167e9636f4d19e38e265e95fd9
F src/whereexpr.c f7b5469e83db3c3b9eb14e4ba44559a2e125523761d12e5ac8d8fb88301af393
@@ -1832,7 +1832,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
F tool/mkkeywordhash.c 750f25aef0e23f8e3367af6d824fbf5ed7d3e285f27cea91aa2dd72c367630eb
F tool/mkkeywordhash.c 08b6e4d7a482a7f37a9a0032e7ba968e26624a027b6b2e9ba589be6f5e3d8c2c
F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
@@ -1904,7 +1904,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 05dbea9b26bdcb096194dc531816333f3784eca50b133fe3efede6ab0d233472
R 654376f36a79c8c3f0a8b98f1eb136d6
U dan
Z 6b0bcca992d79748743aedfd1336dec2
P 30bb18b45013bf2ac214863c97f64544511cae62029b9762c97cbb69ca658887 9edf2ddc4799c8830c4b7b91d7aacee50029a4b9db329fd4c5674fbedea33034
R f20aa0dd913bdc4d647b2a24ee109141
U drh
Z 69e153bc572b9edf99468fd37ca7f36e
+1 -1
View File
@@ -1 +1 @@
9edf2ddc4799c8830c4b7b91d7aacee50029a4b9db329fd4c5674fbedea33034
7a90571e0a0e43017cf5b9636ff111e0d0f7f9e9a4678d39b0e4f9c176ae232e
+147 -23
View File
@@ -5204,24 +5204,112 @@ 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 */
int eMaterialize /* Force or prohibit materialization */
){
Cte *pNew;
sqlite3 *db = pParse->db;
assert( eMaterialize==Materialize_Any
|| eMaterialize==Materialize_Yes
|| eMaterialize==Materialize_No );
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);
pNew->zCteErr = 0;
pNew->eMaterialize = (u8)eMaterialize;
}
return pNew;
}
#ifdef SQLITE_DEBUG
/*
** Validate the back-reference from a SrcList_item back to its Cte object.
** Raise an assertion fault if anything is wrong. This routine is for
** sanity and design checking and does not appear in release builds.
*/
void sqlite3AssertValidCteBackRef(struct SrcList_item *pItem){
Cte *pCte;
if( !pItem->fg.isCte ) return; /* Nothing to check here */
assert( pItem->pSelect!=0 );
pCte = pItem->u2.pCteSrc;
if( pCte==0 ) return;
assert( pCte->eCteMagic==CTE_MAGIC );
assert( pItem->pTab!=0 );
assert( sqlite3_stricmp(pCte->zName, pItem->pTab->zName)==0 );
assert( pCte->nRefCte>=1 );
}
#endif /* SQLITE_DEBUG */
/*
** Clear information from a Cte object, but do not deallocate storage.
*/
static void cteClear(sqlite3 *db, Cte *pCte){
sqlite3ExprListDelete(db, pCte->pCols);
sqlite3SelectDelete(db, pCte->pSelect);
sqlite3DbFree(db, pCte->zName);
#ifdef SQLITE_DEBUG
if( db==0 || db->pnBytesFreed==0 ){
pCte->zName = 0;
pCte->eCteMagic = 0;
}
#endif
}
/*
** Free the contents of the CTE object passed as the second argument.
*/
void sqlite3CteDelete(sqlite3 *db, Cte *pCte){
if( pCte ){
cteClear(db, pCte);
sqlite3DbFree(db, pCte);
}
}
/*
** This routine is invoked once per CTE by the parser while parsing a
** WITH clause.
** WITH clause. Construct a new Cte object and append it to the
** input With object, reallocating the With object as needed. If the
** pWith input is NULL, then create a new With object.
**
** The returned With object is unowned. The caller needs to assign
** ownership. Note, however, that ownership should not be assigned
** until the object stops growing. The input pWith parameter must be
** either NULL or a pointer to an unowned With object.
*/
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 /* The CTE to add to the WITH clause */
){
sqlite3 *db = pParse->db;
With *pNew;
char *zName;
/* The input WITH clause is yet unowned */
assert( pWith==0 || pWith->mOwner==0 );
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++){
@@ -5237,37 +5325,73 @@ With *sqlite3WithAdd(
}else{
pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
}
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->a[pNew->nCte] = *pCte;
sqlite3DbFree(db, pCte);
pNew->nCte++;
}
return pNew;
}
/*
** Free the contents of the With object passed as the second argument.
**
** The sqlite3WithReleaseBySelect() causes the Select-object ownership
** to be released, and sqlite3WithReleaseByParse() causes the Parse-object
** ownership to be released. The With object is only deallocated after
** both owners release it.
*/
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);
}
sqlite3DbFree(db, pWith);
int i;
for(i=0; i<pWith->nCte; i++){
cteClear(db, &pWith->a[i]);
}
sqlite3DbFree(db, pWith);
}
void sqlite3WithReleaseBySelect(sqlite3 *db, With *pWith){
if( pWith==0 ) return;
if( db && db->pnBytesFreed ){
if( pWith->mOwner==WithOwnedBySelect ) sqlite3WithDelete(db, pWith);
}else{
pWith->mOwner &= ~WithOwnedBySelect;
if( pWith->mOwner==0 ) sqlite3WithDelete(db, pWith);
}
}
void sqlite3WithReleaseByParse(sqlite3 *db, With *pWith){
if( pWith==0 ) return;
assert( db==0 || db->pnBytesFreed==0 );
pWith->mOwner &= ~WithOwnedByParse;
if( pWith->mOwner==0 ) sqlite3WithDelete(db, pWith);
}
/*
** Add Select-object or Parse-object ownership to a With object. If
** the With object is already owned appropriately, then these routines
** are no-ops.
**
** The first time that a With becomes owned by a Parse-object, make
** arrangements to automatically release the ownership when the Parse
** object is destroyed using a call to sqlite3ParserAddCleanup().
** This involves a memory allocation. If that memory allocation fails,
** the With object might be destroyed immediately. In that
** case, return a NULL pointer. But usually, return a copy of the pWith
** pointer.
*/
void sqlite3WithClaimedBySelect(With *pWith){
pWith->mOwner |= WithOwnedBySelect;
}
With *sqlite3WithClaimedByParse(Parse *pParse, With *pWith){
if( (pWith->mOwner & WithOwnedByParse)==0 ){
pWith->mOwner |= WithOwnedByParse;
pWith = (With*)sqlite3ParserAddCleanup(pParse,
(void(*)(sqlite3*,void*))sqlite3WithReleaseByParse,
(void*)pWith);
}
return pWith;
}
#endif /* !defined(SQLITE_OMIT_CTE) */
+1 -1
View File
@@ -209,7 +209,7 @@ Expr *sqlite3LimitWhere(
pSrc->a[0].pTab = 0;
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
pSrc->a[0].pTab = pTab;
pSrc->a[0].pIBIndex = 0;
pSrc->a[0].u2.pIBIndex = 0; assert( !pSrc->a[0].fg.isCte );
/* generate the SELECT expression tree. */
pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
+2 -1
View File
@@ -1401,6 +1401,7 @@ static With *withDup(sqlite3 *db, With *p){
if( pRet ){
int i;
pRet->nCte = p->nCte;
pRet->mOwner = WithOwnedBySelect;
for(i=0; i<p->nCte; i++){
pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
@@ -1544,7 +1545,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}
pNewItem->pIBIndex = pOldItem->pIBIndex;
pNewItem->u2 = pOldItem->u2;
if( pNewItem->fg.isTabFunc ){
pNewItem->u1.pFuncArg =
sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
+40 -28
View File
@@ -249,6 +249,9 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
%endif SQLITE_OMIT_WINDOWFUNC
%ifndef SQLITE_OMIT_GENERATED_COLUMNS
GENERATED ALWAYS
%endif
%ifndef SQLITE_OMIT_CTE
MATERIALIZED
%endif
REINDEX RENAME CTIME_KW IF
.
@@ -510,29 +513,29 @@ cmd ::= select(X). {
}
}
}
}
#ifndef SQLITE_OMIT_CTE
/* Link a With object to a Select object.
** Both Select- and Parse-ownership is assigned to the With object
*/
static Select *linkWithToSelect(Parse *pParse, Select *pSelect, With *pWith){
assert( pWith->mOwner==0 );
if( pSelect ){
pSelect->pWith = pWith;
sqlite3WithClaimedBySelect(pWith);
parserDoubleLinkSelect(pParse, pSelect);
}else{
sqlite3WithClaimedByParse(pParse,pWith);
}
return pSelect;
}
#endif /* SQLITE_OMIT_CTE */
} // end %include
%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 = linkWithToSelect(pParse,X,W); }
select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X).
{ A = linkWithToSelect(pParse,X,W); }
%endif /* SQLITE_OMIT_CTE */
select(A) ::= selectnowith(X). {
Select *p = X;
@@ -1661,18 +1664,27 @@ anylist ::= anylist ANY.
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
%type wqlist {With*}
%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
%destructor wqlist {if($$)sqlite3WithDelete(pParse->db, $$);}
%type wqitem {Cte*}
%destructor wqitem {sqlite3CteDelete(pParse->db, $$);}
%type wqas {int}
with ::= .
%ifndef SQLITE_OMIT_CTE
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W); }
with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W); }
wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. {
A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/
wqas(A) ::= AS. {A = Materialize_Any;}
wqas(A) ::= AS MATERIALIZED. {A = Materialize_Yes;}
wqas(A) ::= AS NOT MATERIALIZED. {A = Materialize_No;}
wqitem(A) ::= nm(X) eidlist_opt(Y) wqas(F) LP select(Z) RP. {
A = sqlite3CteNew(pParse, &X, Y, Z, F); /*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
+3 -1
View File
@@ -597,7 +597,7 @@ void sqlite3ParserReset(Parse *pParse){
**
** testcase( pParse->earlyCleanup );
*/
void sqlite3ParserAddCleanup(
void *sqlite3ParserAddCleanup(
Parse *pParse, /* Destroy when this Parser finishes */
void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
void *pPtr /* Pointer to object to be cleaned up */
@@ -610,10 +610,12 @@ void sqlite3ParserAddCleanup(
pCleanup->xCleanup = xCleanup;
}else{
xCleanup(pParse->db, pPtr);
pPtr = 0;
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
pParse->earlyCleanup = 1;
#endif
}
return pPtr;
}
/*
+60 -38
View File
@@ -90,7 +90,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
sqlite3WindowListDelete(db, p->pWinDefn);
}
#endif
if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
if( p->pWith ) sqlite3WithReleaseBySelect(db, p->pWith);
if( bFree ) sqlite3DbFreeNN(db, p);
p = pPrior;
bFree = 1;
@@ -4724,7 +4724,9 @@ int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
pParse->checkSchema = 1;
return SQLITE_ERROR;
}
pFrom->pIBIndex = pIdx;
assert( pFrom->fg.isCte==0 );
assert( pFrom->pSelect==0 );
pFrom->u2.pIBIndex = pIdx;
}
return SQLITE_OK;
}
@@ -4856,25 +4858,13 @@ static struct Cte *searchWith(
/* The code generator maintains a stack of active WITH clauses
** with the inner-most WITH clause being at the top of the stack.
**
** This routine pushes the WITH clause passed as the second argument
** onto the top of the stack. If argument bFree is true, then this
** WITH clause will never be popped from the stack. In this case it
** should be freed along with the Parse object. In other cases, when
** bFree==0, the With object will be freed along with the SELECT
** statement with which it is associated.
*/
void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
void sqlite3WithPush(Parse *pParse, With *pWith){
if( pWith ){
assert( pParse->pWith!=pWith );
pWith->pOuter = pParse->pWith;
pParse->pWith = pWith;
if( bFree ){
sqlite3ParserAddCleanup(pParse,
(void(*)(sqlite3*,void*))sqlite3WithDelete,
pWith);
testcase( pParse->earlyCleanup );
}
sqlite3WithClaimedByParse(pParse, pWith);
}
}
@@ -4939,6 +4929,17 @@ static int withExpand(
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
assert( pFrom->pSelect );
if( pCte->eMaterialize==Materialize_Yes ){
pFrom->pSelect->selFlags |= SF_OptBarrier;
}
assert( !pFrom->fg.isIndexedBy );
pFrom->fg.isCte = 1;
assert( (pCte->eCteMagic==0 && pCte->nRefCte==0)
|| (pCte->eCteMagic==CTE_MAGIC && pCte->nRefCte>0) );
pFrom->u2.pCteSrc = pCte;
sqlite3WithClaimedByParse(pParse, pWith);
pCte->nRefCte++;
VVA_ONLY( pCte->eCteMagic = CTE_MAGIC );
/* Check if this is a recursive CTE. */
pRecTerm = pSel = pFrom->pSelect;
@@ -5119,7 +5120,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
pTabList = p->pSrc;
pEList = p->pEList;
sqlite3WithPush(pParse, p->pWith, 0);
sqlite3WithPush(pParse, p->pWith);
/* Make sure cursor numbers have been assigned to all entries in
** the FROM clause of the SELECT statement.
@@ -5758,9 +5759,10 @@ static void havingToWhere(Parse *pParse, Select *p){
}
/*
** Check to see if the pThis entry of pTabList is a self-join of a prior view.
** If it is, then return the SrcList_item for the prior view. If it is not,
** then return 0.
** Check to see if the pThis entry of pTabList is a self-join of a prior view,
** or any reuse of a CTE that is not necessarily a self-join.
** If it is, then return the SrcList_item for the prior view or CTE.
** If it is not, then return 0.
*/
static struct SrcList_item *isSelfJoinView(
SrcList *pTabList, /* Search for self-joins in this FROM clause */
@@ -5769,6 +5771,11 @@ static struct SrcList_item *isSelfJoinView(
struct SrcList_item *pItem;
assert( pThis->pSelect!=0 );
if( pThis->pSelect->selFlags & SF_PushDown ) return 0;
if( pThis->fg.isCte ){
Cte *pCte = pThis->u2.pCteSrc;
sqlite3AssertValidCteBackRef(pThis);
return pCte->pCteMat;
}
for(pItem = pTabList->a; pItem<pThis; pItem++){
Select *pS1;
if( pItem->pSelect==0 ) continue;
@@ -6080,6 +6087,9 @@ int sqlite3Select(
continue;
}
/* Do not flatten across an optimization barrier */
if( pSub->selFlags & SF_OptBarrier ) continue;
if( flattenSubquery(pParse, p, i, isAgg) ){
if( pParse->nErr ) goto select_end;
/* This subquery can be absorbed into its parent. */
@@ -6145,6 +6155,7 @@ int sqlite3Select(
*/
for(i=0; i<pTabList->nSrc; i++){
struct SrcList_item *pItem = &pTabList->a[i];
struct SrcList_item *pPrior;
SelectDest dest;
Select *pSub;
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
@@ -6204,6 +6215,7 @@ int sqlite3Select(
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( OptimizationEnabled(db, SQLITE_PushDown)
&& (pSub->selFlags & SF_OptBarrier)==0
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
(pItem->fg.jointype & JT_OUTER)!=0)
){
@@ -6222,18 +6234,27 @@ int sqlite3Select(
zSavedAuthContext = pParse->zAuthContext;
pParse->zAuthContext = pItem->zName;
/* Verify that the CTE back-reference information is valid */
sqlite3AssertValidCteBackRef(pItem);
/* Generate code to implement the subquery
**
** The subquery is implemented as a co-routine if the subquery is
** The subquery is implemented as a co-routine if (1) the subquery is
** guaranteed to be the outer loop (so that it does not need to be
** computed more than once)
** computed more than once) and if (2) the subquery is a CTE that is
** used only this one time.
**
** TODO: Are there other reasons beside (1) to use a co-routine
** TODO: Are there other reasons beside (1) and (2) to use a co-routine
** implementation?
**
** TODO: We might should allow a subquery that is an optimization barrier
** to be implemented as a co-routine as long as we know that it is the
** only use of the subquery.
*/
if( i==0
&& (pTabList->nSrc==1
|| (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
&& (pItem->fg.isCte && pItem->u2.pCteSrc->nRefCte==1) /* (2) */
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
@@ -6253,16 +6274,20 @@ int sqlite3Select(
sqlite3VdbeEndCoroutine(v, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else if( (pPrior = isSelfJoinView(pTabList,pItem))!=0 ){
/* This view has been previously materialized. Use the
** prior materialization */
if( pPrior->addrFillSub ){
sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
}
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
assert( pPrior->pSelect!=0 );
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
}else{
/* Generate a subroutine that will fill an ephemeral table with
** the content of this subquery. pItem->addrFillSub will point
** to the address of the generated subroutine. pItem->regReturn
** is a register allocated to hold the subroutine return address
*/
/* Materialize the view */
int topAddr;
int onceAddr = 0;
int retAddr;
struct SrcList_item *pPrior;
testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
pItem->regReturn = ++pParse->nMem;
@@ -6277,15 +6302,12 @@ int sqlite3Select(
}else{
VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
}
pPrior = isSelfJoinView(pTabList, pItem);
if( pPrior ){
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
assert( pPrior->pSelect!=0 );
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
}else{
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
sqlite3Select(pParse, pSub, &dest);
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
sqlite3Select(pParse, pSub, &dest);
if( pItem->fg.isCte ){
sqlite3AssertValidCteBackRef(pItem);
pItem->u2.pCteSrc->pCteMat = pItem;
}
pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
+74 -17
View File
@@ -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;
@@ -2957,6 +2958,7 @@ struct SrcList {
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; /* True if table is a CTE */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
Expr *pOn; /* The ON clause of a join */
@@ -2966,7 +2968,10 @@ struct SrcList {
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
ExprList *pFuncArg; /* Arguments to table-valued-function */
} u1;
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
union {
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
Cte *pCteSrc; /* The original CTE, if fg.isCte is true */
} u2;
} a[1]; /* One entry for each identifier on the list */
};
@@ -3191,6 +3196,7 @@ struct Select {
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
#define SF_OptBarrier 0x2000000 /* SELECT is an optimization barrier */
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -3873,20 +3879,55 @@ void sqlite3SelectWalkAssert2(Walker*, Select*);
#define WRC_Prune 1 /* Omit children but continue walking siblings */
#define WRC_Abort 2 /* Abandon the tree walk */
/*
** Allowed values for Cte.eMaterialize
*/
#define Materialize_Any 0 /* Not specified */
#define Materialize_Yes 1 /* AS MATERIALIZED ... */
#define Materialize_No 2 /* AS NOT MATERIALIZED. Not currently used */
/*
** An instance of this structure represents a set of one or more CTEs
** (common table expressions) created by a single WITH clause.
**
** A With object describes an entire WITH clause, which is composed of
** one or more Cte objects. During parsing, the Cte objects are created
** separately for each common-table-expression, then copied into the
** With object container.
**
** DUAL OWNERSHIP
** The With object can be owned by a Select object (via the Select.pWith
** field), or it can be owned by the Parse object of the current parse,
** or by both. The With.mOwner bitmask determines who owns each With object.
** The object is not deallocated until all owners have released it.
*/
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];
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 */
u8 eMaterialize; /* One of the Materialize_* options */
u16 eCteMagic; /* Magic number for sanity checking */
u32 nRefCte; /* Number of times used */
struct SrcList_item *pCteMat; /* The materialization of this CTE */
};
struct With {
int nCte; /* Number of CTEs in the WITH clause */
int mOwner; /* Mask of bits to discribe ownership */
With *pOuter; /* Containing WITH clause, or NULL */
Cte a[1]; /* The CTEs of this WITH clause */
};
/*
** The correct value for Cte.eCteMagic if the Cte object is valid
*/
#define CTE_MAGIC 0x3f96 /* Magic number for valid Cte objects */
/*
** Ownership flags for With.
*/
#define WithOwnedBySelect 0x01 /* Owned by a Select object */
#define WithOwnedByParse 0x10 /* Owned by the Parse object */
#ifdef SQLITE_DEBUG
/*
@@ -4876,7 +4917,7 @@ sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
void sqlite3ParserReset(Parse*);
void sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*);
void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*);
#ifdef SQLITE_ENABLE_NORMALIZE
char *sqlite3Normalize(Vdbe*, const char*);
#endif
@@ -4891,13 +4932,29 @@ 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*,int);
With *sqlite3WithAdd(Parse*,With*,Cte*);
void sqlite3CteDelete(sqlite3*,Cte*);
void sqlite3WithDelete(sqlite3*,With*);
void sqlite3WithPush(Parse*, With*, u8);
#else
#define sqlite3WithPush(x,y,z)
#define sqlite3WithDelete(x,y)
#endif
void sqlite3WithReleaseBySelect(sqlite3*,With*);
void sqlite3WithReleaseByParse(sqlite3*,With*);
void sqlite3WithClaimedBySelect(With*);
With *sqlite3WithClaimedByParse(Parse*,With*);
void sqlite3WithPush(Parse*, With*);
# ifdef SQLITE_DEBUG
void sqlite3AssertValidCteBackRef(struct SrcList_item*);
# else
# define sqlite3AssertValidCteBackRef(X)
# endif
#else /* if defined(SQLITE_OMIT_CTE) */
# define sqlite3WithPush(PARSE,W)
# deifne sqlite3WithDelete(Db,W)
# define sqlite3WithReleaseBySelect(DB,W)
# define sqlite3WithReleaseByParse(DB,W)
# define sqlite3WithClaimedBySelect(W)
# define sqlite3WithClaimedByParse(PARSE,W);
# define sqlite3AssertValidCteBackRef(X)
#endif /* SQLITE_OMIT_CTE */
#ifndef SQLITE_OMIT_UPSERT
Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
void sqlite3UpsertDelete(sqlite3*,Upsert*);
+9 -2
View File
@@ -90,7 +90,8 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
if( pWith==0 ) return;
if( pWith->nCte==0 ) return;
if( pWith->pOuter ){
sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter);
sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",
pWith,pWith->pOuter);
}else{
sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith);
}
@@ -101,7 +102,10 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
char zLine[1000];
const struct Cte *pCte = &pWith->a[i];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
sqlite3_str_appendf(&x, "%s", pCte->zName);
sqlite3_str_appendf(&x, "%s%s (0x%p, n=%d)", pCte->zName,
pCte->eMaterialize==Materialize_Any ? "" :
pCte->eMaterialize==Materialize_Yes ? " MATERIALIZED" :
" NOT MATERIALIZED", pCte, pCte->nRefCte);
if( pCte->pCols && pCte->pCols->nExpr>0 ){
char cSep = '(';
int j;
@@ -150,6 +154,9 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
if( pItem->fg.isCte && pItem->u2.pCteSrc ){
sqlite3_str_appendf(&x, " CTE=0x%p", pItem->u2.pCteSrc);
}
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
if( pItem->pSelect ){
+4 -4
View File
@@ -2954,9 +2954,9 @@ static int whereLoopAddBtree(
pWC = pBuilder->pWC;
assert( !IsVirtual(pSrc->pTab) );
if( pSrc->pIBIndex ){
if( pSrc->fg.isIndexedBy ){
/* An INDEXED BY clause specifies a particular index to use */
pProbe = pSrc->pIBIndex;
pProbe = pSrc->u2.pIBIndex;
}else if( !HasRowid(pTab) ){
pProbe = pTab->pIndex;
}else{
@@ -2992,7 +2992,7 @@ static int whereLoopAddBtree(
if( !pBuilder->pOrSet /* Not part of an OR optimization */
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
&& pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
&& !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
@@ -3042,7 +3042,7 @@ static int whereLoopAddBtree(
/* Loop over all indices. If there was an INDEXED BY clause, then only
** consider index pProbe. */
for(; rc==SQLITE_OK && pProbe;
pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++
pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++
){
int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0;
if( pProbe->pPartIdxWhere!=0
+2 -1
View File
@@ -229,7 +229,7 @@ static Keyword aKeywordTable[] = {
{ "FOREIGN", "TK_FOREIGN", FKEY, 1 },
{ "FROM", "TK_FROM", ALWAYS, 10 },
{ "FULL", "TK_JOIN_KW", ALWAYS, 3 },
{ "GENERATED", "TK_GENERATED", GENCOL, 1 },
{ "GENERATED", "TK_GENERATED", ALWAYS, 1 },
{ "GLOB", "TK_LIKE_KW", ALWAYS, 3 },
{ "GROUP", "TK_GROUP", ALWAYS, 5 },
{ "GROUPS", "TK_GROUPS", WINDOWFUNC, 2 },
@@ -255,6 +255,7 @@ static Keyword aKeywordTable[] = {
{ "LIKE", "TK_LIKE_KW", ALWAYS, 5 },
{ "LIMIT", "TK_LIMIT", ALWAYS, 3 },
{ "MATCH", "TK_MATCH", ALWAYS, 2 },
{ "MATERIALIZED", "TK_MATERIALIZED", CTE, 12 },
{ "NATURAL", "TK_JOIN_KW", ALWAYS, 3 },
{ "NO", "TK_NO", FKEY|WINDOWFUNC, 2 },
{ "NOT", "TK_NOT", ALWAYS, 10 },