Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix to previous check-in: Make sure CHECK constraints involving the ROWID are not ignored when the ROWID changes. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
7782cb1dd5914b867caf5ff2f0f83740 |
User & Date: | drh 2016-02-10 18:24:05.782 |
Context
2016-02-10
| ||
19:10 | Add testcase() macros to the CHECK constraint avoidance logic. Avoid creating an unused VDBE label during CHECK constraint code generation. (check-in: 970881befd user: drh tags: trunk) | |
18:24 | Fix to previous check-in: Make sure CHECK constraints involving the ROWID are not ignored when the ROWID changes. (check-in: 7782cb1dd5 user: drh tags: trunk) | |
16:52 | Omit unnecessary CHECK constraints in UPDATE statements, when none of the columns referenced in the CHECK constraint are modified. (check-in: 02fbdbc782 user: drh tags: trunk) | |
Changes
Changes to src/insert.c.
︙ | ︙ | |||
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 | #ifdef pTrigger #undef pTrigger #endif #ifdef tmask #undef tmask #endif /* This is the Walker callback from checkConstraintUnchanged(). Set ** pWalker->eCode to 0 if this expression node references any of the ** columns that are being modifed by an UPDATE statement. */ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ | > > > > > > > | > | | < > > > | > > > | | | | | | > | | 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 | #ifdef pTrigger #undef pTrigger #endif #ifdef tmask #undef tmask #endif /* ** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged() */ #define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */ #define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */ /* This is the Walker callback from checkConstraintUnchanged(). Set ** bit 0x01 of pWalker->eCode if ** pWalker->eCode to 0 if this expression node references any of the ** columns that are being modifed by an UPDATE statement. */ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_COLUMN ){ assert( pExpr->iColumn>=0 || pExpr->iColumn==-1 ); if( pExpr->iColumn>=0 ){ if( pWalker->u.aiCol[pExpr->iColumn]>=0 ){ pWalker->eCode |= CKCNSTRNT_COLUMN; } }else{ pWalker->eCode |= CKCNSTRNT_ROWID; } } return WRC_Continue; } /* ** pExpr is a CHECK constraint on a row that is being UPDATE-ed. The ** only columns that are modified by the UPDATE are those for which ** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true. ** ** Return true if CHECK constraint pExpr does not use any of the ** changing columns (or the rowid if it is changing). In other words, ** return true if this CHECK constraint can be skipped when validating ** the new row in the UPDATE statement. */ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ Walker w; memset(&w, 0, sizeof(w)); w.eCode = 0; w.xExprCallback = checkConstraintExprNode; w.u.aiCol = aiChng; sqlite3WalkExpr(&w, pExpr); if( !chngRowid ) w.eCode &= ~CKCNSTRNT_ROWID; return !w.eCode; } /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE ** on table pTab. ** ** The regNewData parameter is the first register in a range that contains |
︙ | ︙ | |||
1304 1305 1306 1307 1308 1309 1310 | if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = pTab->pCheck; pParse->ckBase = regNewData+1; onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; i<pCheck->nExpr; i++){ int allOk = sqlite3VdbeMakeLabel(v); Expr *pExpr = pCheck->a[i].pExpr; | | | 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 | if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = pTab->pCheck; pParse->ckBase = regNewData+1; onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; i<pCheck->nExpr; i++){ int allOk = sqlite3VdbeMakeLabel(v); Expr *pExpr = pCheck->a[i].pExpr; if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue; sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); if( onError==OE_Ignore ){ sqlite3VdbeGoto(v, ignoreDest); }else{ char *zName = pCheck->a[i].zName; if( zName==0 ) zName = pTab->zName; if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ |
︙ | ︙ |
Changes to test/check.test.
︙ | ︙ | |||
454 455 456 457 458 459 460 461 462 | # 2013-08-02: Silently ignore database name qualifiers in CHECK constraints. # do_execsql_test 8.1 { CREATE TABLE t810(a, CHECK( main.t810.a>0 )); CREATE TABLE t811(b, CHECK( xyzzy.t811.b BETWEEN 5 AND 10 )); } {} finish_test | > > > > > > > > > > > > > > > > > > > > | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | # 2013-08-02: Silently ignore database name qualifiers in CHECK constraints. # do_execsql_test 8.1 { CREATE TABLE t810(a, CHECK( main.t810.a>0 )); CREATE TABLE t811(b, CHECK( xyzzy.t811.b BETWEEN 5 AND 10 )); } {} # Make sure check constraints involving the ROWID are not ignored # do_execsql_test 9.1 { CREATE TABLE t1( a INTEGER PRIMARY KEY, b INTEGER NOT NULL CONSTRAINT 'b-check' CHECK( b>a ), c INTEGER NOT NULL CONSTRAINT 'c-check' CHECK( c>rowid*2 ), d INTEGER NOT NULL CONSTRAINT 'd-check' CHECK( d BETWEEN b AND c ) ); INSERT INTO t1(a,b,c,d) VALUES(1,2,4,3),(2,4,6,5),(3,10,30,20); } {} do_catchsql_test 9.2 { UPDATE t1 SET b=0 WHERE a=1; } {1 {CHECK constraint failed: b-check}} do_catchsql_test 9.3 { UPDATE t1 SET c=a*2 WHERE a=1; } {1 {CHECK constraint failed: c-check}} finish_test |