/ Check-in [c8bf40df]
Login

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

Overview
Comment:Make explicit the restrictions on UPDATE, DELETE, and INSERT statement syntax within triggers. Ticket #3947. (CVS 6840)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:c8bf40df7be728b11bb633516d1988c6064a9d70
User & Date: drh 2009-07-03 15:37:28
Context
2009-07-03
16:25
Simplify the way the read-uncommitted flag is handled to facilitate test coverage. (CVS 6841) check-in: e2112d61 user: danielk1977 tags: trunk
15:37
Make explicit the restrictions on UPDATE, DELETE, and INSERT statement syntax within triggers. Ticket #3947. (CVS 6840) check-in: c8bf40df user: drh tags: trunk
12:57
Test the result of pthread_create() and do not call pthread_join() if the thread creation failed. Ticket #3933. (CVS 6839) check-in: 304c5110 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/parse.y.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
....
1174
1175
1176
1177
1178
1179
1180
1181































1182
1183
1184

1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
**
*************************************************************************
** 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.284 2009/07/01 14:56:40 danielk1977 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.
................................................................................
  A = Y;
}
trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { 
  assert( X!=0 );
  X->pLast = X;
  A = X;
}
































%type trigger_cmd {TriggerStep*}
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
// UPDATE 

trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z).  
               { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); }

// INSERT
trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) 
                   VALUES LP itemlist(Y) RP.  
               {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);}

trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S).
               {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}

// DELETE
trigger_cmd(A) ::= DELETE FROM nm(X) where_opt(Y).
               {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);}

// SELECT
trigger_cmd(A) ::= select(X).  {A = sqlite3TriggerSelectStep(pParse->db, X); }

// The special RAISE expression that may occur in trigger programs
expr(A) ::= RAISE(X) LP IGNORE RP(Y).  {







|







 








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



>
|
|


|
|
|

|



|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
....
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
**
*************************************************************************
** 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.285 2009/07/03 15:37:28 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.
................................................................................
  A = Y;
}
trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { 
  assert( X!=0 );
  X->pLast = X;
  A = X;
}

// Disallow qualified table names on INSERT, UPDATE, and DELETE statements
// within a trigger.  The table to INSERT, UPDATE, or DELETE is always in 
// the same database as the table that the trigger fires on.
//
%type trnm {Token}
trnm(A) ::= nm(X).   {A = X;}
trnm(A) ::= nm DOT nm(X). {
  A = X;
  sqlite3ErrorMsg(pParse, 
        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
        "statements within triggers");
}

// Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE
// statements within triggers.  We make a specific error message for this
// since it is an exception to the default grammar rules.
//
tridxby ::= .
tridxby ::= INDEXED BY nm. {
  sqlite3ErrorMsg(pParse,
        "the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
        "within triggers");
}
tridxby ::= NOT INDEXED. {
  sqlite3ErrorMsg(pParse,
        "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
        "within triggers");
}



%type trigger_cmd {TriggerStep*}
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
// UPDATE 
trigger_cmd(A) ::=
   UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z).  
   { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); }

// INSERT
trigger_cmd(A) ::=
   insert_cmd(R) INTO trnm(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.  
   {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);}

trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S).
               {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}

// DELETE
trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y).
               {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);}

// SELECT
trigger_cmd(A) ::= select(X).  {A = sqlite3TriggerSelectStep(pParse->db, X); }

// The special RAISE expression that may occur in trigger programs
expr(A) ::= RAISE(X) LP IGNORE RP(Y).  {

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153

2154
2155
2156
2157
2158
2159
2160
2161
**    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.890 2009/06/26 15:14:55 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
 *              Otherwise NULL.
 * pExprList -> A list of the columns to update and the expressions to update
 *              them to. See sqlite3Update() documentation of "pChanges"
 *              argument.
 * 
 */
struct TriggerStep {
  int op;              /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
  int orconf;          /* OE_Rollback etc. */
  Trigger *pTrig;      /* The trigger that this step is a part of */

  Select *pSelect;     /* Valid for SELECT and sometimes 
                          INSERT steps (when pExprList == 0) */
  Token target;        /* Target table for DELETE, UPDATE, INSERT.  Quoted */
  Expr *pWhere;        /* Valid for DELETE, UPDATE steps */
  ExprList *pExprList; /* Valid for UPDATE statements and sometimes 
                           INSERT steps (when pSelect == 0)         */

  IdList *pIdList;     /* Valid for INSERT statements only */
  TriggerStep *pNext;  /* Next in the link-list */
  TriggerStep *pLast;  /* Last element in link-list. Valid for 1st elem only */
};

/*
 * An instance of struct TriggerStack stores information required during code
 * generation of a single trigger program. While the trigger program is being







|







 







|
|

<
|
<
|
|
<
<
>
|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146

2147

2148
2149


2150
2151
2152
2153
2154
2155
2156
2157
2158
**    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.891 2009/07/03 15:37:28 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
 *              Otherwise NULL.
 * pExprList -> A list of the columns to update and the expressions to update
 *              them to. See sqlite3Update() documentation of "pChanges"
 *              argument.
 * 
 */
struct TriggerStep {
  u8 op;               /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
  u8 orconf;           /* OE_Rollback etc. */
  Trigger *pTrig;      /* The trigger that this step is a part of */

  Select *pSelect;     /* SELECT statment or RHS of INSERT INTO .. SELECT ... */

  Token target;        /* Target table for DELETE, UPDATE, INSERT */
  Expr *pWhere;        /* The WHERE clause for DELETE or UPDATE steps */


  ExprList *pExprList; /* SET clause for UPDATE.  VALUES clause for INSERT */
  IdList *pIdList;     /* Column names for INSERT */
  TriggerStep *pNext;  /* Next in the link-list */
  TriggerStep *pLast;  /* Last element in link-list. Valid for 1st elem only */
};

/*
 * An instance of struct TriggerStack stores information required during code
 * generation of a single trigger program. While the trigger program is being

Changes to test/trigger1.test.

635
636
637
638
639
640
641




























































642
  }
  catchsql { UPDATE tA SET a = 'abc' }
} {1 {datatype mismatch}}
do_test trigger1-15.2 {
  catchsql { INSERT INTO tA VALUES('abc', 2, 3) }
} {1 {datatype mismatch}}





























































finish_test







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

635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
  }
  catchsql { UPDATE tA SET a = 'abc' }
} {1 {datatype mismatch}}
do_test trigger1-15.2 {
  catchsql { INSERT INTO tA VALUES('abc', 2, 3) }
} {1 {datatype mismatch}}

# Ticket #3947:  Do not allow qualified table names on INSERT, UPDATE, and
# DELETE statements within triggers.  Actually, this has never been allowed
# by the grammar.  But the error message is confusing: one simply gets a
# "syntax error".  That has now been changed to give a full error message.
#
do_test trigger1-16.1 {
  db eval {
    CREATE TABLE t16(a,b,c);
    CREATE INDEX t16a ON t16(a);
    CREATE INDEX t16b ON t16(b);
  }
  catchsql {
    CREATE TRIGGER main.t16err1 AFTER INSERT ON tA BEGIN
      INSERT INTO main.t16 VALUES(1,2,3);
    END;
  }
} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
do_test trigger1-16.2 {
  catchsql {
    CREATE TRIGGER main.t16err2 AFTER INSERT ON tA BEGIN
      UPDATE main.t16 SET rowid=rowid+1;
    END;
  }
} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
do_test trigger1-16.3 {
  catchsql {
    CREATE TRIGGER main.t16err3 AFTER INSERT ON tA BEGIN
      DELETE FROM main.t16;
    END;
  }
} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
do_test trigger1-16.4 {
  catchsql {
    CREATE TRIGGER main.t16err4 AFTER INSERT ON tA BEGIN
      UPDATE t16 NOT INDEXED SET rowid=rowid+1;
    END;
  }
} {1 {the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers}}
do_test trigger1-16.5 {
  catchsql {
    CREATE TRIGGER main.t16err5 AFTER INSERT ON tA BEGIN
      UPDATE t16 INDEXED BY t16a SET rowid=rowid+1 WHERE a=1;
    END;
  }
} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}}
do_test trigger1-16.6 {
  catchsql {
    CREATE TRIGGER main.t16err6 AFTER INSERT ON tA BEGIN
      DELETE FROM t16 NOT INDEXED WHERE a=123;
    END;
  }
} {1 {the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers}}
do_test trigger1-16.7 {
  catchsql {
    CREATE TRIGGER main.t16err7 AFTER INSERT ON tA BEGIN
      DELETE FROM t16 INDEXED BY t16a WHERE a=123;
    END;
  }
} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}}

finish_test