mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Refactor the SrcItem object to move fields associated with subqueries out
into a separate object named Subquery. This reduces the size of the SrcItem object by about 1/3rd and provides improved performance. FossilOrigin-Name: 484bcd75bc95491d8540c791c1c4d40d996cb465839564662e14f98739699bf1
This commit is contained in:
15
src/alter.c
15
src/alter.c
@ -1366,8 +1366,9 @@ static int renameResolveTrigger(Parse *pParse){
|
||||
int i;
|
||||
for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
|
||||
SrcItem *p = &pStep->pFrom->a[i];
|
||||
if( p->pSelect ){
|
||||
sqlite3SelectPrep(pParse, p->pSelect, 0);
|
||||
if( p->fg.isSubquery ){
|
||||
assert( p->u4.pSubq!=0 );
|
||||
sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1435,8 +1436,12 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
|
||||
}
|
||||
if( pStep->pFrom ){
|
||||
int i;
|
||||
for(i=0; i<pStep->pFrom->nSrc; i++){
|
||||
sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1683,7 +1688,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
|
||||
}
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
SrcItem *pItem = &pSrc->a[i];
|
||||
if( pItem->pTab==p->pTab ){
|
||||
if( pItem->pSTab==p->pTab ){
|
||||
renameTokenFind(pWalker->pParse, p, pItem->zName);
|
||||
}
|
||||
}
|
||||
|
15
src/attach.c
15
src/attach.c
@ -479,20 +479,21 @@ static int fixSelectCb(Walker *p, Select *pSelect){
|
||||
|
||||
if( NEVER(pList==0) ) return WRC_Continue;
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pFix->bTemp==0 ){
|
||||
if( pItem->zDatabase ){
|
||||
if( iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
|
||||
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,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
pFix->zType, pFix->pName, pItem->u4.zDatabase);
|
||||
return WRC_Abort;
|
||||
}
|
||||
sqlite3DbFree(db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
sqlite3DbFree(db, pItem->u4.zDatabase);
|
||||
pItem->fg.notCte = 1;
|
||||
pItem->fg.hadSchema = 1;
|
||||
}
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
pItem->u4.pSchema = pFix->pSchema;
|
||||
pItem->fg.fromDDL = 1;
|
||||
pItem->fg.fixedSchema = 1;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( pList->a[i].fg.isUsing==0
|
||||
|
@ -165,7 +165,7 @@ void sqlite3AuthRead(
|
||||
assert( pTabList );
|
||||
for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
|
||||
pTab = pTabList->a[iSrc].pTab;
|
||||
pTab = pTabList->a[iSrc].pSTab;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
130
src/build.c
130
src/build.c
@ -497,12 +497,12 @@ Table *sqlite3LocateTableItem(
|
||||
SrcItem *p
|
||||
){
|
||||
const char *zDb;
|
||||
assert( p->pSchema==0 || p->zDatabase==0 );
|
||||
if( p->pSchema ){
|
||||
int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
|
||||
if( p->fg.fixedSchema ){
|
||||
int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema);
|
||||
zDb = pParse->db->aDb[iDb].zDbSName;
|
||||
}else{
|
||||
zDb = p->zDatabase;
|
||||
assert( !p->fg.isSubquery );
|
||||
zDb = p->u4.zDatabase;
|
||||
}
|
||||
return sqlite3LocateTable(pParse, flags, p->zName, zDb);
|
||||
}
|
||||
@ -3487,6 +3487,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
assert( pParse->nErr==0 );
|
||||
assert( pName->nSrc==1 );
|
||||
assert( pName->a[0].fg.fixedSchema==0 );
|
||||
assert( pName->a[0].fg.isSubquery==0 );
|
||||
if( sqlite3ReadSchema(pParse) ) goto exit_drop_table;
|
||||
if( noErr ) db->suppressErr++;
|
||||
assert( isView==0 || isView==LOCATE_VIEW );
|
||||
@ -3495,7 +3497,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
|
||||
if( pTab==0 ){
|
||||
if( noErr ){
|
||||
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
|
||||
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase);
|
||||
sqlite3ForceNotReadOnly(pParse);
|
||||
}
|
||||
goto exit_drop_table;
|
||||
@ -4586,15 +4588,17 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
}
|
||||
assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */
|
||||
assert( pName->nSrc==1 );
|
||||
assert( pName->a[0].fg.fixedSchema==0 );
|
||||
assert( pName->a[0].fg.isSubquery==0 );
|
||||
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
|
||||
goto exit_drop_index;
|
||||
}
|
||||
pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
|
||||
pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].u4.zDatabase);
|
||||
if( pIndex==0 ){
|
||||
if( !ifExists ){
|
||||
sqlite3ErrorMsg(pParse, "no such index: %S", pName->a);
|
||||
}else{
|
||||
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
|
||||
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase);
|
||||
sqlite3ForceNotReadOnly(pParse);
|
||||
}
|
||||
pParse->checkSchema = 1;
|
||||
@ -4891,12 +4895,14 @@ SrcList *sqlite3SrcListAppend(
|
||||
if( pDatabase && pDatabase->z==0 ){
|
||||
pDatabase = 0;
|
||||
}
|
||||
assert( pItem->fg.fixedSchema==0 );
|
||||
assert( pItem->fg.isSubquery==0 );
|
||||
if( pDatabase ){
|
||||
pItem->zName = sqlite3NameFromToken(db, pDatabase);
|
||||
pItem->zDatabase = sqlite3NameFromToken(db, pTable);
|
||||
pItem->u4.zDatabase = sqlite3NameFromToken(db, pTable);
|
||||
}else{
|
||||
pItem->zName = sqlite3NameFromToken(db, pTable);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->u4.zDatabase = 0;
|
||||
}
|
||||
return pList;
|
||||
}
|
||||
@ -4912,13 +4918,40 @@ void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pItem->iCursor>=0 ) continue;
|
||||
pItem->iCursor = pParse->nTab++;
|
||||
if( pItem->pSelect ){
|
||||
sqlite3SrcListAssignCursors(pParse, pItem->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.
|
||||
*/
|
||||
@ -4928,13 +4961,26 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
|
||||
assert( db!=0 );
|
||||
if( pList==0 ) return;
|
||||
for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
|
||||
if( pItem->zDatabase ) sqlite3DbNNFreeNN(db, pItem->zDatabase);
|
||||
|
||||
/* Check invariants on SrcItem */
|
||||
assert( !pItem->fg.isIndexedBy || !pItem->fg.isTabFunc );
|
||||
assert( !pItem->fg.isCte || !pItem->fg.isIndexedBy );
|
||||
assert( !pItem->fg.hadSchema || !pItem->fg.isSubquery );
|
||||
assert( !pItem->fg.hadSchema || pItem->fg.fixedSchema );
|
||||
assert( !pItem->fg.fixedSchema || !pItem->fg.isSubquery );
|
||||
assert( !pItem->fg.isSubquery || (pItem->u4.pSubq!=0 &&
|
||||
pItem->u4.pSubq->pSelect!=0) );
|
||||
|
||||
if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName);
|
||||
if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias);
|
||||
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->pTab);
|
||||
if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect);
|
||||
sqlite3DeleteTable(db, pItem->pSTab);
|
||||
if( pItem->fg.isUsing ){
|
||||
sqlite3IdListDelete(db, pItem->u3.pUsing);
|
||||
}else if( pItem->u3.pOn ){
|
||||
@ -4944,6 +4990,54 @@ void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
|
||||
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;
|
||||
assert( pSelect!=0 );
|
||||
assert( pItem->fg.isSubquery==0 );
|
||||
if( pItem->fg.fixedSchema ){
|
||||
pItem->u4.pSchema = 0;
|
||||
pItem->fg.fixedSchema = 0;
|
||||
}else if( pItem->u4.zDatabase!=0 ){
|
||||
sqlite3DbFree(pParse->db, pItem->u4.zDatabase);
|
||||
pItem->u4.zDatabase = 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
|
||||
@ -4993,10 +5087,12 @@ SrcList *sqlite3SrcListAppendFromTerm(
|
||||
if( pAlias->n ){
|
||||
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
|
||||
}
|
||||
assert( pSubquery==0 || pDatabase==0 );
|
||||
if( pSubquery ){
|
||||
pItem->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 );
|
||||
|
17
src/delete.c
17
src/delete.c
@ -24,8 +24,8 @@
|
||||
**
|
||||
** The following fields are initialized appropriate in pSrc:
|
||||
**
|
||||
** pSrc->a[0].pTab Pointer to the Table object
|
||||
** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one
|
||||
** pSrc->a[0].spTab Pointer to the Table object
|
||||
** pSrc->a[0].u2.pIBIndex Pointer to the INDEXED BY index, if there is one
|
||||
**
|
||||
*/
|
||||
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
@ -33,8 +33,8 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
Table *pTab;
|
||||
assert( pItem && pSrc->nSrc>=1 );
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
|
||||
if( pItem->pTab ) sqlite3DeleteTable(pParse->db, pItem->pTab);
|
||||
pItem->pTab = pTab;
|
||||
if( pItem->pSTab ) sqlite3DeleteTable(pParse->db, pItem->pSTab);
|
||||
pItem->pSTab = pTab;
|
||||
pItem->fg.notCte = 1;
|
||||
if( pTab ){
|
||||
pTab->nTabRef++;
|
||||
@ -156,7 +156,8 @@ void sqlite3MaterializeView(
|
||||
if( pFrom ){
|
||||
assert( pFrom->nSrc==1 );
|
||||
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
|
||||
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
assert( pFrom->a[0].fg.fixedSchema==0 && pFrom->a[0].fg.isSubquery==0 );
|
||||
pFrom->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
assert( pFrom->a[0].fg.isUsing==0 );
|
||||
assert( pFrom->a[0].u3.pOn==0 );
|
||||
}
|
||||
@ -218,7 +219,7 @@ Expr *sqlite3LimitWhere(
|
||||
** );
|
||||
*/
|
||||
|
||||
pTab = pSrc->a[0].pTab;
|
||||
pTab = pSrc->a[0].pSTab;
|
||||
if( HasRowid(pTab) ){
|
||||
pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
pEList = sqlite3ExprListAppend(
|
||||
@ -251,9 +252,9 @@ Expr *sqlite3LimitWhere(
|
||||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSrc->a[0].pTab = 0;
|
||||
pSrc->a[0].pSTab = 0;
|
||||
pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);
|
||||
pSrc->a[0].pTab = pTab;
|
||||
pSrc->a[0].pSTab = pTab;
|
||||
if( pSrc->a[0].fg.isIndexedBy ){
|
||||
assert( pSrc->a[0].fg.isCte==0 );
|
||||
pSrc->a[0].u2.pIBIndex = 0;
|
||||
|
37
src/expr.c
37
src/expr.c
@ -1877,15 +1877,30 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
|
||||
SrcItem *pNewItem = &pNew->a[i];
|
||||
const SrcItem *pOldItem = &p->a[i];
|
||||
Table *pTab;
|
||||
pNewItem->pSchema = pOldItem->pSchema;
|
||||
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
|
||||
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->addrFillSub = pOldItem->addrFillSub;
|
||||
pNewItem->regReturn = pOldItem->regReturn;
|
||||
pNewItem->regResult = pOldItem->regResult;
|
||||
if( pNewItem->fg.isIndexedBy ){
|
||||
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
|
||||
}else if( pNewItem->fg.isTabFunc ){
|
||||
@ -1898,11 +1913,10 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
|
||||
if( pNewItem->fg.isCte ){
|
||||
pNewItem->u2.pCteUse->nUse++;
|
||||
}
|
||||
pTab = pNewItem->pTab = pOldItem->pTab;
|
||||
pTab = pNewItem->pSTab = pOldItem->pSTab;
|
||||
if( pTab ){
|
||||
pTab->nTabRef++;
|
||||
}
|
||||
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
|
||||
if( pOldItem->fg.isUsing ){
|
||||
assert( pNewItem->fg.isUsing );
|
||||
pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing);
|
||||
@ -1976,7 +1990,6 @@ Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int flags){
|
||||
pp = &pNew->pPrior;
|
||||
pNext = pNew;
|
||||
}
|
||||
|
||||
return pRet;
|
||||
}
|
||||
#else
|
||||
@ -2996,8 +3009,8 @@ static Select *isCandidateForInOpt(const Expr *pX){
|
||||
pSrc = p->pSrc;
|
||||
assert( pSrc!=0 );
|
||||
if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */
|
||||
if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */
|
||||
pTab = pSrc->a[0].pTab;
|
||||
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 */
|
||||
if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */
|
||||
@ -3180,7 +3193,7 @@ int sqlite3FindInIndex(
|
||||
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
pTab = p->pSrc->a[0].pTab;
|
||||
pTab = p->pSrc->a[0].pSTab;
|
||||
|
||||
/* Code an OP_Transaction and OP_TableLock for <table>. */
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
@ -1043,9 +1043,9 @@ void sqlite3FkCheck(
|
||||
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
|
||||
if( pSrc ){
|
||||
SrcItem *pItem = pSrc->a;
|
||||
pItem->pTab = pFKey->pFrom;
|
||||
pItem->pSTab = pFKey->pFrom;
|
||||
pItem->zName = pFKey->pFrom->zName;
|
||||
pItem->pTab->nTabRef++;
|
||||
pItem->pSTab->nTabRef++;
|
||||
pItem->iCursor = pParse->nTab++;
|
||||
|
||||
if( regNew!=0 ){
|
||||
@ -1337,7 +1337,8 @@ static Trigger *fkActionTrigger(
|
||||
if( pSrc ){
|
||||
assert( pSrc->nSrc==1 );
|
||||
pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom);
|
||||
pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
assert( pSrc->a[0].fg.fixedSchema==0 && pSrc->a[0].fg.isSubquery==0 );
|
||||
pSrc->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
|
||||
}
|
||||
pSelect = sqlite3SelectNew(pParse,
|
||||
sqlite3ExprListAppend(pParse, 0, pRaise),
|
||||
|
73
src/insert.c
73
src/insert.c
@ -585,8 +585,11 @@ void sqlite3AutoincrementEnd(Parse *pParse){
|
||||
void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
|
||||
if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){
|
||||
SrcItem *pItem = &pVal->pSrc->a[0];
|
||||
sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn);
|
||||
sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1);
|
||||
assert( (pItem->fg.isSubquery && pItem->u4.pSubq!=0) || pParse->nErr );
|
||||
if( pItem->fg.isSubquery ){
|
||||
sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->u4.pSubq->regReturn);
|
||||
sqlite3VdbeJumpHere(pParse->pVdbe, pItem->u4.pSubq->addrFillSub - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,6 +717,7 @@ Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
|
||||
|
||||
if( pRet ){
|
||||
SelectDest dest;
|
||||
Subquery *pSubq;
|
||||
pRet->pSrc->nSrc = 1;
|
||||
pRet->pPrior = pLeft->pPrior;
|
||||
pRet->op = pLeft->op;
|
||||
@ -723,28 +727,32 @@ Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
|
||||
assert( pLeft->pNext==0 );
|
||||
assert( pRet->pNext==0 );
|
||||
p = &pRet->pSrc->a[0];
|
||||
p->pSelect = pLeft;
|
||||
p->fg.viaCoroutine = 1;
|
||||
p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
p->regReturn = ++pParse->nMem;
|
||||
p->iCursor = -1;
|
||||
assert( !p->fg.isIndexedBy && !p->fg.isTabFunc );
|
||||
p->u1.nRow = 2;
|
||||
sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub);
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
|
||||
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;
|
||||
/* 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->regResult = dest.iSdst;
|
||||
assert( pParse->nErr || dest.iSdst>0 );
|
||||
pLeft->selFlags |= SF_MultiValue;
|
||||
sqlite3Select(pParse, pLeft, &dest);
|
||||
pSubq->regResult = dest.iSdst;
|
||||
assert( pParse->nErr || dest.iSdst>0 );
|
||||
}
|
||||
pLeft = pRet;
|
||||
}
|
||||
}else{
|
||||
@ -754,12 +762,18 @@ Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
|
||||
}
|
||||
|
||||
if( pParse->nErr==0 ){
|
||||
Subquery *pSubq;
|
||||
assert( p!=0 );
|
||||
if( p->pSelect->pEList->nExpr!=pRow->nExpr ){
|
||||
sqlite3SelectWrongNumTermsError(pParse, p->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->regResult, 0, 0);
|
||||
sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn);
|
||||
sqlite3ExprCodeExprList(pParse, pRow, pSubq->regResult, 0, 0);
|
||||
sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, pSubq->regReturn);
|
||||
}
|
||||
}
|
||||
sqlite3ExprListDelete(pParse->db, pRow);
|
||||
@ -1110,9 +1124,14 @@ void sqlite3Insert(
|
||||
&& pSelect->pPrior==0
|
||||
){
|
||||
SrcItem *pItem = &pSelect->pSrc->a[0];
|
||||
dest.iSDParm = pItem->regReturn;
|
||||
regFromSelect = pItem->regResult;
|
||||
nColumn = pItem->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;
|
||||
@ -3032,7 +3051,7 @@ static int xferOptimization(
|
||||
if( pSelect->pSrc->nSrc!=1 ){
|
||||
return 0; /* FROM clause must have exactly one term */
|
||||
}
|
||||
if( pSelect->pSrc->a[0].pSelect ){
|
||||
if( pSelect->pSrc->a[0].fg.isSubquery ){
|
||||
return 0; /* FROM clause cannot contain a subquery */
|
||||
}
|
||||
if( pSelect->pWhere ){
|
||||
|
21
src/parse.y
21
src/parse.y
@ -740,11 +740,21 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N
|
||||
if( A ){
|
||||
SrcItem *pNew = &A->a[A->nSrc-1];
|
||||
SrcItem *pOld = F->a;
|
||||
assert( pOld->fg.fixedSchema==0 );
|
||||
pNew->zName = pOld->zName;
|
||||
pNew->zDatabase = pOld->zDatabase;
|
||||
pNew->pSelect = pOld->pSelect;
|
||||
if( pNew->pSelect && (pNew->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;
|
||||
assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 );
|
||||
if( (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;
|
||||
@ -752,8 +762,7 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N
|
||||
pOld->fg.isTabFunc = 0;
|
||||
pNew->fg.isTabFunc = 1;
|
||||
}
|
||||
pOld->zName = pOld->zDatabase = 0;
|
||||
pOld->pSelect = 0;
|
||||
pOld->zName = 0;
|
||||
}
|
||||
sqlite3SrcListDelete(pParse->db, F);
|
||||
}else{
|
||||
|
13
src/printf.c
13
src/printf.c
@ -848,16 +848,19 @@ void sqlite3_str_vappendf(
|
||||
if( pItem->zAlias && !flag_altform2 ){
|
||||
sqlite3_str_appendall(pAccum, pItem->zAlias);
|
||||
}else if( pItem->zName ){
|
||||
if( pItem->zDatabase ){
|
||||
sqlite3_str_appendall(pAccum, pItem->zDatabase);
|
||||
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->pSelect;
|
||||
assert( pSel!=0 ); /* Because of tag-20240424-1 */
|
||||
}else if( ALWAYS(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 ){
|
||||
|
@ -215,7 +215,7 @@ static void extendFJMatch(
|
||||
if( pNew ){
|
||||
pNew->iTable = pMatch->iCursor;
|
||||
pNew->iColumn = iColumn;
|
||||
pNew->y.pTab = pMatch->pTab;
|
||||
pNew->y.pTab = pMatch->pSTab;
|
||||
assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 );
|
||||
ExprSetProperty(pNew, EP_CanBeNull);
|
||||
*ppList = sqlite3ExprListAppend(pParse, *ppList, pNew);
|
||||
@ -346,10 +346,10 @@ static int lookupName(
|
||||
if( pSrcList ){
|
||||
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
|
||||
u8 hCol;
|
||||
pTab = pItem->pTab;
|
||||
pTab = pItem->pSTab;
|
||||
assert( pTab!=0 && pTab->zName!=0 );
|
||||
assert( pTab->nCol>0 || pParse->nErr );
|
||||
assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->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:
|
||||
@ -358,8 +358,12 @@ static int lookupName(
|
||||
** This pItem -------------^
|
||||
*/
|
||||
int hit = 0;
|
||||
assert( pItem->pSelect!=0 );
|
||||
pEList = pItem->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++){
|
||||
@ -483,8 +487,8 @@ static int lookupName(
|
||||
if( cntTab==0
|
||||
|| (cntTab==1
|
||||
&& ALWAYS(pMatch!=0)
|
||||
&& ALWAYS(pMatch->pTab!=0)
|
||||
&& (pMatch->pTab->tabFlags & TF_Ephemeral)!=0
|
||||
&& ALWAYS(pMatch->pSTab!=0)
|
||||
&& (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0
|
||||
&& (pTab->tabFlags & TF_Ephemeral)==0)
|
||||
){
|
||||
cntTab = 1;
|
||||
@ -505,7 +509,7 @@ static int lookupName(
|
||||
if( pMatch ){
|
||||
pExpr->iTable = pMatch->iCursor;
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
pExpr->y.pTab = pMatch->pTab;
|
||||
pExpr->y.pTab = pMatch->pSTab;
|
||||
if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){
|
||||
ExprSetProperty(pExpr, EP_CanBeNull);
|
||||
}
|
||||
@ -547,7 +551,7 @@ static int lookupName(
|
||||
if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){
|
||||
Upsert *pUpsert = pNC->uNC.pUpsert;
|
||||
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
||||
pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
||||
pTab = pUpsert->pUpsertSrc->a[0].pSTab;
|
||||
pExpr->iTable = EXCLUDED_TABLE_NUMBER;
|
||||
}
|
||||
}
|
||||
@ -630,11 +634,11 @@ static int lookupName(
|
||||
&& pMatch
|
||||
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
|
||||
&& sqlite3IsRowid(zCol)
|
||||
&& ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
|
||||
&& ALWAYS(VisibleRowid(pMatch->pSTab) || pMatch->fg.isNestedFrom)
|
||||
){
|
||||
cnt = cntTab;
|
||||
#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2
|
||||
if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){
|
||||
if( pMatch->pSTab!=0 && IsView(pMatch->pSTab) ){
|
||||
eNewExprOp = TK_NULL;
|
||||
}
|
||||
#endif
|
||||
@ -871,7 +875,7 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
|
||||
SrcItem *pItem = &pSrc->a[iSrc];
|
||||
Table *pTab;
|
||||
assert( ExprUseYTab(p) );
|
||||
pTab = p->y.pTab = pItem->pTab;
|
||||
pTab = p->y.pTab = pItem->pSTab;
|
||||
p->iTable = pItem->iCursor;
|
||||
if( p->y.pTab->iPKey==iCol ){
|
||||
p->iColumn = -1;
|
||||
@ -990,7 +994,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
pItem = pSrcList->a;
|
||||
pExpr->op = TK_COLUMN;
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
pExpr->y.pTab = pItem->pTab;
|
||||
pExpr->y.pTab = pItem->pSTab;
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->iColumn--;
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
@ -1880,7 +1884,11 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
** 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].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;
|
||||
@ -1892,13 +1900,16 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
if( pOuterNC ) pOuterNC->nNestedSelect++;
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
SrcItem *pItem = &p->pSrc->a[i];
|
||||
assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/
|
||||
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
|
||||
assert( pItem->zName!=0
|
||||
|| pItem->fg.isSubquery ); /* Test of tag-20240424-1*/
|
||||
if( pItem->fg.isSubquery
|
||||
&& (pItem->u4.pSubq->pSelect->selFlags & SF_Resolved)==0
|
||||
){
|
||||
int nRef = pOuterNC ? pOuterNC->nRef : 0;
|
||||
const char *zSavedContext = pParse->zAuthContext;
|
||||
|
||||
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
|
||||
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
|
||||
sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pOuterNC);
|
||||
pParse->zAuthContext = zSavedContext;
|
||||
if( pParse->nErr ) return WRC_Abort;
|
||||
assert( db->mallocFailed==0 );
|
||||
@ -2000,7 +2011,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
** 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].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;
|
||||
}
|
||||
@ -2267,7 +2281,7 @@ int sqlite3ResolveSelfReference(
|
||||
if( pTab ){
|
||||
sSrc.nSrc = 1;
|
||||
sSrc.a[0].zName = pTab->zName;
|
||||
sSrc.a[0].pTab = pTab;
|
||||
sSrc.a[0].pSTab = pTab;
|
||||
sSrc.a[0].iCursor = -1;
|
||||
if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
|
||||
/* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
|
||||
|
287
src/select.c
287
src/select.c
@ -332,11 +332,13 @@ int sqlite3ColumnIndex(Table *pTab, const char *zCol){
|
||||
*/
|
||||
void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){
|
||||
assert( pItem!=0 );
|
||||
assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
|
||||
assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) );
|
||||
if( pItem->fg.isNestedFrom ){
|
||||
ExprList *pResults;
|
||||
assert( pItem->pSelect!=0 );
|
||||
pResults = pItem->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;
|
||||
@ -370,9 +372,9 @@ static int tableAndColumnIndex(
|
||||
assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */
|
||||
|
||||
for(i=iStart; i<=iEnd; i++){
|
||||
iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol);
|
||||
iCol = sqlite3ColumnIndex(pSrc->a[i].pSTab, zCol);
|
||||
if( iCol>=0
|
||||
&& (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0)
|
||||
&& (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pSTab->aCol[iCol])==0)
|
||||
){
|
||||
if( piTab ){
|
||||
sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol);
|
||||
@ -501,10 +503,10 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
|
||||
pLeft = &pSrc->a[0];
|
||||
pRight = &pLeft[1];
|
||||
for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
|
||||
Table *pRightTab = pRight->pTab;
|
||||
Table *pRightTab = pRight->pSTab;
|
||||
u32 joinType;
|
||||
|
||||
if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue;
|
||||
if( NEVER(pLeft->pSTab==0 || pRightTab==0) ) continue;
|
||||
joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON;
|
||||
|
||||
/* If this is a NATURAL join, synthesize an appropriate USING clause
|
||||
@ -1930,8 +1932,12 @@ static const char *columnTypeImpl(
|
||||
SrcList *pTabList = pNC->pSrcList;
|
||||
for(j=0;j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++);
|
||||
if( j<pTabList->nSrc ){
|
||||
pTab = pTabList->a[j].pTab;
|
||||
pS = pTabList->a[j].pSelect;
|
||||
pTab = pTabList->a[j].pSTab;
|
||||
if( pTabList->a[j].fg.isSubquery ){
|
||||
pS = pTabList->a[j].u4.pSubq->pSelect;
|
||||
}else{
|
||||
pS = 0;
|
||||
}
|
||||
}else{
|
||||
pNC = pNC->pNext;
|
||||
}
|
||||
@ -3983,7 +3989,9 @@ static void substSelect(
|
||||
pSrc = p->pSrc;
|
||||
assert( pSrc!=0 );
|
||||
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
||||
substSelect(pSubst, pItem->pSelect, 1);
|
||||
if( pItem->fg.isSubquery ){
|
||||
substSelect(pSubst, pItem->u4.pSubq->pSelect, 1);
|
||||
}
|
||||
if( pItem->fg.isTabFunc ){
|
||||
substExprList(pSubst, pItem->u1.pFuncArg);
|
||||
}
|
||||
@ -4014,7 +4022,7 @@ static void recomputeColumnsUsed(
|
||||
SrcItem *pSrcItem /* Which FROM clause item to recompute */
|
||||
){
|
||||
Walker w;
|
||||
if( NEVER(pSrcItem->pTab==0) ) return;
|
||||
if( NEVER(pSrcItem->pSTab==0) ) return;
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = recomputeColumnsUsedExpr;
|
||||
w.xSelectCallback = sqlite3SelectWalkNoop;
|
||||
@ -4054,8 +4062,10 @@ static void srclistRenumberCursors(
|
||||
aCsrMap[pItem->iCursor+1] = pParse->nTab++;
|
||||
}
|
||||
pItem->iCursor = aCsrMap[pItem->iCursor+1];
|
||||
for(p=pItem->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4366,7 +4376,8 @@ static int flattenSubquery(
|
||||
assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
|
||||
pSubitem = &pSrc->a[iFrom];
|
||||
iParent = pSubitem->iCursor;
|
||||
pSub = pSubitem->pSelect;
|
||||
assert( pSubitem->fg.isSubquery );
|
||||
pSub = pSubitem->u4.pSubq->pSelect;
|
||||
assert( pSub!=0 );
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
@ -4419,7 +4430,7 @@ static int flattenSubquery(
|
||||
*/
|
||||
if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
|
||||
if( pSubSrc->nSrc>1 /* (3a) */
|
||||
|| IsVirtual(pSubSrc->a[0].pTab) /* (3b) */
|
||||
|| IsVirtual(pSubSrc->a[0].pSTab) /* (3b) */
|
||||
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
|
||||
|| (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */
|
||||
){
|
||||
@ -4505,14 +4516,18 @@ static int flattenSubquery(
|
||||
pParse->zAuthContext = zSavedAuthContext;
|
||||
|
||||
/* Delete the transient structures associated with the subquery */
|
||||
pSub1 = pSubitem->pSelect;
|
||||
sqlite3DbFree(db, pSubitem->zDatabase);
|
||||
|
||||
if( ALWAYS(pSubitem->fg.isSubquery) ){
|
||||
pSub1 = sqlite3SubqueryDetach(db, pSubitem);
|
||||
}else{
|
||||
pSub1 = 0;
|
||||
}
|
||||
assert( pSubitem->fg.isSubquery==0 );
|
||||
assert( pSubitem->fg.fixedSchema==0 );
|
||||
sqlite3DbFree(db, pSubitem->zName);
|
||||
sqlite3DbFree(db, pSubitem->zAlias);
|
||||
pSubitem->zDatabase = 0;
|
||||
pSubitem->zName = 0;
|
||||
pSubitem->zAlias = 0;
|
||||
pSubitem->pSelect = 0;
|
||||
assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 );
|
||||
|
||||
/* If the sub-query is a compound SELECT statement, then (by restrictions
|
||||
@ -4553,8 +4568,8 @@ static int flattenSubquery(
|
||||
ExprList *pOrderBy = p->pOrderBy;
|
||||
Expr *pLimit = p->pLimit;
|
||||
Select *pPrior = p->pPrior;
|
||||
Table *pItemTab = pSubitem->pTab;
|
||||
pSubitem->pTab = 0;
|
||||
Table *pItemTab = pSubitem->pSTab;
|
||||
pSubitem->pSTab = 0;
|
||||
p->pOrderBy = 0;
|
||||
p->pPrior = 0;
|
||||
p->pLimit = 0;
|
||||
@ -4562,7 +4577,7 @@ static int flattenSubquery(
|
||||
p->pLimit = pLimit;
|
||||
p->pOrderBy = pOrderBy;
|
||||
p->op = TK_ALL;
|
||||
pSubitem->pTab = pItemTab;
|
||||
pSubitem->pSTab = pItemTab;
|
||||
if( pNew==0 ){
|
||||
p->pPrior = pPrior;
|
||||
}else{
|
||||
@ -4577,11 +4592,14 @@ static int flattenSubquery(
|
||||
TREETRACE(0x4,pParse,p,("compound-subquery flattener"
|
||||
" creates %u as peer\n",pNew->selId));
|
||||
}
|
||||
assert( pSubitem->pSelect==0 );
|
||||
assert( pSubitem->fg.isSubquery==0 );
|
||||
}
|
||||
sqlite3DbFree(db, aCsrMap);
|
||||
if( db->mallocFailed ){
|
||||
pSubitem->pSelect = pSub1;
|
||||
assert( pSubitem->fg.fixedSchema==0 );
|
||||
assert( pSubitem->fg.isSubquery==0 );
|
||||
assert( pSubitem->u4.zDatabase==0 );
|
||||
sqlite3SrcItemAttachSubquery(pParse, pSubitem, pSub1, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -4592,8 +4610,8 @@ static int flattenSubquery(
|
||||
**
|
||||
** pSubitem->pTab is always non-NULL by test restrictions and tests above.
|
||||
*/
|
||||
if( ALWAYS(pSubitem->pTab!=0) ){
|
||||
Table *pTabToDel = pSubitem->pTab;
|
||||
if( ALWAYS(pSubitem->pSTab!=0) ){
|
||||
Table *pTabToDel = pSubitem->pSTab;
|
||||
if( pTabToDel->nTabRef==1 ){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);
|
||||
@ -4601,7 +4619,7 @@ static int flattenSubquery(
|
||||
}else{
|
||||
pTabToDel->nTabRef--;
|
||||
}
|
||||
pSubitem->pTab = 0;
|
||||
pSubitem->pSTab = 0;
|
||||
}
|
||||
|
||||
/* The following loop runs once for each term in a compound-subquery
|
||||
@ -4657,8 +4675,11 @@ static int flattenSubquery(
|
||||
*/
|
||||
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;
|
||||
@ -5342,10 +5363,10 @@ static int disableUnusedSubqueryResultColumns(SrcItem *pItem){
|
||||
if( pItem->fg.isCorrelated || pItem->fg.isCte ){
|
||||
return 0;
|
||||
}
|
||||
assert( pItem->pTab!=0 );
|
||||
pTab = pItem->pTab;
|
||||
assert( pItem->pSelect!=0 );
|
||||
pSub = pItem->pSelect;
|
||||
assert( pItem->pSTab!=0 );
|
||||
pTab = pItem->pSTab;
|
||||
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 ){
|
||||
@ -5474,13 +5495,13 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|
||||
if( p->pWhere
|
||||
|| p->pEList->nExpr!=1
|
||||
|| p->pSrc->nSrc!=1
|
||||
|| p->pSrc->a[0].pSelect
|
||||
|| p->pSrc->a[0].fg.isSubquery
|
||||
|| pAggInfo->nFunc!=1
|
||||
|| p->pHaving
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
pTab = p->pSrc->a[0].pTab;
|
||||
pTab = p->pSrc->a[0].pSTab;
|
||||
assert( pTab!=0 );
|
||||
assert( !IsView(pTab) );
|
||||
if( !IsOrdinaryTable(pTab) ) return 0;
|
||||
@ -5505,7 +5526,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|
||||
** pFrom->pIndex and return SQLITE_OK.
|
||||
*/
|
||||
int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){
|
||||
Table *pTab = pFrom->pTab;
|
||||
Table *pTab = pFrom->pSTab;
|
||||
char *zIndexedBy = pFrom->u1.zIndexedBy;
|
||||
Index *pIdx;
|
||||
assert( pTab!=0 );
|
||||
@ -5582,7 +5603,11 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
|
||||
if( pNew==0 ) return WRC_Abort;
|
||||
memset(&dummy, 0, sizeof(dummy));
|
||||
pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0);
|
||||
if( pNewSrc==0 ) return WRC_Abort;
|
||||
assert( pNewSrc!=0 || pParse->nErr );
|
||||
if( pParse->nErr ){
|
||||
sqlite3SrcListDelete(db, pNewSrc);
|
||||
return WRC_Abort;
|
||||
}
|
||||
*pNew = *p;
|
||||
p->pSrc = pNewSrc;
|
||||
p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0));
|
||||
@ -5637,7 +5662,7 @@ static struct Cte *searchWith(
|
||||
){
|
||||
const char *zName = pItem->zName;
|
||||
With *p;
|
||||
assert( pItem->zDatabase==0 );
|
||||
assert( pItem->fg.fixedSchema || pItem->u4.zDatabase==0 );
|
||||
assert( zName!=0 );
|
||||
for(p=pWith; p; p=p->pOuter){
|
||||
int i;
|
||||
@ -5707,7 +5732,7 @@ static int resolveFromTermToCte(
|
||||
Cte *pCte; /* Matched CTE (or NULL if no match) */
|
||||
With *pWith; /* The matching WITH */
|
||||
|
||||
assert( pFrom->pTab==0 );
|
||||
assert( pFrom->pSTab==0 );
|
||||
if( pParse->pWith==0 ){
|
||||
/* There are no WITH clauses in the stack. No match is possible */
|
||||
return 0;
|
||||
@ -5717,7 +5742,8 @@ static int resolveFromTermToCte(
|
||||
** go no further. */
|
||||
return 0;
|
||||
}
|
||||
if( pFrom->zDatabase!=0 ){
|
||||
assert( pFrom->fg.hadSchema==0 || pFrom->fg.notCte!=0 );
|
||||
if( pFrom->fg.fixedSchema==0 && pFrom->u4.zDatabase!=0 ){
|
||||
/* The FROM term contains a schema qualifier (ex: main.t1) and so
|
||||
** it cannot possibly be a CTE reference. */
|
||||
return 0;
|
||||
@ -5753,7 +5779,7 @@ static int resolveFromTermToCte(
|
||||
}
|
||||
if( cannotBeFunction(pParse, pFrom) ) return 2;
|
||||
|
||||
assert( pFrom->pTab==0 );
|
||||
assert( pFrom->pSTab==0 );
|
||||
pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
||||
if( pTab==0 ) return 2;
|
||||
pCteUse = pCte->pUse;
|
||||
@ -5767,26 +5793,29 @@ static int resolveFromTermToCte(
|
||||
}
|
||||
pCteUse->eM10d = pCte->eM10d;
|
||||
}
|
||||
pFrom->pTab = pTab;
|
||||
pFrom->pSTab = pTab;
|
||||
pTab->nTabRef = 1;
|
||||
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
|
||||
sqlite3SrcItemAttachSubquery(pParse, pFrom, pCte->pSelect, 1);
|
||||
if( db->mallocFailed ) return 2;
|
||||
pFrom->pSelect->selFlags |= SF_CopyCte;
|
||||
assert( pFrom->pSelect );
|
||||
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;
|
||||
}
|
||||
assert( !pFrom->fg.isIndexedBy );
|
||||
pFrom->fg.isCte = 1;
|
||||
pFrom->u2.pCteUse = pCteUse;
|
||||
pCteUse->nUse++;
|
||||
|
||||
/* Check if this is a recursive CTE. */
|
||||
pRecTerm = pSel = pFrom->pSelect;
|
||||
pRecTerm = pSel;
|
||||
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
|
||||
while( bMayRecursive && pRecTerm->op==pSel->op ){
|
||||
int i;
|
||||
@ -5794,11 +5823,13 @@ static int resolveFromTermToCte(
|
||||
assert( pRecTerm->pPrior!=0 );
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
SrcItem *pItem = &pSrc->a[i];
|
||||
if( pItem->zDatabase==0
|
||||
&& pItem->zName!=0
|
||||
if( pItem->zName!=0
|
||||
&& !pItem->fg.hadSchema
|
||||
&& ALWAYS( !pItem->fg.isSubquery )
|
||||
&& (pItem->fg.fixedSchema || pItem->u4.zDatabase==0)
|
||||
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
|
||||
){
|
||||
pItem->pTab = pTab;
|
||||
pItem->pSTab = pTab;
|
||||
pTab->nTabRef++;
|
||||
pItem->fg.isRecursive = 1;
|
||||
if( pRecTerm->selFlags & SF_Recursive ){
|
||||
@ -5900,11 +5931,14 @@ void sqlite3SelectPopWith(Walker *pWalker, Select *p){
|
||||
** SQLITE_NOMEM.
|
||||
*/
|
||||
int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
|
||||
Select *pSel = pFrom->pSelect;
|
||||
Select *pSel;
|
||||
Table *pTab;
|
||||
|
||||
assert( pFrom->fg.isSubquery );
|
||||
assert( pFrom->u4.pSubq!=0 );
|
||||
pSel = pFrom->u4.pSubq->pSelect;
|
||||
assert( pSel );
|
||||
pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table));
|
||||
pFrom->pSTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table));
|
||||
if( pTab==0 ) return SQLITE_NOMEM;
|
||||
pTab->nTabRef = 1;
|
||||
if( pFrom->zAlias ){
|
||||
@ -6024,33 +6058,35 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
*/
|
||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||
Table *pTab;
|
||||
assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
|
||||
if( pFrom->pTab ) continue;
|
||||
assert( pFrom->fg.isRecursive==0 || pFrom->pSTab!=0 );
|
||||
if( pFrom->pSTab ) continue;
|
||||
assert( pFrom->fg.isRecursive==0 );
|
||||
if( pFrom->zName==0 ){
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
Select *pSel = pFrom->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->pTab==0 );
|
||||
assert( pFrom->pSTab==0 );
|
||||
if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
|
||||
if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort;
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
}else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){
|
||||
if( rc>1 ) return WRC_Abort;
|
||||
pTab = pFrom->pTab;
|
||||
pTab = pFrom->pSTab;
|
||||
assert( pTab!=0 );
|
||||
#endif
|
||||
}else{
|
||||
/* An ordinary table or view name in the FROM clause */
|
||||
assert( pFrom->pTab==0 );
|
||||
pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom);
|
||||
assert( pFrom->pSTab==0 );
|
||||
pFrom->pSTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom);
|
||||
if( pTab==0 ) return WRC_Abort;
|
||||
if( pTab->nTabRef>=0xffff ){
|
||||
sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535",
|
||||
pTab->zName);
|
||||
pFrom->pTab = 0;
|
||||
pFrom->pSTab = 0;
|
||||
return WRC_Abort;
|
||||
}
|
||||
pTab->nTabRef++;
|
||||
@ -6062,7 +6098,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
i16 nCol;
|
||||
u8 eCodeOrig = pWalker->eCode;
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
|
||||
assert( pFrom->pSelect==0 );
|
||||
assert( pFrom->fg.isSubquery==0 );
|
||||
if( IsView(pTab) ){
|
||||
if( (db->flags & SQLITE_EnableView)==0
|
||||
&& pTab->pSchema!=db->aDb[1].pSchema
|
||||
@ -6070,7 +6106,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
|
||||
pTab->zName);
|
||||
}
|
||||
pFrom->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))
|
||||
@ -6086,7 +6122,9 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
nCol = pTab->nCol;
|
||||
pTab->nCol = -1;
|
||||
pWalker->eCode = 1; /* Turn on Select.selId renumbering */
|
||||
sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
||||
if( pFrom->fg.isSubquery ){
|
||||
sqlite3WalkSelect(pWalker, pFrom->u4.pSubq->pSelect);
|
||||
}
|
||||
pWalker->eCode = eCodeOrig;
|
||||
pTab->nCol = nCol;
|
||||
}
|
||||
@ -6173,7 +6211,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
}
|
||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||
int nAdd; /* Number of cols including rowid */
|
||||
Table *pTab = pFrom->pTab; /* Table for this data source */
|
||||
Table *pTab = pFrom->pSTab; /* Table for this data source */
|
||||
ExprList *pNestedFrom; /* Result-set of a nested FROM clause */
|
||||
char *zTabName; /* AS name for this data source */
|
||||
const char *zSchemaName = 0; /* Schema name for this data source */
|
||||
@ -6184,10 +6222,11 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
zTabName = pTab->zName;
|
||||
}
|
||||
if( db->mallocFailed ) break;
|
||||
assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) );
|
||||
assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom) );
|
||||
if( pFrom->fg.isNestedFrom ){
|
||||
assert( pFrom->pSelect!=0 );
|
||||
pNestedFrom = pFrom->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 );
|
||||
@ -6426,14 +6465,12 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
||||
assert( (p->selFlags & SF_Resolved) );
|
||||
pTabList = p->pSrc;
|
||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||
Table *pTab = pFrom->pTab;
|
||||
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->pSelect;
|
||||
if( pSel ){
|
||||
sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
|
||||
}
|
||||
Select *pSel = pFrom->u4.pSubq->pSelect;
|
||||
sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7083,25 +7120,28 @@ static SrcItem *isSelfJoinView(
|
||||
int iFirst, int iEnd /* Range of FROM-clause entries to search. */
|
||||
){
|
||||
SrcItem *pItem;
|
||||
assert( pThis->pSelect!=0 );
|
||||
if( pThis->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->pSelect==0 ) continue;
|
||||
if( !pItem->fg.isSubquery ) continue;
|
||||
if( pItem->fg.viaCoroutine ) continue;
|
||||
if( pItem->zName==0 ) continue;
|
||||
assert( pItem->pTab!=0 );
|
||||
assert( pThis->pTab!=0 );
|
||||
if( pItem->pTab->pSchema!=pThis->pTab->pSchema ) 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->pSelect;
|
||||
if( pItem->pTab->pSchema==0 && pThis->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->pSelect->selFlags & SF_PushDown ){
|
||||
if( pS1->selFlags & SF_PushDown ){
|
||||
/* The view was modified by some other optimization such as
|
||||
** pushDownWhereTerms() */
|
||||
continue;
|
||||
@ -7145,6 +7185,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
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;
|
||||
@ -7159,8 +7200,9 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
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].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{
|
||||
@ -7169,7 +7211,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
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 */
|
||||
@ -7177,8 +7219,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
db = pParse->db;
|
||||
pCount = pExpr;
|
||||
pExpr = 0;
|
||||
pSub = p->pSrc->a[0].pSelect;
|
||||
p->pSrc->a[0].pSelect = 0;
|
||||
pSub = sqlite3SubqueryDetach(db, pFrom);
|
||||
sqlite3SrcListDelete(db, p->pSrc);
|
||||
p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
|
||||
while( pSub ){
|
||||
@ -7223,12 +7264,12 @@ static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
SrcItem *p1 = &pSrc->a[i];
|
||||
if( p1==p0 ) continue;
|
||||
if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
|
||||
if( p0->pSTab==p1->pSTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
|
||||
return 1;
|
||||
}
|
||||
if( p1->pSelect
|
||||
&& (p1->pSelect->selFlags & SF_NestedFrom)!=0
|
||||
&& sameSrcAlias(p0, p1->pSelect->pSrc)
|
||||
if( p1->fg.isSubquery
|
||||
&& (p1->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0
|
||||
&& sameSrcAlias(p0, p1->u4.pSubq->pSelect->pSrc)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
@ -7293,7 +7334,7 @@ static int fromClauseTermCanBeCoroutine(
|
||||
if( i==0 ) break;
|
||||
i--;
|
||||
pItem--;
|
||||
if( pItem->pSelect!=0 ) return 0; /* (1c-i) */
|
||||
if( pItem->fg.isSubquery ) return 0; /* (1c-i) */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -7404,7 +7445,7 @@ int sqlite3Select(
|
||||
if( sameSrcAlias(p0, p->pSrc) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"target object/alias may not appear in FROM clause: %s",
|
||||
p0->zAlias ? p0->zAlias : p0->pTab->zName
|
||||
p0->zAlias ? p0->zAlias : p0->pSTab->zName
|
||||
);
|
||||
goto select_end;
|
||||
}
|
||||
@ -7443,8 +7484,8 @@ int sqlite3Select(
|
||||
#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->pSelect;
|
||||
Table *pTab = pItem->pTab;
|
||||
Select *pSub = pItem->fg.isSubquery ? pItem->u4.pSubq->pSelect : 0;
|
||||
Table *pTab = pItem->pSTab;
|
||||
|
||||
/* The expander should have already created transient Table objects
|
||||
** even for FROM clause elements such as subqueries that do not correspond
|
||||
@ -7662,6 +7703,7 @@ int sqlite3Select(
|
||||
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;
|
||||
@ -7684,17 +7726,29 @@ int sqlite3Select(
|
||||
** string for the fake column name seems safer.
|
||||
*/
|
||||
if( pItem->colUsed==0 && pItem->zName!=0 ){
|
||||
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase);
|
||||
const char *zDb;
|
||||
if( pItem->fg.fixedSchema ){
|
||||
int iDb = sqlite3SchemaToIndex(pParse->db, pItem->u4.pSchema);
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
}else if( pItem->fg.isSubquery ){
|
||||
zDb = 0;
|
||||
}else{
|
||||
zDb = pItem->u4.zDatabase;
|
||||
}
|
||||
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", zDb);
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
/* Generate code for all sub-queries in the FROM clause
|
||||
*/
|
||||
pSub = pItem->pSelect;
|
||||
if( pSub==0 || pItem->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->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
|
||||
@ -7720,7 +7774,7 @@ int sqlite3Select(
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
assert( pItem->pSelect && (pItem->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"));
|
||||
}
|
||||
@ -7752,17 +7806,17 @@ int sqlite3Select(
|
||||
*/
|
||||
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
|
||||
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
|
||||
pSubq->regReturn = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop);
|
||||
VdbeComment((v, "%!S", pItem));
|
||||
pItem->addrFillSub = addrTop;
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
pSubq->addrFillSub = addrTop;
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn);
|
||||
ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem));
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
pItem->pSTab->nRowLogEst = pSub->nSelectRow;
|
||||
pItem->fg.viaCoroutine = 1;
|
||||
pItem->regResult = dest.iSdst;
|
||||
sqlite3VdbeEndCoroutine(v, pItem->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 ){
|
||||
@ -7780,11 +7834,16 @@ int sqlite3Select(
|
||||
}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->addrFillSub ){
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->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->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
|
||||
@ -7795,9 +7854,9 @@ int sqlite3Select(
|
||||
int addrExplain;
|
||||
#endif
|
||||
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
pSubq->regReturn = ++pParse->nMem;
|
||||
topAddr = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
pItem->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
|
||||
@ -7812,17 +7871,17 @@ int sqlite3Select(
|
||||
|
||||
ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem));
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
pItem->pSTab->nRowLogEst = pSub->nSelectRow;
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
sqlite3VdbeAddOp2(v, OP_Return, pItem->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->addrFillSub;
|
||||
pCteUse->regRtn = pItem->regReturn;
|
||||
pCteUse->addrM9e = pSubq->addrFillSub;
|
||||
pCteUse->regRtn = pSubq->regReturn;
|
||||
pCteUse->iCur = pItem->iCursor;
|
||||
pCteUse->nRowEst = pSub->nSelectRow;
|
||||
}
|
||||
|
@ -1335,6 +1335,7 @@ typedef struct Savepoint Savepoint;
|
||||
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 */
|
||||
@ -3275,6 +3276,16 @@ struct IdList {
|
||||
#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.
|
||||
@ -3287,29 +3298,40 @@ struct IdList {
|
||||
** 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.
|
||||
**
|
||||
** Union member validity:
|
||||
** Aggressive use of "union" helps keep the size of the object small. This
|
||||
** has been shown to boost performance, in addition to saving memory.
|
||||
** Access to union elements is gated by the following rules which should
|
||||
** always be checked, either by an if-statement or by an assert().
|
||||
**
|
||||
** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
|
||||
** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
|
||||
** Field Only access if this is true
|
||||
** --------------- -----------------------------------
|
||||
** u1.zIndexedBy fg.isIndexedBy
|
||||
** u1.pFuncArg fg.isTabFunc
|
||||
** u1.nRow !fg.isTabFunc && !fg.isIndexedBy
|
||||
**
|
||||
** u2.pIBIndex fg.isIndexedBy && !fg.isCte
|
||||
** u2.pCteUse fg.isCte && !fg.isIndexedBy
|
||||
** u2.pIBIndex fg.isIndexedBy
|
||||
** u2.pCteUse fg.isCte
|
||||
**
|
||||
** u3.pOn !fg.isUsing
|
||||
** u3.pUsing fg.isUsing
|
||||
**
|
||||
** u4.zDatabase !fg.fixedSchema && !fg.isSubquery
|
||||
** u4.pSchema fg.fixedSchema
|
||||
** u4.pSubq fg.isSubquery
|
||||
**
|
||||
** See also the sqlite3SrcListDelete() routine for assert() statements that
|
||||
** check invariants on the fields of this object, especially the flags
|
||||
** inside the fg struct.
|
||||
*/
|
||||
struct SrcItem {
|
||||
Schema *pSchema; /* Schema to which this item is fixed */
|
||||
char *zDatabase; /* Name of database holding this table */
|
||||
char *zName; /* Name of the table */
|
||||
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
|
||||
Table *pTab; /* An SQL table corresponding to zName */
|
||||
Select *pSelect; /* A SELECT statement used in place of a table name */
|
||||
int addrFillSub; /* Address of subroutine to manifest a subquery */
|
||||
int regReturn; /* Register holding return address of addrFillSub */
|
||||
int regResult; /* Registers holding results of a co-routine */
|
||||
Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */
|
||||
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 */
|
||||
@ -3323,12 +3345,10 @@ struct SrcItem {
|
||||
unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */
|
||||
unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
|
||||
unsigned rowidUsed :1; /* The ROWID of this table is referenced */
|
||||
unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */
|
||||
unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */
|
||||
} fg;
|
||||
int iCursor; /* The VDBE cursor number used to access this table */
|
||||
union {
|
||||
Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */
|
||||
IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */
|
||||
} u3;
|
||||
Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
|
||||
union {
|
||||
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
|
||||
@ -3339,6 +3359,15 @@ struct SrcItem {
|
||||
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
|
||||
CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */
|
||||
} u2;
|
||||
union {
|
||||
Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */
|
||||
IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */
|
||||
} u3;
|
||||
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;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3598,8 +3627,10 @@ struct Select {
|
||||
#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 SrcItem 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
|
||||
@ -4991,6 +5022,9 @@ int sqlite3IdListIndex(IdList*,const char*);
|
||||
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 *);
|
||||
|
@ -193,9 +193,9 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
|
||||
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
||||
x.printfFlags |= SQLITE_PRINTF_INTERNAL;
|
||||
sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem);
|
||||
if( pItem->pTab ){
|
||||
if( pItem->pSTab ){
|
||||
sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s",
|
||||
pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab,
|
||||
pItem->pSTab->zName, pItem->pSTab->nCol, pItem->pSTab,
|
||||
pItem->colUsed,
|
||||
pItem->fg.rowidUsed ? "+rowid" : "");
|
||||
}
|
||||
@ -230,19 +230,19 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
|
||||
sqlite3StrAccumFinish(&x);
|
||||
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
|
||||
n = 0;
|
||||
if( pItem->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->pSelect ){
|
||||
if( pItem->pTab ){
|
||||
Table *pTab = pItem->pTab;
|
||||
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->pSelect) );
|
||||
sqlite3TreeViewSelect(pView, pItem->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:");
|
||||
|
@ -152,8 +152,10 @@ void sqlite3BeginTrigger(
|
||||
** name on pTableName if we are reparsing out of the schema table
|
||||
*/
|
||||
if( db->init.busy && iDb!=1 ){
|
||||
sqlite3DbFree(db, pTableName->a[0].zDatabase);
|
||||
pTableName->a[0].zDatabase = 0;
|
||||
assert( pTableName->a[0].fg.fixedSchema==0 );
|
||||
assert( pTableName->a[0].fg.isSubquery==0 );
|
||||
sqlite3DbFree(db, pTableName->a[0].u4.zDatabase);
|
||||
pTableName->a[0].u4.zDatabase = 0;
|
||||
}
|
||||
|
||||
/* If the trigger name was unqualified, and the table is a temp table,
|
||||
@ -631,7 +633,8 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
|
||||
}
|
||||
|
||||
assert( pName->nSrc==1 );
|
||||
zDb = pName->a[0].zDatabase;
|
||||
assert( pName->a[0].fg.fixedSchema==0 && pName->a[0].fg.isSubquery==0 );
|
||||
zDb = pName->a[0].u4.zDatabase;
|
||||
zName = pName->a[0].zName;
|
||||
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
@ -868,7 +871,9 @@ SrcList *sqlite3TriggerStepSrc(
|
||||
Schema *pSchema = pStep->pTrig->pSchema;
|
||||
pSrc->a[0].zName = zName;
|
||||
if( pSchema!=db->aDb[1].pSchema ){
|
||||
pSrc->a[0].pSchema = pSchema;
|
||||
assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 );
|
||||
pSrc->a[0].u4.pSchema = pSchema;
|
||||
pSrc->a[0].fg.fixedSchema = 1;
|
||||
}
|
||||
if( pStep->pFrom ){
|
||||
SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0);
|
||||
@ -981,7 +986,7 @@ static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){
|
||||
pSrc = pSelect->pSrc;
|
||||
assert( pSrc!=0 );
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
if( pSrc->a[i].pTab==pWalker->u.pTab ){
|
||||
if( pSrc->a[i].pSTab==pWalker->u.pTab ){
|
||||
testcase( pSelect->selFlags & SF_Correlated );
|
||||
pSelect->selFlags |= SF_Correlated;
|
||||
pWalker->eCode = 1;
|
||||
@ -1052,7 +1057,7 @@ static void codeReturningTrigger(
|
||||
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
|
||||
sSelect.pSrc = &sFrom;
|
||||
sFrom.nSrc = 1;
|
||||
sFrom.a[0].pTab = pTab;
|
||||
sFrom.a[0].pSTab = pTab;
|
||||
sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
|
||||
sFrom.a[0].iCursor = -1;
|
||||
sqlite3SelectPrep(pParse, &sSelect, 0);
|
||||
|
@ -202,7 +202,7 @@ static void updateFromSelect(
|
||||
Expr *pLimit2 = 0;
|
||||
ExprList *pOrderBy2 = 0;
|
||||
sqlite3 *db = pParse->db;
|
||||
Table *pTab = pTabList->a[0].pTab;
|
||||
Table *pTab = pTabList->a[0].pSTab;
|
||||
SrcList *pSrc;
|
||||
Expr *pWhere2;
|
||||
int eDest;
|
||||
@ -226,8 +226,8 @@ static void updateFromSelect(
|
||||
if( pSrc ){
|
||||
assert( pSrc->a[0].fg.notCte );
|
||||
pSrc->a[0].iCursor = -1;
|
||||
pSrc->a[0].pTab->nTabRef--;
|
||||
pSrc->a[0].pTab = 0;
|
||||
pSrc->a[0].pSTab->nTabRef--;
|
||||
pSrc->a[0].pSTab = 0;
|
||||
}
|
||||
if( pPk ){
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
|
@ -104,7 +104,7 @@ int sqlite3UpsertAnalyzeTarget(
|
||||
int nClause = 0; /* Counter of ON CONFLICT clauses */
|
||||
|
||||
assert( pTabList->nSrc==1 );
|
||||
assert( pTabList->a[0].pTab!=0 );
|
||||
assert( pTabList->a[0].pSTab!=0 );
|
||||
assert( pUpsert!=0 );
|
||||
assert( pUpsert->pUpsertTarget!=0 );
|
||||
|
||||
@ -123,7 +123,7 @@ int sqlite3UpsertAnalyzeTarget(
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Check to see if the conflict target matches the rowid. */
|
||||
pTab = pTabList->a[0].pTab;
|
||||
pTab = pTabList->a[0].pSTab;
|
||||
pTarget = pUpsert->pUpsertTarget;
|
||||
iCursor = pTabList->a[0].iCursor;
|
||||
if( HasRowid(pTab)
|
||||
|
@ -171,7 +171,9 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
|
||||
pSrc = p->pSrc;
|
||||
if( ALWAYS(pSrc) ){
|
||||
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
||||
if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
|
||||
if( pItem->fg.isSubquery
|
||||
&& sqlite3WalkSelect(pWalker, pItem->u4.pSubq->pSelect)
|
||||
){
|
||||
return WRC_Abort;
|
||||
}
|
||||
if( pItem->fg.isTabFunc
|
||||
|
105
src/where.c
105
src/where.c
@ -644,7 +644,7 @@ static int isDistinctRedundant(
|
||||
** clause is redundant. */
|
||||
if( pTabList->nSrc!=1 ) return 0;
|
||||
iBase = pTabList->a[0].iCursor;
|
||||
pTab = pTabList->a[0].pTab;
|
||||
pTab = pTabList->a[0].pSTab;
|
||||
|
||||
/* If any of the expressions is an IPK column on table iBase, then return
|
||||
** true. Note: The (p->iTable==iBase) part of this test may be false if the
|
||||
@ -908,10 +908,10 @@ static int termCanDriveIndex(
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
leftCol = pTerm->u.x.leftColumn;
|
||||
if( leftCol<0 ) return 0;
|
||||
aff = pSrc->pTab->aCol[leftCol].affinity;
|
||||
aff = pSrc->pSTab->aCol[leftCol].affinity;
|
||||
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
|
||||
testcase( pTerm->pExpr->op==TK_IS );
|
||||
return columnIsGoodIndexCandidate(pSrc->pTab, leftCol);
|
||||
return columnIsGoodIndexCandidate(pSrc->pSTab, leftCol);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1019,7 +1019,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
|
||||
nKeyCol = 0;
|
||||
pTabList = pWC->pWInfo->pTabList;
|
||||
pSrc = &pTabList->a[pLevel->iFrom];
|
||||
pTable = pSrc->pTab;
|
||||
pTable = pSrc->pSTab;
|
||||
pWCEnd = &pWC->a[pWC->nTerm];
|
||||
pLoop = pLevel->pWLoop;
|
||||
idxCols = 0;
|
||||
@ -1161,12 +1161,17 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
|
||||
/* Fill the automatic index with content */
|
||||
assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] );
|
||||
if( pSrc->fg.viaCoroutine ){
|
||||
int regYield = pSrc->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->addrFillSub);
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub);
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
|
||||
VdbeCoverage(v);
|
||||
VdbeComment((v, "next row of %s", pSrc->pTab->zName));
|
||||
VdbeComment((v, "next row of %s", pSrc->pSTab->zName));
|
||||
}else{
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
|
||||
}
|
||||
@ -1188,11 +1193,12 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
|
||||
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->regResult, pLevel->iIdxCur);
|
||||
pSrc->u4.pSubq->regResult, pLevel->iIdxCur);
|
||||
sqlite3VdbeGoto(v, addrTop);
|
||||
pSrc->fg.viaCoroutine = 0;
|
||||
}else{
|
||||
@ -1283,7 +1289,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
iSrc = pLevel->iFrom;
|
||||
pItem = &pTabList->a[iSrc];
|
||||
assert( pItem!=0 );
|
||||
pTab = pItem->pTab;
|
||||
pTab = pItem->pSTab;
|
||||
assert( pTab!=0 );
|
||||
sz = sqlite3LogEstToInt(pTab->nRowLogEst);
|
||||
if( sz<10000 ){
|
||||
@ -1314,7 +1320,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
int r1 = sqlite3GetTempRange(pParse, n);
|
||||
int jj;
|
||||
for(jj=0; jj<n; jj++){
|
||||
assert( pIdx->pTable==pItem->pTab );
|
||||
assert( pIdx->pTable==pItem->pSTab );
|
||||
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj);
|
||||
}
|
||||
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
|
||||
@ -1395,7 +1401,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
WhereClause *p;
|
||||
|
||||
assert( pSrc!=0 );
|
||||
pTab = pSrc->pTab;
|
||||
pTab = pSrc->pSTab;
|
||||
assert( pTab!=0 );
|
||||
assert( IsVirtual(pTab) );
|
||||
|
||||
@ -2403,7 +2409,7 @@ void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
|
||||
WhereInfo *pWInfo = pWC->pWInfo;
|
||||
int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
|
||||
SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
|
||||
Table *pTab = pItem->pTab;
|
||||
Table *pTab = pItem->pSTab;
|
||||
Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
|
||||
sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
|
||||
p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
|
||||
@ -3391,7 +3397,7 @@ static int whereLoopAddBtreeIndex(
|
||||
** 2. Stepping forward in the index pNew->nOut times to find all
|
||||
** additional matching entries.
|
||||
*/
|
||||
assert( pSrc->pTab->szTabRow>0 );
|
||||
assert( pSrc->pSTab->szTabRow>0 );
|
||||
if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){
|
||||
/* The pProbe->szIdxRow is low for an IPK table since the interior
|
||||
** pages are small. Thus szIdxRow gives a good estimate of seek cost.
|
||||
@ -3399,7 +3405,7 @@ static int whereLoopAddBtreeIndex(
|
||||
** under-estimate the scanning cost. */
|
||||
rCostIdx = pNew->nOut + 16;
|
||||
}else{
|
||||
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
|
||||
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pSTab->szTabRow;
|
||||
}
|
||||
rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx);
|
||||
|
||||
@ -3864,9 +3870,9 @@ static int whereLoopAddBtree(
|
||||
pWInfo = pBuilder->pWInfo;
|
||||
pTabList = pWInfo->pTabList;
|
||||
pSrc = pTabList->a + pNew->iTab;
|
||||
pTab = pSrc->pTab;
|
||||
pTab = pSrc->pSTab;
|
||||
pWC = pBuilder->pWC;
|
||||
assert( !IsVirtual(pSrc->pTab) );
|
||||
assert( !IsVirtual(pSrc->pSTab) );
|
||||
|
||||
if( pSrc->fg.isIndexedBy ){
|
||||
assert( pSrc->fg.isCte==0 );
|
||||
@ -3891,7 +3897,7 @@ static int whereLoopAddBtree(
|
||||
sPk.idxType = SQLITE_IDXTYPE_IPK;
|
||||
aiRowEstPk[0] = pTab->nRowLogEst;
|
||||
aiRowEstPk[1] = 0;
|
||||
pFirst = pSrc->pTab->pIndex;
|
||||
pFirst = pSrc->pSTab->pIndex;
|
||||
if( pSrc->fg.notIndexed==0 ){
|
||||
/* The real indices of the table are only considered if the
|
||||
** NOT INDEXED qualifier is omitted from the FROM clause */
|
||||
@ -4010,9 +4016,9 @@ static int whereLoopAddBtree(
|
||||
#endif
|
||||
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
|
||||
whereLoopOutputAdjust(pWC, pNew, rSize);
|
||||
if( pSrc->pSelect ){
|
||||
if( pSrc->fg.isSubquery ){
|
||||
if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE;
|
||||
pNew->u.btree.pOrderBy = pSrc->pSelect->pOrderBy;
|
||||
pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy;
|
||||
}
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
pNew->nOut = rSize;
|
||||
@ -4238,7 +4244,7 @@ static int whereLoopAddVirtualOne(
|
||||
pHidden->mHandleIn = 0;
|
||||
|
||||
/* Invoke the virtual table xBestIndex() method */
|
||||
rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
|
||||
rc = vtabBestIndex(pParse, pSrc->pSTab, pIdxInfo);
|
||||
if( rc ){
|
||||
if( rc==SQLITE_CONSTRAINT ){
|
||||
/* If the xBestIndex method returns SQLITE_CONSTRAINT, that means
|
||||
@ -4268,7 +4274,7 @@ static int whereLoopAddVirtualOne(
|
||||
|| pNew->aLTerm[iTerm]!=0
|
||||
|| pIdxCons->usable==0
|
||||
){
|
||||
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
|
||||
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName);
|
||||
freeIdxStr(pIdxInfo);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@ -4331,7 +4337,7 @@ static int whereLoopAddVirtualOne(
|
||||
if( pNew->aLTerm[i]==0 ){
|
||||
/* The non-zero argvIdx values must be contiguous. Raise an
|
||||
** error if they are not */
|
||||
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
|
||||
sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName);
|
||||
freeIdxStr(pIdxInfo);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@ -4533,7 +4539,7 @@ static int whereLoopAddVirtual(
|
||||
pWC = pBuilder->pWC;
|
||||
pNew = pBuilder->pNew;
|
||||
pSrc = &pWInfo->pTabList->a[pNew->iTab];
|
||||
assert( IsVirtual(pSrc->pTab) );
|
||||
assert( IsVirtual(pSrc->pSTab) );
|
||||
p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit);
|
||||
if( p==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pNew->rSetup = 0;
|
||||
@ -4547,7 +4553,7 @@ static int whereLoopAddVirtual(
|
||||
}
|
||||
|
||||
/* First call xBestIndex() with all constraints usable. */
|
||||
WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName));
|
||||
WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pSTab->zName));
|
||||
WHERETRACE(0x800, (" VirtualOne: all usable\n"));
|
||||
rc = whereLoopAddVirtualOne(
|
||||
pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry
|
||||
@ -4629,7 +4635,7 @@ static int whereLoopAddVirtual(
|
||||
}
|
||||
|
||||
freeIndexInfo(pParse->db, p);
|
||||
WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc));
|
||||
WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pSTab->zName, rc));
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
@ -4701,7 +4707,7 @@ static int whereLoopAddOr(
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pItem->pTab) ){
|
||||
if( IsVirtual(pItem->pSTab) ){
|
||||
rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable);
|
||||
}else
|
||||
#endif
|
||||
@ -4815,7 +4821,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
|
||||
mPrereq = 0;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pItem->pTab) ){
|
||||
if( IsVirtual(pItem->pSTab) ){
|
||||
SrcItem *p;
|
||||
for(p=&pItem[1]; p<pEnd; p++){
|
||||
if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){
|
||||
@ -5451,7 +5457,7 @@ static int computeMxChoice(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
if( sqlite3WhereTrace&0x4 ){
|
||||
SrcItem *pItem = pWInfo->pTabList->a + iLoop;
|
||||
sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n",
|
||||
pItem->zAlias ? pItem->zAlias : pItem->pTab->zName,
|
||||
pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName,
|
||||
nDep, rDelta);
|
||||
}
|
||||
#endif
|
||||
@ -6001,7 +6007,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
|
||||
if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0;
|
||||
assert( pWInfo->pTabList->nSrc>=1 );
|
||||
pItem = pWInfo->pTabList->a;
|
||||
pTab = pItem->pTab;
|
||||
pTab = pItem->pSTab;
|
||||
if( IsVirtual(pTab) ) return 0;
|
||||
if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){
|
||||
testcase( pItem->fg.isIndexedBy );
|
||||
@ -6264,7 +6270,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
|
||||
WhereLoop *pLoop = pWInfo->a[i].pWLoop;
|
||||
const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
|
||||
SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
|
||||
Table *pTab = pItem->pTab;
|
||||
Table *pTab = pItem->pSTab;
|
||||
if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
|
||||
pTab->tabFlags |= TF_MaybeReanalyze;
|
||||
if( i>=1
|
||||
@ -6421,8 +6427,8 @@ static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){
|
||||
SrcItem *pItem = &pWInfo->pTabList->a[ii];
|
||||
if( !pItem->fg.isCte
|
||||
|| pItem->u2.pCteUse->eM10d!=M10d_Yes
|
||||
|| NEVER(pItem->pSelect==0)
|
||||
|| pItem->pSelect->pOrderBy==0
|
||||
|| NEVER(pItem->fg.isSubquery==0)
|
||||
|| pItem->u4.pSubq->pSelect->pOrderBy==0
|
||||
){
|
||||
pWInfo->revMask |= MASKBIT(ii);
|
||||
}
|
||||
@ -6912,15 +6918,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
|
||||
int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
|
||||
int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
|
||||
assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) );
|
||||
assert( !(wsFlags&WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pSTab) );
|
||||
if( bOnerow || (
|
||||
0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW)
|
||||
&& !IsVirtual(pTabList->a[0].pTab)
|
||||
&& !IsVirtual(pTabList->a[0].pSTab)
|
||||
&& (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK))
|
||||
&& OptimizationEnabled(db, SQLITE_OnePass)
|
||||
)){
|
||||
pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
|
||||
if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){
|
||||
if( HasRowid(pTabList->a[0].pSTab) && (wsFlags & WHERE_IDX_ONLY) ){
|
||||
if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){
|
||||
bFordelete = OPFLAG_FORDELETE;
|
||||
}
|
||||
@ -6938,7 +6944,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
SrcItem *pTabItem;
|
||||
|
||||
pTabItem = &pTabList->a[pLevel->iFrom];
|
||||
pTab = pTabItem->pTab;
|
||||
pTab = pTabItem->pSTab;
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
pLoop = pLevel->pWLoop;
|
||||
if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){
|
||||
@ -7009,7 +7015,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
iIndexCur = pLevel->iTabCur;
|
||||
op = 0;
|
||||
}else if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
Index *pJ = pTabItem->pTab->pIndex;
|
||||
Index *pJ = pTabItem->pSTab->pIndex;
|
||||
iIndexCur = iAuxArg;
|
||||
assert( wctrlFlags & WHERE_ONEPASS_DESIRED );
|
||||
while( ALWAYS(pJ) && pJ!=pIx ){
|
||||
@ -7076,7 +7082,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom);
|
||||
pRJ->regReturn = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn);
|
||||
assert( pTab==pTabItem->pTab );
|
||||
assert( pTab==pTabItem->pSTab );
|
||||
if( HasRowid(pTab) ){
|
||||
KeyInfo *pInfo;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1);
|
||||
@ -7115,11 +7121,14 @@ WhereInfo *sqlite3WhereBegin(
|
||||
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->regReturn, pSrc->addrFillSub);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub);
|
||||
}else{
|
||||
int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub);
|
||||
sqlite3VdbeJumpHere(v, iOnce);
|
||||
}
|
||||
}
|
||||
@ -7334,9 +7343,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
assert( pLevel->iTabCur==pSrc->iCursor );
|
||||
if( pSrc->fg.viaCoroutine ){
|
||||
int m, n;
|
||||
n = pSrc->regResult;
|
||||
assert( pSrc->pTab!=0 );
|
||||
m = pSrc->pTab->nCol;
|
||||
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);
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur);
|
||||
@ -7360,7 +7370,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
VdbeModuleComment((v, "End WHERE-loop%d: %s", i,
|
||||
pWInfo->pTabList->a[pLevel->iFrom].pTab->zName));
|
||||
pWInfo->pTabList->a[pLevel->iFrom].pSTab->zName));
|
||||
}
|
||||
|
||||
assert( pWInfo->nLevel<=pTabList->nSrc );
|
||||
@ -7369,7 +7379,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
VdbeOp *pOp, *pLastOp;
|
||||
Index *pIdx = 0;
|
||||
SrcItem *pTabItem = &pTabList->a[pLevel->iFrom];
|
||||
Table *pTab = pTabItem->pTab;
|
||||
Table *pTab = pTabItem->pSTab;
|
||||
assert( pTab!=0 );
|
||||
pLoop = pLevel->pWLoop;
|
||||
|
||||
@ -7388,9 +7398,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
*/
|
||||
if( pTabItem->fg.viaCoroutine ){
|
||||
testcase( pParse->db->mallocFailed );
|
||||
assert( pTabItem->regResult>=0 );
|
||||
assert( pTabItem->fg.isSubquery );
|
||||
assert( pTabItem->u4.pSubq->regResult>=0 );
|
||||
translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur,
|
||||
pTabItem->regResult, 0);
|
||||
pTabItem->u4.pSubq->regResult, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ int sqlite3WhereExplainOneScan(
|
||||
assert( pLoop->u.btree.pIndex!=0 );
|
||||
pIdx = pLoop->u.btree.pIndex;
|
||||
assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
|
||||
if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
|
||||
if( !HasRowid(pItem->pSTab) && IsPrimaryKeyIndex(pIdx) ){
|
||||
if( isSearch ){
|
||||
zFmt = "PRIMARY KEY";
|
||||
}
|
||||
@ -254,7 +254,7 @@ int sqlite3WhereExplainBloomFilter(
|
||||
sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem);
|
||||
pLoop = pLevel->pWLoop;
|
||||
if( pLoop->wsFlags & WHERE_IPK ){
|
||||
const Table *pTab = pItem->pTab;
|
||||
const Table *pTab = pItem->pSTab;
|
||||
if( pTab->iPKey>=0 ){
|
||||
sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName);
|
||||
}else{
|
||||
@ -317,7 +317,9 @@ void sqlite3WhereAddScanStatus(
|
||||
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
|
||||
}
|
||||
}else{
|
||||
int addr = pSrclist->a[pLvl->iFrom].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 );
|
||||
@ -1454,7 +1456,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iCur = pTabItem->iCursor;
|
||||
pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
|
||||
bRev = (pWInfo->revMask>>iLevel)&1;
|
||||
VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
|
||||
VdbeModuleComment((v, "Begin WHERE-loop%d: %s",
|
||||
iLevel, pTabItem->pSTab->zName));
|
||||
#if WHERETRACE_ENABLED /* 0x4001 */
|
||||
if( sqlite3WhereTrace & 0x1 ){
|
||||
sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n",
|
||||
@ -1509,11 +1512,15 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
|
||||
/* Special case of a FROM clause subquery implemented as a co-routine */
|
||||
if( pTabItem->fg.viaCoroutine ){
|
||||
int regYield = pTabItem->regReturn;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->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->pTab->zName));
|
||||
VdbeComment((v, "next row of %s", pTabItem->pSTab->zName));
|
||||
pLevel->op = OP_Goto;
|
||||
}else
|
||||
|
||||
@ -2242,7 +2249,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
int untestedTerms = 0; /* Some terms not completely tested */
|
||||
int ii; /* Loop counter */
|
||||
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
|
||||
Table *pTab = pTabItem->pTab;
|
||||
Table *pTab = pTabItem->pSTab;
|
||||
|
||||
pTerm = pLoop->aLTerm[0];
|
||||
assert( pTerm!=0 );
|
||||
@ -2701,7 +2708,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** least once. This is accomplished by storing the PK for the row in
|
||||
** both the iMatch index and the regBloom Bloom filter.
|
||||
*/
|
||||
pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab;
|
||||
pTab = pWInfo->pTabList->a[pLevel->iFrom].pSTab;
|
||||
if( HasRowid(pTab) ){
|
||||
r = sqlite3GetTempRange(pParse, 2);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1);
|
||||
@ -2808,7 +2815,7 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
Bitmask mAll = 0;
|
||||
int k;
|
||||
|
||||
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName));
|
||||
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
|
||||
sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
|
||||
pRJ->regReturn);
|
||||
for(k=0; k<iLevel; k++){
|
||||
@ -2818,9 +2825,13 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
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->regResult,
|
||||
pRight->regResult + pRight->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);
|
||||
@ -2858,7 +2869,7 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
int nPk;
|
||||
int jmp;
|
||||
int addrCont = sqlite3WhereContinueLabel(pSubWInfo);
|
||||
Table *pTab = pTabItem->pTab;
|
||||
Table *pTab = pTabItem->pSTab;
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r);
|
||||
nPk = 1;
|
||||
|
@ -967,7 +967,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
|
||||
if( ALWAYS(pSrc!=0) ){
|
||||
int i;
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
mask |= exprSelectUsage(pMaskSet, pSrc->a[i].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);
|
||||
}
|
||||
@ -1005,7 +1007,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2(
|
||||
int iCur;
|
||||
do{
|
||||
iCur = pFrom->a[j].iCursor;
|
||||
for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
for(pIdx=pFrom->a[j].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->aColExpr==0 ) continue;
|
||||
for(i=0; i<pIdx->nKeyCol; i++){
|
||||
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
|
||||
@ -1049,7 +1051,7 @@ static int exprMightBeIndexed(
|
||||
|
||||
for(i=0; i<pFrom->nSrc; i++){
|
||||
Index *pIdx;
|
||||
for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
for(pIdx=pFrom->a[i].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->aColExpr ){
|
||||
return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i);
|
||||
}
|
||||
@ -1637,7 +1639,7 @@ void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
|
||||
assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */
|
||||
if( p->pGroupBy==0
|
||||
&& (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */
|
||||
&& (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */
|
||||
&& (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pSTab)) /* 3 */
|
||||
){
|
||||
ExprList *pOrderBy = p->pOrderBy;
|
||||
int iCsr = p->pSrc->a[0].iCursor;
|
||||
@ -1858,7 +1860,7 @@ void sqlite3WhereTabFuncArgs(
|
||||
Expr *pColRef;
|
||||
Expr *pTerm;
|
||||
if( pItem->fg.isTabFunc==0 ) return;
|
||||
pTab = pItem->pTab;
|
||||
pTab = pItem->pSTab;
|
||||
assert( pTab!=0 );
|
||||
pArgs = pItem->u1.pFuncArg;
|
||||
if( pArgs==0 ) return;
|
||||
|
22
src/window.c
22
src/window.c
@ -1077,9 +1077,10 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
|
||||
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].pSelect = pSub;
|
||||
p->pSrc->a[0].fg.isCorrelated = 1;
|
||||
sqlite3SrcListAssignCursors(pParse, p->pSrc);
|
||||
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
|
||||
@ -1093,7 +1094,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
|
||||
}else{
|
||||
memcpy(pTab, pTab2, sizeof(Table));
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
p->pSrc->a[0].pTab = pTab;
|
||||
p->pSrc->a[0].pSTab = pTab;
|
||||
pTab = pTab2;
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = sqlite3WindowExtraAggFuncDepth;
|
||||
@ -1101,8 +1102,6 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
|
||||
w.xSelectCallback2 = sqlite3WalkerDepthDecrease;
|
||||
sqlite3WalkSelect(&w, pSub);
|
||||
}
|
||||
}else{
|
||||
sqlite3SelectDelete(db, pSub);
|
||||
}
|
||||
if( db->mallocFailed ) rc = SQLITE_NOMEM;
|
||||
|
||||
@ -1389,10 +1388,15 @@ int sqlite3WindowCompare(
|
||||
** and initialize registers and cursors used by sqlite3WindowCodeStep().
|
||||
*/
|
||||
void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){
|
||||
int nEphExpr = pSelect->pSrc->a[0].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);
|
||||
@ -2789,7 +2793,7 @@ void sqlite3WindowCodeStep(
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int csrWrite; /* Cursor used to write to eph. table */
|
||||
int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */
|
||||
int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */
|
||||
int nInput = p->pSrc->a[0].pSTab->nCol; /* Number of cols returned by sub */
|
||||
int iInput; /* To iterate through sub cols */
|
||||
int addrNe; /* Address of OP_Ne */
|
||||
int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
|
||||
|
Reference in New Issue
Block a user