1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Enhance the sqlite3VdbeMemAboutToChange() shallow-copy validation mechanism

by adding the new OP_ReleaseReg opcode to tell MemAboutToChange() that a
range of registers is no longer needed so that the source register can be
freely changed.  This is a change to debugging and test builds only and
does not impact release builds.  Fix for ticket
[c62c5e58524b204d] and [5ad2aa6921faa1ee].  The previous fix to ticket
[5ad2aa6921faa1ee] is backed out by this change since this change is a better
fix.

FossilOrigin-Name: 36fdeb4f0a66970a35de688b617f90899c89cfdfab659f864df99aa7ebf854ea
This commit is contained in:
drh
2019-12-23 02:18:49 +00:00
parent 4799488e16
commit 13d7950267
7 changed files with 134 additions and 24 deletions

View File

@@ -2130,16 +2130,31 @@ compare_op:
/* Opcode: ElseNotEq * P2 * * *
**
** This opcode must immediately follow an OP_Lt or OP_Gt comparison operator.
** If result of an OP_Eq comparison on the same two operands
** would have be NULL or false (0), then then jump to P2.
** If the result of an OP_Eq comparison on the two previous operands
** would have been true (1), then fall through.
** This opcode must follow an OP_Lt or OP_Gt comparison operator. There
** can be zero or more OP_ReleaseReg opcodes intervening, but no other
** opcodes are allowed to occur between this instruction and the previous
** OP_Lt or OP_Gt. Furthermore, the prior OP_Lt or OP_Gt must have the
** SQLITE_STOREP2 bit set in the P5 field.
**
** If result of an OP_Eq comparison on the same two operands as the
** prior OP_Lt or OP_Gt would have been NULL or false (0), then then
** jump to P2. If the result of an OP_Eq comparison on the two previous
** operands would have been true (1), then fall through.
*/
case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
assert( pOp>aOp );
assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt );
assert( pOp[-1].p5 & SQLITE_STOREP2 );
#ifdef SQLITE_DEBUG
/* Verify the preconditions of this opcode - that it follows an OP_Lt or
** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening
** OP_ReleaseReg opcodes */
int iAddr;
for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){
if( aOp[iAddr].opcode==OP_ReleaseReg ) continue;
assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt );
assert( aOp[iAddr].p5 & SQLITE_STOREP2 );
break;
}
#endif /* SQLITE_DEBUG */
VdbeBranchTaken(iCompare!=0, 2);
if( iCompare!=0 ) goto jump_to_p2;
break;
@@ -7685,6 +7700,53 @@ case OP_Abortable: {
}
#endif
#ifdef SQLITE_DEBUG
/* Opcode: ReleaseReg P1 P2 P3 * *
** Synopsis: release r[P1@P2] mask P3
**
** Release registers from service. Any content that was in the
** the registers is unreliable after this opcode completes.
**
** The registers released will be the P2 registers starting at P1,
** except if bit ii of P3 set, then do not release register P1+ii.
** In other words, P3 is a mask of registers to preserve.
**
** Releasing a register clears the Mem.pScopyFrom pointer. That means
** that if the content of the released register was set using OP_SCopy,
** a change to the value of the source register for the OP_SCopy will no longer
** generate an assertion fault in sqlite3VdbeMemAboutToChange().
**
** TODO: Released registers ought to also have their datatype set to
** MEM_Undefined so that any subsequent attempt to read the released
** register (before it is reinitialized) will generate an assertion fault.
** However, there are places in the code generator which release registers
** before their are used, under the (valid) assumption that the registers
** will not be reallocated for some other purpose before they are used and
** hence are safe to release.
**
** This opcode is only available in testing and debugging builds. It is
** not generated for release builds. The purpose of this opcode is to help
** validate the generated bytecode. This opcode does not actually contribute
** to computing an answer.
*/
case OP_ReleaseReg: {
Mem *pMem;
int i;
u32 constMask;
assert( pOp->p1>0 );
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
pMem = &aMem[pOp->p1];
constMask = pOp->p3;
for(i=0; i<pOp->p2; i++, pMem++){
if( (constMask & MASKBIT32(i))==0 ){
pMem->pScopyFrom = 0;
/* MemSetTypeFlag(pMem, MEM_Undefined); // See the TODO */
}
}
break;
}
#endif
/* Opcode: Noop * * * * *
**
** Do nothing. This instruction is often useful as a jump