Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -20,11 +20,11 @@ ** creating ID lists ** BEGIN TRANSACTION ** 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 /* @@ -454,10 +454,13 @@ */ sqliteResetColumnNames(pTable); sqliteFree(pTable->zName); sqliteFree(pTable->zColAff); sqlite3SelectDelete(pTable->pSelect); +#ifndef SQLITE_OMIT_CHECK + sqlite3ExprDelete(pTable->pCheck); +#endif sqliteFree(pTable); } /* ** Unlink the given table from the hash tables and the delete the @@ -1044,10 +1047,29 @@ primary_key_exit: sqlite3ExprListDelete(pList); 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. */ @@ -1268,10 +1290,31 @@ p = pParse->pNewTable; if( p==0 ) return; 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 ** for the table from the db->init.newTnum field. (The page number ** should have been put there by the sqliteOpenCb routine.) Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** 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 /* @@ -800,11 +800,11 @@ ** ** If the name cannot be resolved unambiguously, leave an error message ** 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. */ NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ @@ -828,14 +828,13 @@ goto lookupname_end; } 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; Column *pCol; @@ -950,11 +949,11 @@ ** In cases like this, replace pExpr with a copy of the expression that ** forms the result set entry ("a+b" in the example) and return immediately. ** 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 ){ assert( pExpr->pLeft==0 && pExpr->pRight==0 ); pExpr->op = TK_AS; @@ -1079,11 +1078,11 @@ pParse = pNC->pParse; 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); } } @@ -1439,10 +1438,12 @@ ** below verify that the numbers are aligned correctly. */ 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); return; } @@ -1460,11 +1461,15 @@ break; } /* 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{ sqlite3VdbeAddOp(v, OP_Rowid, pExpr->iTable, 0); } @@ -1523,10 +1528,11 @@ case SQLITE_AFF_NUMERIC: op = OP_ToNumeric; break; case SQLITE_AFF_TEXT: op = OP_ToText; break; case SQLITE_AFF_NONE: op = OP_ToBlob; break; } sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = 0; break; } #endif /* SQLITE_OMIT_CAST */ case TK_LT: case TK_LE: @@ -1541,10 +1547,11 @@ assert( TK_EQ==OP_Eq ); assert( TK_NE==OP_Ne ); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0); + stackChng = -1; break; } case TK_AND: case TK_OR: case TK_PLUS: @@ -1569,10 +1576,11 @@ assert( TK_RSHIFT==OP_ShiftRight ); assert( TK_CONCAT==OP_Concat ); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = -1; break; } case TK_UMINUS: { Expr *pLeft = pExpr->pLeft; assert( pLeft ); @@ -1594,10 +1602,11 @@ case TK_NOT: { assert( TK_BITNOT==OP_BitNot ); assert( TK_NOT==OP_Not ); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3VdbeAddOp(v, op, 0, 0); + stackChng = 0; break; } case TK_ISNULL: case TK_NOTNULL: { int dest; @@ -1606,10 +1615,11 @@ sqlite3VdbeAddOp(v, OP_Integer, 1, 0); sqlite3ExprCode(pParse, pExpr->pLeft); dest = sqlite3VdbeCurrentAddr(v) + 2; sqlite3VdbeAddOp(v, op, 1, dest); sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); + stackChng = 0; break; } case TK_AGG_FUNCTION: { AggInfo *pInfo = pExpr->pAggInfo; sqlite3VdbeAddOp(v, OP_MemLoad, pInfo->aFunc[pExpr->iAgg].iMem, 0); @@ -1642,10 +1652,11 @@ if( pDef->needCollSeq ){ if( !pColl ) pColl = pParse->db->pDfltColl; 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 case TK_EXISTS: case TK_SELECT: { @@ -1700,10 +1711,11 @@ break; } case TK_UPLUS: case TK_AS: { sqlite3ExprCode(pParse, pExpr->pLeft); + stackChng = 0; break; } case TK_CASE: { int expr_end_label; int jumpInst; @@ -1765,13 +1777,19 @@ assert( pExpr->iColumn == OE_Ignore ); sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0); 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 ); } } #ifndef SQLITE_OMIT_TRIGGER /* @@ -1835,10 +1853,11 @@ ** below verify that the numbers are aligned correctly. */ 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 ){ case TK_AND: { int d2 = sqlite3VdbeMakeLabel(v); @@ -1909,10 +1928,11 @@ sqlite3ExprCode(pParse, pExpr); sqlite3VdbeAddOp(v, OP_If, jumpIfNull, dest); break; } } + pParse->ckOffset = ckOffset; } /* ** Generate code for a boolean expression such that a jump is made ** to the label "dest" if the expression is false but execution @@ -1922,10 +1942,11 @@ ** jump if jumpIfNull is true or fall through if jumpIfNull is false. */ 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: ** ** pExpr->op op @@ -2018,10 +2039,11 @@ sqlite3ExprCode(pParse, pExpr); sqlite3VdbeAddOp(v, OP_IfNot, jumpIfNull, dest); break; } } + pParse->ckOffset = ckOffset; } /* ** Do a deep comparison of two expression trees. Return TRUE (non-zero) ** if they are identical and return FALSE if they differ in any way. Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** 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" /* ** Set P3 of the most recently inserted opcode to a column affinity @@ -866,11 +866,22 @@ sqlite3VdbeJumpHere(v, addr); } /* 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 ** is an UPDATE and the primary key is not changing, that is OK. */ Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -12,11 +12,11 @@ ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** 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_" %token_prefix TK_ @@ -266,11 +266,11 @@ ccons ::= NULL onconf. 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);} ccons ::= COLLATE id(C). {sqlite3AddCollateType(pParse, C.z, C.n);} Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** 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" /* @@ -2382,18 +2382,12 @@ } /* 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: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** 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_ /* @@ -637,10 +637,13 @@ u8 autoInc; /* True if the integer primary key is autoincrement */ int nRef; /* Number of pointers to this 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 }; @@ -1170,10 +1173,11 @@ u8 nested; /* Number of nested calls to the parser/code generator */ int nErr; /* Number of errors seen */ 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 */ int cookieValue[MAX_ATTACHED+2]; /* Values of cookies to verify */ @@ -1447,10 +1451,11 @@ void sqlite3OpenMasterTable(Vdbe *v, int); 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); void sqlite3EndTable(Parse*,Token*,Token*,Select*); Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing the printf() interface to SQLite. This code ** 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" #include "os.h" #include @@ -2873,10 +2873,16 @@ #ifdef SQLITE_OMIT_CAST Tcl_SetVar2(interp, "sqlite_options", "cast", "0", TCL_GLOBAL_ONLY); #else 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 Tcl_SetVar2(interp, "sqlite_options", "complete", "1", TCL_GLOBAL_ONLY); ADDED test/check.test Index: test/check.test ================================================================== --- /dev/null +++ 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 Index: www/lang.tcl ================================================================== --- www/lang.tcl +++ www/lang.tcl @@ -1,9 +1,9 @@ # # 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} { set outputdir [lindex $argv 0] } else { @@ -509,17 +509,17 @@ ( , ) } {column-constraint} { NOT NULL [ ] | PRIMARY KEY [] [ ] [AUTOINCREMENT] | UNIQUE [ ] | -CHECK ( ) [ ] | +CHECK ( ) | DEFAULT | COLLATE } {constraint} { PRIMARY KEY ( ) [ ] | UNIQUE ( ) [ ] | -CHECK ( ) [ ] +CHECK ( ) } {conflict-clause} { ON CONFLICT } puts { @@ -594,14 +594,12 @@ resolution algorithm, then that algorithm is used in place of the 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. The total amount of data in a single row is limited to about 1 megabytes in version 2.8. In version 3.0 there is no arbitrary Index: www/omitted.tcl ================================================================== --- www/omitted.tcl +++ www/omitted.tcl @@ -1,9 +1,9 @@ # # 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 {

SQL Features That SQLite Does Not Implement

@@ -26,15 +26,10 @@ puts "$name" puts " " 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. } feature {Complete trigger support} {