1
0
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:
drh
2009-11-12 19:59:44 +00:00
parent e3365e6c37
commit 8cff69df11
11 changed files with 91 additions and 64 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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{