From: drh Date: Thu, 3 Nov 2005 00:41:17 +0000 (+0000) Subject: First cut at supporting CHECK constraints. Everything appears to work, X-Git-Tag: version-3.6.10~3406 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ffe07b2dc14ce66925cdc695e0cfef3ff99d4824;p=thirdparty%2Fsqlite.git First cut at supporting CHECK constraints. Everything appears to work, but much more testing is needed as well as documentation. (CVS 2754) FossilOrigin-Name: 2313d912baeca0fd516d524f16708953de483729 --- diff --git a/manifest b/manifest index a4e2a434a4..8e0c1d60f9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Omit\sthe\sSQLITE_AFF_INTEGER\stype\saffinity.\s\sAll\snumeric\svalues\sare\snow\nof\stype\sreal,\sthough\san\sinteger\srepresentation\sis\sstill\ssometimes\sused\ninternally\sfor\sefficiency.\s(CVS\s2753) -D 2005-11-01T15:48:24 +C First\scut\sat\ssupporting\sCHECK\sconstraints.\s\sEverything\sappears\sto\swork,\nbut\smuch\smore\stesting\sis\sneeded\sas\swell\sas\sdocumentation.\s(CVS\s2754) +D 2005-11-03T00:41:17 F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -35,17 +35,17 @@ F src/attach.c 4b21689700a72ae281fa85dbaff06b2a62bd49ee F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454 F src/btree.c 1ccc3b3931774a68ee0d6a8e2c8ea83f27b853fb F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e -F src/build.c a9dc62b900e83d70ff4a065e760064ded379c5bf +F src/build.c 5441ae700097557051c40a1d7a67d170f19d94f3 F src/callback.c 90ab4f235a2603c4cb8e6a2497091a71fb732bfa F src/complete.c 4de937dfdd4c79a501772ab2035b26082f337a79 F src/date.c 7444b0900a28da77e57e3337a636873cff0ae940 F src/delete.c 29dac493f4d83b05f91233b116827c133bcdab72 F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d -F src/expr.c acf80a3ce4a668f4b7ae40c064049befa2f83d91 +F src/expr.c e74e9c265c3572f71ab9d8f795cb656469209bb6 F src/func.c 7d81dccd9c440c6c4e761056333e629192814af0 F src/hash.c 8747cf51d12de46512880dfcf1b68b4e24072863 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 -F src/insert.c b7757ac308a7ea8124f2d6d1a6821ea4ae045a29 +F src/insert.c b814e8d73b725cf34ff9328573ea052226c290bd F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b F src/main.c 97bb830cdbd378d1f87469618471f52d9d263d09 F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070 @@ -59,18 +59,18 @@ F src/os_win.c fbccc85e7011174068c27d54256746321a1f0059 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/pager.c adbb27f13ac75cd5bc29a3d84803e0cab1edfa88 F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140 -F src/parse.y 5602d5cb894dda2932bf50b7e88782a4440ae3ae +F src/parse.y 416bc5ed6239356173d7283881750f6b7ed2b899 F src/pragma.c 9ec219dc4ee2d4e78f4ec5c9d1422089758af13f F src/prepare.c fc098db25d2a121affb08686cf04833fd50452d4 F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8 F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4 -F src/select.c 80c95f3cebd6f7096cdcad1968316e4bb96b18b2 +F src/select.c be02f123e8651bee22beb07d89dcfa75bcc2e291 F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3 F src/sqlite.h.in 8e648e1f386e4509f2f96c09ded7c07b0df0c9a2 -F src/sqliteInt.h 922d71882bbfc64a3e7ca0f3e173a5f5ef8d00ed +F src/sqliteInt.h d4f226034a9f1b2414aed408908984008db5c44f F src/table.c e03b60eaabaeb54a00d7e931566d77302dfc19b0 F src/tclsqlite.c 4f274fae3d4a1863451a553dd8e5015747a5d91d -F src/test1.c 0f1a66f65a54fba029f7e93b7500d49443dc959b +F src/test1.c 77506b6b88125c26f00a11bf3ff5c8dc824e837e F src/test2.c 4196848c845626e7df894470f27329e80bfe92aa F src/test3.c f4e6a16a602091696619a1171bda25c0e3df49f7 F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f @@ -124,6 +124,7 @@ F test/capi2.test f897209386fb21cfdc9267595e0c667ebaca9164 F test/capi3.test fc8e573467049add3bfaf81f53827e8ff153cf8f F test/capi3b.test 5b6a66f9f295f79f443b5d3f33187fa5ef6cf336 F test/cast.test 2543165ced4249c89ce5f0352222df503a98b9e5 +F test/check.test 073a4b542793e4acb7bcfa2abb838586a3462625 F test/collate1.test add9454cef160677bb8b34148b8f277ce7f9f1c4 F test/collate2.test 224a632ba04907c049804b08162efd234aa7871f F test/collate3.test 947a77f5b8227e037a7094d0e338a5504f155cc4 @@ -295,12 +296,12 @@ F www/fullscanb.gif f7c94cb227f060511f8909e10f570157263e9a25 F www/index-ex1-x-b.gif f9b1d85c3fa2435cf38b15970c7e3aa1edae23a3 F www/index.tcl 2fff45565fd52fba6746b27eb60ea1d7834042ec F www/indirect1b1.gif adfca361d2df59e34f9c5cac52a670c2bfc303a1 -F www/lang.tcl b04a87ce05cdbd8d356d6b760a9a0b6f6fce927e +F www/lang.tcl 6ec7f6f3250e7f671cf8ada7c765e56b0cc1f169 F www/lockingv3.tcl f59b19d6c8920a931f096699d6faaf61c05db55f F www/mingw.tcl d96b451568c5d28545fefe0c80bee3431c73f69c F www/nulls.tcl ec35193f92485b87b90a994a01d0171b58823fcf F www/oldnews.tcl 1a808d86882621557774bf7741ed81c7f4ef9f19 -F www/omitted.tcl 658ebdc83781ac419dc8a08b3f6cf93929023470 +F www/omitted.tcl ee6b46f83d513b2187869740da829a700e1a355e F www/opcode.tcl 5bd68059416b223515a680d410a9f7cb6736485f F www/optimizer.tcl d6812a10269bd0d7c488987aac0ad5036cace9dc F www/optimizing.tcl f0b2538988d1bbad16cbfe63ec6e8f48c9eb04e5 @@ -315,7 +316,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 0d3357b5f65887f7db03db2ae021f28f480f90e4 -R 129a42da3960f8ddb76cd0aa7a6f3c7c +P e0d6f61c7de2c03b8fd17ef37cf1a0add36ee618 +R 1ff7e911d1dc61bef39e78e87be5de02 U drh -Z 4057f17a6edfdde3a8a1c1ea6fb897b0 +Z c4af40f7e2f1f1ea9a28a66e94a942b3 diff --git a/manifest.uuid b/manifest.uuid index 582316171e..f9f5b3ad09 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e0d6f61c7de2c03b8fd17ef37cf1a0add36ee618 \ No newline at end of file +2313d912baeca0fd516d524f16708953de483729 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 69a652b334..11b7a039c0 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.352 2005/11/01 15:48:24 drh Exp $ +** $Id: build.c,v 1.353 2005/11/03 00:41:17 drh Exp $ */ #include "sqliteInt.h" #include @@ -456,6 +456,9 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ sqliteFree(pTable->zName); sqliteFree(pTable->zColAff); sqlite3SelectDelete(pTable->pSelect); +#ifndef SQLITE_OMIT_CHECK + sqlite3ExprDelete(pTable->pCheck); +#endif sqliteFree(pTable); } @@ -1047,6 +1050,25 @@ primary_key_exit: return; } +/* +** Add a new CHECK constraint to the table currently under construction. +*/ +void sqlite3AddCheckConstraint( + Parse *pParse, /* Parsing context */ + Expr *pCheckExpr /* The check expression */ +){ +#ifndef SQLITE_OMIT_CHECK + Table *pTab = pParse->pNewTable; + if( pTab ){ + /* The CHECK expression must be duplicated so that tokens refer + ** to malloced space and not the (ephemeral) text of the CREATE TABLE + ** statement */ + pTab->pCheck = sqlite3ExprAnd(pTab->pCheck, sqlite3ExprDup(pCheckExpr)); + } +#endif + sqlite3ExprDelete(pCheckExpr); +} + /* ** Set the collation function of the most recently parsed table column ** to the CollSeq given. @@ -1270,6 +1292,27 @@ void sqlite3EndTable( assert( !db->init.busy || !pSelect ); +#ifndef SQLITE_OMIT_CHECK + /* Resolve names in all CHECK constraint expressions. + */ + if( p->pCheck ){ + SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ + NameContext sNC; /* Name context for pParse->pNewTable */ + + memset(&sNC, 0, sizeof(sNC)); + memset(&sSrc, 0, sizeof(sSrc)); + sSrc.nSrc = 1; + sSrc.a[0].zName = p->zName; + sSrc.a[0].pTab = p; + sSrc.a[0].iCursor = -1; + sNC.pParse = pParse; + sNC.pSrcList = &sSrc; + if( sqlite3ExprResolveNames(&sNC, p->pCheck) ){ + return; + } + } +#endif /* !defined(SQLITE_OMIT_CHECK) */ + /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. ** So do not write to the disk again. Extract the root page number diff --git a/src/expr.c b/src/expr.c index b915059d4e..88c48cfba8 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.232 2005/11/01 15:48:24 drh Exp $ +** $Id: expr.c,v 1.233 2005/11/03 00:41:17 drh Exp $ */ #include "sqliteInt.h" #include @@ -802,7 +802,7 @@ int sqlite3IsRowid(const char *z){ ** in pParse and return non-zero. Return zero on success. */ static int lookupName( - Parse *pParse, /* The parsing context */ + Parse *pParse, /* The parsing context */ Token *pDbToken, /* Name of the database containing table, or NULL */ Token *pTableToken, /* Name of table containing column, or NULL */ Token *pColumnToken, /* Name of the column. */ @@ -830,10 +830,9 @@ static int lookupName( pExpr->iTable = -1; while( pNC && cnt==0 ){ + ExprList *pEList; SrcList *pSrcList = pNC->pSrcList; - ExprList *pEList = pNC->pEList; - /* assert( zTab==0 || pEList==0 ); */ if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ Table *pTab = pItem->pTab; @@ -952,7 +951,7 @@ static int lookupName( ** Note that the expression in the result set should have already been ** resolved by the time the WHERE clause is resolved. */ - if( cnt==0 && pEList!=0 && zTab==0 ){ + if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){ for(j=0; jnExpr; j++){ char *zAs = pEList->a[j].zName; if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ @@ -1081,7 +1080,7 @@ static int nameResolverStep(void *pArg, Expr *pExpr){ if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return 1; ExprSetProperty(pExpr, EP_Resolved); #ifndef NDEBUG - if( pSrcList ){ + if( pSrcList && pSrcList->nAlloc>0 ){ int i; for(i=0; inSrc; i++){ assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursornTab); @@ -1441,6 +1440,8 @@ static void codeInteger(Vdbe *v, const char *z, int n){ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ Vdbe *v = pParse->pVdbe; int op; + int stackChng = 1; /* Amount of change to stack depth */ + if( v==0 ) return; if( pExpr==0 ){ sqlite3VdbeAddOp(v, OP_Null, 0, 0); @@ -1462,7 +1463,11 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ /* Otherwise, fall thru into the TK_COLUMN case */ } case TK_COLUMN: { - if( pExpr->iColumn>=0 ){ + if( pExpr->iTable<0 ){ + /* This only happens when coding check constraints */ + assert( pParse->ckOffset>0 ); + sqlite3VdbeAddOp(v, OP_Dup, pParse->ckOffset-pExpr->iColumn-1, 1); + }else if( pExpr->iColumn>=0 ){ sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn); sqlite3ColumnDefault(v, pExpr->pTab, pExpr->iColumn); }else{ @@ -1525,6 +1530,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ case SQLITE_AFF_NONE: op = OP_ToBlob; break; } sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = 0; break; } #endif /* SQLITE_OMIT_CAST */ @@ -1543,6 +1549,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0); + stackChng = -1; break; } case TK_AND: @@ -1571,6 +1578,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = -1; break; } case TK_UMINUS: { @@ -1596,6 +1604,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ assert( TK_NOT==OP_Not ); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = 0; break; } case TK_ISNULL: @@ -1608,6 +1617,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ dest = sqlite3VdbeCurrentAddr(v) + 2; sqlite3VdbeAddOp(v, op, 1, dest); sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); + stackChng = 0; break; } case TK_AGG_FUNCTION: { @@ -1644,6 +1654,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ); } sqlite3VdbeOp3(v, OP_Function, constMask, nExpr, (char*)pDef, P3_FUNCDEF); + stackChng = 1-nExpr; break; } #ifndef SQLITE_OMIT_SUBQUERY @@ -1702,6 +1713,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ case TK_UPLUS: case TK_AS: { sqlite3ExprCode(pParse, pExpr->pLeft); + stackChng = 0; break; } case TK_CASE: { @@ -1767,9 +1779,15 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump); VdbeComment((v, "# raise(IGNORE)")); } + stackChng = 0; + break; } #endif - break; + } + + if( pParse->ckOffset ){ + pParse->ckOffset += stackChng; + assert( pParse->ckOffset ); } } @@ -1837,6 +1855,7 @@ int sqlite3ExprCodeExprList( void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ Vdbe *v = pParse->pVdbe; int op = 0; + int ckOffset = pParse->ckOffset; if( v==0 || pExpr==0 ) return; op = pExpr->op; switch( op ){ @@ -1911,6 +1930,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ break; } } + pParse->ckOffset = ckOffset; } /* @@ -1924,6 +1944,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ Vdbe *v = pParse->pVdbe; int op = 0; + int ckOffset = pParse->ckOffset; if( v==0 || pExpr==0 ) return; /* The value of pExpr->op and op are related as follows: @@ -2020,6 +2041,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ break; } } + pParse->ckOffset = ckOffset; } /* diff --git a/src/insert.c b/src/insert.c index dcc57d5718..f4122435cb 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.144 2005/11/01 15:48:24 drh Exp $ +** $Id: insert.c,v 1.145 2005/11/03 00:41:17 drh Exp $ */ #include "sqliteInt.h" @@ -868,7 +868,18 @@ void sqlite3GenerateConstraintChecks( /* Test all CHECK constraints */ - /**** TBD ****/ +#ifndef SQLITE_OMIT_CHECK + if( pTab->pCheck ){ + int allOk = sqlite3VdbeMakeLabel(v); + assert( pParse->ckOffset==0 ); + pParse->ckOffset = nCol; + sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, 0); + assert( pParse->ckOffset==nCol ); + pParse->ckOffset = 0; + sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort); + sqlite3VdbeResolveLabel(v, allOk); + } +#endif /* !defined(SQLITE_OMIT_CHECK) */ /* If we have an INTEGER PRIMARY KEY, make sure the primary key ** of the new record does not previously exist. Except, if this diff --git a/src/parse.y b/src/parse.y index 7601e869ad..490ceb2e27 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.180 2005/09/16 02:38:10 drh Exp $ +** @(#) $Id: parse.y,v 1.181 2005/11/03 00:41:17 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" @@ -268,7 +268,7 @@ ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);} ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I). {sqlite3AddPrimaryKey(pParse,0,R,I);} ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0);} -ccons ::= CHECK LP expr(X) RP onconf. {sqlite3ExprDelete(X);} +ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse, X);} ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R). {sqlite3CreateForeignKey(pParse,0,&T,TA,R);} ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);} diff --git a/src/select.c b/src/select.c index fde787d872..674157a940 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.277 2005/10/06 16:53:15 drh Exp $ +** $Id: select.c,v 1.278 2005/11/03 00:41:17 drh Exp $ */ #include "sqliteInt.h" @@ -2384,14 +2384,8 @@ int sqlite3SelectResolve( /* Resolve the expressions in the LIMIT and OFFSET clauses. These ** are not allowed to refer to any names, so pass an empty NameContext. */ + memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; - sNC.hasAgg = 0; - sNC.nErr = 0; - sNC.nRef = 0; - sNC.pEList = 0; - sNC.allowAgg = 0; - sNC.pSrcList = 0; - sNC.pNext = 0; if( sqlite3ExprResolveNames(&sNC, p->pLimit) || sqlite3ExprResolveNames(&sNC, p->pOffset) ){ return SQLITE_ERROR; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 51d1ce76c0..048d070747 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.424 2005/11/01 15:48:24 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.425 2005/11/03 00:41:17 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -639,6 +639,9 @@ struct Table { Trigger *pTrigger; /* List of SQL triggers on this 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 */ +#endif #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE statement to add a new column */ #endif @@ -1172,6 +1175,7 @@ struct Parse { int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ + int ckOffset; /* Stack offset to data used by CHECK constraints */ u32 writeMask; /* Start a write transaction on these databases */ u32 cookieMask; /* Bitmask of schema verified databases */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ @@ -1449,6 +1453,7 @@ void sqlite3StartTable(Parse*,Token*,Token*,Token*,int,int); void sqlite3AddColumn(Parse*,Token*); void sqlite3AddNotNull(Parse*, int); void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int); +void sqlite3AddCheckConstraint(Parse*, Expr*); void sqlite3AddColumnType(Parse*,Token*); void sqlite3AddDefaultValue(Parse*,Expr*); void sqlite3AddCollateType(Parse*, const char*, int); diff --git a/src/test1.c b/src/test1.c index 29af5b6d64..f7e9871573 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.162 2005/09/19 13:15:23 drh Exp $ +** $Id: test1.c,v 1.163 2005/11/03 00:41:17 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -2876,6 +2876,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "cast", "1", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_OMIT_CHECK + Tcl_SetVar2(interp, "sqlite_options", "check", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "check", "1", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_COMPLETE Tcl_SetVar2(interp, "sqlite_options", "complete", "0", TCL_GLOBAL_ONLY); #else diff --git a/test/check.test b/test/check.test new file mode 100644 index 0000000000..392531f95c --- /dev/null +++ b/test/check.test @@ -0,0 +1,153 @@ +# 2005 November 2 +# +# 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. The +# focus of this file is testing CHECK constraints +# +# $Id: check.test,v 1.1 2005/11/03 00:41:18 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Only run these tests if the build includes support for CHECK constraints +ifcapable !check { + finish_test + return +} + +do_test check-1.1 { + execsql { + CREATE TABLE t1( + x INTEGER CHECK( x<5 ), + y REAL CHECK( y>x ) + ); + } +} {} +do_test check-1.2 { + execsql { + INSERT INTO t1 VALUES(3,4); + SELECT * FROM t1; + } +} {3 4} +do_test check-1.3 { + catchsql { + INSERT INTO t1 VALUES(6,7); + } +} {1 {constraint failed}} +do_test check-1.4 { + execsql { + SELECT * FROM t1; + } +} {3 4} +do_test check-1.5 { + catchsql { + INSERT INTO t1 VALUES(4,3); + } +} {1 {constraint failed}} +do_test check-1.6 { + execsql { + SELECT * FROM t1; + } +} {3 4} +do_test check-1.7 { + catchsql { + INSERT INTO t1 VALUES(NULL,6); + } +} {1 {constraint failed}} +do_test check-1.8 { + execsql { + SELECT * FROM t1; + } +} {3 4} +do_test check-1.9 { + catchsql { + INSERT INTO t1 VALUES(2,NULL); + } +} {1 {constraint failed}} +do_test check-1.10 { + execsql { + SELECT * FROM t1; + } +} {3 4} +do_test check-1.11 { + execsql { + UPDATE t1 SET x=2 WHERE x==3; + SELECT * FROM t1; + } +} {2 4} +do_test check-1.12 { + catchsql { + UPDATE t1 SET x=7 WHERE x==2 + } +} {1 {constraint failed}} +do_test check-1.13 { + execsql { + SELECT * FROM t1; + } +} {2 4} +do_test check-1.14 { + catchsql { + UPDATE t1 SET x=5 WHERE x==2 + } +} {1 {constraint failed}} +do_test check-1.15 { + execsql { + SELECT * FROM t1; + } +} {2 4} +do_test check-1.16 { + catchsql { + UPDATE t1 SET x=4, y=11 WHERE x==2 + } +} {0 {}} +do_test check-1.17 { + execsql { + SELECT * FROM t1; + } +} {4 11} + +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" ) + ); + } +} {} +do_test check-2.2 { + execsql { + INSERT INTO t2 VALUES(1,2.2,'three'); + SELECT * FROM t2; + } +} {1 2.2 three} +do_test check-2.3 { + execsql { + INSERT INTO t2 VALUES(NULL, NULL, NULL); + SELECT * FROM t2; + } +} {1 2.2 three {} {} {}} +do_test check-2.4 { + catchsql { + INSERT INTO t2 VALUES(1.1, NULL, NULL); + } +} {1 {constraint failed}} +do_test check-2.5 { + catchsql { + INSERT INTO t2 VALUES(NULL, 5, NULL); + } +} {1 {constraint failed}} +do_test check-2.6 { + catchsql { + INSERT INTO t2 VALUES(NULL, NULL, 3.14159); + } +} {1 {constraint failed}} + +finish_test diff --git a/www/lang.tcl b/www/lang.tcl index 26094b7a31..d9f78846a3 100644 --- a/www/lang.tcl +++ b/www/lang.tcl @@ -1,7 +1,7 @@ # # Run this Tcl script to generate the lang-*.html files. # -set rcsid {$Id: lang.tcl,v 1.100 2005/09/11 11:56:28 drh Exp $} +set rcsid {$Id: lang.tcl,v 1.101 2005/11/03 00:41:18 drh Exp $} source common.tcl if {[llength $argv]>0} { @@ -511,13 +511,13 @@ CREATE [TEMP | TEMPORARY] TABLE [.] AS ] | PRIMARY KEY [] [ ] [AUTOINCREMENT] | UNIQUE [ ] | -CHECK ( ) [ ] | +CHECK ( ) | DEFAULT | COLLATE } {constraint} { PRIMARY KEY ( ) [ ] | UNIQUE ( ) [ ] | -CHECK ( ) [ ] +CHECK ( ) } {conflict-clause} { ON CONFLICT } @@ -596,10 +596,8 @@ default algorithm specified in the CREATE TABLE statement. See the section titled ON CONFLICT for additional information.

-

CHECK constraints are ignored in the current implementation. -Support for CHECK constraints may be added in the future. As of -version 2.3.0, NOT NULL, PRIMARY KEY, and UNIQUE constraints all -work.

+

CHECK constraints are supported as of version 3.3.0. Prior +to version 3.3.0, CHECK constraints were parsed but not enforced.

There are no arbitrary limits on the number of columns or on the number of constraints in a table. diff --git a/www/omitted.tcl b/www/omitted.tcl index ef2c8c70d9..23241e7d69 100644 --- a/www/omitted.tcl +++ b/www/omitted.tcl @@ -1,7 +1,7 @@ # # Run this script to generated a omitted.html output file # -set rcsid {$Id: omitted.tcl,v 1.9 2005/09/11 11:56:28 drh Exp $} +set rcsid {$Id: omitted.tcl,v 1.10 2005/11/03 00:41:18 drh Exp $} source common.tcl header {SQL Features That SQLite Does Not Implement} puts { @@ -28,11 +28,6 @@ proc feature {name desc} { puts "$desc" } -feature {CHECK constraints} { - CHECK constraints are parsed but they are not enforced. - NOT NULL and UNIQUE constraints are enforced, however. -} - feature {FOREIGN KEY constraints} { FOREIGN KEY constraints are parsed but are not enforced. }