/ Check-in [28aed847]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Parser bug fix: Make sure the table constraints allowed by prior releases can still be parsed, even if they are technically not allowed by the syntax diagram. This is a cherry-pick of [a1c014d8a87c8940b3], [38bf90af1ede6ee64e], and [e536ac041815b118c4].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | apple-osx-ml
Files: files | file ages | folders
SHA1: 28aed847c6a9d5a3eae4627e1c23eb5cb9c3aabe
User & Date: drh 2012-08-07 01:37:15
Context
2012-08-07
01:37
Parser bug fix: Make sure the table constraints allowed by prior releases can still be parsed, even if they are technically not allowed by the syntax diagram. This is a cherry-pick of [a1c014d8a87c8940b3], [38bf90af1ede6ee64e], and [e536ac041815b118c4]. Leaf check-in: 28aed847 user: drh tags: apple-osx-ml
2012-04-03
19:43
Cherrypick commit [1d5e744cc6] from the trunk in order to avoid leaving a file-descriptor open in test scripts capi3.test and capi3c.test. This is the version of SQLite that ships by default on the original Mountain Lion release. check-in: 86b8481b user: dan tags: apple-osx, mountain-lion
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/parse.y.

181
182
183
184
185
186
187

188
189
190
191
192
193
194
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
337
338
339
340
341
342
343
344
345



346
347
348
349
350
351
352
column(A) ::= columnid(X) type carglist. {
  A.z = X.z;
  A.n = (int)(pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
}
columnid(A) ::= nm(X). {
  sqlite3AddColumn(pParse,&X);
  A = X;

}


// An IDENTIFIER can be a generic identifier, or one of several
// keywords.  Any non-standard keyword can also be an identifier.
//
%type id {Token}
................................................................................
typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(int)(Y.z-X.z);}
signed ::= plus_num.
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 ::= .
cname ::= CONSTRAINT nm(X).           {pParse->constraintName = X;}
cname ::= .                           {pParse->constraintName.n = 0;}
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;
  v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, X.pExpr, 0, 0);
  v.zStart = A.z;
................................................................................
%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.



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.
                                 {sqlite3AddCheckConstraint(pParse,E.pExpr);}
tcons ::= FOREIGN KEY LP idxlist(FA) RP







>







 







|

|
<







 







|
|
>
>
>







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
...
270
271
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
column(A) ::= columnid(X) type carglist. {
  A.z = X.z;
  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.
//
%type id {Token}
................................................................................
typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(int)(Y.z-X.z);}
signed ::= plus_num.
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 ccons.
carglist ::= .
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;
  v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, X.pExpr, 0, 0);
  v.zStart = A.z;
................................................................................
%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 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.
                                 {sqlite3AddCheckConstraint(pParse,E.pExpr);}
tcons ::= FOREIGN KEY LP idxlist(FA) RP

Changes to test/check.test.

148
149
150
151
152
153
154










































155
156
157
158
159
160
161
  }
} {1 {constraint two failed}}
do_test check-2.6 {
  catchsql {
    INSERT INTO t2 VALUES(NULL, NULL, 3.14159);
  }
} {1 {constraint three failed}}











































ifcapable subquery {
  do_test check-3.1 {
    catchsql {
      CREATE TABLE t3(
        x, y, z,
        CHECK( x<(SELECT min(x) FROM t1) )







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  }
} {1 {constraint two failed}}
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(
        x, y, z,
        CHECK( x<(SELECT min(x) FROM t1) )

Added test/schema5.test.











































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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