aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2012-10-30 18:09:46 +0000
committerdrh <drh@noemail.net>2012-10-30 18:09:46 +0000
commit6e24ff83d8c80c4ef9772b3b22445976e3037194 (patch)
tree4ddfaefd6e42a5392e28c69894bb03e9fe146a08 /src
parent6e227bf3e0641c6eace29e5b198f82cf9344ed48 (diff)
parentda998c8ec01fd838faf413c7c9177361b0e16136 (diff)
downloadsqlite-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.c1
-rw-r--r--src/expr.c1
-rw-r--r--src/main.c2
-rw-r--r--src/select.c40
-rw-r--r--src/sqliteInt.h27
-rw-r--r--src/vdbe.c15
-rw-r--r--src/vdbeInt.h1
-rw-r--r--src/where.c16
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