Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -1075,40 +1075,54 @@ #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 - && pExpr->iColumn>=0 - && pWalker->u.aiCol[pExpr->iColumn]>=0 - ){ - pWalker->eCode = 0; + 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. Return true if CHECK constraint pExpr does not use -** any of the changing columns. In other words, return true if this -** CHECK constraint can be skipped when validating the new row in -** the UPDATE statement. +** 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){ +static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ Walker w; memset(&w, 0, sizeof(w)); - w.eCode = 1; + w.eCode = 0; w.xExprCallback = checkConstraintExprNode; w.u.aiCol = aiChng; sqlite3WalkExpr(&w, pExpr); - return w.eCode; + 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. @@ -1306,11 +1320,11 @@ pParse->ckBase = regNewData+1; onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; inExpr; i++){ int allOk = sqlite3VdbeMakeLabel(v); Expr *pExpr = pCheck->a[i].pExpr; - if( aiChng && checkConstraintUnchanged(pExpr, aiChng) ) continue; + 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; Index: test/check.test ================================================================== --- test/check.test +++ test/check.test @@ -456,7 +456,27 @@ # 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