diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 17 | ||||
-rw-r--r-- | src/btree.h | 1 | ||||
-rw-r--r-- | src/select.c | 8 | ||||
-rw-r--r-- | src/vdbe.c | 25 |
4 files changed, 51 insertions, 0 deletions
diff --git a/src/btree.c b/src/btree.c index 1806a914a..1dc7b851f 100644 --- a/src/btree.c +++ b/src/btree.c @@ -5667,6 +5667,23 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ return rc; } +/* Return true if the BTree pointed to by cursor pCur contains zero +** rows of content. Return false if the table contains content or if +** if there is some kind of error. This routine is used as an optimization. +** Returning false (a false negative) will always result in a correct +** answer, though perhaps more slowly. But a false positive (an incorrect +** return of true) can yield an incorrect answer. +*/ +int sqlite3BtreeIsEmpty(BtCursor *pCur){ + int rc; + + assert( cursorOwnsBtShared(pCur) ); + assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + if( pCur->eState==CURSOR_VALID ) return 0; + rc = moveToRoot(pCur); + return rc==SQLITE_EMPTY; +} + #ifdef SQLITE_DEBUG /* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that ** this flags are true for a consistent database. diff --git a/src/btree.h b/src/btree.h index 241261dc6..23fb12791 100644 --- a/src/btree.h +++ b/src/btree.h @@ -317,6 +317,7 @@ struct BtreePayload { int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, int flags, int seekResult); int sqlite3BtreeFirst(BtCursor*, int *pRes); +int sqlite3BtreeIsEmpty(BtCursor *pCur); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int flags); int sqlite3BtreeEof(BtCursor*); diff --git a/src/select.c b/src/select.c index 95b2925e3..e2dd24b3e 100644 --- a/src/select.c +++ b/src/select.c @@ -3036,7 +3036,9 @@ static int multiSelect( int priorOp; /* The SRT_ operation to apply to prior selects */ Expr *pLimit; /* Saved values of p->nLimit */ int addr; + int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */ SelectDest uniondest; + testcase( p->op==TK_EXCEPT ); testcase( p->op==TK_UNION ); @@ -3075,6 +3077,8 @@ static int multiSelect( */ if( p->op==TK_EXCEPT ){ op = SRT_Except; + emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab); + VdbeCoverage(v); }else{ assert( p->op==TK_UNION ); op = SRT_Union; @@ -3117,6 +3121,7 @@ static int multiSelect( sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v); sqlite3VdbeResolveLabel(v, iBreak); + if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass); sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); } break; @@ -3128,6 +3133,7 @@ static int multiSelect( int addr; SelectDest intersectdest; int r1; + int emptyBypass; /* INTERSECT is different from the others since it requires ** two temporary tables. Hence it has its own case. Begin @@ -3151,6 +3157,7 @@ static int multiSelect( if( rc ){ goto multi_select_end; } + emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v); /* Code the current SELECT into temporary table "tab2" */ @@ -3194,6 +3201,7 @@ static int multiSelect( sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); + sqlite3VdbeJumpHere(v, emptyBypass); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); break; } diff --git a/src/vdbe.c b/src/vdbe.c index 0020b52bf..895ef109c 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -6398,6 +6398,31 @@ case OP_Rewind: { /* jump0, ncycle */ break; } +/* Opcode: IfEmpty P1 P2 * * * +** Synopsis: if( empty(P1) ) goto P2 +** +** Check to see if the b-tree table that cursor P1 references is empty +** and jump to P2 if it is. +*/ +case OP_IfEmpty: { /* jump */ + VdbeCursor *pC; + BtCursor *pCrsr; + int res; + + assert( pOp->p1>=0 && pOp->p1<p->nCursor ); + assert( pOp->p2>=0 && pOp->p2<p->nOp ); + + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; + assert( pCrsr ); + res = sqlite3BtreeIsEmpty(pCrsr); + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; + break; +} + /* Opcode: Next P1 P2 P3 * P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its |