mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-24 08:21:29 +03:00
Enhance the OP_Found and OP_NotFound opcodes so that they can accept an
array of registers as an unpacked record in addition to a record built using OP_MakeRecord. Use this to avoid OP_MakeRecord calls during IN expression processing. FossilOrigin-Name: b9eab885cd2ca1a1633329e7036c125e8dba62c5
This commit is contained in:
18
src/expr.c
18
src/expr.c
@@ -1546,7 +1546,7 @@ int sqlite3CodeSubselect(
|
||||
affinity = sqlite3ExprAffinity(pLeft);
|
||||
|
||||
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
|
||||
** expression it is handled the same way. A virtual table is
|
||||
** expression it is handled the same way. An ephemeral table is
|
||||
** filled with single-field index keys representing the results
|
||||
** from the SELECT or the <exprlist>.
|
||||
**
|
||||
@@ -1754,12 +1754,7 @@ static void sqlite3ExprCodeIN(
|
||||
}else{
|
||||
/* In this case, the RHS is an index b-tree.
|
||||
*/
|
||||
int r2; /* Register holding LHS value as a Record */
|
||||
|
||||
/* Create a record that can be used for membership testing.
|
||||
*/
|
||||
r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1);
|
||||
|
||||
/* If the set membership test fails, then the result of the
|
||||
** "x IN (...)" expression must be either 0 or NULL. If the set
|
||||
@@ -1775,21 +1770,20 @@ static void sqlite3ExprCodeIN(
|
||||
** Also run this branch if NULL is equivalent to FALSE
|
||||
** for this particular IN operator.
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_NotFound, pExpr->iTable, destIfFalse, r2);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1);
|
||||
|
||||
}else{
|
||||
/* In this branch, the RHS of the IN might contain a NULL and
|
||||
** the presence of a NULL on the RHS makes a difference in the
|
||||
** outcome.
|
||||
*/
|
||||
static const char nullRecord[] = { 0x02, 0x00 };
|
||||
int j1, j2, j3;
|
||||
|
||||
/* First check to see if the LHS is contained in the RHS. If so,
|
||||
** then the presence of NULLs in the RHS does not matter, so jump
|
||||
** over all of the code that follows.
|
||||
*/
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2);
|
||||
j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
|
||||
|
||||
/* Here we begin generating code that runs if the LHS is not
|
||||
** contained within the RHS. Generate additional code that
|
||||
@@ -1798,8 +1792,7 @@ static void sqlite3ExprCodeIN(
|
||||
** jump to destIfFalse.
|
||||
*/
|
||||
j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull);
|
||||
sqlite3VdbeAddOp4(v, OP_Blob, 2, r2, 0, nullRecord, P4_STATIC);
|
||||
j3 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2);
|
||||
j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull);
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1);
|
||||
@@ -1816,7 +1809,6 @@ static void sqlite3ExprCodeIN(
|
||||
*/
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
|
||||
@@ -399,7 +399,7 @@ static void fkLookupParent(
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
|
||||
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Found, iCur, iOk, regRec);
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
|
||||
|
||||
sqlite3ReleaseTempReg(pParse, regRec);
|
||||
sqlite3ReleaseTempRange(pParse, regTemp, nCol);
|
||||
|
||||
@@ -1142,7 +1142,7 @@ void sqlite3Pragma(
|
||||
{ OP_Halt, 0, 0, 0},
|
||||
};
|
||||
sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 1);
|
||||
jmp2 = sqlite3VdbeAddOp3(v, OP_Found, j+2, 0, 3);
|
||||
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, 3, 0);
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
|
||||
sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
|
||||
sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
|
||||
|
||||
@@ -440,7 +440,7 @@ static void codeDistinct(
|
||||
v = pParse->pVdbe;
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_Found, iTab, addrRepeat, r1);
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, r1, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
@@ -1670,7 +1670,7 @@ static int multiSelect(
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_NotFound, tab2, iCont, r1);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
|
||||
0, -1, &dest, iCont, iBreak);
|
||||
|
||||
61
src/vdbe.c
61
src/vdbe.c
@@ -3354,19 +3354,27 @@ case OP_Seek: { /* in2 */
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: Found P1 P2 P3 * *
|
||||
/* Opcode: Found P1 P2 P3 P4 *
|
||||
**
|
||||
** Register P3 holds a blob constructed by MakeRecord. P1 is an index.
|
||||
** If P3 is a prefix of any entry in P1 then a jump is made to P2 and
|
||||
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
|
||||
** P4>0 then register P3 is the first of P4 registers that form an unpacked
|
||||
** record.
|
||||
**
|
||||
** Cursor P1 is on an index btree. If the record identified by P3 and P4
|
||||
** is a prefix of any entry in P1 then a jump is made to P2 and
|
||||
** P1 is left pointing at the matching entry.
|
||||
*/
|
||||
/* Opcode: NotFound P1 P2 P3 * *
|
||||
/* Opcode: NotFound P1 P2 P3 P4 *
|
||||
**
|
||||
** Register P3 holds a blob constructed by MakeRecord. P1 is
|
||||
** an index. If P3 is not the prefix of any entry in P1 then a jump
|
||||
** is made to P2. If P1 does contain an entry whose prefix matches
|
||||
** P3 then control falls through to the next instruction and P1 is
|
||||
** left pointing at the matching entry.
|
||||
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
|
||||
** P4>0 then register P3 is the first of P4 registers that form an unpacked
|
||||
** record.
|
||||
**
|
||||
** Cursor P1 is on an index btree. If the record identified by P3 and P4
|
||||
** is not the prefix of any entry in P1 then a jump is made to P2. If P1
|
||||
** does contain an entry whose prefix matches the P3/P4 record then control
|
||||
** falls through to the next instruction and P1 is left pointing at the
|
||||
** matching entry.
|
||||
**
|
||||
** See also: Found, NotExists, IsUnique
|
||||
*/
|
||||
@@ -3376,6 +3384,7 @@ case OP_Found: { /* jump, in3 */
|
||||
VdbeCursor *pC;
|
||||
int res;
|
||||
UnpackedRecord *pIdxKey;
|
||||
UnpackedRecord r;
|
||||
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
@@ -3384,21 +3393,32 @@ case OP_Found: { /* jump, in3 */
|
||||
|
||||
alreadyExists = 0;
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
if( ALWAYS(pC->pCursor!=0) ){
|
||||
|
||||
assert( pC->isTable==0 );
|
||||
assert( pIn3->flags & MEM_Blob );
|
||||
ExpandBlob(pIn3);
|
||||
pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
|
||||
aTempRec, sizeof(aTempRec));
|
||||
if( pIdxKey==0 ){
|
||||
goto no_mem;
|
||||
if( pOp->p4.i>0 ){
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = pOp->p4.i;
|
||||
r.aMem = pIn3;
|
||||
r.flags = UNPACKED_PREFIX_MATCH;
|
||||
pIdxKey = &r;
|
||||
}else{
|
||||
assert( pIn3->flags & MEM_Blob );
|
||||
ExpandBlob(pIn3);
|
||||
pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
|
||||
aTempRec, sizeof(aTempRec));
|
||||
if( pIdxKey==0 ){
|
||||
goto no_mem;
|
||||
}
|
||||
pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
|
||||
}
|
||||
pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
if( pOp->p4.i==0 ){
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
break;
|
||||
}
|
||||
@@ -3416,9 +3436,10 @@ case OP_Found: { /* jump, in3 */
|
||||
|
||||
/* Opcode: IsUnique P1 P2 P3 P4 *
|
||||
**
|
||||
** Cursor P1 is open on an index. So it has no data and its key consists
|
||||
** of a record generated by OP_MakeRecord where the last field is the
|
||||
** rowid of the entry that the index refers to.
|
||||
** Cursor P1 is open on an index b-tree - that is to say, a btree which
|
||||
** no data and where the key are records generated by OP_MakeRecord with
|
||||
** the list field being the integer ROWID of the entry that the index
|
||||
** entry refers to.
|
||||
**
|
||||
** The P3 register contains an integer record number. Call this record
|
||||
** number R. Register P4 is the first in a set of N contiguous registers
|
||||
|
||||
@@ -170,6 +170,7 @@ int sqlite3VdbeAddOp1(Vdbe*,int,int);
|
||||
int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
|
||||
int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
|
||||
int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
|
||||
int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
|
||||
int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
|
||||
@@ -195,6 +195,22 @@ int sqlite3VdbeAddOp4(
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an opcode that includes the p4 value as an integer.
|
||||
*/
|
||||
int sqlite3VdbeAddOp4Int(
|
||||
Vdbe *p, /* Add the opcode to this VM */
|
||||
int op, /* The new opcode */
|
||||
int p1, /* The P1 operand */
|
||||
int p2, /* The P2 operand */
|
||||
int p3, /* The P3 operand */
|
||||
int p4 /* The P4 operand as an integer */
|
||||
){
|
||||
int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
|
||||
sqlite3VdbeChangeP4(p, addr, SQLITE_INT_TO_PTR(p4), P4_INT32);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new symbolic label for an instruction that has yet to be
|
||||
** coded. The symbolic label is really just a negative number. The
|
||||
|
||||
14
src/where.c
14
src/where.c
@@ -3119,8 +3119,7 @@ static Bitmask codeOneLoopStart(
|
||||
testcase( op==OP_SeekGe );
|
||||
testcase( op==OP_SeekLe );
|
||||
testcase( op==OP_SeekLt );
|
||||
sqlite3VdbeAddOp4(v, op, iIdxCur, addrNxt, regBase,
|
||||
SQLITE_INT_TO_PTR(nConstraint), P4_INT32);
|
||||
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
|
||||
|
||||
/* Load the value for the inequality constraint at the end of the
|
||||
** range (if any).
|
||||
@@ -3153,8 +3152,7 @@ static Bitmask codeOneLoopStart(
|
||||
testcase( op==OP_IdxGE );
|
||||
testcase( op==OP_IdxLT );
|
||||
if( op!=OP_Noop ){
|
||||
sqlite3VdbeAddOp4(v, op, iIdxCur, addrNxt, regBase,
|
||||
SQLITE_INT_TO_PTR(nConstraint), P4_INT32);
|
||||
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
|
||||
sqlite3VdbeChangeP5(v, endEq!=bRev ?1:0);
|
||||
}
|
||||
|
||||
@@ -3283,9 +3281,8 @@ static Bitmask codeOneLoopStart(
|
||||
int r;
|
||||
r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
|
||||
regRowid, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_RowSetTest, regRowset,
|
||||
sqlite3VdbeCurrentAddr(v)+2,
|
||||
r, SQLITE_INT_TO_PTR(iSet), P4_INT32);
|
||||
sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset,
|
||||
sqlite3VdbeCurrentAddr(v)+2, r, iSet);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
|
||||
|
||||
@@ -3816,7 +3813,8 @@ WhereInfo *sqlite3WhereBegin(
|
||||
Bitmask b = pTabItem->colUsed;
|
||||
int n = 0;
|
||||
for(; b; b=b>>1, n++){}
|
||||
sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32);
|
||||
sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1,
|
||||
SQLITE_INT_TO_PTR(n), P4_INT32);
|
||||
assert( n<=pTab->nCol );
|
||||
}
|
||||
}else{
|
||||
|
||||
Reference in New Issue
Block a user