Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | The FOR EACH STATEMENT clause in a trigger is now a syntax error. It used to be silently ignored. STATEMENT is no longer a keyword. (CVS 3821) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
8e2559b4da6329d7c21925d0850f7f91 |
User & Date: | drh 2007-04-06 11:26:00.000 |
Context
2007-04-06
| ||
15:02 | Binary file I/O infrastructure added and used to increase test coverage for detection of corrupt database files. (CVS 3822) (check-in: 479b3d965b user: drh tags: trunk) | |
11:26 | The FOR EACH STATEMENT clause in a trigger is now a syntax error. It used to be silently ignored. STATEMENT is no longer a keyword. (CVS 3821) (check-in: 8e2559b4da user: drh tags: trunk) | |
02:32 | Test coverage improvements. Over 90% of branches are now executed in both directions. (CVS 3820) (check-in: a776d93cca user: drh tags: trunk) | |
Changes
Changes to src/parse.y.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** 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. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** 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.217 2007/04/06 11:26:00 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" %token_prefix TK_ // The type of the data attached to each token is Token. This is also the // default type for non-terminals. |
︙ | ︙ | |||
171 172 173 174 175 176 177 | // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN CASCADE CAST CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH PLAN QUERY KEY | | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN CASCADE CAST CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH PLAN QUERY KEY OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW TEMP TRIGGER VACUUM VIEW VIRTUAL %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT REINDEX RENAME CTIME_KW IF . %wildcard ANY. |
︙ | ︙ | |||
235 236 237 238 239 240 241 | typetoken(A) ::= typename(X) LP signed COMMA signed RP(Y). { A.z = X.z; A.n = &Y.z[Y.n] - X.z; } %type typename {Token} typename(A) ::= ids(X). {A = X;} typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(Y.z-X.z);} | | < | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | typetoken(A) ::= typename(X) LP signed COMMA signed RP(Y). { A.z = X.z; A.n = &Y.z[Y.n] - X.z; } %type typename {Token} typename(A) ::= ids(X). {A = X;} typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(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 carg. carglist ::= . carg ::= CONSTRAINT nm ccons. |
︙ | ︙ | |||
924 925 926 927 928 929 930 | all.z = A.z; all.n = (Z.z - A.z) + Z.n; sqlite3FinishTrigger(pParse, S, &all); } trigger_decl(A) ::= temp(T) TRIGGER ifnotexists(NOERR) nm(B) dbnm(Z) trigger_time(C) trigger_event(D) | | | | < | < | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 | all.z = A.z; all.n = (Z.z - A.z) + Z.n; sqlite3FinishTrigger(pParse, S, &all); } trigger_decl(A) ::= temp(T) TRIGGER ifnotexists(NOERR) nm(B) dbnm(Z) trigger_time(C) trigger_event(D) ON fullname(E) foreach_clause when_clause(G). { sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR); A = (Z.n==0?B:Z); } %type trigger_time {int} trigger_time(A) ::= BEFORE. { A = TK_BEFORE; } trigger_time(A) ::= AFTER. { A = TK_AFTER; } trigger_time(A) ::= INSTEAD OF. { A = TK_INSTEAD;} trigger_time(A) ::= . { A = TK_BEFORE; } %type trigger_event {struct TrigEvent} %destructor trigger_event {sqlite3IdListDelete($$.b);} trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;} trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;} trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;} foreach_clause ::= . foreach_clause ::= FOR EACH ROW. %type when_clause {Expr*} %destructor when_clause {sqlite3ExprDelete($$);} when_clause(A) ::= . { A = 0; } when_clause(A) ::= WHEN expr(X). { A = X; } %type trigger_cmd_list {TriggerStep*} |
︙ | ︙ |
Changes to src/sqliteInt.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** ** @(#) $Id: sqliteInt.h,v 1.550 2007/04/06 11:26:00 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ #if defined(SQLITE_TCL) || defined(TCLSH) # include <tcl.h> #endif |
︙ | ︙ | |||
1403 1404 1405 1406 1407 1408 1409 | char *name; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Expr *pWhen; /* The WHEN clause of the expresion (may be NULL) */ IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger, the <column-list> is stored here */ | < | 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 | char *name; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Expr *pWhen; /* The WHEN clause of the expresion (may be NULL) */ IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger, the <column-list> is stored here */ Token nameToken; /* Token containing zName. Use during parsing only */ Schema *pSchema; /* Schema containing the trigger */ Schema *pTabSchema; /* Schema containing the table */ TriggerStep *step_list; /* Link list of trigger program steps */ Trigger *pNext; /* Next trigger associated with the table */ }; |
︙ | ︙ | |||
1713 1714 1715 1716 1717 1718 1719 | int sqlite3SafetyOn(sqlite3*); int sqlite3SafetyOff(sqlite3*); int sqlite3SafetyCheck(sqlite3*); void sqlite3ChangeCookie(sqlite3*, Vdbe*, int); #ifndef SQLITE_OMIT_TRIGGER void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, | | | 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 | int sqlite3SafetyOn(sqlite3*); int sqlite3SafetyOff(sqlite3*); int sqlite3SafetyCheck(sqlite3*); void sqlite3ChangeCookie(sqlite3*, Vdbe*, int); #ifndef SQLITE_OMIT_TRIGGER void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, Expr*,int, int); void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); void sqlite3DropTrigger(Parse*, SrcList*, int); void sqlite3DropTriggerPtr(Parse*, Trigger*); int sqlite3TriggersExist(Parse*, Table*, int, ExprList*); int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, int, int); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
43 44 45 46 47 48 49 | Parse *pParse, /* The parse context of the CREATE TRIGGER statement */ Token *pName1, /* The name of the trigger */ Token *pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList *pColumns, /* column list if this is an UPDATE OF trigger */ SrcList *pTableName,/* The name of the table/view the trigger applies to */ | < | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | Parse *pParse, /* The parse context of the CREATE TRIGGER statement */ Token *pName1, /* The name of the trigger */ Token *pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList *pColumns, /* column list if this is an UPDATE OF trigger */ SrcList *pTableName,/* The name of the table/view the trigger applies to */ Expr *pWhen, /* WHEN clause */ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ){ Trigger *pTrigger = 0; Table *pTab; char *zName = 0; /* Name of the trigger */ |
︙ | ︙ | |||
176 177 178 179 180 181 182 | pTrigger->table = sqliteStrDup(pTableName->a[0].zName); pTrigger->pSchema = db->aDb[iDb].pSchema; pTrigger->pTabSchema = pTab->pSchema; pTrigger->op = op; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger->pWhen = sqlite3ExprDup(pWhen); pTrigger->pColumns = sqlite3IdListDup(pColumns); | < | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | pTrigger->table = sqliteStrDup(pTableName->a[0].zName); pTrigger->pSchema = db->aDb[iDb].pSchema; pTrigger->pTabSchema = pTab->pSchema; pTrigger->op = op; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger->pWhen = sqlite3ExprDup(pWhen); pTrigger->pColumns = sqlite3IdListDup(pColumns); sqlite3TokenCopy(&pTrigger->nameToken,pName); assert( pParse->pNewTrigger==0 ); pParse->pNewTrigger = pTrigger; trigger_cleanup: sqliteFree(zName); sqlite3SrcListDelete(pTableName); |
︙ | ︙ |
Changes to test/fkey1.test.
︙ | ︙ | |||
42 43 44 45 46 47 48 | y TEXT ); } } {} do_test fkey1-1.2 { execsql { CREATE TABLE t3( | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | y TEXT ); } } {} do_test fkey1-1.2 { execsql { CREATE TABLE t3( a INTEGER REFERENCES t2 ON INSERT RESTRICT, b INTEGER REFERENCES t1, FOREIGN KEY (a,b) REFERENCES t2(x,y) ); } } {} do_test fkey1-2.1 { |
︙ | ︙ |
Changes to test/trigger1.test.
︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 | } } {1 {no such table: no_such_table}} } execsql { CREATE TABLE t1(a); } execsql { CREATE TRIGGER tr1 INSERT ON t1 BEGIN INSERT INTO t1 values(1); END; } do_test trigger1-1.2.0 { catchsql { | > > > > > > > | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | } } {1 {no such table: no_such_table}} } execsql { CREATE TABLE t1(a); } do_test trigger1-1.1.3 { catchsql { CREATE TRIGGER trig UPDATE ON t1 FOR EACH STATEMENT BEGIN SELECT * FROM sqlite_master; END; } } {1 {near "STATEMENT": syntax error}} execsql { CREATE TRIGGER tr1 INSERT ON t1 BEGIN INSERT INTO t1 values(1); END; } do_test trigger1-1.2.0 { catchsql { |
︙ | ︙ |
Changes to tool/mkkeywordhash.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ** A header comment placed at the beginning of generated code. */ static const char zHdr[] = "/***** This file contains automatically generated code ******\n" "**\n" "** The code in this file has been automatically generated by\n" "**\n" | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ** A header comment placed at the beginning of generated code. */ static const char zHdr[] = "/***** This file contains automatically generated code ******\n" "**\n" "** The code in this file has been automatically generated by\n" "**\n" "** $Header: /home/drh/sqlite/trans/cvs/sqlite/sqlite/tool/mkkeywordhash.c,v 1.27 2007/04/06 11:26:00 drh Exp $\n" "**\n" "** The code in this file implements a function that determines whether\n" "** or not a given identifier is really an SQL keyword. The same thing\n" "** might be implemented more directly using a hand-written hash table.\n" "** But by using this automatically generated code, the size of the code\n" "** is substantially reduced. This is important for embedded applications\n" "** on platforms with limited memory.\n" |
︙ | ︙ | |||
229 230 231 232 233 234 235 | { "REPLACE", "TK_REPLACE", CONFLICT }, { "RESTRICT", "TK_RESTRICT", FKEY }, { "RIGHT", "TK_JOIN_KW", ALWAYS }, { "ROLLBACK", "TK_ROLLBACK", ALWAYS }, { "ROW", "TK_ROW", TRIGGER }, { "SELECT", "TK_SELECT", ALWAYS }, { "SET", "TK_SET", ALWAYS }, | < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | { "REPLACE", "TK_REPLACE", CONFLICT }, { "RESTRICT", "TK_RESTRICT", FKEY }, { "RIGHT", "TK_JOIN_KW", ALWAYS }, { "ROLLBACK", "TK_ROLLBACK", ALWAYS }, { "ROW", "TK_ROW", TRIGGER }, { "SELECT", "TK_SELECT", ALWAYS }, { "SET", "TK_SET", ALWAYS }, { "TABLE", "TK_TABLE", ALWAYS }, { "TEMP", "TK_TEMP", ALWAYS }, { "TEMPORARY", "TK_TEMP", ALWAYS }, { "THEN", "TK_THEN", ALWAYS }, { "TO", "TK_TO", ALTER }, { "TRANSACTION", "TK_TRANSACTION", ALWAYS }, { "TRIGGER", "TK_TRIGGER", TRIGGER }, |
︙ | ︙ |
Changes to www/lang.tcl.
1 2 3 | # # Run this Tcl script to generate the lang-*.html files. # | | | 1 2 3 4 5 6 7 8 9 10 11 | # # Run this Tcl script to generate the lang-*.html files. # set rcsid {$Id: lang.tcl,v 1.128 2007/04/06 11:26:00 drh Exp $} source common.tcl if {[llength $argv]>0} { set outputdir [lindex $argv 0] } else { set outputdir "" } |
︙ | ︙ | |||
654 655 656 657 658 659 660 | DELETE | INSERT | UPDATE | UPDATE OF <column-list> } Syntax {trigger-action} { | | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | DELETE | INSERT | UPDATE | UPDATE OF <column-list> } Syntax {trigger-action} { [ FOR EACH ROW ] [ WHEN <expression> ] BEGIN <trigger-step> ; [ <trigger-step> ; ]* END } Syntax {trigger-step} { <update-statement> | <insert-statement> | |
︙ | ︙ | |||
2004 2005 2006 2007 2008 2009 2010 | REPLACE* RESTRICT* RIGHT ROLLBACK ROW* SELECT SET | < | 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 | REPLACE* RESTRICT* RIGHT ROLLBACK ROW* SELECT SET TABLE TEMP* TEMPORARY* THEN TO TRANSACTION TRIGGER* |
︙ | ︙ |