diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 1 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/update.c | 25 | ||||
-rw-r--r-- | src/vdbe.c | 19 | ||||
-rw-r--r-- | src/vdbeInt.h | 1 | ||||
-rw-r--r-- | src/vdbeaux.c | 4 | ||||
-rw-r--r-- | src/where.c | 10 | ||||
-rw-r--r-- | src/whereInt.h | 1 | ||||
-rw-r--r-- | src/wherecode.c | 1 |
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) |