Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -10,14 +10,14 @@ ** ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.67 2002/06/02 16:09:02 drh Exp $ +** $Id: expr.c,v 1.68 2002/06/02 18:19:00 drh Exp $ */ #include "sqliteInt.h" - +#include /* ** Construct a new expression node and return a pointer to it. Memory ** for this node is obtained from sqliteMalloc(). The calling function ** is responsible for making sure the node eventually gets freed. 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.69 2002/05/24 16:14:15 drh Exp $ +** @(#) $Id: parse.y,v 1.70 2002/06/02 18:19:00 drh Exp $ */ %token_prefix TK_ %token_type {Token} %default_type {Token} %extra_argument {Parse *pParse} @@ -113,29 +113,37 @@ id(A) ::= ABORT(X). {A = X;} id(A) ::= AFTER(X). {A = X;} id(A) ::= ASC(X). {A = X;} id(A) ::= BEFORE(X). {A = X;} id(A) ::= BEGIN(X). {A = X;} +id(A) ::= CASCADE(X). {A = X;} id(A) ::= CLUSTER(X). {A = X;} id(A) ::= CONFLICT(X). {A = X;} id(A) ::= COPY(X). {A = X;} +id(A) ::= DEFERRED(X). {A = X;} id(A) ::= DELIMITERS(X). {A = X;} id(A) ::= DESC(X). {A = X;} id(A) ::= EACH(X). {A = X;} id(A) ::= END(X). {A = X;} id(A) ::= EXPLAIN(X). {A = X;} id(A) ::= FAIL(X). {A = X;} id(A) ::= FOR(X). {A = X;} +id(A) ::= FULL(X). {A = X;} id(A) ::= ID(X). {A = X;} id(A) ::= IGNORE(X). {A = X;} +id(A) ::= IMMEDATE(X). {A = X;} +id(A) ::= INITIALLY(X). {A = X;} id(A) ::= INSTEAD(X). {A = X;} +id(A) ::= MATCH(X). {A = X;} id(A) ::= JOIN(X). {A = X;} id(A) ::= KEY(X). {A = X;} id(A) ::= OF(X). {A = X;} id(A) ::= OFFSET(X). {A = X;} +id(A) ::= PARTIAL(X). {A = X;} id(A) ::= PRAGMA(X). {A = X;} id(A) ::= REPLACE(X). {A = X;} +id(A) ::= RESTRICT(X). {A = X;} id(A) ::= ROW(X). {A = X;} id(A) ::= STATEMENT(X). {A = X;} id(A) ::= TEMP(X). {A = X;} id(A) ::= TRIGGER(X). {A = X;} id(A) ::= VACUUM(X). {A = X;} @@ -177,10 +185,33 @@ // ccons ::= NOT NULL onconf(R). {sqliteAddNotNull(pParse, R);} ccons ::= PRIMARY KEY sortorder onconf(R). {sqliteAddPrimaryKey(pParse,0,R);} ccons ::= UNIQUE onconf(R). {sqliteCreateIndex(pParse,0,0,0,R,0,0);} ccons ::= CHECK LP expr RP onconf. +ccons ::= references. +ccons ::= defer_subclause. + +// A REFERENCES clause is parsed but the current implementation does not +// do anything with it. +// +references ::= REFERENCES ids LP idxlist RP refargs. +references ::= REFERENCES ids refargs. +refargs ::= . +refargs ::= refargs refarg. +refarg ::= MATCH FULL. +refarg ::= MATCH PARTIAL. +refarg ::= ON DELETE refact. +refarg ::= ON UPDATE refact. +refact ::= SET NULL. +refact ::= SET DEFAULT. +refact ::= CASCADE. +refact ::= RESTRICT. +defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt. +defer_subclause ::= DEFERRABLE init_deferred_pred_opt. +init_deferred_pred_opt ::= . +init_deferred_pred_opt ::= INITIALLY DEFERRED. +init_deferred_pred_opt ::= INITIALLY IMMEDIATE. // For the time being, the only constraint we care about is the primary // key and UNIQUE. Both create indices. // conslist_opt ::= . @@ -192,10 +223,13 @@ tcons ::= PRIMARY KEY LP idxlist(X) RP onconf(R). {sqliteAddPrimaryKey(pParse,X,R);} tcons ::= UNIQUE LP idxlist(X) RP onconf(R). {sqliteCreateIndex(pParse,0,0,X,R,0,0);} tcons ::= CHECK expr onconf. +tcons ::= FOREIGN KEY LP idxlist RP references defer_subclause_opt. +defer_subclause_opt ::= . +defer_subclause_opt ::= defer_subclause. // The following is a non-standard extension that allows us to declare the // default behavior when there is a constraint conflict. // %type onconf {int} Index: src/tokenize.c ================================================================== --- src/tokenize.c +++ src/tokenize.c @@ -13,11 +13,11 @@ ** ** This file contains C code that splits an SQL input string up into ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.43 2002/05/24 16:14:15 drh Exp $ +** $Id: tokenize.c,v 1.44 2002/06/02 18:19:00 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include #include @@ -46,19 +46,22 @@ { "ASC", 0, TK_ASC, 0 }, { "BEFORE", 0, TK_BEFORE, 0 }, { "BEGIN", 0, TK_BEGIN, 0 }, { "BETWEEN", 0, TK_BETWEEN, 0 }, { "BY", 0, TK_BY, 0 }, + { "CASCADE", 0, TK_CASCADE, 0 }, { "CASE", 0, TK_CASE, 0 }, { "CHECK", 0, TK_CHECK, 0 }, { "CLUSTER", 0, TK_CLUSTER, 0 }, { "COMMIT", 0, TK_COMMIT, 0 }, { "CONFLICT", 0, TK_CONFLICT, 0 }, { "CONSTRAINT", 0, TK_CONSTRAINT, 0 }, { "COPY", 0, TK_COPY, 0 }, { "CREATE", 0, TK_CREATE, 0 }, { "DEFAULT", 0, TK_DEFAULT, 0 }, + { "DEFERRED", 0, TK_DEFERRED, 0 }, + { "DEFERRABLE", 0, TK_DEFERRABLE, 0 }, { "DELETE", 0, TK_DELETE, 0 }, { "DELIMITERS", 0, TK_DELIMITERS, 0 }, { "DESC", 0, TK_DESC, 0 }, { "DISTINCT", 0, TK_DISTINCT, 0 }, { "DROP", 0, TK_DROP, 0 }, @@ -67,17 +70,21 @@ { "ELSE", 0, TK_ELSE, 0 }, { "EXCEPT", 0, TK_EXCEPT, 0 }, { "EXPLAIN", 0, TK_EXPLAIN, 0 }, { "FAIL", 0, TK_FAIL, 0 }, { "FOR", 0, TK_FOR, 0 }, + { "FOREIGN", 0, TK_FOREIGN, 0 }, { "FROM", 0, TK_FROM, 0 }, + { "FULL", 0, TK_FULL, 0 }, { "GLOB", 0, TK_GLOB, 0 }, { "GROUP", 0, TK_GROUP, 0 }, { "HAVING", 0, TK_HAVING, 0 }, { "IGNORE", 0, TK_IGNORE, 0 }, + { "IMMEDIATE", 0, TK_IMMEDIATE, 0 }, { "IN", 0, TK_IN, 0 }, { "INDEX", 0, TK_INDEX, 0 }, + { "INITIALLY", 0, TK_INITIALLY, 0 }, { "INSERT", 0, TK_INSERT, 0 }, { "INSTEAD", 0, TK_INSTEAD, 0 }, { "INTERSECT", 0, TK_INTERSECT, 0 }, { "INTO", 0, TK_INTO, 0 }, { "IS", 0, TK_IS, 0 }, @@ -84,21 +91,25 @@ { "ISNULL", 0, TK_ISNULL, 0 }, { "JOIN", 0, TK_JOIN, 0 }, { "KEY", 0, TK_KEY, 0 }, { "LIKE", 0, TK_LIKE, 0 }, { "LIMIT", 0, TK_LIMIT, 0 }, + { "MATCH", 0, TK_MATCH, 0 }, { "NOT", 0, TK_NOT, 0 }, { "NOTNULL", 0, TK_NOTNULL, 0 }, { "NULL", 0, TK_NULL, 0 }, { "OF", 0, TK_OF, 0 }, { "OFFSET", 0, TK_OFFSET, 0 }, { "ON", 0, TK_ON, 0 }, { "OR", 0, TK_OR, 0 }, { "ORDER", 0, TK_ORDER, 0 }, + { "PARTIAL", 0, TK_PARTIAL, 0 }, { "PRAGMA", 0, TK_PRAGMA, 0 }, { "PRIMARY", 0, TK_PRIMARY, 0 }, + { "REFERENCES", 0, TK_REFERENCES, 0 }, { "REPLACE", 0, TK_REPLACE, 0 }, + { "RESTRICT", 0, TK_RESTRICT, 0 }, { "ROLLBACK", 0, TK_ROLLBACK, 0 }, { "ROW", 0, TK_ROW, 0 }, { "SELECT", 0, TK_SELECT, 0 }, { "SET", 0, TK_SET, 0 }, { "STATEMENT", 0, TK_STATEMENT, 0 }, Index: test/table.test ================================================================== --- test/table.test +++ test/table.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE TABLE statement. # -# $Id: table.test,v 1.16 2002/05/22 21:27:04 drh Exp $ +# $Id: table.test,v 1.17 2002/06/02 18:19:00 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a basic table and verify it is added to sqlite_master @@ -375,8 +375,65 @@ do_test table-9.1 { catchsql { CREATE TABLE t6(a,b,a); } } {1 {duplicate column name: a}} + +# Check the foreign key syntax. +# +do_test table-10.1 { + catchsql { + CREATE TABLE t6(a REFERENCES t4(a) NOT NULL); + INSERT INTO t6 VALUES(NULL); + } +} {1 {constraint failed}} +do_test table-10.2 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a REFERENCES t4(a) MATCH PARTIAL); + } +} {0 {}} +do_test table-10.3 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a REFERENCES t4 MATCH FULL ON DELETE SET NULL NOT NULL); + } +} {0 {}} +do_test table-10.4 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a REFERENCES t4 MATCH FULL ON UPDATE SET DEFAULT DEFAULT 1); + } +} {0 {}} +do_test table-10.5 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a NOT NULL NOT DEFERRABLE INITIALLY IMMEDIATE); + } +} {0 {}} +do_test table-10.6 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a NOT NULL DEFERRABLE INITIALLY DEFERRED); + } +} {0 {}} +do_test table-10.7 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a, + FOREIGN KEY (a) REFERENCES t4(b) DEFERRABLE INITIALLY DEFERRED + ); + } +} {0 {}} +do_test table-10.8 { + catchsql { + DROP TABLE t6; + CREATE TABLE t6(a,b,c, + FOREIGN KEY (b,c) REFERENCES t4(x,y) MATCH PARTIAL + ON UPDATE SET NULL ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED + ); + } +} {0 {}} + finish_test