Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -183,10 +183,11 @@ A.n = (int)(pParse->sLastToken.z-X.z) + pParse->sLastToken.n; } columnid(A) ::= nm(X). { sqlite3AddColumn(pParse,&X); A = X; + pParse->constraintName.n = 0; } // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. @@ -271,14 +272,13 @@ 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 cname ccons. +carglist ::= carglist ccons. carglist ::= . -cname ::= CONSTRAINT nm(X). {pParse->constraintName = X;} -cname ::= . {pParse->constraintName.n = 0;} +ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT MINUS(A) term(X). { ExprSpan v; @@ -337,14 +337,17 @@ %type init_deferred_pred_opt {int} init_deferred_pred_opt(A) ::= . {A = 0;} init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;} init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;} -conslist_opt(A) ::= . {A.n = 0; A.z = 0;} -conslist_opt(A) ::= COMMA(X) conslist. {A = X;} -conslist ::= conslist COMMA cname tcons. -conslist ::= cname tcons. +conslist_opt(A) ::= . {A.n = 0; A.z = 0;} +conslist_opt(A) ::= COMMA(X) conslist. {A = X;} +conslist ::= conslist tconscomma tcons. +conslist ::= tcons. +tconscomma ::= COMMA. {pParse->constraintName.n = 0;} +tconscomma ::= . +tcons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R). {sqlite3AddPrimaryKey(pParse,X,R,I,0);} tcons ::= UNIQUE LP idxlist(X) RP onconf(R). {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0);} tcons ::= CHECK LP expr(E) RP onconf. Index: test/check.test ================================================================== --- test/check.test +++ test/check.test @@ -150,10 +150,52 @@ do_test check-2.6 { catchsql { INSERT INTO t2 VALUES(NULL, NULL, 3.14159); } } {1 {constraint three failed}} + +# Undocumented behavior: The CONSTRAINT name clause can follow a constraint. +# Such a clause is ignored. But the parser must accept it for backwards +# compatibility. +# +do_test check-2.10 { + execsql { + CREATE TABLE t2b( + x INTEGER CHECK( typeof(coalesce(x,0))=='integer' ) CONSTRAINT one, + y TEXT PRIMARY KEY constraint two, + z INTEGER, + UNIQUE(x,z) constraint three + ); + } +} {} +do_test check-2.11 { + catchsql { + INSERT INTO t2b VALUES('xyzzy','hi',5); + } +} {1 {constraint failed}} +do_test check-2.12 { + execsql { + CREATE TABLE t2c( + x INTEGER CONSTRAINT x_one CONSTRAINT x_two + CHECK( typeof(coalesce(x,0))=='integer' ) + CONSTRAINT x_two CONSTRAINT x_three, + y INTEGER, z INTEGER, + CONSTRAINT u_one UNIQUE(x,y,z) CONSTRAINT u_two + ); + } +} {} +do_test check-2.13 { + catchsql { + INSERT INTO t2c VALUES('xyzzy',7,8); + } +} {1 {constraint x_two failed}} +do_test check-2.cleanup { + execsql { + DROP TABLE IF EXISTS t2b; + DROP TABLE IF EXISTS t2c; + } +} {} ifcapable subquery { do_test check-3.1 { catchsql { CREATE TABLE t3( ADDED test/schema5.test Index: test/schema5.test ================================================================== --- /dev/null +++ test/schema5.test @@ -0,0 +1,69 @@ +# 2010 September 28 +# +# 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 checks corner cases in the CREATE TABLE syntax to make +# sure that legacy syntax (syntax that is disallowed according to the +# syntax diagrams) is still accepted, so that older databases that use +# that syntax can still be read. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Table constraints should be separated by commas, but they do not have +# to be. +# +do_test schema5-1.1 { + db eval { + CREATE TABLE t1(a,b,c, PRIMARY KEY(a) UNIQUE (a) CONSTRAINT one); + INSERT INTO t1 VALUES(1,2,3); + SELECT * FROM t1; + } +} {1 2 3} +do_test schema5-1.2 { + catchsql {INSERT INTO t1 VALUES(1,3,4);} +} {1 {column a is not unique}} +do_test schema5-1.3 { + db eval { + DROP TABLE t1; + CREATE TABLE t1(a,b,c, + CONSTRAINT one PRIMARY KEY(a) CONSTRAINT two CHECK(b<10) UNIQUE(b) + CONSTRAINT three + ); + INSERT INTO t1 VALUES(1,2,3); + SELECT * FROM t1; + } +} {1 2 3} +do_test schema5-1.4 { + catchsql {INSERT INTO t1 VALUES(10,11,12);} +} {1 {constraint two failed}} +do_test schema5-1.5 { + db eval { + DROP TABLE t1; + CREATE TABLE t1(a,b,c, + UNIQUE(a) CONSTRAINT one, + PRIMARY KEY(b,c) CONSTRAINT two + ); + INSERT INTO t1 VALUES(1,2,3); + } +} {} +do_test schema5-1.6 { + catchsql {INSERT INTO t1 VALUES(1,3,4)} +} {1 {column a is not unique}} +do_test schema5-1.7 { + catchsql {INSERT INTO t1 VALUES(10,2,3)} +} {1 {columns b, c are not unique}} + + + + + +finish_test