-C Give\sunique\snames\sto\sfields\sin\sthe\sSrcItem\sobject,\sto\sfacilitate\sanalysis\sof\nhow\sthose\sfields\sare\sused\susing\s"grep".
-D 2024-08-17T23:23:23.271
+C Refactor\sthe\sSrcItem\sobject\sso\sthat\sinformation\sabout\ssubqueries\sis\sstored\nin\sa\sseparately\sallocated\sSubquery\sobject.\s\sThis\sreduces\sthe\smemory\srequirements\nfor\sSrcItem\sand\smakes\sthe\scode\srun\sfaster.\s\sIt\salso\sprovides\san\sexpansion\spath\nfor\ssubquery\sprocessing\sthat\sdoes\snot\sburden\ssimple\squeries.\s\sThe\scurrent\nchecking\smostly\sworks,\sbut\sthere\sare\sstill\sissues\sthat\sneed\sto\sbe\stracked\ndown\sand\sfixed.
+D 2024-08-19T22:48:30.539
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F sqlite_cfg.h.in baf2e409c63d4e7a765e17769b6ff17c5a82bbd9cbf1e284fd2e4cefaff3fcf2
-F src/alter.c b73964bd6bc3122e884993c0666bae96d4e1e50a94b6f9008df32e6a18e99cd0
+F src/alter.c aa93e37e4a36a0525bbb2a2aeda20d2018f0aa995542c7dc658e031375e3f532
F src/analyze.c 30bf40ec4208ead9e977bec017bccc8a9681820936e38ca5a4a7443100a6d5c5
-F src/attach.c d778bacb2ee5bc6a74c81a17376064e6a16ee447d82ee4ad1f7717a320d5a232
+F src/attach.c 08235ab62ed5ccc93c22bf36e640d19effcd632319615851bccf724ec9341333
F src/auth.c 4c1ea890e0069ad73bead5d17a5b12c34cfa4f1a24175c8147ea439b64be271c
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
F src/btree.c 8b42fc7d9efdb2df05c30e8f91ff6cfbd979724ae24bf90269028468b7a13333
F src/btree.h 55066f513eb095db935169dab1dc2f7c7a747ef223c533f5d4ad4dfed346cbd0
F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6
-F src/build.c 664804cbefaf43c13fcb369474609cf8befa4d89933804dc276d0a15fbd66c34
+F src/build.c d243a17292e027c1d662e992fbd395123aacc4b74a4a9419d0b010088ba318dd
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 64e4b1227b4ed123146f0aa2989131d1fbd9b927b11e80c9d58c6a68f9cd5ce3
F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43
F src/delete.c 8aaf9d05d6ff6349a7a6001cbf90c7a46b19b9f52ef2e47aa0415a529d5307a1
-F src/expr.c 835b637da89fe20a1ff90bd372904fd421ff92d1dfb9bbea9c81bbffbea46715
+F src/expr.c 6d5f2c38fe3ec06a7eac599dac822788b36064124e20112a844e9cd5156cb239
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 849049c74a4c68961154c124087b5c67d277217a2e642570d0c1bd6336859940
F src/func.c 1f61e32e7a357e615b5d2e774bee563761fce4f2fd97ecb0f72c33e62a2ada5f
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 2152b159ac214519e778b6996a85b9073ace054f5c9d430b375852e343a5ec92
+F src/insert.c b07865339e67869ff12d2eee3a1417f4779c742e2a51ae3d14ad45e5168f6f41
F src/json.c 5b6a1d6015997b9ee848a32948720bdb26a0ef2de5a2127ebf7355ce66dbdc0d
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c b08600ebf0db90b6d1e9b8b6577c6fa3877cbe1a100bd0b2899e4c6e9adad4b3
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
-F src/parse.y bc8df5d36afd06d50c1828072e4f3120d892af685436db3b8e776288803535e0
+F src/parse.y 967e0a712de8bee4b2cf82bd9efa36b68afd03cc486d4ea77a945feb49b057dc
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 49516ad7718a3626f28f710fa7448ef1fce3c07fd169acbb4817341950264319
F src/pragma.c 52bfbf6dfd668b69b5eb9bd1186e3a67367c8453807150d6e75239229924f684
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
F src/prepare.c d99931f45416652895e502328ca49fe782cfc4e1ebdcda13b3736d991ebf42ce
-F src/printf.c efa3973bdcacdbc8cf0ff3b3a58e16a105319eac8819823f5807579dc2fa4138
+F src/printf.c 17054fb94ffcf7a28362e9a5af4a0f813bd0c52200ae408eeebddc81feed9274
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
-F src/resolve.c d25fc99cd54d2acf4c655f942dd48ee60e3ada94d73c8b992487515f1295a3bc
+F src/resolve.c 9afed5fd7b9111633bdb74a73cdc47324e28e4dc6c27113e3e9aee38fb9422ab
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
-F src/select.c 10ff02352f4c6eb7bc8d5711f0f74199b94ae9adc346f10391931f5c55d1c68f
+F src/select.c 3e284895745f57de3b37b17fce9eb0e26bc54fccc94ea1ff5342be4360e3674b
F src/shell.c.in 94571558b0fb28c37a5cf6dbd6ea27285341023a28a8cb5795cd2768fab67704
F src/sqlite.h.in 1ad9110150773c38ebababbad11b5cb361bcd3997676dec1c91ac5e0416a7b86
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
-F src/sqliteInt.h 95186ebaf79560fa44b1a05bd7b4b14846e037a285177ca65082a9ae952a6c29
+F src/sqliteInt.h 630e356b7d94df1dcbc32d4f7d4569725a12c6de014fe064311219c22d9cbd54
F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
-F src/treeview.c a337ab329f94e627c9a5efee0326d3359e352c4f0c0bb5c8f4b686fe03e6e7e8
+F src/treeview.c e98dbc8068d27f3d0bd2a4cebfce6a7c21c776edc23d80e12c67a70cd2ba3fe5
F src/trigger.c 68d849ea6ccab67beb450d1a0d065c6118f0884a779388b07f0d9a27dbda38c9
F src/update.c 0e01aa6a3edf9ec112b33eb714b9016a81241497b1fb7c3e74332f4f71756508
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 887fc4ca3f020ebb2e376f222069570834ac63bf50111ef0cbf3ae417048ed89
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
-F src/walker.c e5898234c5920481d962e29b96ecf403b992b12ae243ad0e53659386a1b3e554
-F src/where.c d0140395a35fbc451e9d07c9816895d399f02ae76cbcd255e8ce1069548b02c8
+F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
+F src/where.c b6f79b189fcba459bb80420d3b4102f42a399be36ff29a3deff4ae4888fee46d
F src/whereInt.h 6444b888ce395cb80511284b8a73b63472d34247fcb1b125ee06a54fa6ae878e
-F src/wherecode.c 3e130abafc185a29c8dbdcc33e6150dff2aa884f75483e159feef170961af65d
-F src/whereexpr.c 784f641ba3d6f1fdb4f65e3891daa50067a4c51f1acd11b461397f7d4c622907
-F src/window.c ffbf65ba62ce329e2fd68b35f11e920121fac45dab0f9a4bbc486125ccc98b6c
+F src/wherecode.c 137797b0de9ddf1ff43e5b0edffcc76fb05184ed651fc4f5a0a01a45c0b89d04
+F src/whereexpr.c 6a72cf607548765a262f216e87373fd675a4646f9cc4278fc519b66cf03dbc13
+F src/window.c 499d48f315a09242dc68f2fac635ed27dcf6bbb0d9ab9084857898c64489e975
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a4c59ac3c6ec979c25b544d29e47b8e39f6439c098eed8f84b3bd506c9adf047
-R 1d81e98efd997ac91c2ceea4c803b7e6
+P 9f5aeef3cbc2c95267c8f7bf60d5c66971a76789669fb0e8f853273ff6f616f2
+R 0dac77664f5c62d0dcc882ea3148d6f4
U drh
-Z 8ec986f4842c563a443c2f1a23450bf4
+Z a11cd0d49c593b1144d9510fd31aa2a7
# Remove this line to create a well-formed Fossil manifest.
-9f5aeef3cbc2c95267c8f7bf60d5c66971a76789669fb0e8f853273ff6f616f2
+8ff5dda8448d7e1a533d7f27db2573ce68fa9956b9d9847ced45e83c1f06bab0
int i;
for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
SrcItem *p = &pStep->pFrom->a[i];
- if( p->sq.pSelect ){
- sqlite3SelectPrep(pParse, p->sq.pSelect, 0);
+ if( p->fg.isSubquery ){
+ assert( p->u4.pSubq!=0 );
+ sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0);
}
}
}
}
if( pStep->pFrom ){
int i;
- for(i=0; i<pStep->pFrom->nSrc; i++){
- sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].sq.pSelect);
+ SrcList *pFrom = pStep->pFrom;
+ for(i=0; i<pFrom->nSrc; i++){
+ if( pFrom->a[i].fg.isSubquery ){
+ assert( pFrom->a[i].u4.pSubq!=0 );
+ sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect);
+ }
}
}
}
if( NEVER(pList==0) ) return WRC_Continue;
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pFix->bTemp==0 ){
+ if( pFix->bTemp==0 && pItem->fg.isSubquery==0 ){
if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){
if( iDb!=sqlite3FindDbName(db, pItem->u4.zDatabase) ){
sqlite3ErrorMsg(pFix->pParse,
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
if( pItem->iCursor>=0 ) continue;
pItem->iCursor = pParse->nTab++;
- if( pItem->sq.pSelect ){
- sqlite3SrcListAssignCursors(pParse, pItem->sq.pSelect->pSrc);
+ if( pItem->fg.isSubquery ){
+ assert( pItem->u4.pSubq!=0 );
+ assert( pItem->u4.pSubq->pSelect!=0 );
+ assert( pItem->u4.pSubq->pSelect->pSrc!=0 );
+ sqlite3SrcListAssignCursors(pParse, pItem->u4.pSubq->pSelect->pSrc);
}
}
}
}
+/*
+** Delete a Subquery object and its substructure.
+*/
+void sqlite3SubqueryDelete(sqlite3 *db, Subquery *pSubq){
+ assert( pSubq!=0 && pSubq->pSelect!=0 );
+ sqlite3SelectDelete(db, pSubq->pSelect);
+ sqlite3DbFree(db, pSubq);
+}
+
+/*
+** Remove a Subquery from a SrcItem. Return the associated Select object.
+** The returned Select becomes the responsibility of the caller.
+*/
+Select *sqlite3SubqueryDetach(sqlite3 *db, SrcItem *pItem){
+ Select *pSel;
+ assert( pItem!=0 );
+ assert( pItem->fg.isSubquery );
+ pSel = pItem->u4.pSubq->pSelect;
+ sqlite3DbFree(db, pItem->u4.pSubq);
+ pItem->u4.pSubq = 0;
+ pItem->fg.isSubquery = 0;
+ return pSel;
+}
+
/*
** Delete an entire SrcList including all its substructure.
*/
for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
/* Check invariants on SrcItem */
- assert( pItem->sq.pSelect==0
- || (pItem->sq.pSelect->selFlags & SF_MultiValue)==0
- || (pItem->fg.hadSchema==0
- && (pItem->fg.fixedSchema || pItem->u4.zDatabase==0)) );
+ assert( !pItem->fg.hadSchema || !pItem->fg.isSubquery );
assert( pItem->fg.hadSchema==0 || pItem->fg.fixedSchema==1 );
-
if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName);
if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias);
- if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){
+ if( pItem->fg.isSubquery ){
+ sqlite3SubqueryDelete(db, pItem->u4.pSubq);
+ }else if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){
sqlite3DbNNFreeNN(db, pItem->u4.zDatabase);
}
if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pSTab);
- if( pItem->sq.pSelect ) sqlite3SelectDelete(db, pItem->sq.pSelect);
if( pItem->fg.isUsing ){
sqlite3IdListDelete(db, pItem->u3.pUsing);
}else if( pItem->u3.pOn ){
sqlite3DbNNFreeNN(db, pList);
}
+/*
+** Attach a Subquery object to pItem->uv.pSubq. Set the
+** pSelect value but leave all the other values initialized
+** to zero.
+**
+** A copy of the Select object is made if dupSelect is true, and the
+** SrcItem takes responsibility for deleting the copy. If dupSelect is
+** false, ownership of the Select passes to the SrcItem. Either way,
+** the SrcItem will take responsibility for deleting the Select.
+**
+** When dupSelect is zero, that means the Select might get deleted right
+** away if there is an OOM error. Beware.
+**
+** Return non-zero on success. Return zero on an OOM error.
+*/
+int sqlite3SrcItemAttachSubquery(
+ Parse *pParse, /* Parsing context */
+ SrcItem *pItem, /* Item to which the subquery is to be attached */
+ Select *pSelect, /* The subquery SELECT. Must be non-NULL */
+ int dupSelect /* If true, attach a copy of pSelect, not pSelect itself.*/
+){
+ Subquery *p;
+ if( pSelect==0 ) return SQLITE_OK;
+ assert( pItem->fg.isSubquery==0 );
+ if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){
+ sqlite3DbFree(pParse->db, pItem->u4.zDatabase);
+ pItem->u4.zDatabase = 0;
+ }
+ pItem->fg.fixedSchema = 0;
+ if( dupSelect ){
+ pSelect = sqlite3SelectDup(pParse->db, pSelect, 0);
+ if( pSelect==0 ) return 0;
+ }
+ p = pItem->u4.pSubq = sqlite3DbMallocRawNN(pParse->db, sizeof(Subquery));
+ if( p==0 ){
+ sqlite3SelectDelete(pParse->db, pSelect);
+ return 0;
+ }
+ pItem->fg.isSubquery = 1;
+ p->pSelect = pSelect;
+ assert( offsetof(Subquery, pSelect)==0 );
+ memset(((char*)p)+sizeof(p->pSelect), 0, sizeof(*p)-sizeof(p->pSelect));
+ return 1;
+}
+
+
/*
** This routine is called by the parser to add a new term to the
** end of a growing FROM clause. The "p" parameter is the part of
if( pAlias->n ){
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
}
+ assert( pSubquery==0 || pDatabase==0 );
if( pSubquery ){
- pItem->sq.pSelect = pSubquery;
- if( pSubquery->selFlags & SF_NestedFrom ){
- pItem->fg.isNestedFrom = 1;
+ if( sqlite3SrcItemAttachSubquery(pParse, pItem, pSubquery, 0) ){
+ if( pSubquery->selFlags & SF_NestedFrom ){
+ pItem->fg.isNestedFrom = 1;
+ }
}
}
assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 );
SrcItem *pNewItem = &pNew->a[i];
const SrcItem *pOldItem = &p->a[i];
Table *pTab;
- if( pOldItem->fg.fixedSchema ){
+ pNewItem->fg = pOldItem->fg;
+ if( pOldItem->fg.isSubquery ){
+ Subquery *pNewSubq = sqlite3DbMallocRaw(db, sizeof(Subquery));
+ if( pNewSubq==0 ){
+ assert( db->mallocFailed );
+ pNewItem->fg.isSubquery = 0;
+ }else{
+ memcpy(pNewSubq, pOldItem->u4.pSubq, sizeof(*pNewSubq));
+ pNewSubq->pSelect = sqlite3SelectDup(db, pNewSubq->pSelect, flags);
+ if( pNewSubq->pSelect==0 ){
+ sqlite3DbFree(db, pNewSubq);
+ pNewSubq = 0;
+ pNewItem->fg.isSubquery = 0;
+ }
+ }
+ pNewItem->u4.pSubq = pNewSubq;
+ }else if( pOldItem->fg.fixedSchema ){
pNewItem->u4.pSchema = pOldItem->u4.pSchema;
}else{
pNewItem->u4.zDatabase = sqlite3DbStrDup(db, pOldItem->u4.zDatabase);
}
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
- pNewItem->fg = pOldItem->fg;
pNewItem->iCursor = pOldItem->iCursor;
- pNewItem->sq.addrFillSub = pOldItem->sq.addrFillSub;
- pNewItem->sq.regReturn = pOldItem->sq.regReturn;
- pNewItem->sq.regResult = pOldItem->sq.regResult;
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}else if( pNewItem->fg.isTabFunc ){
if( pTab ){
pTab->nTabRef++;
}
- pNewItem->sq.pSelect = sqlite3SelectDup(db, pOldItem->sq.pSelect, flags);
if( pOldItem->fg.isUsing ){
assert( pNewItem->fg.isUsing );
pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing);
pp = &pNew->pPrior;
pNext = pNew;
}
-
return pRet;
}
#else
pSrc = p->pSrc;
assert( pSrc!=0 );
if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */
- if( pSrc->a[0].sq.pSelect ) return 0; /* FROM is not a subquery or view */
+ if( pSrc->a[0].fg.isSubquery) return 0;/* FROM is not a subquery or view */
pTab = pSrc->a[0].pSTab;
assert( pTab!=0 );
assert( !IsView(pTab) ); /* FROM clause is not a view */
void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){
SrcItem *pItem = &pVal->pSrc->a[0];
- sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->sq.regReturn);
- sqlite3VdbeJumpHere(pParse->pVdbe, pItem->sq.addrFillSub - 1);
+ assert( pItem->fg.isSubquery && pItem->u4.pSubq!=0 );
+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->u4.pSubq->regReturn);
+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->u4.pSubq->addrFillSub - 1);
}
}
if( pRet ){
SelectDest dest;
+ Subquery *pSubq;
pRet->pSrc->nSrc = 1;
pRet->pPrior = pLeft->pPrior;
pRet->op = pLeft->op;
assert( pLeft->pNext==0 );
assert( pRet->pNext==0 );
p = &pRet->pSrc->a[0];
- p->sq.pSelect = pLeft;
p->fg.viaCoroutine = 1;
- p->sq.addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
- p->sq.regReturn = ++pParse->nMem;
p->iCursor = -1;
p->u1.nRow = 2;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine,
- p->sq.regReturn, 0, p->sq.addrFillSub);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, p->sq.regReturn);
-
- /* Allocate registers for the output of the co-routine. Do so so
- ** that there are two unused registers immediately before those
- ** used by the co-routine. This allows the code in sqlite3Insert()
- ** to use these registers directly, instead of copying the output
- ** of the co-routine to a separate array for processing. */
- dest.iSdst = pParse->nMem + 3;
- dest.nSdst = pLeft->pEList->nExpr;
- pParse->nMem += 2 + dest.nSdst;
-
- pLeft->selFlags |= SF_MultiValue;
- sqlite3Select(pParse, pLeft, &dest);
- p->sq.regResult = dest.iSdst;
- assert( pParse->nErr || dest.iSdst>0 );
+ if( sqlite3SrcItemAttachSubquery(pParse, p, pLeft, 0) ){
+ pSubq = p->u4.pSubq;
+ pSubq->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
+ pSubq->regReturn = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine,
+ pSubq->regReturn, 0, pSubq->addrFillSub);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn);
+
+ /* Allocate registers for the output of the co-routine. Do so so
+ ** that there are two unused registers immediately before those
+ ** used by the co-routine. This allows the code in sqlite3Insert()
+ ** to use these registers directly, instead of copying the output
+ ** of the co-routine to a separate array for processing. */
+ dest.iSdst = pParse->nMem + 3;
+ dest.nSdst = pLeft->pEList->nExpr;
+ pParse->nMem += 2 + dest.nSdst;
+
+ pLeft->selFlags |= SF_MultiValue;
+ sqlite3Select(pParse, pLeft, &dest);
+ pSubq->regResult = dest.iSdst;
+ assert( pParse->nErr || dest.iSdst>0 );
+ }
pLeft = pRet;
}
}else{
}
if( pParse->nErr==0 ){
+ Subquery *pSubq;
assert( p!=0 );
- if( p->sq.pSelect->pEList->nExpr!=pRow->nExpr ){
- sqlite3SelectWrongNumTermsError(pParse, p->sq.pSelect);
+ assert( p->fg.isSubquery );
+ pSubq = p->u4.pSubq;
+ assert( pSubq!=0 );
+ assert( pSubq->pSelect!=0 );
+ assert( pSubq->pSelect->pEList!=0 );
+ if( pSubq->pSelect->pEList->nExpr!=pRow->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, pSubq->pSelect);
}else{
- sqlite3ExprCodeExprList(pParse, pRow, p->sq.regResult, 0, 0);
- sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->sq.regReturn);
+ sqlite3ExprCodeExprList(pParse, pRow, pSubq->regResult, 0, 0);
+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, pSubq->regReturn);
}
}
sqlite3ExprListDelete(pParse->db, pRow);
&& pSelect->pPrior==0
){
SrcItem *pItem = &pSelect->pSrc->a[0];
- dest.iSDParm = pItem->sq.regReturn;
- regFromSelect = pItem->sq.regResult;
- nColumn = pItem->sq.pSelect->pEList->nExpr;
+ Subquery *pSubq;
+ assert( pItem->fg.isSubquery );
+ pSubq = pItem->u4.pSubq;
+ dest.iSDParm = pSubq->regReturn;
+ regFromSelect = pSubq->regResult;
+ assert( pSubq->pSelect!=0 );
+ assert( pSubq->pSelect->pEList!=0 );
+ nColumn = pSubq->pSelect->pEList->nExpr;
ExplainQueryPlan((pParse, 0, "SCAN %S", pItem));
if( bIdListInOrder && nColumn==pTab->nCol ){
regData = regFromSelect;
if( pSelect->pSrc->nSrc!=1 ){
return 0; /* FROM clause must have exactly one term */
}
- if( pSelect->pSrc->a[0].sq.pSelect ){
+ if( pSelect->pSrc->a[0].fg.isSubquery ){
return 0; /* FROM clause cannot contain a subquery */
}
if( pSelect->pWhere ){
SrcItem *pOld = F->a;
assert( pOld->fg.fixedSchema==0 );
pNew->zName = pOld->zName;
- pNew->u4.zDatabase = pOld->u4.zDatabase;
- pNew->sq.pSelect = pOld->sq.pSelect;
- if( pNew->sq.pSelect && (pNew->sq.pSelect->selFlags & SF_NestedFrom)!=0 ){
- pNew->fg.isNestedFrom = 1;
+ assert( pOld->fg.fixedSchema==0 );
+ if( pOld->fg.isSubquery ){
+ pNew->fg.isSubquery = 1;
+ pNew->u4.pSubq = pOld->u4.pSubq;
+ pOld->u4.pSubq = 0;
+ pOld->fg.isSubquery = 0;
+ if( pNew->u4.pSubq->pSelect
+ && (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0
+ ){
+ pNew->fg.isNestedFrom = 1;
+ }
+ }else{
+ pNew->u4.zDatabase = pOld->u4.zDatabase;
+ pOld->u4.zDatabase = 0;
}
if( pOld->fg.isTabFunc ){
pNew->u1.pFuncArg = pOld->u1.pFuncArg;
pOld->fg.isTabFunc = 0;
pNew->fg.isTabFunc = 1;
}
- pOld->zName = pOld->u4.zDatabase = 0;
- pOld->sq.pSelect = 0;
+ pOld->zName = 0;
}
sqlite3SrcListDelete(pParse->db, F);
}else{
if( pItem->zAlias && !flag_altform2 ){
sqlite3_str_appendall(pAccum, pItem->zAlias);
}else if( pItem->zName ){
- if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){
+ if( pItem->fg.fixedSchema==0
+ && pItem->fg.isSubquery==0
+ && pItem->u4.zDatabase!=0
+ ){
sqlite3_str_appendall(pAccum, pItem->u4.zDatabase);
sqlite3_str_append(pAccum, ".", 1);
}
sqlite3_str_appendall(pAccum, pItem->zName);
}else if( pItem->zAlias ){
sqlite3_str_appendall(pAccum, pItem->zAlias);
- }else{
- Select *pSel = pItem->sq.pSelect;
- assert( pSel!=0 ); /* Because of tag-20240424-1 */
+ }else if( pItem->fg.isSubquery ){/* Because of tag-20240424-1 */
+ Select *pSel = pItem->u4.pSubq->pSelect;
+ assert( pSel!=0 );
if( pSel->selFlags & SF_NestedFrom ){
sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId);
}else if( pSel->selFlags & SF_MultiValue ){
}else{
sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId);
}
+ }else{
+ sqlite3_str_appendf(pAccum, "(unknown-data-source-%p)", pItem);
}
length = width = 0;
break;
pTab = pItem->pSTab;
assert( pTab!=0 && pTab->zName!=0 );
assert( pTab->nCol>0 || pParse->nErr );
- assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->sq.pSelect));
+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem));
if( pItem->fg.isNestedFrom ){
/* In this case, pItem is a subquery that has been formed from a
** parenthesized subset of the FROM clause terms. Example:
** This pItem -------------^
*/
int hit = 0;
- assert( pItem->sq.pSelect!=0 );
- pEList = pItem->sq.pSelect->pEList;
+ Select *pSel;
+ assert( pItem->fg.isSubquery );
+ assert( pItem->u4.pSubq!=0 );
+ pSel = pItem->u4.pSubq->pSelect;
+ assert( pSel!=0 );
+ pEList = pSel->pEList;
assert( pEList!=0 );
assert( pEList->nExpr==pTab->nCol );
for(j=0; j<pEList->nExpr; j++){
** moves the pOrderBy down to the sub-query. It will be moved back
** after the names have been resolved. */
if( p->selFlags & SF_Converted ){
- Select *pSub = p->pSrc->a[0].sq.pSelect;
+ Select *pSub;
+ assert( p->pSrc->a[0].fg.isSubquery );
+ assert( p->pSrc->a[0].u4.pSubq!=0 );
+ pSub = p->pSrc->a[0].u4.pSubq->pSelect;
+ assert( pSub!=0 );
assert( p->pSrc->nSrc==1 && p->pOrderBy );
assert( pSub->pPrior && pSub->pOrderBy==0 );
pSub->pOrderBy = p->pOrderBy;
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
assert( pItem->zName!=0
- || pItem->sq.pSelect!=0 ); /* Test of tag-20240424-1*/
- if( pItem->sq.pSelect && (pItem->sq.pSelect->selFlags & SF_Resolved)==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;
const char *zSavedContext = pParse->zAuthContext;
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
- sqlite3ResolveSelectNames(pParse, pItem->sq.pSelect, pOuterNC);
+ sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pOuterNC);
pParse->zAuthContext = zSavedContext;
if( pParse->nErr ) return WRC_Abort;
assert( db->mallocFailed==0 );
** These integers will be replaced by copies of the corresponding result
** set expressions by the call to resolveOrderGroupBy() below. */
if( p->selFlags & SF_Converted ){
- Select *pSub = p->pSrc->a[0].sq.pSelect;
+ Select *pSub;
+ assert( p->pSrc->a[0].fg.isSubquery );
+ pSub = p->pSrc->a[0].u4.pSubq->pSelect;
+ assert( pSub!=0 );
p->pOrderBy = pSub->pOrderBy;
pSub->pOrderBy = 0;
}
*/
void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){
assert( pItem!=0 );
- assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->sq.pSelect) );
+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) );
if( pItem->fg.isNestedFrom ){
ExprList *pResults;
- assert( pItem->sq.pSelect!=0 );
- pResults = pItem->sq.pSelect->pEList;
+ assert( pItem->fg.isSubquery );
+ assert( pItem->u4.pSubq!=0 );
+ assert( pItem->u4.pSubq->pSelect!=0 );
+ pResults = pItem->u4.pSubq->pSelect->pEList;
assert( pResults!=0 );
assert( iCol>=0 && iCol<pResults->nExpr );
pResults->a[iCol].fg.bUsed = 1;
for(j=0;j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++);
if( j<pTabList->nSrc ){
pTab = pTabList->a[j].pSTab;
- pS = pTabList->a[j].sq.pSelect;
+ if( pTabList->a[j].fg.isSubquery ){
+ pS = pTabList->a[j].u4.pSubq->pSelect;
+ }else{
+ pS = 0;
+ }
}else{
pNC = pNC->pNext;
}
pSrc = p->pSrc;
assert( pSrc!=0 );
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
- substSelect(pSubst, pItem->sq.pSelect, 1);
+ if( pItem->fg.isSubquery ){
+ substSelect(pSubst, pItem->u4.pSubq->pSelect, 1);
+ }
if( pItem->fg.isTabFunc ){
substExprList(pSubst, pItem->u1.pFuncArg);
}
aCsrMap[pItem->iCursor+1] = pParse->nTab++;
}
pItem->iCursor = aCsrMap[pItem->iCursor+1];
- for(p=pItem->sq.pSelect; p; p=p->pPrior){
- srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1);
+ if( pItem->fg.isSubquery ){
+ for(p=pItem->u4.pSubq->pSelect; p; p=p->pPrior){
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1);
+ }
}
}
}
assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
pSubitem = &pSrc->a[iFrom];
iParent = pSubitem->iCursor;
- pSub = pSubitem->sq.pSelect;
+ assert( pSubitem->fg.isSubquery );
+ pSub = pSubitem->u4.pSubq->pSelect;
assert( pSub!=0 );
#ifndef SQLITE_OMIT_WINDOWFUNC
pParse->zAuthContext = zSavedAuthContext;
/* Delete the transient structures associated with the subquery */
- pSub1 = pSubitem->sq.pSelect;
+
+ if( ALWAYS(pSubitem->fg.isSubquery) ){
+ pSub1 = sqlite3SubqueryDetach(db, pSubitem);
+ }else{
+ pSub1 = 0;
+ }
if( pSubitem->fg.fixedSchema==0 ){
sqlite3DbFree(db, pSubitem->u4.zDatabase);
pSubitem->u4.zDatabase = 0;
sqlite3DbFree(db, pSubitem->zAlias);
pSubitem->zName = 0;
pSubitem->zAlias = 0;
- pSubitem->sq.pSelect = 0;
assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 );
/* If the sub-query is a compound SELECT statement, then (by restrictions
TREETRACE(0x4,pParse,p,("compound-subquery flattener"
" creates %u as peer\n",pNew->selId));
}
- assert( pSubitem->sq.pSelect==0 );
+ assert( pSubitem->fg.isSubquery==0 );
}
sqlite3DbFree(db, aCsrMap);
if( db->mallocFailed ){
- pSubitem->sq.pSelect = pSub1;
+ assert( pSubitem->fg.fixedSchema==0 );
+ assert( pSubitem->fg.isSubquery==0 );
+ assert( pSubitem->u4.zDatabase==0 );
+ sqlite3SrcItemAttachSubquery(pParse, pSubitem, pSub1, 0);
return 1;
}
*/
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
- if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
assert( pItem->fg.isTabFunc==0 );
+ assert( pItem->fg.isSubquery
+ || pItem->fg.fixedSchema
+ || pItem->u4.zDatabase==0 );
+ if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
iNewParent = pSubSrc->a[i].iCursor;
}
assert( pItem->pSTab!=0 );
pTab = pItem->pSTab;
- assert( pItem->sq.pSelect!=0 );
- pSub = pItem->sq.pSelect;
+ assert( pItem->fg.isSubquery );
+ pSub = pItem->u4.pSubq->pSelect;
assert( pSub->pEList->nExpr==pTab->nCol );
for(pX=pSub; pX; pX=pX->pPrior){
if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){
if( p->pWhere
|| p->pEList->nExpr!=1
|| p->pSrc->nSrc!=1
- || p->pSrc->a[0].sq.pSelect
+ || p->pSrc->a[0].fg.isSubquery
|| pAggInfo->nFunc!=1
|| p->pHaving
){
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
- pFrom->sq.pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
+ sqlite3SrcItemAttachSubquery(pParse, pFrom, pCte->pSelect, 1);
if( db->mallocFailed ) return 2;
- assert( pFrom->sq.pSelect );
- pFrom->sq.pSelect->selFlags |= SF_CopyCte;
+ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq );
+ pSel = pFrom->u4.pSubq->pSelect;
+ assert( pSel!=0 );
+ pSel->selFlags |= SF_CopyCte;
if( pFrom->fg.isIndexedBy ){
sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy);
return 2;
pCteUse->nUse++;
/* Check if this is a recursive CTE. */
- pRecTerm = pSel = pFrom->sq.pSelect;
+ pRecTerm = pSel;
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
while( bMayRecursive && pRecTerm->op==pSel->op ){
int i;
** SQLITE_NOMEM.
*/
int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
- Select *pSel = pFrom->sq.pSelect;
+ Select *pSel;
Table *pTab;
+ assert( pFrom->fg.isSubquery );
+ assert( pFrom->u4.pSubq!=0 );
+ pSel = pFrom->u4.pSubq->pSelect;
assert( pSel );
pFrom->pSTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table));
if( pTab==0 ) return SQLITE_NOMEM;
assert( pFrom->fg.isRecursive==0 );
if( pFrom->zName==0 ){
#ifndef SQLITE_OMIT_SUBQUERY
- Select *pSel = pFrom->sq.pSelect;
+ Select *pSel;
+ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq!=0 );
+ pSel = pFrom->u4.pSubq->pSelect;
/* A sub-query in the FROM clause of a SELECT */
assert( pSel!=0 );
assert( pFrom->pSTab==0 );
i16 nCol;
u8 eCodeOrig = pWalker->eCode;
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
- assert( pFrom->sq.pSelect==0 );
+ assert( pFrom->fg.isSubquery==0 );
if( IsView(pTab) ){
if( (db->flags & SQLITE_EnableView)==0
&& pTab->pSchema!=db->aDb[1].pSchema
sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
pTab->zName);
}
- pFrom->sq.pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0);
+ sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( ALWAYS(IsVirtual(pTab))
nCol = pTab->nCol;
pTab->nCol = -1;
pWalker->eCode = 1; /* Turn on Select.selId renumbering */
- sqlite3WalkSelect(pWalker, pFrom->sq.pSelect);
+ if( pFrom->fg.isSubquery ){
+ sqlite3WalkSelect(pWalker, pFrom->u4.pSubq->pSelect);
+ }
pWalker->eCode = eCodeOrig;
pTab->nCol = nCol;
}
zTabName = pTab->zName;
}
if( db->mallocFailed ) break;
- assert( (int)pFrom->fg.isNestedFrom ==
- IsNestedFrom(pFrom->sq.pSelect) );
+ assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom) );
if( pFrom->fg.isNestedFrom ){
- assert( pFrom->sq.pSelect!=0 );
- pNestedFrom = pFrom->sq.pSelect->pEList;
+ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq );
+ assert( pFrom->u4.pSubq->pSelect!=0 );
+ pNestedFrom = pFrom->u4.pSubq->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid );
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pSTab;
assert( pTab!=0 );
- if( (pTab->tabFlags & TF_Ephemeral)!=0 ){
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 && pFrom->fg.isSubquery ){
/* A sub-query in the FROM clause of a SELECT */
- Select *pSel = pFrom->sq.pSelect;
- if( pSel ){
- sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
- }
+ Select *pSel = pFrom->u4.pSubq->pSelect;
+ sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
}
}
}
int iFirst, int iEnd /* Range of FROM-clause entries to search. */
){
SrcItem *pItem;
- assert( pThis->sq.pSelect!=0 );
- if( pThis->sq.pSelect->selFlags & SF_PushDown ) return 0;
+ Select *pSel;
+ assert( pThis->fg.isSubquery );
+ pSel = pThis->u4.pSubq->pSelect;
+ assert( pSel!=0 );
+ if( pSel->selFlags & SF_PushDown ) return 0;
while( iFirst<iEnd ){
Select *pS1;
pItem = &pTabList->a[iFirst++];
- if( pItem->sq.pSelect==0 ) continue;
+ if( !pItem->fg.isSubquery ) continue;
if( pItem->fg.viaCoroutine ) continue;
if( pItem->zName==0 ) continue;
assert( pItem->pSTab!=0 );
assert( pThis->pSTab!=0 );
if( pItem->pSTab->pSchema!=pThis->pSTab->pSchema ) continue;
if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue;
- pS1 = pItem->sq.pSelect;
- if( pItem->pSTab->pSchema==0 && pThis->sq.pSelect->selId!=pS1->selId ){
+ pS1 = pItem->u4.pSubq->pSelect;
+ if( pItem->pSTab->pSchema==0 && pSel->selId!=pS1->selId ){
/* The query flattener left two different CTE tables with identical
** names in the same FROM clause. */
continue;
}
- if( pItem->sq.pSelect->selFlags & SF_PushDown ){
+ if( pS1->selFlags & SF_PushDown ){
/* The view was modified by some other optimization such as
** pushDownWhereTerms() */
continue;
Expr *pExpr;
Expr *pCount;
sqlite3 *db;
+ SrcItem *pFrom;
if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */
if( p->pEList->nExpr!=1 ) return 0; /* Single result column */
if( p->pWhere ) return 0;
if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */
if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */
if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */
- pSub = p->pSrc->a[0].sq.pSelect;
- if( pSub==0 ) return 0; /* The FROM is a subquery */
+ pFrom = p->pSrc->a;
+ if( pFrom->fg.isSubquery==0 ) return 0; /* FROM is a subquery */
+ pSub = pFrom->u4.pSubq->pSelect;
if( pSub->pPrior==0 ) return 0; /* Must be a compound */
if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
do{
if( pSub->pLimit ) return 0; /* No LIMIT clause */
if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
assert( pSub->pHaving==0 ); /* Due to the previous */
- pSub = pSub->pPrior; /* Repeat over compound */
+ pSub = pSub->pPrior; /* Repeat over compound */
}while( pSub );
/* If we reach this point then it is OK to perform the transformation */
db = pParse->db;
pCount = pExpr;
pExpr = 0;
- pSub = p->pSrc->a[0].sq.pSelect;
- p->pSrc->a[0].sq.pSelect = 0;
+ pSub = sqlite3SubqueryDetach(db, pFrom);
sqlite3SrcListDelete(db, p->pSrc);
p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
while( pSub ){
if( p0->pSTab==p1->pSTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
return 1;
}
- if( p1->sq.pSelect
- && (p1->sq.pSelect->selFlags & SF_NestedFrom)!=0
- && sameSrcAlias(p0, p1->sq.pSelect->pSrc)
+ if( p1->fg.isSubquery
+ && (p1->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0
+ && sameSrcAlias(p0, p1->u4.pSubq->pSelect->pSrc)
){
return 1;
}
if( i==0 ) break;
i--;
pItem--;
- if( pItem->sq.pSelect!=0 ) return 0; /* (1c-i) */
+ if( pItem->fg.isSubquery ) return 0; /* (1c-i) */
}
return 1;
}
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
SrcItem *pItem = &pTabList->a[i];
- Select *pSub = pItem->sq.pSelect;
+ Select *pSub = pItem->fg.isSubquery ? pItem->u4.pSubq->pSelect : 0;
Table *pTab = pItem->pSTab;
/* The expander should have already created transient Table objects
SrcItem *pItem = &pTabList->a[i];
SrcItem *pPrior;
SelectDest dest;
+ Subquery *pSubq;
Select *pSub;
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
const char *zSavedAuthContext;
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/* Generate code for all sub-queries in the FROM clause
*/
- pSub = pItem->sq.pSelect;
- if( pSub==0 || pItem->sq.addrFillSub!=0 ) continue;
+ if( pItem->fg.isSubquery==0 ) continue;
+ pSubq = pItem->u4.pSubq;
+ assert( pSubq!=0 );
+ pSub = pSubq->pSelect;
+ if( pSubq->addrFillSub!=0 ) continue;
/* The code for a subquery should only be generated once. */
- assert( pItem->sq.addrFillSub==0 );
+ assert( pSubq->addrFillSub==0 );
/* Increment Parse.nHeight by the height of the largest expression
** tree referred to by this, the parent select. The child select
sqlite3TreeViewSelect(0, p, 0);
}
#endif
- assert( pItem->sq.pSelect
- && (pItem->sq.pSelect->selFlags & SF_PushDown)!=0 );
+ assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
}else{
TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n"));
}
*/
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
- pItem->sq.regReturn = ++pParse->nMem;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->sq.regReturn, 0, addrTop);
+ pSubq->regReturn = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop);
VdbeComment((v, "%!S", pItem));
- pItem->sq.addrFillSub = addrTop;
- sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->sq.regReturn);
+ 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;
pItem->fg.viaCoroutine = 1;
- pItem->sq.regResult = dest.iSdst;
- sqlite3VdbeEndCoroutine(v, pItem->sq.regReturn);
+ pSubq->regResult = dest.iSdst;
+ sqlite3VdbeEndCoroutine(v, pSubq->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){
}else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){
/* This view has already been materialized by a prior entry in
** this same FROM clause. Reuse it. */
- if( pPrior->sq.addrFillSub ){
- sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->sq.regReturn,
- pPrior->sq.addrFillSub);
+ Subquery *pPriorSubq;
+ assert( pPrior->fg.isSubquery );
+ pPriorSubq = pPrior->u4.pSubq;
+ assert( pPriorSubq!=0 );
+ if( pPriorSubq->addrFillSub ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pPriorSubq->regReturn,
+ pPriorSubq->addrFillSub);
}
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
- pSub->nSelectRow = pPrior->sq.pSelect->nSelectRow;
+ pSub->nSelectRow = pPriorSubq->pSelect->nSelectRow;
}else{
/* Materialize the view. If the view is not correlated, generate a
** subroutine to do the materialization so that subsequent uses of
int addrExplain;
#endif
- pItem->sq.regReturn = ++pParse->nMem;
+ pSubq->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp0(v, OP_Goto);
- pItem->sq.addrFillSub = topAddr+1;
+ pSubq->addrFillSub = topAddr+1;
pItem->fg.isMaterialized = 1;
if( pItem->fg.isCorrelated==0 ){
/* If the subquery is not correlated and if we are not inside of
sqlite3Select(pParse, pSub, &dest);
pItem->pSTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
- sqlite3VdbeAddOp2(v, OP_Return, pItem->sq.regReturn, topAddr+1);
+ 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 ){
CteUse *pCteUse = pItem->u2.pCteUse;
- pCteUse->addrM9e = pItem->sq.addrFillSub;
- pCteUse->regRtn = pItem->sq.regReturn;
+ pCteUse->addrM9e = pSubq->addrFillSub;
+ pCteUse->regRtn = pSubq->regReturn;
pCteUse->iCur = pItem->iCursor;
pCteUse->nRowEst = pSub->nSelectRow;
}
typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;
typedef struct SelectDest SelectDest;
+typedef struct Subquery Subquery;
typedef struct SrcItem SrcItem;
typedef struct SrcList SrcList;
typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
#define EU4_IDX 1 /* Uses IdList.a.u4.idx */
#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */
+/*
+** Details of the implementation of a subquery.
+*/
+struct Subquery {
+ Select *pSelect; /* A SELECT statement used in place of a table name */
+ int addrFillSub; /* Address of subroutine to initialize a subquery */
+ int regReturn; /* Register holding return address of addrFillSub */
+ 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.
** 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.
**
+** Intenstive use of "union" helps keep the size of the object small. This
+** has been shown to boost performance due to less time spend initializing
+** fields to zero when a new instance of this object is allocated. The unions
+** also help SrcItem, and hence SrcList and Select, use less memory.
+**
** Union member validity:
**
-** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
-** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
+** u1.zIndexedBy fg.isIndexedBy
+** u1.pFuncArg fg.isTabFunc
** u1.nRow !fg.isTabFunc && !fg.isIndexedBy
**
** u2.pIBIndex fg.isIndexedBy && !fg.isCte
** u3.pOn fg.isUsing==0
** u3.pUsing fg.isUsing==1
**
-** u4.zDatabase fg.fixedSchema==0
+** u4.zDatabase fg.fixedSchema==0 && !fg.isSubquery
** u4.pSchema fg.fixedSchema==1
+** u4.pSubq fg.isSubquery
*/
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 SrcItemSubquery {
- Select *pSelect; /* A SELECT statement used in place of a table name */
- int addrFillSub; /* Address of subroutine to initialize a subquery */
- int regReturn; /* Register holding return address of addrFillSub */
- int regResult; /* Registers holding results of a co-routine */
- } sq;
struct {
u8 jointype; /* Type of join between this table and the previous */
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 isMaterialized:1; /* This is a materialized view */
union {
Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
+ Subquery *pSubq; /* Description of a subquery */
} u4;
};
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
#define SF_Correlated 0x20000000 /* True if references the outer context */
-/* True if S exists and has SF_NestedFrom */
-#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
+/* True if SrcList item X is a subquery that has SF_NestedFrom */
+#define IsNestedFrom(X) \
+ ((X)->fg.isSubquery && \
+ ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0)
/*
** The results of a SELECT can be distributed in several ways, as defined
SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int);
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2);
SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*);
+void sqlite3SubqueryDelete(sqlite3*,Subquery*);
+Select *sqlite3SubqueryDetach(sqlite3*,SrcItem*);
+int sqlite3SrcItemAttachSubquery(Parse*, SrcItem*, Select*, int);
SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, OnOrUsing*);
void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
- if( pItem->sq.pSelect ) n++;
+ if( pItem->fg.isSubquery ) n++;
if( pItem->fg.isTabFunc ) n++;
if( pItem->fg.isUsing ) n++;
if( pItem->fg.isUsing ){
sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
}
- if( pItem->sq.pSelect ){
+ if( pItem->fg.isSubquery ){
if( pItem->pSTab ){
Table *pTab = pItem->pSTab;
sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1);
}
- assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->sq.pSelect) );
- sqlite3TreeViewSelect(pView, pItem->sq.pSelect, (--n)>0);
+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) );
+ sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, (--n)>0);
}
if( pItem->fg.isTabFunc ){
sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
pSrc = p->pSrc;
if( ALWAYS(pSrc) ){
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
- if( pItem->sq.pSelect && sqlite3WalkSelect(pWalker, pItem->sq.pSelect) ){
+ if( pItem->fg.isSubquery
+ && sqlite3WalkSelect(pWalker, pItem->u4.pSubq->pSelect)
+ ){
return WRC_Abort;
}
if( pItem->fg.isTabFunc
/* Fill the automatic index with content */
assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] );
if( pSrc->fg.viaCoroutine ){
- int regYield = pSrc->sq.regReturn;
+ int regYield;
+ Subquery *pSubq;
+ assert( pSrc->fg.isSubquery );
+ pSubq = pSrc->u4.pSubq;
+ assert( pSubq!=0 );
+ regYield = pSubq->regReturn;
addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0);
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->sq.addrFillSub);
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub);
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
VdbeCoverage(v);
VdbeComment((v, "next row of %s", pSrc->pSTab->zName));
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 );
translateColumnToCopy(pParse, addrTop, pLevel->iTabCur,
- pSrc->sq.regResult, pLevel->iIdxCur);
+ pSrc->u4.pSubq->regResult, pLevel->iIdxCur);
sqlite3VdbeGoto(v, addrTop);
pSrc->fg.viaCoroutine = 0;
}else{
#endif
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
- if( pSrc->sq.pSelect ){
+ if( pSrc->fg.isSubquery ){
if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE;
- pNew->u.btree.pOrderBy = pSrc->sq.pSelect->pOrderBy;
+ pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy;
}
rc = whereLoopInsert(pBuilder, pNew);
pNew->nOut = rSize;
SrcItem *pItem = &pWInfo->pTabList->a[ii];
if( !pItem->fg.isCte
|| pItem->u2.pCteUse->eM10d!=M10d_Yes
- || NEVER(pItem->sq.pSelect==0)
- || pItem->sq.pSelect->pOrderBy==0
+ || NEVER(pItem->fg.isSubquery==0)
+ || pItem->u4.pSubq->pSelect->pOrderBy==0
){
pWInfo->revMask |= MASKBIT(ii);
}
wsFlags = pLevel->pWLoop->wsFlags;
pSrc = &pTabList->a[pLevel->iFrom];
if( pSrc->fg.isMaterialized ){
+ Subquery *pSubq;
+ assert( pSrc->fg.isSubquery );
+ pSubq = pSrc->u4.pSubq;
if( pSrc->fg.isCorrelated ){
- sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->sq.regReturn,pSrc->sq.addrFillSub);
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub);
}else{
int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->sq.regReturn,pSrc->sq.addrFillSub);
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub);
sqlite3VdbeJumpHere(v, iOnce);
}
}
assert( pLevel->iTabCur==pSrc->iCursor );
if( pSrc->fg.viaCoroutine ){
int m, n;
- n = pSrc->sq.regResult;
+ assert( pSrc->fg.isSubquery );
+ n = pSrc->u4.pSubq->regResult;
assert( pSrc->pSTab!=0 );
m = pSrc->pSTab->nCol;
sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1);
*/
if( pTabItem->fg.viaCoroutine ){
testcase( pParse->db->mallocFailed );
- assert( pTabItem->sq.regResult>=0 );
+ assert( pTabItem->fg.isSubquery );
+ assert( pTabItem->u4.pSubq->regResult>=0 );
translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur,
- pTabItem->sq.regResult, 0);
+ pTabItem->u4.pSubq->regResult, 0);
continue;
}
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
}
}else{
- int addr = pSrclist->a[pLvl->iFrom].sq.addrFillSub;
+ int addr;
+ assert( pSrclist->a[pLvl->iFrom].fg.isSubquery );
+ addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub;
VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1);
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine );
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr );
/* Special case of a FROM clause subquery implemented as a co-routine */
if( pTabItem->fg.viaCoroutine ){
- int regYield = pTabItem->sq.regReturn;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield,0,pTabItem->sq.addrFillSub);
+ int regYield;
+ Subquery *pSubq;
+ assert( pTabItem->fg.isSubquery && pTabItem->u4.pSubq!=0 );
+ pSubq = pTabItem->u4.pSubq;
+ regYield = pSubq->regReturn;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub);
pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
VdbeCoverage(v);
VdbeComment((v, "next row of %s", pTabItem->pSTab->zName));
pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom];
mAll |= pWInfo->a[k].pWLoop->maskSelf;
if( pRight->fg.viaCoroutine ){
+ Subquery *pSubq;
+ assert( pRight->fg.isSubquery && pRight->u4.pSubq!=0 );
+ pSubq = pRight->u4.pSubq;
+ assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 );
sqlite3VdbeAddOp3(
- v, OP_Null, 0, pRight->sq.regResult,
- pRight->sq.regResult + pRight->sq.pSelect->pEList->nExpr-1
+ v, OP_Null, 0, pSubq->regResult,
+ pSubq->regResult + pSubq->pSelect->pEList->nExpr-1
);
}
sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur);
if( ALWAYS(pSrc!=0) ){
int i;
for(i=0; i<pSrc->nSrc; i++){
- mask |= exprSelectUsage(pMaskSet, pSrc->a[i].sq.pSelect);
+ if( pSrc->a[i].fg.isSubquery ){
+ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].u4.pSubq->pSelect);
+ }
if( pSrc->a[i].fg.isUsing==0 ){
mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn);
}
assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside
** of sqlite3DbMallocRawNN() called from
** sqlite3SrcListAppend() */
- if( p->pSrc ){
+ if( p->pSrc==0 ){
+ sqlite3SelectDelete(db, pSub);
+ }else if( sqlite3SrcItemAttachSubquery(pParse, &p->pSrc->a[0], pSub, 0) ){
Table *pTab2;
- p->pSrc->a[0].sq.pSelect = pSub;
p->pSrc->a[0].fg.isCorrelated = 1;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
w.xSelectCallback2 = sqlite3WalkerDepthDecrease;
sqlite3WalkSelect(&w, pSub);
}
- }else{
- sqlite3SelectDelete(db, pSub);
}
if( db->mallocFailed ) rc = SQLITE_NOMEM;
** and initialize registers and cursors used by sqlite3WindowCodeStep().
*/
void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){
- int nEphExpr = pSelect->pSrc->a[0].sq.pSelect->pEList->nExpr;
- Window *pMWin = pSelect->pWin;
Window *pWin;
- Vdbe *v = sqlite3GetVdbe(pParse);
+ int nEphExpr;
+ Window *pMWin;
+ Vdbe *v;
+
+ assert( pSelect->pSrc->a[0].fg.isSubquery );
+ nEphExpr = pSelect->pSrc->a[0].u4.pSubq->pSelect->pEList->nExpr;
+ pMWin = pSelect->pWin;
+ v = sqlite3GetVdbe(pParse);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, nEphExpr);
sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr);