1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Split the code generation for the RHS of IN operators and for SELECT and

EXISTS expressions into two separate subroutines, because there is now little
commonality between those to functions. This is intended to help make the
code easier to read and maintain.

FossilOrigin-Name: 2b6494b1509f0d0189f98aa34c990eee99c775ff57826e79b2c5b0a12b4c97ad
This commit is contained in:
drh
2018-12-23 21:27:29 +00:00
parent 8d69a58119
commit 85bcdce270
5 changed files with 244 additions and 229 deletions

View File

@@ -1,5 +1,5 @@
C Improve\sthe\scoverage\sof\swal.c\sprovided\sby\sthe\s"coverage-wal"\stest\spermutation. C Split\sthe\scode\sgeneration\sfor\sthe\sRHS\sof\sIN\soperators\sand\sfor\sSELECT\sand\nEXISTS\sexpressions\sinto\stwo\sseparate\ssubroutines,\sbecause\sthere\sis\snow\slittle\ncommonality\sbetween\sthose\sto\sfunctions.\sThis\sis\sintended\sto\shelp\smake\sthe\ncode\seasier\sto\sread\sand\smaintain.
D 2018-12-22T20:32:28.027 D 2018-12-23T21:27:29.955
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in d8b254f8bb81bab43c340d70d17dc3babab40fcc8a348c8255881f780a45fee6 F Makefile.in d8b254f8bb81bab43c340d70d17dc3babab40fcc8a348c8255881f780a45fee6
@@ -462,7 +462,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7 F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
F src/dbstat.c 3c8bd4e77f0244fd2bd7cc90acf116ad2f8e82d70e536637f35ac2bc99b726f9 F src/dbstat.c 3c8bd4e77f0244fd2bd7cc90acf116ad2f8e82d70e536637f35ac2bc99b726f9
F src/delete.c f7938125847e8ef485448db5fbad29acb2991381a02887dd854c1617315ab9fb F src/delete.c f7938125847e8ef485448db5fbad29acb2991381a02887dd854c1617315ab9fb
F src/expr.c 291e764ce46eb2998296df5a1280bf3aa331e8fb05daf2763253ece530c314e9 F src/expr.c 3f398a6698da6fccff353c09d5fc86e7e815386eeeaa9343360c6ef66167dcc6
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 012dd7dba1a62fda6b76e633ab303b2232ee2874a685c915065227ab20ad6ae0 F src/fkey.c 012dd7dba1a62fda6b76e633ab303b2232ee2874a685c915065227ab20ad6ae0
F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f
@@ -515,7 +515,7 @@ F src/shell.c.in 207da30342db0b6fac8b2487abd60b059a5ea80cc9494bd1db76a1dd4aae7cc
F src/sqlite.h.in b54cd42d2f3b739a00de540cafe2dcd0de3b8e1748a2db33a68def487e9e602f F src/sqlite.h.in b54cd42d2f3b739a00de540cafe2dcd0de3b8e1748a2db33a68def487e9e602f
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683 F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683
F src/sqliteInt.h 24afd47780e8815486e6bf47750e9319edbc31b86988c6f79a88057deea91fb5 F src/sqliteInt.h 031e09cf6ea600e23fb4ce6e0a81f3f0e256b990dc6a71d21324d9ab8533b6cd
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -597,7 +597,7 @@ F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c fb94aadc9099ff9c6506d0a8b88d51266005bcaa265403f3d7caf732a562eb66 F src/walker.c fb94aadc9099ff9c6506d0a8b88d51266005bcaa265403f3d7caf732a562eb66
F src/where.c 3818e8a736a05d2cb194e64399af707e367fbcc5c251d785804d02eaf121288e F src/where.c 3818e8a736a05d2cb194e64399af707e367fbcc5c251d785804d02eaf121288e
F src/whereInt.h f125f29fca80890768e0b2caa14f95db74b2dacd3a122a168f97aa7b64d6968f F src/whereInt.h f125f29fca80890768e0b2caa14f95db74b2dacd3a122a168f97aa7b64d6968f
F src/wherecode.c c45f03aefc2266b990df0fc4d7acc4e27f56f881f4fc0fc355b7cbc4d7189da5 F src/wherecode.c 1945ccec1bdcc783a4a1f810b26c33a0845d0ff44478094c82057bd03a527d39
F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442 F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
F src/window.c ea81ecd031ed2cbc14b7db6fd7f4bee2471b894feae5fea0547b15b1e2dd8fb2 F src/window.c ea81ecd031ed2cbc14b7db6fd7f4bee2471b894feae5fea0547b15b1e2dd8fb2
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1792,7 +1792,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P fb17fea4b9779fbd3adb6ff9500da83a6ca4fa7cba379aa70074e4328814a7f2 P 6231485114eb07b258cd0e6e163ca05f7e9cf5664e071808fcb1329b33e4c4f5
R 5ce80528daf81279c922499c0aa02e3a R 2a6002750376d4a69fc079d42ab7e257
U dan U drh
Z 726829f36755ef843b888b06987bbace Z 5671bf9f2889b8158f9e3e76a5d3d072

View File

@@ -1 +1 @@
6231485114eb07b258cd0e6e163ca05f7e9cf5664e071808fcb1329b33e4c4f5 2b6494b1509f0d0189f98aa34c990eee99c775ff57826e79b2c5b0a12b4c97ad

View File

@@ -481,7 +481,7 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
int reg = 0; int reg = 0;
#ifndef SQLITE_OMIT_SUBQUERY #ifndef SQLITE_OMIT_SUBQUERY
if( pExpr->op==TK_SELECT ){ if( pExpr->op==TK_SELECT ){
reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); reg = sqlite3CodeSubselect(pParse, pExpr);
} }
#endif #endif
return reg; return reg;
@@ -2542,7 +2542,11 @@ int sqlite3FindInIndex(
}else if( prRhsHasNull ){ }else if( prRhsHasNull ){
*prRhsHasNull = rMayHaveNull = ++pParse->nMem; *prRhsHasNull = rMayHaveNull = ++pParse->nMem;
} }
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); assert( pX->op==TK_IN );
sqlite3CodeRhsOfIN(pParse, pX, eType==IN_INDEX_ROWID);
if( rMayHaveNull ){
sqlite3SetHasNullFlag(v, pX->iTable, rMayHaveNull);
}
pParse->nQueryLoop = savedNQueryLoop; pParse->nQueryLoop = savedNQueryLoop;
}else{ }else{
pX->iTable = iTab; pX->iTable = iTab;
@@ -2626,48 +2630,47 @@ void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){
} }
} }
#ifndef SQLITE_OMIT_SUBQUERY
/* /*
** Generate code for scalar subqueries used as a subquery expression, EXISTS, ** Generate code that will construct an ephemeral table containing all terms
** or IN operators. Examples: ** in the RHS of an IN operator. The IN operator can be in either of two
** forms:
** **
** (SELECT a FROM b) -- subquery
** EXISTS (SELECT a FROM b) -- EXISTS subquery
** x IN (4,5,11) -- IN operator with list on right-hand side ** x IN (4,5,11) -- IN operator with list on right-hand side
** x IN (SELECT a FROM b) -- IN operator with subquery on the right ** x IN (SELECT a FROM b) -- IN operator with subquery on the right
** **
** The pExpr parameter describes the expression that contains the IN ** The pExpr parameter is the IN operator.
** operator or subquery.
** **
** If parameter isRowid is non-zero, then expression pExpr is guaranteed ** If parameter isRowid is non-zero, then LHS of the IN operator is guaranteed
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference ** to be a non-null integer. In this case, the ephemeral table can be an
** to some integer key column of a table B-Tree. In this case, use an ** table B-Tree that keyed by only integers. The more general cases uses
** intkey B-Tree to store the set of IN(...) values instead of the usual ** an index B-Tree which can have arbitrary keys, but is slower to both
** (slower) variable length keys B-Tree. ** read and write.
** **
** If rMayHaveNull is non-zero, that means that the operation is an IN ** If the LHS expression ("x" in the examples) is a column value, or
** (not a SELECT or EXISTS) and that the RHS might contains NULLs. ** the SELECT statement returns a column value, then the affinity of that
** All this routine does is initialize the register given by rMayHaveNull ** column is used to build the index keys. If both 'x' and the
** to NULL. Calling routines will take care of changing this register ** SELECT... statement are columns, then numeric affinity is used
** value to non-NULL if the RHS is NULL-free. ** if either column has NUMERIC or INTEGER affinity. If neither
** ** 'x' nor the SELECT... statement are columns, then numeric affinity
** For a SELECT or EXISTS operator, return the register that holds the ** is used.
** result. For a multi-column SELECT, the result is stored in a contiguous
** array of registers and the return value is the register of the left-most
** result column. Return 0 for IN operators or if an error occurs.
*/ */
#ifndef SQLITE_OMIT_SUBQUERY void sqlite3CodeRhsOfIN(
int sqlite3CodeSubselect(
Parse *pParse, /* Parsing context */ Parse *pParse, /* Parsing context */
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */ Expr *pExpr, /* The IN operator */
int rHasNullFlag, /* Register that records whether NULLs exist in RHS */ int isRowid /* If true, LHS is a rowid */
int isRowid /* If true, LHS of IN operator is a rowid */
){ ){
int jmpIfDynamic = -1; /* One-time test address */ int jmpIfDynamic = -1; /* One-time test address */
int rReg = 0; /* Register storing resulting */ int addr; /* Address of OP_OpenEphemeral instruction */
Vdbe *v = sqlite3GetVdbe(pParse); Expr *pLeft; /* the LHS of the IN operator */
if( NEVER(v==0) ) return 0; KeyInfo *pKeyInfo = 0; /* Key information */
int nVal; /* Size of vector pLeft */
Vdbe *v; /* The prepared statement under construction */
/* The evaluation of the IN/EXISTS/SELECT must be repeated every time it v = sqlite3GetVdbe(pParse);
assert( v!=0 );
/* The evaluation of the RHS of IN operator must be repeated every time it
** is encountered if any of the following is true: ** is encountered if any of the following is true:
** **
** * The right-hand side is a correlated subquery ** * The right-hand side is a correlated subquery
@@ -2681,202 +2684,211 @@ int sqlite3CodeSubselect(
jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
} }
switch( pExpr->op ){ /* Check to see if this is a vector IN operator */
case TK_IN: { pLeft = pExpr->pLeft;
int addr; /* Address of OP_OpenEphemeral instruction */ nVal = sqlite3ExprVectorSize(pLeft);
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ assert( !isRowid || nVal==1 );
KeyInfo *pKeyInfo = 0; /* Key information */
int nVal; /* Size of vector pLeft */
nVal = sqlite3ExprVectorSize(pLeft); /* Construct the ephemeral table that will contain the content of
assert( !isRowid || nVal==1 ); ** RHS of the IN operator.
*/
pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
pExpr->iTable, (isRowid?0:nVal));
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)' if( ExprHasProperty(pExpr, EP_xIsSelect) ){
** expression it is handled the same way. An ephemeral table is /* Case 1: expr IN (SELECT ...)
** filled with index keys representing the results from the **
** SELECT or the <exprlist>. ** Generate code to write the results of the select into the temporary
** ** table allocated and opened above.
** If the 'x' expression is a column value, or the SELECT... */
** statement returns a column value, then the affinity of that Select *pSelect = pExpr->x.pSelect;
** column is used to build the index keys. If both 'x' and the ExprList *pEList = pSelect->pEList;
** SELECT... statement are columns, then numeric affinity is used
** if either column has NUMERIC or INTEGER affinity. If neither
** 'x' nor the SELECT... statement are columns, then numeric affinity
** is used.
*/
pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
pExpr->iTable, (isRowid?0:nVal));
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){ ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
/* Case 1: expr IN (SELECT ...) jmpIfDynamic>=0?"":"CORRELATED "
** ));
** Generate code to write the results of the select into the temporary assert( !isRowid );
** table allocated and opened above. /* If the LHS and RHS of the IN operator do not match, that
*/ ** error will have been caught long before we reach this point. */
Select *pSelect = pExpr->x.pSelect; if( ALWAYS(pEList->nExpr==nVal) ){
ExprList *pEList = pSelect->pEList; SelectDest dest;
int i;
ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY", sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
jmpIfDynamic>=0?"":"CORRELATED " dest.zAffSdst = exprINAffinity(pParse, pExpr);
)); pSelect->iLimit = 0;
assert( !isRowid ); testcase( pSelect->selFlags & SF_Distinct );
/* If the LHS and RHS of the IN operator do not match, that testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
** error will have been caught long before we reach this point. */ if( sqlite3Select(pParse, pSelect, &dest) ){
if( ALWAYS(pEList->nExpr==nVal) ){ sqlite3DbFree(pParse->db, dest.zAffSdst);
SelectDest dest; sqlite3KeyInfoUnref(pKeyInfo);
int i; return;
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
dest.zAffSdst = exprINAffinity(pParse, pExpr);
pSelect->iLimit = 0;
testcase( pSelect->selFlags & SF_Distinct );
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
if( sqlite3Select(pParse, pSelect, &dest) ){
sqlite3DbFree(pParse->db, dest.zAffSdst);
sqlite3KeyInfoUnref(pKeyInfo);
return 0;
}
sqlite3DbFree(pParse->db, dest.zAffSdst);
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
assert( pEList!=0 );
assert( pEList->nExpr>0 );
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
for(i=0; i<nVal; i++){
Expr *p = sqlite3VectorFieldSubexpr(pLeft, i);
pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
pParse, p, pEList->a[i].pExpr
);
}
}
}else if( ALWAYS(pExpr->x.pList!=0) ){
/* Case 2: expr IN (exprlist)
**
** For each expression, build an index key from the evaluation and
** store it in the temporary table. If <expr> is a column, then use
** that columns affinity when building index keys. If <expr> is not
** a column, use numeric affinity.
*/
char affinity; /* Affinity of the LHS of the IN */
int i;
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
int r1, r2, r3;
affinity = sqlite3ExprAffinity(pLeft);
if( !affinity ){
affinity = SQLITE_AFF_BLOB;
}
if( pKeyInfo ){
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
}
/* Loop through each expression in <exprlist>. */
r1 = sqlite3GetTempReg(pParse);
r2 = sqlite3GetTempReg(pParse);
if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC);
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
Expr *pE2 = pItem->pExpr;
int iValToIns;
/* If the expression is not constant then we will need to
** disable the test that was generated above that makes sure
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){
sqlite3VdbeChangeToNoop(v, jmpIfDynamic);
jmpIfDynamic = -1;
}
/* Evaluate the expression and insert it into the temp table */
if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
}else{
r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
if( isRowid ){
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
sqlite3VdbeCurrentAddr(v)+2);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
}else{
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1);
}
}
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
} }
if( pKeyInfo ){ sqlite3DbFree(pParse->db, dest.zAffSdst);
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
assert( pEList!=0 );
assert( pEList->nExpr>0 );
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
for(i=0; i<nVal; i++){
Expr *p = sqlite3VectorFieldSubexpr(pLeft, i);
pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
pParse, p, pEList->a[i].pExpr
);
} }
break; }
}else if( ALWAYS(pExpr->x.pList!=0) ){
/* Case 2: expr IN (exprlist)
**
** For each expression, build an index key from the evaluation and
** store it in the temporary table. If <expr> is a column, then use
** that columns affinity when building index keys. If <expr> is not
** a column, use numeric affinity.
*/
char affinity; /* Affinity of the LHS of the IN */
int i;
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
int r1, r2, r3;
affinity = sqlite3ExprAffinity(pLeft);
if( !affinity ){
affinity = SQLITE_AFF_BLOB;
}
if( pKeyInfo ){
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
} }
case TK_EXISTS: /* Loop through each expression in <exprlist>. */
case TK_SELECT: r1 = sqlite3GetTempReg(pParse);
default: { r2 = sqlite3GetTempReg(pParse);
/* Case 3: (SELECT ... FROM ...) if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC);
** or: EXISTS(SELECT ... FROM ...) for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
** Expr *pE2 = pItem->pExpr;
** For a SELECT, generate code to put the values for all columns of int iValToIns;
** the first row into an array of registers and return the index of
** the first register. /* If the expression is not constant then we will need to
** ** disable the test that was generated above that makes sure
** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists) ** this code only executes once. Because for a non-constant
** into a register and return that register number. ** expression we need to rerun this code each time.
**
** In both cases, the query is augmented with "LIMIT 1". Any
** preexisting limit is discarded in place of the new LIMIT 1.
*/ */
Select *pSel; /* SELECT statement to encode */ if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){
SelectDest dest; /* How to deal with SELECT result */ sqlite3VdbeChangeToNoop(v, jmpIfDynamic);
int nReg; /* Registers to allocate */ jmpIfDynamic = -1;
Expr *pLimit; /* New limit expression */ }
testcase( pExpr->op==TK_EXISTS ); /* Evaluate the expression and insert it into the temp table */
testcase( pExpr->op==TK_SELECT ); if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
pSel = pExpr->x.pSelect;
ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
jmpIfDynamic>=0?"":"CORRELATED "));
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
pParse->nMem += nReg;
if( pExpr->op==TK_SELECT ){
dest.eDest = SRT_Mem;
dest.iSdst = dest.iSDParm;
dest.nSdst = nReg;
sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
VdbeComment((v, "Init subquery result"));
}else{ }else{
dest.eDest = SRT_Exists; r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm); if( isRowid ){
VdbeComment((v, "Init EXISTS result")); sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
sqlite3VdbeCurrentAddr(v)+2);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
}else{
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1);
}
} }
pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0);
if( pSel->pLimit ){
sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft);
pSel->pLimit->pLeft = pLimit;
}else{
pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
}
pSel->iLimit = 0;
if( sqlite3Select(pParse, pSel, &dest) ){
return 0;
}
rReg = dest.iSDParm;
ExprSetVVAProperty(pExpr, EP_NoReduce);
break;
} }
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
}
if( pKeyInfo ){
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
}
if( jmpIfDynamic>=0 ){
sqlite3VdbeJumpHere(v, jmpIfDynamic);
}
}
#endif /* SQLITE_OMIT_SUBQUERY */
/*
** Generate code for scalar subqueries used as a subquery expression
** or EXISTS operator:
**
** (SELECT a FROM b) -- subquery
** EXISTS (SELECT a FROM b) -- EXISTS subquery
**
** The pExpr parameter is the SELECT or EXISTS operator to be coded.
**
** The register that holds the result. For a multi-column SELECT,
** the result is stored in a contiguous array of registers and the
** return value is the register of the left-most result column.
** Return 0 if an error occurs.
*/
#ifndef SQLITE_OMIT_SUBQUERY
int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
int jmpIfDynamic = -1; /* One-time test address */
int rReg = 0; /* Register storing resulting */
Select *pSel; /* SELECT statement to encode */
SelectDest dest; /* How to deal with SELECT result */
int nReg; /* Registers to allocate */
Expr *pLimit; /* New limit expression */
Vdbe *v = sqlite3GetVdbe(pParse);
assert( v!=0 );
/* The evaluation of the EXISTS/SELECT must be repeated every time it
** is encountered if any of the following is true:
**
** * The right-hand side is a correlated subquery
** * The right-hand side is an expression list containing variables
** * We are inside a trigger
**
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
if( !ExprHasProperty(pExpr, EP_VarSelect) ){
jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
} }
if( rHasNullFlag ){ /* For a SELECT, generate code to put the values for all columns of
sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag); ** the first row into an array of registers and return the index of
** the first register.
**
** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists)
** into a register and return that register number.
**
** In both cases, the query is augmented with "LIMIT 1". Any
** preexisting limit is discarded in place of the new LIMIT 1.
*/
testcase( pExpr->op==TK_EXISTS );
testcase( pExpr->op==TK_SELECT );
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
pSel = pExpr->x.pSelect;
ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
jmpIfDynamic>=0?"":"CORRELATED "));
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
pParse->nMem += nReg;
if( pExpr->op==TK_SELECT ){
dest.eDest = SRT_Mem;
dest.iSdst = dest.iSDParm;
dest.nSdst = nReg;
sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
VdbeComment((v, "Init subquery result"));
}else{
dest.eDest = SRT_Exists;
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
VdbeComment((v, "Init EXISTS result"));
} }
pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0);
if( pSel->pLimit ){
sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft);
pSel->pLimit->pLeft = pLimit;
}else{
pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
}
pSel->iLimit = 0;
if( sqlite3Select(pParse, pSel, &dest) ){
return 0;
}
rReg = dest.iSDParm;
ExprSetVVAProperty(pExpr, EP_NoReduce);
if( jmpIfDynamic>=0 ){ if( jmpIfDynamic>=0 ){
sqlite3VdbeJumpHere(v, jmpIfDynamic); sqlite3VdbeJumpHere(v, jmpIfDynamic);
@@ -3343,7 +3355,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
#if SQLITE_OMIT_SUBQUERY #if SQLITE_OMIT_SUBQUERY
iResult = 0; iResult = 0;
#else #else
iResult = sqlite3CodeSubselect(pParse, p, 0, 0); iResult = sqlite3CodeSubselect(pParse, p);
#endif #endif
}else{ }else{
int i; int i;
@@ -3817,14 +3829,14 @@ expr_code_doover:
if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
sqlite3SubselectError(pParse, nCol, 1); sqlite3SubselectError(pParse, nCol, 1);
}else{ }else{
return sqlite3CodeSubselect(pParse, pExpr, 0, 0); return sqlite3CodeSubselect(pParse, pExpr);
} }
break; break;
} }
case TK_SELECT_COLUMN: { case TK_SELECT_COLUMN: {
int n; int n;
if( pExpr->pLeft->iTable==0 ){ if( pExpr->pLeft->iTable==0 ){
pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0); pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft);
} }
assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT );
if( pExpr->iTable if( pExpr->iTable

View File

@@ -4258,7 +4258,8 @@ void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
int sqlite3GetToken(const unsigned char *, int *); int sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3NestedParse(Parse*, const char*, ...);
void sqlite3ExpirePreparedStatements(sqlite3*, int); void sqlite3ExpirePreparedStatements(sqlite3*, int);
int sqlite3CodeSubselect(Parse*, Expr *, int, int); void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
int sqlite3CodeSubselect(Parse*, Expr*);
void sqlite3SelectPrep(Parse*, Select*, NameContext*); void sqlite3SelectPrep(Parse*, Select*, NameContext*);
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);

View File

@@ -1076,7 +1076,9 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
#ifndef SQLITE_OMIT_SUBQUERY #ifndef SQLITE_OMIT_SUBQUERY
if( (p->flags & EP_xIsSelect) ){ if( (p->flags & EP_xIsSelect) ){
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); int iSelect;
assert( p->op==TK_SELECT );
iSelect = sqlite3CodeSubselect(pParse, p);
sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
}else }else
#endif #endif