diff options
author | drh <drh@noemail.net> | 2012-10-30 18:09:46 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2012-10-30 18:09:46 +0000 |
commit | 6e24ff83d8c80c4ef9772b3b22445976e3037194 (patch) | |
tree | 4ddfaefd6e42a5392e28c69894bb03e9fe146a08 /src | |
parent | 6e227bf3e0641c6eace29e5b198f82cf9344ed48 (diff) | |
parent | da998c8ec01fd838faf413c7c9177361b0e16136 (diff) | |
download | sqlite-6e24ff83d8c80c4ef9772b3b22445976e3037194.tar.gz sqlite-6e24ff83d8c80c4ef9772b3b22445976e3037194.zip |
Enable the use of coroutines as an alternative to manifesting views used
in a FROM clause.
FossilOrigin-Name: 9dca18f5fea84afbecb314ee1cdfb98430656af3
Diffstat (limited to 'src')
-rw-r--r-- | src/delete.c | 1 | ||||
-rw-r--r-- | src/expr.c | 1 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/select.c | 40 | ||||
-rw-r--r-- | src/sqliteInt.h | 27 | ||||
-rw-r--r-- | src/vdbe.c | 15 | ||||
-rw-r--r-- | src/vdbeInt.h | 1 | ||||
-rw-r--r-- | src/where.c | 16 |
8 files changed, 84 insertions, 19 deletions
diff --git a/src/delete.c b/src/delete.c index c83ea9897..01a130d65 100644 --- a/src/delete.c +++ b/src/delete.c @@ -112,6 +112,7 @@ void sqlite3MaterializeView( sqlite3SelectDelete(db, pDup); } pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); + if( pDup ) pDup->selFlags |= SF_Materialize; } sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); sqlite3Select(pParse, pDup, &dest); diff --git a/src/expr.c b/src/expr.c index 04216b171..3eee3c6d6 100644 --- a/src/expr.c +++ b/src/expr.c @@ -939,6 +939,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; pNewItem->isCorrelated = pOldItem->isCorrelated; + pNewItem->viaCoroutine = pOldItem->viaCoroutine; pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex); pNewItem->notIndexed = pOldItem->notIndexed; pNewItem->pIndex = pOldItem->pIndex; diff --git a/src/main.c b/src/main.c index 8f3d25dd2..b2826c0c7 100644 --- a/src/main.c +++ b/src/main.c @@ -3019,7 +3019,7 @@ int sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { sqlite3 *db = va_arg(ap, sqlite3*); - db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff); + db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff); break; } diff --git a/src/select.c b/src/select.c index ea85d1d71..cba766f99 100644 --- a/src/select.c +++ b/src/select.c @@ -3909,8 +3909,17 @@ int sqlite3Select( int isAggSub; if( pSub==0 ) continue; + + /* Sometimes the code for a subquery will be generated more than + ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, + ** for example. In that case, do not regenerate the code to manifest + ** a view or the co-routine to implement a view. The first instance + ** is sufficient, though the subroutine to manifest the view does need + ** to be invoked again. */ if( pItem->addrFillSub ){ - sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); + if( pItem->viaCoroutine==0 ){ + sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); + } continue; } @@ -3931,6 +3940,35 @@ int sqlite3Select( p->selFlags |= SF_Aggregate; } i = -1; + }else if( pTabList->nSrc==1 && (p->selFlags & SF_Materialize)==0 + && OptimizationEnabled(db, SQLITE_SubqCoroutine) + ){ + /* Implement a co-routine that will return a single row of the result + ** set on each invocation. + */ + int addrTop; + int addrEof; + pItem->regReturn = ++pParse->nMem; + addrEof = ++pParse->nMem; + sqlite3VdbeAddOp0(v, OP_Goto); + addrTop = sqlite3VdbeAddOp1(v, OP_OpenPseudo, pItem->iCursor); + sqlite3VdbeChangeP5(v, 1); + VdbeComment((v, "coroutine for %s", pItem->pTab->zName)); + pItem->addrFillSub = addrTop; + sqlite3VdbeAddOp2(v, OP_Integer, 0, addrEof); + sqlite3VdbeChangeP5(v, 1); + sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); + explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); + sqlite3Select(pParse, pSub, &dest); + pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; + pItem->viaCoroutine = 1; + sqlite3VdbeChangeP2(v, addrTop, dest.iSdst); + sqlite3VdbeChangeP3(v, addrTop, dest.nSdst); + sqlite3VdbeAddOp2(v, OP_Integer, 1, addrEof); + sqlite3VdbeAddOp1(v, OP_Yield, pItem->regReturn); + VdbeComment((v, "end %s", pItem->pTab->zName)); + sqlite3VdbeJumpHere(v, addrTop-1); + sqlite3ClearTempRegCache(pParse); }else{ /* Generate a subroutine that will fill an ephemeral table with ** the content of this subquery. pItem->addrFillSub will point diff --git a/src/sqliteInt.h b/src/sqliteInt.h index dbefb4c58..e44ce7b7e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -827,7 +827,7 @@ struct sqlite3 { unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ - u8 dbOptFlags; /* Flags to enable/disable optimizations */ + u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ @@ -972,7 +972,8 @@ struct sqlite3 { #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ -#define SQLITE_AllOpts 0x00ff /* All optimizations */ +#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ +#define SQLITE_AllOpts 0xffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. @@ -1878,8 +1879,9 @@ struct SrcList { int addrFillSub; /* Address of subroutine to manifest a subquery */ int regReturn; /* Register holding return address of addrFillSub */ u8 jointype; /* Type of join between this able and the previous */ - u8 notIndexed; /* True if there is a NOT INDEXED clause */ - u8 isCorrelated; /* True if sub-query is correlated */ + unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ + unsigned isCorrelated :1; /* True if sub-query is correlated */ + unsigned viaCoroutine :1; /* Implemented as a co-routine */ #ifndef SQLITE_OMIT_EXPLAIN u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ #endif @@ -2103,14 +2105,15 @@ struct Select { ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ -#define SF_Distinct 0x01 /* Output should be DISTINCT */ -#define SF_Resolved 0x02 /* Identifiers have been resolved */ -#define SF_Aggregate 0x04 /* Contains aggregate functions */ -#define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */ -#define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */ -#define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */ -#define SF_UseSorter 0x40 /* Sort using a sorter */ -#define SF_Values 0x80 /* Synthesized from VALUES clause */ +#define SF_Distinct 0x0001 /* Output should be DISTINCT */ +#define SF_Resolved 0x0002 /* Identifiers have been resolved */ +#define SF_Aggregate 0x0004 /* Contains aggregate functions */ +#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ +#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ +#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ +#define SF_UseSorter 0x0040 /* Sort using a sorter */ +#define SF_Values 0x0080 /* Synthesized from VALUES clause */ +#define SF_Materialize 0x0100 /* Force materialization of views */ /* diff --git a/src/vdbe.c b/src/vdbe.c index c8066d63c..df11b71ea 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2237,6 +2237,11 @@ case OP_Column: { } }else if( ALWAYS(pC->pseudoTableReg>0) ){ pReg = &aMem[pC->pseudoTableReg]; + if( pC->multiPseudo ){ + sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); + Deephemeralize(pDest); + goto op_column_out; + } assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); payloadSize = pReg->n; @@ -3318,12 +3323,13 @@ case OP_SorterOpen: { break; } -/* Opcode: OpenPseudo P1 P2 P3 * * +/* Opcode: OpenPseudo P1 P2 P3 * P5 ** ** Open a new cursor that points to a fake table that contains a single ** row of data. The content of that one row in the content of memory -** register P2. In other words, cursor P1 becomes an alias for the -** MEM_Blob content contained in register P2. +** register P2 when P5==0. In other words, cursor P1 becomes an alias for the +** MEM_Blob content contained in register P2. When P5==1, then the +** row is represented by P3 consecutive registers beginning with P2. ** ** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into @@ -3343,6 +3349,7 @@ case OP_OpenPseudo: { pCx->pseudoTableReg = pOp->p2; pCx->isTable = 1; pCx->isIndex = 0; + pCx->multiPseudo = pOp->p5; break; } @@ -4307,7 +4314,7 @@ case OP_Rowid: { /* out2-prerelease */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pseudoTableReg==0 ); + assert( pC->pseudoTableReg==0 || pC->nullRow ); if( pC->nullRow ){ pOut->flags = MEM_Null; break; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index aecada603..fac0b2f6e 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -63,6 +63,7 @@ struct VdbeCursor { Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ Bool isSorter; /* True if a new-style sorter */ + Bool multiPseudo; /* Multi-register pseudo-cursor */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ diff --git a/src/where.c b/src/where.c index 35e7759de..38f6f1436 100644 --- a/src/where.c +++ b/src/where.c @@ -1821,6 +1821,10 @@ static void bestAutomaticIndex(WhereBestIdx *p){ /* We already have some kind of index in use for this query. */ return; } + if( pSrc->viaCoroutine ){ + /* Cannot index a co-routine */ + return; + } if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; @@ -2998,7 +3002,7 @@ static int isSortingIndex( ** SQLITE_BIG_DBL. If a plan is found that uses the named index, ** then the cost is calculated in the usual way. ** -** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table +** If a NOT INDEXED clause was attached to the table ** in the SELECT statement, then no indexes are considered. However, the ** selected plan may still take advantage of the built-in rowid primary key ** index. @@ -4026,6 +4030,16 @@ static Bitmask codeOneLoopStart( VdbeComment((v, "init LEFT JOIN no-match flag")); } + /* Special case of a FROM clause subquery implemented as a co-routine */ + if( pTabItem->viaCoroutine ){ + int regYield = pTabItem->regReturn; + sqlite3VdbeAddOp2(v, OP_Integer, pTabItem->addrFillSub-1, regYield); + pLevel->p2 = sqlite3VdbeAddOp1(v, OP_Yield, regYield); + VdbeComment((v, "next row of co-routine %s", pTabItem->pTab->zName)); + sqlite3VdbeAddOp2(v, OP_If, regYield+1, addrBrk); + pLevel->op = OP_Goto; + }else + #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ /* Case 0: The table is a virtual-table. Use the VFilter and VNext |