]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
First cut at supporting CHECK constraints. Everything appears to work,
authordrh <drh@noemail.net>
Thu, 3 Nov 2005 00:41:17 +0000 (00:41 +0000)
committerdrh <drh@noemail.net>
Thu, 3 Nov 2005 00:41:17 +0000 (00:41 +0000)
but much more testing is needed as well as documentation. (CVS 2754)

FossilOrigin-Name: 2313d912baeca0fd516d524f16708953de483729

12 files changed:
manifest
manifest.uuid
src/build.c
src/expr.c
src/insert.c
src/parse.y
src/select.c
src/sqliteInt.h
src/test1.c
test/check.test [new file with mode: 0644]
www/lang.tcl
www/omitted.tcl

index a4e2a434a4dc2065fe39b23f9d1aeb210f039de7..8e0c1d60f988d3e5c7d293522106399df9e5ba60 100644 (file)
--- 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
index 582316171e597b05d3d3addde0036082c9c67193..f9f5b3ad09b85c9a277ea13905464336d021d238 100644 (file)
@@ -1 +1 @@
-e0d6f61c7de2c03b8fd17ef37cf1a0add36ee618
\ No newline at end of file
+2313d912baeca0fd516d524f16708953de483729
\ No newline at end of file
index 69a652b3347bcfaf3c73b925e861cc6b88551d35..11b7a039c0737a88aa143740371f4fd860299523 100644 (file)
@@ -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 <ctype.h>
@@ -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
index b915059d4ee2ff32ac9a0a17c60b6e3870c2dd7f..88c48cfba8fed35b79e67a4a8d56fc410060ad92 100644 (file)
@@ -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 <ctype.h>
@@ -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; i<pSrcList->nSrc; 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; j<pEList->nExpr; 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; i<pSrcList->nSrc; i++){
       assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab);
@@ -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;
 }
 
 /*
index dcc57d5718f18739b899c6985763200673bee9f4..f4122435cb7c9dbd7d981c1b33a3793bdc9e94cc 100644 (file)
@@ -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
index 7601e869adcfc077588f7d94831dc4fc34618d8e..490ceb2e2737820aa91262a56d99b06950617eb2 100644 (file)
@@ -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);}
index fde787d8723337ce36e411603be465344eb89a67..674157a940e6bff22cd484de6135b585dda8d6a8 100644 (file)
@@ -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;
index 51d1ce76c0fe297beb688afd62d650b598ababad..048d070747b513ddf1261512989dfc33c961d30b 100644 (file)
@@ -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);
index 29af5b6d64d271b7c98879e5973a9f1f64eaa5d0..f7e98715739f67b2b61a9df3365558ba2b2ff644 100644 (file)
@@ -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 (file)
index 0000000..392531f
--- /dev/null
@@ -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
index 26094b7a31ea74bc3d077b43c1a483b0f5fe3db4..d9f78846a37bdd8815f8e635566f02214c357813 100644 (file)
@@ -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 [<database-name>.] <table-name> AS <select-state
 NOT NULL [ <conflict-clause> ] |
 PRIMARY KEY [<sort-order>] [ <conflict-clause> ] [AUTOINCREMENT] |
 UNIQUE [ <conflict-clause> ] |
-CHECK ( <expr> ) [ <conflict-clause> ] |
+CHECK ( <expr> ) |
 DEFAULT <value> |
 COLLATE <collation-name>
 } {constraint} {
 PRIMARY KEY ( <column-list> ) [ <conflict-clause> ] |
 UNIQUE ( <column-list> ) [ <conflict-clause> ] |
-CHECK ( <expr> ) [ <conflict-clause> ]
+CHECK ( <expr> )
 } {conflict-clause} {
 ON CONFLICT <conflict-algorithm>
 }
@@ -596,10 +596,8 @@ default algorithm specified in the CREATE TABLE statement.
 See the section titled
 <a href="#conflict">ON CONFLICT</a> for additional information.</p>
 
-<p>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.</p>
+<p>CHECK constraints are supported as of version 3.3.0.  Prior
+to version 3.3.0, CHECK constraints were parsed but not enforced.</p>
 
 <p>There are no arbitrary limits on the number
 of columns or on the number of constraints in a table.
index ef2c8c70d9d66d57f9520f1f02705368e594f279..23241e7d698b79720656c9b5157fc5cad099eb22 100644 (file)
@@ -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 "<td valign=\"top\">$desc</td></tr>"
 }
 
-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.
 }