aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2019-12-29 00:52:41 +0000
committerdrh <drh@noemail.net>2019-12-29 00:52:41 +0000
commitbe3da24134f53a0c8f10291611af758dc0ced611 (patch)
treea5ed589b142b6d9ab3230a9f85c106f2ee161788 /src
parent997d7434b7688f9fbd48fdacdbf1769e2f4cccbe (diff)
downloadsqlite-be3da24134f53a0c8f10291611af758dc0ced611.tar.gz
sqlite-be3da24134f53a0c8f10291611af758dc0ced611.zip
Add the OP_FinishSeek opcode which completes an OP_DeferredSeek if the seek
has not already completed. Also add the sqlite3WhereUsesDeferredSeek() interface to the query planner. The UPDATE implementation adds an OP_FinishSeek before running the final OP_Insert if one is needed. Ticket [ec8abb025e78f40c] and also an assertion fault reported by Yongheng. FossilOrigin-Name: 21ef6e99331210b80fa7c71b4f02e8f768a748d01aef884368af2f6b51a067e0
Diffstat (limited to 'src')
-rw-r--r--src/btree.c1
-rw-r--r--src/sqliteInt.h1
-rw-r--r--src/update.c25
-rw-r--r--src/vdbe.c19
-rw-r--r--src/vdbeInt.h1
-rw-r--r--src/vdbeaux.c4
-rw-r--r--src/where.c10
-rw-r--r--src/whereInt.h1
-rw-r--r--src/wherecode.c1
9 files changed, 45 insertions, 18 deletions
diff --git a/src/btree.c b/src/btree.c
index 52c2ceaff..eb80816bb 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -8658,7 +8658,6 @@ int sqlite3BtreeInsert(
if( flags & BTREE_SAVEPOSITION ){
assert( pCur->curFlags & BTCF_ValidNKey );
assert( pX->nKey==pCur->info.nKey );
- assert( pCur->info.nSize!=0 );
assert( loc==0 );
}
#endif
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index c1ed684ff..84f8e1e5c 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -4115,6 +4115,7 @@ int sqlite3WhereOkOnePass(WhereInfo*, int*);
#define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */
#define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */
#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
+int sqlite3WhereUsesDeferredSeek(WhereInfo*);
void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
diff --git a/src/update.c b/src/update.c
index 52ae8a030..8ff92daea 100644
--- a/src/update.c
+++ b/src/update.c
@@ -191,6 +191,7 @@ void sqlite3Update(
int iPk = 0; /* First of nPk cells holding PRIMARY KEY value */
i16 nPk = 0; /* Number of components of the PRIMARY KEY */
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
+ int bFinishSeek = 1; /* The OP_FinishSeek opcode is needed */
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
@@ -524,6 +525,7 @@ void sqlite3Update(
pWInfo = 0;
eOnePass = ONEPASS_SINGLE;
sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
+ bFinishSeek = 0;
}else{
/* Begin the database scan.
**
@@ -550,6 +552,7 @@ void sqlite3Update(
** strategy that uses an index for which one or more columns are being
** updated. */
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo);
if( eOnePass!=ONEPASS_SINGLE ){
sqlite3MultiWrite(pParse);
if( eOnePass==ONEPASS_MULTI ){
@@ -713,6 +716,7 @@ void sqlite3Update(
testcase( i==31 );
testcase( i==32 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k);
+ bFinishSeek = 0;
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, k);
}
@@ -800,21 +804,14 @@ void sqlite3Update(
/* Delete the index entries associated with the current record. */
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
-#ifndef SQLITE_OMIT_GENERATED_COLUMNS
- /* If pTab contains one or more virtual columns, then it is possible
- ** (though unlikely) that no OP_Column opcodes have been run against
- ** the table since the OP_SeekDeferred, meaning that there has not been
- ** a seek against the cursor yet. The OP_Delete opcode and OP_Insert
- ** opcodes that follow will be needing this seek, so code a bogus
- ** OP_Column just to make sure the seek has been done.
- ** See ticket ec8abb025e78f40c 2019-12-26
- */
- if( eOnePass!=ONEPASS_OFF && (pTab->tabFlags & TF_HasVirtual)!=0 ){
- int r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_Column, iDataCur, 0, r1);
- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ /* We must run the OP_FinishSeek opcode to resolve a prior
+ ** OP_DeferredSeek if there is any possibility that there have been
+ ** no OP_Column opcodes since the OP_DeferredSeek was issued. But
+ ** we want to avoid the OP_FinishSeek if possible, as running it
+ ** costs CPU cycles. */
+ if( bFinishSeek ){
+ sqlite3VdbeAddOp1(v, OP_FinishSeek, iDataCur);
}
-#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
/* If changing the rowid value, or if there are foreign key constraints
** to process, delete the old record. Otherwise, add a noop OP_Delete
diff --git a/src/vdbe.c b/src/vdbe.c
index 23c8b18ac..0aeaa072c 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -4827,6 +4827,7 @@ case OP_Insert: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->deferredMoveto==0 );
assert( pC->uc.pCursor!=0 );
assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
@@ -5700,6 +5701,24 @@ case OP_IdxRowid: { /* out2 */
break;
}
+/* Opcode: FinishSeek P1 * * * *
+**
+** If cursor P1 was previously moved via OP_DeferredSeek, complete that
+** seek operation now, without further delay. If the cursor seek has
+** already occurred, this instruction is a no-op.
+*/
+case OP_FinishSeek: {
+ VdbeCursor *pC; /* The P1 index cursor */
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ if( pC->deferredMoveto ){
+ rc = sqlite3VdbeFinishMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ }
+ break;
+}
+
/* Opcode: IdxGE P1 P2 P3 P4 P5
** Synopsis: key=r[P3@P4]
**
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index cc410510a..138377def 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -483,6 +483,7 @@ struct PreUpdate {
void sqlite3VdbeError(Vdbe*, const char *, ...);
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
+int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*);
int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
int sqlite3VdbeCursorRestore(VdbeCursor*);
u32 sqlite3VdbeSerialTypeLen(u32);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index ac52303d5..9d04c160a 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -3414,7 +3414,7 @@ void sqlite3VdbeDelete(Vdbe *p){
** carried out. Seek the cursor now. If an error occurs, return
** the appropriate error code.
*/
-static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
+int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){
int res, rc;
#ifdef SQLITE_TEST
extern int sqlite3_search_count;
@@ -3486,7 +3486,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
*piCol = iMap - 1;
return SQLITE_OK;
}
- return handleDeferredMoveto(p);
+ return sqlite3VdbeFinishMoveto(p);
}
if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
return handleMovedCursor(p);
diff --git a/src/where.c b/src/where.c
index 244bc57a4..7c05a58e4 100644
--- a/src/where.c
+++ b/src/where.c
@@ -120,7 +120,7 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
/*
** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to
-** operate directly on the rowis returned by a WHERE clause. Return
+** operate directly on the rowids returned by a WHERE clause. Return
** ONEPASS_SINGLE (1) if the statement can operation directly because only
** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass
** optimization can be used on multiple
@@ -148,6 +148,14 @@ int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
}
/*
+** Return TRUE if the WHERE loop uses the OP_DeferredSeek opcode to move
+** the data cursor to the row selected by the index cursor.
+*/
+int sqlite3WhereUsesDeferredSeek(WhereInfo *pWInfo){
+ return pWInfo->bDeferredSeek;
+}
+
+/*
** Move the content of pSrc into pDest
*/
static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
diff --git a/src/whereInt.h b/src/whereInt.h
index 8d333c032..f500e01d4 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -459,6 +459,7 @@ struct WhereInfo {
i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
u8 sorted; /* True if really sorted (not just grouped) */
u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
+ u8 bDeferredSeek; /* Uses OP_DeferredSeek */
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
u8 eDistinct; /* One of the WHERE_DISTINCT_* values */
u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
diff --git a/src/wherecode.c b/src/wherecode.c
index 9c51b5669..96c2971a5 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -1045,6 +1045,7 @@ static void codeDeferredSeek(
assert( iIdxCur>0 );
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
+ pWInfo->bDeferredSeek = 1;
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)