Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -5437,10 +5437,18 @@ pExpr->affExpr, r1); } break; } #endif + + /* Special opcode used to generate a cursor that raises an + ** SQLITE_INTERNAL error if it is every accessed. Used by the + ** sqlite3OpenDeathCursor() routine. */ + case TK_TABLE: { + sqlite3VdbeAddOp3(v, OP_OpenPseudo, pExpr->iTable, -99, 1); + break; + } } sqlite3ReleaseTempReg(pParse, regFree1); sqlite3ReleaseTempReg(pParse, regFree2); return inReg; } Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -4691,10 +4691,34 @@ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff); } #endif break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_EDITSTMT, pStmt,iAddr,iField,iVal) + ** + ** Make changes to the bytecode in prepared statement pStmt. Modify + ** instruction iAddr. iField is 1, 2, or 3 for p1, p2, or p3. iVal + ** is the new value. + ** + ** This operation is used to deliberately corrupt bytecode in order to + ** exercise the internal self-checks that prevent crashes due to bugs in + ** the query planner and/or code generator. + */ + case SQLITE_TESTCTRL_EDITSTMT: { + sqlite3_stmt *p; /* The prepared statement */ + int iAddr; /* Instruction to change */ + int iField; /* 1, 2, or 3 for P1, P2, or P3 */ + int iVal; /* New value */ + + p = va_arg(ap, sqlite3_stmt*); + iAddr = va_arg(ap, int); + iField = va_arg(ap, int); + iVal = va_arg(ap, int); + sqlite3VdbeEditStmt(p,iAddr,iField,iVal); + break; + } } va_end(ap); #endif /* SQLITE_UNTESTABLE */ return rc; } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -8328,10 +8328,11 @@ #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ +#define SQLITE_TESTCTRL_EDITSTMT 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2940,11 +2940,11 @@ p2 = (u32)pOp->p2; op_column_restart: assert( pC!=0 ); assert( p2<(u32)pC->nField - || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) ); + || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult<=0) ); aOffset = pC->aOffset; assert( aOffset==pC->aType+pC->nField ); assert( pC->eCurType!=CURTYPE_VTAB ); assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); assert( pC->eCurType!=CURTYPE_SORTER ); @@ -2957,10 +2957,14 @@ pReg = &aMem[pC->seekResult]; assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); pC->payloadSize = pC->szRow = pReg->n; pC->aRow = (u8*)pReg->z; + }else if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult<0 ){ + rc = SQLITE_INTERNAL; + sqlite3VdbeError(p, "bad bytecode"); + goto abort_due_to_error; }else{ pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); sqlite3VdbeMemSetNull(pDest); goto op_column_out; Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -264,10 +264,11 @@ void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); VdbeOp *sqlite3VdbeGetLastOp(Vdbe*); +void sqlite3VdbeEditStmt(sqlite3_stmt*,int,int,int); int sqlite3VdbeMakeLabel(Parse*); void sqlite3VdbeRunOnlyOnce(Vdbe*); void sqlite3VdbeReusable(Vdbe*); void sqlite3VdbeDelete(Vdbe*); void sqlite3VdbeMakeReady(Vdbe*,Parse*); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -1703,10 +1703,24 @@ /* Return the most recently added opcode */ VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){ return sqlite3VdbeGetOp(p, p->nOp - 1); } + +/* Edit the bytecode of a runable prepared statement. Used to implement +** SQLITE_TESTCTRL_EDITSTMT. +*/ +void sqlite3VdbeEditStmt(sqlite3_stmt *p, int iAddr, int iField, int iVal){ + VdbeOp *pOp = &((Vdbe*)p)->aOp[iAddr]; + if( iField==1 ){ + pOp->p1 = iVal; + }else if( iField==2 ){ + pOp->p2 = iVal; + }else{ + pOp->p3 = iVal; + } +} #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) /* ** Return an integer value for one of the parameters to the opcode pOp ** determined by character c. Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -7076,29 +7076,23 @@ if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; sqlite3VdbePrintOp(0, pc, pOp); } #endif -#ifdef SQLITE_DEBUG /* -** Return true if cursor iCur is opened by instruction k of the -** bytecode. Used inside of assert() only. +** Make arrangements to open cursor number iCur in the startup code of +** the prepared statement. If this cursor is every accessed via OP_Cursor, +** it will cause an SQLITE_INTERNAL error to be raised. */ -static int cursorIsOpen(Vdbe *v, int iCur, int k){ - while( k>=0 ){ - VdbeOp *pOp = sqlite3VdbeGetOp(v,k--); - if( pOp->p1!=iCur ) continue; - if( pOp->opcode==OP_Close ) return 0; - if( pOp->opcode==OP_OpenRead ) return 1; - if( pOp->opcode==OP_OpenWrite ) return 1; - if( pOp->opcode==OP_OpenDup ) return 1; - if( pOp->opcode==OP_OpenAutoindex ) return 1; - if( pOp->opcode==OP_OpenEphemeral ) return 1; - } - return 0; -} -#endif /* SQLITE_DEBUG */ +static SQLITE_NOINLINE void sqlite3OpenDeathCursor(Parse *pParse, int iCur){ + Expr e; + memset(&e, 0, sizeof(e)); + e.op = TK_TABLE; + e.iTable = iCur; + sqlite3ExprCodeRunJustOnce(pParse, &e, -1); +} + /* ** Generate the end of the WHERE loop. See comments on ** sqlite3WhereBegin() for additional information. */ @@ -7374,41 +7368,31 @@ ){ int x = pOp->p2; assert( pIdx->pTable==pTab ); #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC if( pOp->opcode==OP_Offset ){ - /* Do not need to translate the column number */ + x = 0; }else #endif - if( !HasRowid(pTab) ){ - Index *pPk = sqlite3PrimaryKeyIndex(pTab); - x = pPk->aiColumn[x]; - assert( x>=0 ); - }else{ - testcase( x!=sqlite3StorageColumnToTable(pTab,x) ); - x = sqlite3StorageColumnToTable(pTab,x); - } - x = sqlite3TableColumnToIndex(pIdx, x); + { + if( !HasRowid(pTab) ){ + Index *pPk = sqlite3PrimaryKeyIndex(pTab); + x = pPk->aiColumn[x]; + assert( x>=0 ); + }else{ + testcase( x!=sqlite3StorageColumnToTable(pTab,x) ); + x = sqlite3StorageColumnToTable(pTab,x); + } + x = sqlite3TableColumnToIndex(pIdx, x); + } if( x>=0 ){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; OpcodeRewriteTrace(db, k, pOp); - }else{ - /* Unable to translate the table reference into an index - ** reference. Verify that this is harmless - that the - ** table being referenced really is open. - */ -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC - assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 - || cursorIsOpen(v,pOp->p1,k) - || pOp->opcode==OP_Offset - ); -#else - assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 - || cursorIsOpen(v,pOp->p1,k) - ); -#endif + }else if( pLoop->wsFlags & WHERE_IDX_ONLY ){ + OpcodeRewriteTrace(db, k, pOp); + sqlite3OpenDeathCursor(pParse, pLevel->iTabCur); } }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; OpcodeRewriteTrace(db, k, pOp);