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:
18
manifest
18
manifest
@@ -1,5 +1,5 @@
|
||||
C Improve\sthe\scoverage\sof\swal.c\sprovided\sby\sthe\s"coverage-wal"\stest\spermutation.
|
||||
D 2018-12-22T20:32:28.027
|
||||
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-23T21:27:29.955
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in d8b254f8bb81bab43c340d70d17dc3babab40fcc8a348c8255881f780a45fee6
|
||||
@@ -462,7 +462,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
|
||||
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
|
||||
F src/dbstat.c 3c8bd4e77f0244fd2bd7cc90acf116ad2f8e82d70e536637f35ac2bc99b726f9
|
||||
F src/delete.c f7938125847e8ef485448db5fbad29acb2991381a02887dd854c1617315ab9fb
|
||||
F src/expr.c 291e764ce46eb2998296df5a1280bf3aa331e8fb05daf2763253ece530c314e9
|
||||
F src/expr.c 3f398a6698da6fccff353c09d5fc86e7e815386eeeaa9343360c6ef66167dcc6
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 012dd7dba1a62fda6b76e633ab303b2232ee2874a685c915065227ab20ad6ae0
|
||||
F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f
|
||||
@@ -515,7 +515,7 @@ F src/shell.c.in 207da30342db0b6fac8b2487abd60b059a5ea80cc9494bd1db76a1dd4aae7cc
|
||||
F src/sqlite.h.in b54cd42d2f3b739a00de540cafe2dcd0de3b8e1748a2db33a68def487e9e602f
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683
|
||||
F src/sqliteInt.h 24afd47780e8815486e6bf47750e9319edbc31b86988c6f79a88057deea91fb5
|
||||
F src/sqliteInt.h 031e09cf6ea600e23fb4ce6e0a81f3f0e256b990dc6a71d21324d9ab8533b6cd
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@@ -597,7 +597,7 @@ F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
|
||||
F src/walker.c fb94aadc9099ff9c6506d0a8b88d51266005bcaa265403f3d7caf732a562eb66
|
||||
F src/where.c 3818e8a736a05d2cb194e64399af707e367fbcc5c251d785804d02eaf121288e
|
||||
F src/whereInt.h f125f29fca80890768e0b2caa14f95db74b2dacd3a122a168f97aa7b64d6968f
|
||||
F src/wherecode.c c45f03aefc2266b990df0fc4d7acc4e27f56f881f4fc0fc355b7cbc4d7189da5
|
||||
F src/wherecode.c 1945ccec1bdcc783a4a1f810b26c33a0845d0ff44478094c82057bd03a527d39
|
||||
F src/whereexpr.c 36b47f7261d6b6f1a72d774c113b74beddf6745aba1018e64b196e29db233442
|
||||
F src/window.c ea81ecd031ed2cbc14b7db6fd7f4bee2471b894feae5fea0547b15b1e2dd8fb2
|
||||
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.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P fb17fea4b9779fbd3adb6ff9500da83a6ca4fa7cba379aa70074e4328814a7f2
|
||||
R 5ce80528daf81279c922499c0aa02e3a
|
||||
U dan
|
||||
Z 726829f36755ef843b888b06987bbace
|
||||
P 6231485114eb07b258cd0e6e163ca05f7e9cf5664e071808fcb1329b33e4c4f5
|
||||
R 2a6002750376d4a69fc079d42ab7e257
|
||||
U drh
|
||||
Z 5671bf9f2889b8158f9e3e76a5d3d072
|
||||
|
@@ -1 +1 @@
|
||||
6231485114eb07b258cd0e6e163ca05f7e9cf5664e071808fcb1329b33e4c4f5
|
||||
2b6494b1509f0d0189f98aa34c990eee99c775ff57826e79b2c5b0a12b4c97ad
|
446
src/expr.c
446
src/expr.c
@@ -481,7 +481,7 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
|
||||
int reg = 0;
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
|
||||
reg = sqlite3CodeSubselect(pParse, pExpr);
|
||||
}
|
||||
#endif
|
||||
return reg;
|
||||
@@ -2542,7 +2542,11 @@ int sqlite3FindInIndex(
|
||||
}else if( prRhsHasNull ){
|
||||
*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;
|
||||
}else{
|
||||
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,
|
||||
** or IN operators. Examples:
|
||||
** Generate code that will construct an ephemeral table containing all terms
|
||||
** 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 (SELECT a FROM b) -- IN operator with subquery on the right
|
||||
**
|
||||
** The pExpr parameter describes the expression that contains the IN
|
||||
** operator or subquery.
|
||||
** The pExpr parameter is the IN operator.
|
||||
**
|
||||
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
|
||||
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
|
||||
** to some integer key column of a table B-Tree. In this case, use an
|
||||
** intkey B-Tree to store the set of IN(...) values instead of the usual
|
||||
** (slower) variable length keys B-Tree.
|
||||
** If parameter isRowid is non-zero, then LHS of the IN operator is guaranteed
|
||||
** to be a non-null integer. In this case, the ephemeral table can be an
|
||||
** table B-Tree that keyed by only integers. The more general cases uses
|
||||
** an index B-Tree which can have arbitrary keys, but is slower to both
|
||||
** read and write.
|
||||
**
|
||||
** If rMayHaveNull is non-zero, that means that the operation is an IN
|
||||
** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
|
||||
** All this routine does is initialize the register given by rMayHaveNull
|
||||
** to NULL. Calling routines will take care of changing this register
|
||||
** value to non-NULL if the RHS is NULL-free.
|
||||
**
|
||||
** For a SELECT or EXISTS operator, return 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 for IN operators or if an error occurs.
|
||||
** If the LHS expression ("x" in the examples) is a column value, or
|
||||
** the SELECT statement returns a column value, then the affinity of that
|
||||
** column is used to build the index keys. If both 'x' and the
|
||||
** 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.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
int sqlite3CodeSubselect(
|
||||
void sqlite3CodeRhsOfIN(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
|
||||
int rHasNullFlag, /* Register that records whether NULLs exist in RHS */
|
||||
int isRowid /* If true, LHS of IN operator is a rowid */
|
||||
Expr *pExpr, /* The IN operator */
|
||||
int isRowid /* If true, LHS is a rowid */
|
||||
){
|
||||
int jmpIfDynamic = -1; /* One-time test address */
|
||||
int rReg = 0; /* Register storing resulting */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( NEVER(v==0) ) return 0;
|
||||
int jmpIfDynamic = -1; /* One-time test address */
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
Expr *pLeft; /* the LHS of the IN operator */
|
||||
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:
|
||||
**
|
||||
** * The right-hand side is a correlated subquery
|
||||
@@ -2681,202 +2684,211 @@ int sqlite3CodeSubselect(
|
||||
jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
switch( pExpr->op ){
|
||||
case TK_IN: {
|
||||
int addr; /* Address of OP_OpenEphemeral instruction */
|
||||
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
|
||||
KeyInfo *pKeyInfo = 0; /* Key information */
|
||||
int nVal; /* Size of vector pLeft */
|
||||
/* Check to see if this is a vector IN operator */
|
||||
pLeft = pExpr->pLeft;
|
||||
nVal = sqlite3ExprVectorSize(pLeft);
|
||||
assert( !isRowid || nVal==1 );
|
||||
|
||||
nVal = sqlite3ExprVectorSize(pLeft);
|
||||
assert( !isRowid || nVal==1 );
|
||||
/* Construct the ephemeral table that will contain the content of
|
||||
** 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>)'
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with index keys representing the results from the
|
||||
** SELECT or the <exprlist>.
|
||||
**
|
||||
** If the 'x' expression is a column value, or the SELECT...
|
||||
** statement returns a column value, then the affinity of that
|
||||
** column is used to build the index keys. If both 'x' and the
|
||||
** 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) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
** table allocated and opened above.
|
||||
*/
|
||||
Select *pSelect = pExpr->x.pSelect;
|
||||
ExprList *pEList = pSelect->pEList;
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
** table allocated and opened above.
|
||||
*/
|
||||
Select *pSelect = pExpr->x.pSelect;
|
||||
ExprList *pEList = pSelect->pEList;
|
||||
|
||||
ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
|
||||
jmpIfDynamic>=0?"":"CORRELATED "
|
||||
));
|
||||
assert( !isRowid );
|
||||
/* If the LHS and RHS of the IN operator do not match, that
|
||||
** error will have been caught long before we reach this point. */
|
||||
if( ALWAYS(pEList->nExpr==nVal) ){
|
||||
SelectDest dest;
|
||||
int i;
|
||||
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);
|
||||
ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
|
||||
jmpIfDynamic>=0?"":"CORRELATED "
|
||||
));
|
||||
assert( !isRowid );
|
||||
/* If the LHS and RHS of the IN operator do not match, that
|
||||
** error will have been caught long before we reach this point. */
|
||||
if( ALWAYS(pEList->nExpr==nVal) ){
|
||||
SelectDest dest;
|
||||
int i;
|
||||
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;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
|
||||
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
|
||||
);
|
||||
}
|
||||
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:
|
||||
case TK_SELECT:
|
||||
default: {
|
||||
/* Case 3: (SELECT ... FROM ...)
|
||||
** or: EXISTS(SELECT ... FROM ...)
|
||||
**
|
||||
** For a SELECT, generate code to put the values for all columns of
|
||||
** 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.
|
||||
/* 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.
|
||||
*/
|
||||
Select *pSel; /* SELECT statement to encode */
|
||||
SelectDest dest; /* How to deal with SELECT result */
|
||||
int nReg; /* Registers to allocate */
|
||||
Expr *pLimit; /* New limit expression */
|
||||
if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, jmpIfDynamic);
|
||||
jmpIfDynamic = -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"));
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
|
||||
sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
|
||||
}else{
|
||||
dest.eDest = SRT_Exists;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
|
||||
VdbeComment((v, "Init EXISTS result"));
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 ){
|
||||
sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag);
|
||||
/* For a SELECT, generate code to put the values for all columns of
|
||||
** 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 ){
|
||||
sqlite3VdbeJumpHere(v, jmpIfDynamic);
|
||||
@@ -3343,7 +3355,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
|
||||
#if SQLITE_OMIT_SUBQUERY
|
||||
iResult = 0;
|
||||
#else
|
||||
iResult = sqlite3CodeSubselect(pParse, p, 0, 0);
|
||||
iResult = sqlite3CodeSubselect(pParse, p);
|
||||
#endif
|
||||
}else{
|
||||
int i;
|
||||
@@ -3817,14 +3829,14 @@ expr_code_doover:
|
||||
if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
|
||||
sqlite3SubselectError(pParse, nCol, 1);
|
||||
}else{
|
||||
return sqlite3CodeSubselect(pParse, pExpr, 0, 0);
|
||||
return sqlite3CodeSubselect(pParse, pExpr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_SELECT_COLUMN: {
|
||||
int n;
|
||||
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 );
|
||||
if( pExpr->iTable
|
||||
|
@@ -4258,7 +4258,8 @@ void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
|
||||
int sqlite3GetToken(const unsigned char *, int *);
|
||||
void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
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 sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
|
||||
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
|
||||
|
@@ -1076,7 +1076,9 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( (p->flags & EP_xIsSelect) ){
|
||||
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);
|
||||
}else
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user