aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2020-01-04 16:55:57 +0000
committerdan <dan@noemail.net>2020-01-04 16:55:57 +0000
commit74ebaadcdd8b1058bbc80bf61334fe09da1c64b5 (patch)
treea1ee43b8fd756ca0f38d6fa570a36ad7f8db83c7 /src
parentf6ea97ea3d278c212ef366e255051e52ca352332 (diff)
downloadsqlite-74ebaadcdd8b1058bbc80bf61334fe09da1c64b5.tar.gz
sqlite-74ebaadcdd8b1058bbc80bf61334fe09da1c64b5.zip
Fix a problem where the loop for the RHS of a LEFT JOIN uses values from an IN() clause as the second or subsequent field of an index.
FossilOrigin-Name: 95ef68966c50f311830cba8c9257a4085c93011d205e0e31867c2917fa62a48e
Diffstat (limited to 'src')
-rw-r--r--src/vdbe.c15
-rw-r--r--src/where.c24
-rw-r--r--src/wherecode.c2
3 files changed, 35 insertions, 6 deletions
diff --git a/src/vdbe.c b/src/vdbe.c
index 5479a311b..8ddc904d1 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -4374,7 +4374,7 @@ seek_not_found:
** Synopsis: seekHit=P2
**
** Set the seekHit flag on cursor P1 to the value in P2.
-** The seekHit flag is used by the IfNoHope opcode.
+* The seekHit flag is used by the IfNoHope opcode.
**
** P1 must be a valid b-tree cursor. P2 must be a boolean value,
** either 0 or 1.
@@ -4389,6 +4389,19 @@ case OP_SeekHit: {
break;
}
+/* Opcode: IfNotOpen P1 P2 * * *
+** Synopsis: if( !csr[P1] ) goto P2
+**
+** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through.
+*/
+case OP_IfNotOpen: { /* jump */
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ if( !p->apCsr[pOp->p1] ){
+ goto jump_to_p2_and_check_for_interrupt;
+ }
+ break;
+}
+
/* Opcode: Found P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
diff --git a/src/where.c b/src/where.c
index 0877b80dd..075276168 100644
--- a/src/where.c
+++ b/src/where.c
@@ -5273,10 +5273,26 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
if( pIn->eEndLoopOp!=OP_Noop ){
if( pIn->nPrefix ){
assert( pLoop->wsFlags & WHERE_IN_EARLYOUT );
- sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur,
- sqlite3VdbeCurrentAddr(v)+2,
- pIn->iBase, pIn->nPrefix);
- VdbeCoverage(v);
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur,
+ sqlite3VdbeCurrentAddr(v)+2+(pLevel->iLeftJoin!=0),
+ pIn->iBase, pIn->nPrefix);
+ VdbeCoverage(v);
+ }
+ if( pLevel->iLeftJoin ){
+ /* For LEFT JOIN queries, cursor pIn->iCur may not have been
+ ** opened yet. This occurs for WHERE clauses such as
+ ** "a = ? AND b IN (...)", where the index is on (a, b). If
+ ** the RHS of the (a=?) is NULL, then the "b IN (...)" may
+ ** never have been coded, but the body of the loop run to
+ ** return the null-row. So, if the cursor is not open yet,
+ ** jump over the OP_Next or OP_Prev instruction about to
+ ** be coded. */
+ sqlite3VdbeAddOp2(v, OP_IfNotOpen, pIn->iCur,
+ sqlite3VdbeCurrentAddr(v) + 2
+ );
+ VdbeCoverage(v);
+ }
}
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
VdbeCoverage(v);
diff --git a/src/wherecode.c b/src/wherecode.c
index beb23e0c1..03e393498 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -593,7 +593,7 @@ static int codeEqualityTerm(
if( i==iEq ){
pIn->iCur = iTab;
pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next;
- if( iEq>0 && (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ if( iEq>0 ){
pIn->iBase = iReg - i;
pIn->nPrefix = i;
pLoop->wsFlags |= WHERE_IN_EARLYOUT;