From 2938f924cc34afaff7a5a11dad621046ea9b0c1b Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 7 Mar 2012 19:13:29 +0000 Subject: [PATCH] If a CHECK constraint is named, report that name on the error message when the constraint fails. FossilOrigin-Name: 9a0f90d9deb335ac71044b8afa81538d85cc7ccf --- manifest | 25 ++++++++++++++----------- manifest.uuid | 2 +- src/build.c | 19 +++++++++++++------ src/insert.c | 40 ++++++++++++++++++++++++++-------------- src/parse.y | 6 +++--- src/sqliteInt.h | 3 ++- test/check.test | 12 ++++++------ 7 files changed, 65 insertions(+), 42 deletions(-) diff --git a/manifest b/manifest index 804df3f20b..dc2ab319db 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\scompiling\sthe\stest\scode\sin\sfts3_test.c\swhen\sSQLITE_ENABLE_FTS3\sis\snot\sdefined. -D 2012-03-05T16:24:26.279 +C If\sa\sCHECK\sconstraint\sis\snamed,\sreport\sthat\sname\son\sthe\serror\smessage\swhen\nthe\sconstraint\sfails. +D 2012-03-07T19:13:29.390 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -128,7 +128,7 @@ F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c 253c3147a4ebbaee42cd329dbdc0856200bbbda7 F src/btree.h 48a013f8964f12d944d90e4700df47b72dd6d923 F src/btreeInt.h 26d8ca625b141927fe6620c1d2cf58eaf494ca0c -F src/build.c c4d36e527f457f9992a6663365871dfa7c5094b8 +F src/build.c d58d314c5837737e15f94959cb55115347725182 F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33 @@ -142,7 +142,7 @@ F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c d7c69718acbb92e10e4b121da7bed13903342962 +F src/insert.c 82b1bc7aaa54d0f058101a034a28d47fe6b70613 F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416 @@ -170,7 +170,7 @@ F src/os_unix.c 0e3d2942d228d0366fb80a3640f35caf413b66d1 F src/os_win.c 5ac061ae1326a71500cee578ed0fd9113b4f6a37 F src/pager.c 3955b62cdb5bb64559607cb474dd12a6c8e1d4a5 F src/pager.h ef1eaf8593e78f73885c1dfac27ad83bee23bdc5 -F src/parse.y 1ddd71ae55f4b7cbb2672526ea4de023de0f519e +F src/parse.y 180976f414ba0473bffa7049b37aceab1e467175 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c F src/pcache1.c b30b1c35908346ecc43d8d9d17f2ddf6817f8f60 @@ -184,7 +184,7 @@ F src/select.c 44ccdcb5d2a1c48622c179b2d72167b716388581 F src/shell.c aa28f117033ba3e44b5eaaf2ad572222bcdfd66e F src/sqlite.h.in f46e368d1a28b09d876e35444785674d170f2d62 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h b013dab7d43fb67c3ca2f0253d7863abb37e233c +F src/sqliteInt.h 306a6f0c6732c0d706dfebfc780a133a38507aee F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -320,7 +320,7 @@ F test/capi3c.test 1b5424d2ac57b7b443b5de5b9a287642c02279b6 F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1 F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 -F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3 +F test/check.test 06795c188bf1776673fd2ac0787aa1c7238970d8 F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04 F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49 @@ -992,7 +992,10 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P 99a9073b5e411ce94f38ce49608baaa15de8b850 -R d0dc4635c0ac977f7342c0a0dac9e48b -U dan -Z d6573fd4e75425ab6ee13bee3b518e09 +P b00ccda307caae597c143ab0586f90acb77f79cf +R 5242d5c6432e029c0fd352c1dbfa159a +T *branch * named-check-constraints +T *sym-named-check-constraints * +T -sym-trunk * +U drh +Z 198934e577031b93f87585ac1616330d diff --git a/manifest.uuid b/manifest.uuid index dda6c5d414..65912e34e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b00ccda307caae597c143ab0586f90acb77f79cf \ No newline at end of file +9a0f90d9deb335ac71044b8afa81538d85cc7ccf \ No newline at end of file diff --git a/src/build.c b/src/build.c index daa5430406..16a9978fcf 100644 --- a/src/build.c +++ b/src/build.c @@ -537,7 +537,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ sqlite3DbFree(db, pTable->zColAff); sqlite3SelectDelete(db, pTable->pSelect); #ifndef SQLITE_OMIT_CHECK - sqlite3ExprDelete(db, pTable->pCheck); + sqlite3ExprListDelete(db, pTable->pCheck); #endif #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3VtabClear(db, pTable); @@ -1200,15 +1200,17 @@ void sqlite3AddCheckConstraint( Parse *pParse, /* Parsing context */ Expr *pCheckExpr /* The check expression */ ){ - sqlite3 *db = pParse->db; #ifndef SQLITE_OMIT_CHECK Table *pTab = pParse->pNewTable; if( pTab && !IN_DECLARE_VTAB ){ - pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck, pCheckExpr); + pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr); + if( pParse->constraintName.n ){ + sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1); + } }else #endif { - sqlite3ExprDelete(db, pCheckExpr); + sqlite3ExprDelete(pParse->db, pCheckExpr); } } @@ -1478,6 +1480,8 @@ void sqlite3EndTable( if( p->pCheck ){ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ NameContext sNC; /* Name context for pParse->pNewTable */ + ExprList *pList; /* List of all CHECK constraints */ + int i; /* Loop counter */ memset(&sNC, 0, sizeof(sNC)); memset(&sSrc, 0, sizeof(sSrc)); @@ -1488,8 +1492,11 @@ void sqlite3EndTable( sNC.pParse = pParse; sNC.pSrcList = &sSrc; sNC.isCheck = 1; - if( sqlite3ResolveExprNames(&sNC, p->pCheck) ){ - return; + pList = p->pCheck; + for(i=0; inExpr; i++){ + if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){ + return; + } } } #endif /* !defined(SQLITE_OMIT_CHECK) */ diff --git a/src/insert.c b/src/insert.c index 6b31e24f2c..18ed8cfcf3 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1157,9 +1157,11 @@ void sqlite3GenerateConstraintChecks( int regData; /* Register containing first data column */ int iCur; /* Table cursor number */ Index *pIdx; /* Pointer to one of the indices */ + sqlite3 *db; /* Database connection */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; + db = pParse->db; v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ @@ -1192,7 +1194,7 @@ void sqlite3GenerateConstraintChecks( char *zMsg; sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT, onError, regData+i); - zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", + zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); break; @@ -1214,18 +1216,28 @@ void sqlite3GenerateConstraintChecks( /* Test all CHECK constraints */ #ifndef SQLITE_OMIT_CHECK - if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){ - int allOk = sqlite3VdbeMakeLabel(v); + if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ + ExprList *pCheck = pTab->pCheck; + int i; pParse->ckBase = regData; - sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL); onError = overrideError!=OE_Default ? overrideError : OE_Abort; - if( onError==OE_Ignore ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - }else{ - if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ - sqlite3HaltConstraint(pParse, onError, 0, 0); + for(i=0; inExpr; i++){ + int allOk = sqlite3VdbeMakeLabel(v); + sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL); + if( onError==OE_Ignore ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); + }else{ + char *zConsName = pCheck->a[i].zName; + if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ + if( zConsName ){ + zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName); + }else{ + zConsName = sqlite3MPrintf(db, "constraint failed"); + } + sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC); + } + sqlite3VdbeResolveLabel(v, allOk); } - sqlite3VdbeResolveLabel(v, allOk); } #endif /* !defined(SQLITE_OMIT_CHECK) */ @@ -1281,7 +1293,7 @@ void sqlite3GenerateConstraintChecks( ** table. */ Trigger *pTrigger = 0; - if( pParse->db->flags&SQLITE_RecTriggers ){ + if( db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ @@ -1370,7 +1382,7 @@ void sqlite3GenerateConstraintChecks( char *zErr; sqlite3StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = pParse->db; + errMsg.db = db; zSep = pIdx->nColumn>1 ? "columns " : "column "; for(j=0; jnColumn; j++){ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; @@ -1394,7 +1406,7 @@ void sqlite3GenerateConstraintChecks( Trigger *pTrigger = 0; assert( onError==OE_Replace ); sqlite3MultiWrite(pParse); - if( pParse->db->flags&SQLITE_RecTriggers ){ + if( db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } sqlite3GenerateRowDelete( @@ -1724,7 +1736,7 @@ static int xferOptimization( } } #ifndef SQLITE_OMIT_CHECK - if( pDest->pCheck && sqlite3ExprCompare(pSrc->pCheck, pDest->pCheck) ){ + if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck, pDest->pCheck) ){ return 0; /* Tables have different CHECK constraints. Ticket #2252 */ } #endif diff --git a/src/parse.y b/src/parse.y index ed18e7f973..29b58a43df 100644 --- a/src/parse.y +++ b/src/parse.y @@ -273,10 +273,10 @@ signed ::= minus_num. // "carglist" is a list of additional constraints that come after the // column name and column type in a CREATE TABLE statement. // -carglist ::= carglist carg. +carglist ::= carglist cname ccons. carglist ::= . -carg ::= CONSTRAINT nm ccons. -carg ::= ccons. +cname ::= CONSTRAINT nm(X). {pParse->constraintName = X;} +cname ::= . {pParse->constraintName.n = 0;} ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);} diff --git a/src/sqliteInt.h b/src/sqliteInt.h index cfe8fd64fb..dcc2ecec9a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1274,7 +1274,7 @@ struct Table { FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK - Expr *pCheck; /* The AND of all CHECK constraints */ + ExprList *pCheck; /* All CHECK constraints */ #endif #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ @@ -2208,6 +2208,7 @@ struct Parse { int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ int nMaxArg; /* Max args passed to user function by sub-program */ + Token constraintName;/* Name of the constraint currently being parsed */ #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ diff --git a/test/check.test b/test/check.test index d2867a096e..4d1a44117d 100644 --- a/test/check.test +++ b/test/check.test @@ -117,9 +117,9 @@ do_test check-1.17 { do_test check-2.1 { execsql { CREATE TABLE t2( - x INTEGER CHECK( typeof(coalesce(x,0))=="integer" ), - y REAL CHECK( typeof(coalesce(y,0.1))=='real' ), - z TEXT CHECK( typeof(coalesce(z,''))=='text' ) + x INTEGER CONSTRAINT one CHECK( typeof(coalesce(x,0))=="integer" ), + y REAL CONSTRAINT two CHECK( typeof(coalesce(y,0.1))=='real' ), + z TEXT CONSTRAINT three CHECK( typeof(coalesce(z,''))=='text' ) ); } } {} @@ -141,17 +141,17 @@ do_test check-2.4 { catchsql { INSERT INTO t2 VALUES(1.1, NULL, NULL); } -} {1 {constraint failed}} +} {1 {constraint one failed}} do_test check-2.5 { catchsql { INSERT INTO t2 VALUES(NULL, 5, NULL); } -} {1 {constraint failed}} +} {1 {constraint two failed}} do_test check-2.6 { catchsql { INSERT INTO t2 VALUES(NULL, NULL, 3.14159); } -} {1 {constraint failed}} +} {1 {constraint three failed}} ifcapable subquery { do_test check-3.1 { -- 2.39.5