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

@ -1,5 +1,5 @@
C Change\sthe\scode\sgenerator\sfor\sthe\sIN\soperator\sso\sthat\sit\savoids\screating\nOP_Eq\sand\sOP_Ne\sopcode\swith\sthe\ssame\sP1\sand\sP3\sarguments.\s\sThis\senables\sus\nto\sback\sout\scheck-in\s[ddb17d92df194337]\sand\salso\sfix\sticket\s[188f912b51cd802].
D 2019-12-22T23:48:36.594
C Enhance\sthe\ssqlite3VdbeMemAboutToChange()\sshallow-copy\svalidation\smechanism\nby\sadding\sthe\snew\sOP_ReleaseReg\sopcode\sto\stell\sMemAboutToChange()\sthat\sa\nrange\sof\sregisters\sis\sno\slonger\sneeded\sso\sthat\sthe\ssource\sregister\scan\sbe\nfreely\schanged.\s\sThis\sis\sa\schange\sto\sdebugging\sand\stest\sbuilds\sonly\sand\ndoes\snot\simpact\srelease\sbuilds.\s\sFix\sfor\sticket\n[c62c5e58524b204d]\sand\s[5ad2aa6921faa1ee].\s\sThe\sprevious\sfix\sto\sticket\n[5ad2aa6921faa1ee]\sis\sbacked\sout\sby\sthis\schange\ssince\sthis\schange\sis\sa\sbetter\nfix.
D 2019-12-23T02:18:49.115
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -479,7 +479,7 @@ F src/date.c e1d8ac7102f3f283e63e13867acb0efa33861cf34f0faf4cdbaf9fa7a1eb7041
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
F src/dbstat.c 6c407e549406c10fde9ac3987f6d734459205239ad370369bc5fcd683084a4fa
F src/delete.c a5c59b9c0251cf7682bc52af0d64f09b1aefc6781a63592c8f1136f7b73c66e4
F src/expr.c 210669714d4f9420b97d2577581ebad7a2da6e87b37b44f62ac67f44a41ca4ab
F src/expr.c 0f38befff4c3794ec051eee3e8a555f73559aab1aa506dd0abc63949358c9ebc
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 92a248ec0fa4ed8ab60c98d9b188ce173aaf218f32e7737ba77deb2a684f9847
F src/func.c ed33e38cd642058182a31a3f518f2e34f4bbe53aa483335705c153c4d3e50b12
@ -599,11 +599,11 @@ F src/upsert.c b445315c8958d8f17ec3297d06842e61dacaad0633ccaec1e4e160de7e562212
F src/utf.c 2f0fac345c7660d5c5bd3df9e9d8d33d4c27f366bcfb09e07443064d751a0507
F src/util.c 2c92bc706bbdb1c45a25180291e7e05a56e297aa5dd7b2bcd2b1c47e8bb05b17
F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf
F src/vdbe.c c1e35ead7ef20b5cfedd30c45b9b7eceb7fa88145bf46c11c528775318e78950
F src/vdbe.h fdbc0a11e5768a702b46ce63286f60e22e71351a29bd98b3666405e1fccc7802
F src/vdbe.c 979cb8e16e4aebd9f1838260902225c68e706f6ab92781fc119937907140cb89
F src/vdbe.h 3f068f00b23aebf392df142312ab5874588371c6d83e60d953f6d6b6453491c5
F src/vdbeInt.h bd589b8b7273286858950717e0e1ec5c88b18af45079a3366dc1371865cea704
F src/vdbeapi.c 1252d80c548711e47a6d84dae88ed4e95d3fbb4e7bd0eaa1347299af7efddf02
F src/vdbeaux.c 858bb43a9d98846cc23fa8c8d0970ada805dd75bc6a01b69e972da608f7f59b1
F src/vdbeaux.c e041d20b5000abbd651f7d470c66eaa13e868f58a13c1a940b4d426c6d0d43b5
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
F src/vdbemem.c 2eb00a4d1a7d2c97510a4d1ccaf4e12c9143f2ced1c6b96b5eddc372183c9121
F src/vdbesort.c a3be032cc3fee0e3af31773af4a7a6f931b7230a34f53282ccf1d9a2a72343be
@ -1604,7 +1604,7 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
F test/update.test 127fc037887808a9ff2d6a7dd9e524c215c939d15f8878dd3ee0169ad50f7b19
F test/update.test 6fdd76fdf3194fb10ee184b3b2999c61d10d2a0b825dbcaa2d08ff394f3e26e3
F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
F test/upsert1.test 0b740c8488fd2f5a06ac317c9913f7ef1eda8282f2db58b544b89480c51efab3
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
@ -1852,7 +1852,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 89a9dad6330270a4c3b962f86a208088d2ea9883c7d291351a77f058e0ed8b0c
R 22f0b35f3db7fedb6dbdf818d6863c3f
P 9ab985a9c8160b905730678f40ed440a246cdec549c798bafefaed5abbc0437f
R 6c6b2e22daef2e006951b2518e920f13
U drh
Z ccb160bfdbbe5ddc520bc949ac9c71d3
Z 39eb7f22f14aaa3d3fa5e8c0432689d5

View File

@ -1 +1 @@
9ab985a9c8160b905730678f40ed440a246cdec549c798bafefaed5abbc0437f
36fdeb4f0a66970a35de688b617f90899c89cfdfab659f864df99aa7ebf854ea

View File

@ -3680,7 +3680,7 @@ expr_code_doover:
}else
#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
if( pCol->affinity==SQLITE_AFF_REAL ){
sqlite3VdbeAddOp2(v, OP_Copy, iSrc, target);
sqlite3VdbeAddOp2(v, OP_SCopy, iSrc, target);
sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
return target;
}else{
@ -4065,8 +4065,12 @@ expr_code_doover:
sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg,
pDef, pExpr->op2);
}
if( nFarg && constMask==0 ){
sqlite3ReleaseTempRange(pParse, r1, nFarg);
if( nFarg ){
if( constMask==0 ){
sqlite3ReleaseTempRange(pParse, r1, nFarg);
}else{
sqlite3VdbeReleaseRegisters(pParse, r1, nFarg, constMask);
}
}
return target;
}
@ -5707,8 +5711,11 @@ int sqlite3GetTempReg(Parse *pParse){
** purpose.
*/
void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
pParse->aTempReg[pParse->nTempReg++] = iReg;
if( iReg ){
sqlite3VdbeReleaseRegisters(pParse, iReg, 1, 0);
if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
pParse->aTempReg[pParse->nTempReg++] = iReg;
}
}
}
@ -5734,6 +5741,7 @@ void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
sqlite3ReleaseTempReg(pParse, iReg);
return;
}
sqlite3VdbeReleaseRegisters(pParse, iReg, nReg, 0);
if( nReg>pParse->nRangeReg ){
pParse->nRangeReg = nReg;
pParse->iRangeReg = iReg;

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

View File

@ -232,6 +232,11 @@ void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
void sqlite3VdbeJumpHere(Vdbe*, int addr);
int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
#ifdef SQLITE_DEBUG
void sqlite3VdbeReleaseRegisters(Parse*,int addr, int n, u32 mask);
#else
# define sqlite3VdbeReleaseRegisters(P,A,N,M)
#endif
void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type);
void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);

View File

@ -1186,6 +1186,29 @@ int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
}
}
#ifdef SQLITE_DEBUG
/*
** Generate an OP_ReleaseReg opcode to indicate that a range of
** registers, except any identified by mask, are no longer in use.
*/
void sqlite3VdbeReleaseRegisters(Parse *pParse, int iFirst, int N, u32 mask){
assert( pParse->pVdbe );
while( N>0 && (mask&1)!=0 ){
mask >>= 1;
iFirst++;
N--;
}
while( N>0 && N<=32 && (mask & MASKBIT32(N-1))!=0 ){
mask &= ~MASKBIT32(N-1);
N--;
}
if( N>0 ){
sqlite3VdbeAddOp3(pParse->pVdbe, OP_ReleaseReg, iFirst, N, *(int*)&mask);
}
}
#endif /* SQLITE_DEBUG */
/*
** Change the value of the P4 operand for a specific instruction.
** This routine is useful when a large program is loaded from a

View File

@ -672,4 +672,16 @@ do_execsql_test update-18.10 {
SELECT * FROM t0;
} {xyz 345 uvw 345}
# 2019-12-22 ticket c62c5e58524b204d
# This is really the same underlying problem as 5ad2aa6921faa1ee
#
reset_db
do_execsql_test update-18.20 {
PRAGMA encoding = 'utf16';
CREATE TABLE t0(c0 TEXT);
CREATE INDEX i0 ON t0(0 LIKE COALESCE(c0, 0));
INSERT INTO t0(c0) VALUES (0), (0);
SELECT * FROM t0;
} {0 0}
finish_test