Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Make sure a transaction is available for rollback whenever a REDUCE conflict resolution occurs and there is the possibility to ABORT. Ticket [4a03edc4c8c] |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
f0c56fa90dc95aff6fe6764b5ab75a90 |
User & Date: | drh 2009-09-24 00:09:58.000 |
References
2009-09-24
| ||
18:42 | • Fixed ticket [4a03edc4c8]: Index corruption following aggressive use of ON CONFLICT clause. plus 3 other changes (artifact: cd3aa8c601 user: drh) | |
Context
2009-09-24
| ||
09:05 | Remove unused parameter from sqlite3CodeRowTrigger(). Fix header comments for this function and CodeRowTriggerDirect(). (check-in: 0443f7c911 user: dan tags: trunk) | |
00:09 | Make sure a transaction is available for rollback whenever a REDUCE conflict resolution occurs and there is the possibility to ABORT. Ticket [4a03edc4c8c] (check-in: f0c56fa90d user: drh tags: trunk) | |
2009-09-23
| ||
18:49 | More fkey tests. (check-in: 2d544bd53d user: shane tags: trunk) | |
Changes
Changes to src/build.c.
︙ | ︙ | |||
3488 3489 3490 3491 3492 3493 3494 3495 | */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); pToplevel->writeMask |= 1<<iDb; pToplevel->isMultiWrite |= setStatement; } | | | > > > > > > > > > | > > > > > > > > > > > > > > > | 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 | */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); pToplevel->writeMask |= 1<<iDb; pToplevel->isMultiWrite |= setStatement; } /* ** Indicate that the statement currently under construction might write ** more than one entry (example: deleting one row then inserting another, ** inserting multiple rows in a table, or inserting a row and index entries.) ** If an abort occurs after some of these writes have completed, then it will ** be necessary to undo the completed writes. */ void sqlite3MultiWrite(Parse *pParse){ Parse *pToplevel = sqlite3ParseToplevel(pParse); pToplevel->isMultiWrite = 1; } /* ** The code generator calls this routine if is discovers that it is ** possible to abort a statement prior to completion. In order to ** perform this abort without corrupting the database, we need to make ** sure that the statement is protected by a statement transaction. ** ** Technically, we only need to set the mayAbort flag if the ** isMultiWrite flag was previously set. There is a time dependency ** such that the abort must occur after the multiwrite. This makes ** some statements involving the REPLACE conflict resolution algorithm ** go a little faster. But taking advantage of this time dependency ** makes it more difficult to prove that the code is correct (in ** particular, it prevents us from writing an effective ** implementation of sqlite3AssertMayAbort()) and so we have chosen ** to take the safe route and skip the optimization. */ void sqlite3MayAbort(Parse *pParse){ Parse *pToplevel = sqlite3ParseToplevel(pParse); pToplevel->mayAbort = 1; } /* |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 | if( pTrigger ){ sqlite3GenerateRowDelete( pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace ); }else{ sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); } seenReplace = 1; break; } case OE_Ignore: { assert( seenReplace==0 ); sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); break; | > | 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 | if( pTrigger ){ sqlite3GenerateRowDelete( pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace ); }else{ sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); } sqlite3MultiWrite(pParse); seenReplace = 1; break; } case OE_Ignore: { assert( seenReplace==0 ); sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); break; |
︙ | ︙ | |||
1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 | assert( onError==OE_Replace ); if( pParse->db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } sqlite3GenerateRowDelete( pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace ); seenReplace = 1; break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); } | > | 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 | assert( onError==OE_Replace ); if( pParse->db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } sqlite3GenerateRowDelete( pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace ); sqlite3MultiWrite(pParse); seenReplace = 1; break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); } |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
2659 2660 2661 2662 2663 2664 2665 | void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, int*,int,int,int,int,int*); void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); void sqlite3BeginWriteOperation(Parse*, int, int); | > | | 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 | void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, int*,int,int,int,int,int*); void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); void sqlite3BeginWriteOperation(Parse*, int, int); void sqlite3MultiWrite(Parse*); void sqlite3MayAbort(Parse*); void sqlite3HaltConstraint(Parse*, int, char*, int); Expr *sqlite3ExprDup(sqlite3*,Expr*,int); ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); IdList *sqlite3IdListDup(sqlite3*,IdList*); Select *sqlite3SelectDup(sqlite3*,Select*,int); void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*); |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
236 237 238 239 240 241 242 | assert( p->magic==VDBE_MAGIC_INIT ); assert( j>=0 && j<p->nLabel ); if( p->aLabel ){ p->aLabel[j] = p->nOp; } } | | | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | assert( p->magic==VDBE_MAGIC_INIT ); assert( j>=0 && j<p->nLabel ); if( p->aLabel ){ p->aLabel[j] = p->nOp; } } #ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ /* ** The following type and function are used to iterate through all opcodes ** in a Vdbe main program and each of the sub-programs (triggers) it may ** invoke directly or indirectly. It should be used as follows: ** ** Op *pOp; |
︙ | ︙ | |||
308 309 310 311 312 313 314 | } return pRet; } /* ** Check if the program stored in the VM associated with pParse may | | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | } return pRet; } /* ** Check if the program stored in the VM associated with pParse may ** throw an ABORT exception (causing the statement, but not entire transaction ** to be rolled back). This condition is true if the main program or any ** sub-programs contains any of the following: ** ** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. ** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. ** * OP_Destroy ** * OP_VUpdate |
︙ | ︙ | |||
355 356 357 358 359 360 361 | /* Return true if hasAbort==mayAbort. Or if a malloc failure occured. ** If malloc failed, then the while() loop above may not have iterated ** through all opcodes and hasAbort may be set incorrectly. Return ** true for this case to prevent the assert() in the callers frame ** from failing. */ return ( v->db->mallocFailed || hasAbort==mayAbort ); } | | | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | /* Return true if hasAbort==mayAbort. Or if a malloc failure occured. ** If malloc failed, then the while() loop above may not have iterated ** through all opcodes and hasAbort may be set incorrectly. Return ** true for this case to prevent the assert() in the callers frame ** from failing. */ return ( v->db->mallocFailed || hasAbort==mayAbort ); } #endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ /* ** Loop through the program looking for P2 values that are negative ** on jump instructions. Each such value is a label. Resolve the ** label by setting the P2 value to its correct non-zero value. ** ** This routine is called once after all opcodes have been inserted. |
︙ | ︙ |
Added test/tkt-4a03edc4c8.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | # 2009 September 23 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests to verify that # ticket [4a03edc4c8c028c93e9269f64fc5e97f632c1166] has been fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-4a03ed-1.1 { db eval { CREATE TABLE t1( a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE ON CONFLICT FAIL ); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 2); } catchsql { BEGIN; INSERT INTO t1 VALUES(1, 2); COMMIT; } } {1 {column b is not unique}} do_test tkt-4a03ed-1.2 { db eval { PRAGMA integrity_check; } } {ok} do_test tkt-4a03ed-1.3 { db eval { SELECT * FROM t1 ORDER BY a; } } {1 1 2 2} finish_test |