-C Fix\sa\slogic\serror\sintroduced\sby\s[cea8bf79e18d55a8]\swhich\scaused\n"0\sOR\s2"\sto\sbe\sevaluated\sas\s"2"\sinstead\sof\sas\s"1".\s\sProblem\sreported\sat\n[forum:/forumpost/d5f32040c5d50d2d|forum\spost\sd5f32040c].
-D 2025-10-29T20:47:01.603
+C Experimental\schange\sto\sallow\striggers\sin\sthe\stemp\sschema\sto\suse\sfully\squalified\stable\snames\sin\sINSERT/UPDATE/DELETE\sstatements.
+D 2025-10-29T20:57:44.300
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366
F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398
-F src/alter.c fc7bbbeb9e89c7124bf5772ce474b333b7bdc18d6e080763211a40fde69fb1da
+F src/alter.c a02edd673c242698f42eecf150a0c6eef11d1cc8ebf555d5c0e9f2847c0e9e9d
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
-F src/attach.c 9af61b63b10ee702b1594ecd24fb8cea0839cfdb6addee52fba26fa879f5db9d
+F src/attach.c 7cf07d4fa42b9fc8662237c60c40b730326c30aa90ae5fffc0b18b2d726ebf61
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
F src/expr.c 17b0cbe08e004c1653030f5de9b6b050e84feaa112239f7f576af2dc5e53a5fb
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
-F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
+F src/fkey.c c065da737307a29e4d240ac727758dbf4102cb3218a1f651eb689b6a6fa12531
F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b
F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
F src/hash.c 73934a7f7ab1cb110614a9388cb516893b0cf5b7b69e4fd1a0780ac4ce166be7
F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
F src/pager.c 113f9149092ccff6cf90e97c2611200e5a237f13d26c394bc9fd933377852764
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
-F src/parse.y 619c3e92a54686c5e47923688c4b9bf7ec534a4690db5677acc28b299c403250
+F src/parse.y 5ba60bd246219225ccb32c15bf692124327810f00128dd779155185848369865
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
F src/sqlite.h.in f7944026ee89ea348f89aec56372d6d25b6cafc1d89df741278d6917e86326a3
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 7f236ca1b175ffe03316d974ef57df79b3938466c28d2f95caef5e08c57f3a52
-F src/sqliteInt.h 88f7fc9ce1630d9a5f7e0a8e1f3287cdc63882fba985c18e7eee1b9f457f59aa
+F src/sqliteInt.h 89e84b1a2e9de855282f2d7fc415105edc284529bad6bbe54018eef0c4e15456
F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c cb3294cf23c11106b50d9af6998a6c1bf389b52e15b17698c9fab97bbaa9b37f
F src/treeview.c 3ce7ac9835d2d70cc1c868b01b747ae8a062322e155701e58e3d62ca79aada7a
-F src/trigger.c d5cf2541ff048f30b6a0507eb3d1ec4e695c53584e3b2298a5bf248714fe185e
+F src/trigger.c 4328fb185c81889299f41734b64ba59f25b571effa0d2f6ea284c611dca685bc
F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F test/altertab.test 8a2712f9076da5012a002d0b5cc0a421398a5bf61c25bab41b77c427586a7a27
F test/altertab2.test 0889ba0700cc1cdb7bc7d25975aa61fece34f621de963d0886e2395716b38576
F test/altertab3.test 471b8898d10bbc6488db9c23dc76811f405de6707d2d342b1b8b6fd1f13cd3c8
-F test/altertrig.test aacc980b657354fe2d3d4d3a004f07d04ccc1a93e5ef82d68a79088c274ddc6b
+F test/altertrig.test b1590647076add5a47aea0f2236c609ca0bc8a7a2462463edd3e5882c7894802
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 2fb21d7d64748636384e6cb8998dbf83968caf644c07fcb4f76c18f2e7ede94b
F test/analyze3.test c5156cef33f04b90a6b9e9d5d0bbc273a0fb44147d4508407bf1080811e2c6c8
F test/e_select.test 327a15f14068bbd6f647cedc67210f8680fcb2f05e481a0a855fccd2abfa1292
F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
F test/e_totalchanges.test c927f7499dc3aa28b9b556b7d6d115a2f0fe41f012b128d16bf1f3b30e9b41e4
-F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528
+F test/e_update.test 9f8bb82b8760ac66f2c9c2aadb78a418bd639d22f041d712570c9db56f20afda
F test/e_uri.test 86564382132d9c453845eeb5293c7e375487b625900ab56c181a0464908417d8
F test/e_vacuum.test 89fc48e8beee2f9dfd6de1fbb2edea6542dae9121dc0fbe6313764169e742104
F test/e_wal.test db7c33642711cf3c7959714b5f012aca08cacfa78da0382f95e849eb3ba66aa4
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d1631311a16
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
-F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
+F test/temptrigger.test 62c0d29ff3fb7fa40c136dcfac3d992c5a6d3b1699459e65a4c1d0feb1258120
F test/tester.tcl 463ae33b8bf75ac77451df19bd65e7c415c2e9891227c7c9e657d0a2d8e1074a
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
F test/testrunner.tcl 9da764507f6bc752961555c0beb58eb6584b9fb0f989342c7eaab3336380f560 x
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P e2c20aa5929e5b79aabca8a51fb3e32e0533526d64d7576d3caf1c847fff58b4
-R 1a124afdb2635430b3fd6e2b8b70ff33
-U drh
-Z 0a36ac0dd51fe98fcc08d99ab2c27dbd
+P 095cc4f22e63c98cbb2acabdbcaf02e59e67ec6d3cc219b5f42a714e3d53a264
+R 85cd0d1d042f2b81ef8d0010c49c6487
+T *branch * temp-trigger-refs
+T *sym-temp-trigger-refs *
+T -sym-trunk *
+U dan
+Z 89ba63f7a028e73214265366183a56b0
# Remove this line to create a well-formed Fossil manifest.
-branch trunk
-tag trunk
+branch temp-trigger-refs
+tag temp-trigger-refs
-095cc4f22e63c98cbb2acabdbcaf02e59e67ec6d3cc219b5f42a714e3d53a264
+48e168007ceb1bdc54cf21a8e959a8e0f9ccdc478827db7a7a0518fc7a226401
sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
if( pParse->nErr ) rc = pParse->rc;
}
- if( rc==SQLITE_OK && pStep->zTarget ){
- SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
+ if( rc==SQLITE_OK && pStep->pSrc ){
+ SrcList *pSrc = sqlite3SrcListDup(db, pStep->pSrc, 0);
if( pSrc ){
Select *pSel = sqlite3SelectNew(
pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
pSel->pSrc = 0;
sqlite3SelectDelete(db, pSel);
}
- if( pStep->pFrom ){
+ if( pStep->pSrc ){
int i;
- for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
- SrcItem *p = &pStep->pFrom->a[i];
+ for(i=0; i<pStep->pSrc->nSrc && rc==SQLITE_OK; i++){
+ SrcItem *p = &pStep->pSrc->a[i];
if( p->fg.isSubquery ){
assert( p->u4.pSubq!=0 );
sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0);
sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
}
- if( pStep->pFrom ){
+ if( pStep->pSrc ){
int i;
- SrcList *pFrom = pStep->pFrom;
- for(i=0; i<pFrom->nSrc; i++){
- if( pFrom->a[i].fg.isSubquery ){
- assert( pFrom->a[i].u4.pSubq!=0 );
- sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect);
+ SrcList *pSrc = pStep->pSrc;
+ for(i=0; i<pSrc->nSrc; i++){
+ if( pSrc->a[i].fg.isSubquery ){
+ assert( pSrc->a[i].u4.pSubq!=0 );
+ sqlite3WalkSelect(pWalker, pSrc->a[i].u4.pSubq->pSelect);
}
}
}
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
- if( pStep->zTarget ){
- Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
+ if( pStep->pSrc ){
+ Table *pTarget = sqlite3LocateTableItem(&sParse, 0, &pStep->pSrc->a[0]);
if( pTarget==pTab ){
if( pStep->pUpsert ){
ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
}
}
-
/* Find tokens to edit in UPDATE OF clause */
if( sParse.pTriggerTab==pTab ){
renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
if( rc==SQLITE_OK ){
renameWalkTrigger(&sWalker, pTrigger);
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
- if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
- renameTokenFind(&sParse, &sCtx, pStep->zTarget);
- }
- if( pStep->pFrom ){
+ if( pStep->pSrc ){
int i;
- for(i=0; i<pStep->pFrom->nSrc; i++){
- SrcItem *pItem = &pStep->pFrom->a[i];
+ for(i=0; i<pStep->pSrc->nSrc; i++){
+ SrcItem *pItem = &pStep->pSrc->a[i];
if( 0==sqlite3_stricmp(pItem->zName, zOld) ){
renameTokenFind(&sParse, &sCtx, pItem->zName);
}
if( sqlite3WalkSelect(&pFix->w, pStep->pSelect)
|| sqlite3WalkExpr(&pFix->w, pStep->pWhere)
|| sqlite3WalkExprList(&pFix->w, pStep->pExprList)
- || sqlite3FixSrcList(pFix, pStep->pFrom)
+ || sqlite3FixSrcList(pFix, pStep->pSrc)
){
return 1;
}
static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
if( p ){
TriggerStep *pStep = p->step_list;
+ sqlite3SrcListDelete(dbMem, pStep->pSrc);
sqlite3ExprDelete(dbMem, pStep->pWhere);
sqlite3ExprListDelete(dbMem, pStep->pExprList);
sqlite3SelectDelete(dbMem, pStep->pSelect);
pTrigger = (Trigger *)sqlite3DbMallocZero(db,
sizeof(Trigger) + /* struct Trigger */
- sizeof(TriggerStep) + /* Single step in trigger program */
- nFrom + 1 /* Space for pStep->zTarget */
+ sizeof(TriggerStep) /* Single step in trigger program */
);
if( pTrigger ){
pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
- pStep->zTarget = (char *)&pStep[1];
- memcpy((char *)pStep->zTarget, zFrom, nFrom);
-
+ pStep->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
+ if( pStep->pSrc ){
+ pStep->pSrc->a[0].zName = sqlite3DbStrNDup(db, zFrom, nFrom);
+ }
pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
%type xfullname {SrcList*}
%destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);}
-xfullname(A) ::= nm(X).
- {A = sqlite3SrcListAppend(pParse,0,&X,0); /*A-overwrites-X*/}
-xfullname(A) ::= nm(X) DOT nm(Y).
- {A = sqlite3SrcListAppend(pParse,0,&X,&Y); /*A-overwrites-X*/}
-xfullname(A) ::= nm(X) DOT nm(Y) AS nm(Z). {
- A = sqlite3SrcListAppend(pParse,0,&X,&Y); /*A-overwrites-X*/
- if( A ) A->a[0].zAlias = sqlite3NameFromToken(pParse->db, &Z);
-}
-xfullname(A) ::= nm(X) AS nm(Z). {
- A = sqlite3SrcListAppend(pParse,0,&X,0); /*A-overwrites-X*/
- if( A ) A->a[0].zAlias = sqlite3NameFromToken(pParse->db, &Z);
+xfullname(A) ::= fullname(X). { A=X; }
+xfullname(A) ::= fullname(X) AS nm(Z). {
+ X->a[0].zAlias = sqlite3NameFromToken(pParse->db, &Z);
+ A = X;
}
%type joinop {int}
%type trigger_cmd_list {TriggerStep*}
%destructor trigger_cmd_list {sqlite3DeleteTriggerStep(pParse->db, $$);}
trigger_cmd_list(A) ::= trigger_cmd_list(A) trigger_cmd(X) SEMI. {
- assert( A!=0 );
A->pLast->pNext = X;
A->pLast = X;
}
trigger_cmd_list(A) ::= trigger_cmd(A) SEMI. {
- assert( A!=0 );
A->pLast = A;
}
-// 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(A).
-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.
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
// UPDATE
trigger_cmd(A) ::=
- UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) from(F) where_opt(Z) scanpt(E).
- {A = sqlite3TriggerUpdateStep(pParse, &X, F, Y, Z, R, B.z, E);}
+ UPDATE(B) orconf(R) xfullname(X) tridxby SET setlist(Y) from(F) where_opt(Z) scanpt(E).
+ {A = sqlite3TriggerUpdateStep(pParse, X, F, Y, Z, R, B.z, E);}
// INSERT
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
- trnm(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). {
- A = sqlite3TriggerInsertStep(pParse,&X,F,S,R,U,B,Z);/*A-overwrites-R*/
+ xfullname(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). {
+ A = sqlite3TriggerInsertStep(pParse,X,F,S,R,U,B,Z);/*A-overwrites-R*/
}
// DELETE
-trigger_cmd(A) ::= DELETE(B) FROM trnm(X) tridxby where_opt(Y) scanpt(E).
- {A = sqlite3TriggerDeleteStep(pParse, &X, Y, B.z, E);}
+trigger_cmd(A) ::= DELETE(B) FROM xfullname(X) tridxby where_opt(Y) scanpt(E).
+ {A = sqlite3TriggerDeleteStep(pParse, X, Y, B.z, E);}
// SELECT
trigger_cmd(A) ::= scanpt(B) select(X) scanpt(E).
** orconf -> stores the ON CONFLICT algorithm
** pSelect -> The content to be inserted - either a SELECT statement or
** a VALUES clause.
-** zTarget -> Dequoted name of the table to insert into.
+** pSrc -> Table to insert into.
** pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
** statement, then this stores the column-names to be
** inserted into.
** pUpsert -> The ON CONFLICT clauses for an Upsert
**
** (op == TK_DELETE)
-** zTarget -> Dequoted name of the table to delete from.
+** pSrc -> Table to delete from
** pWhere -> The WHERE clause of the DELETE statement if one is specified.
** Otherwise NULL.
**
** (op == TK_UPDATE)
-** zTarget -> Dequoted name of the table to update.
+** pSrc -> Table to update, followed by any FROM clause tables.
** pWhere -> The WHERE clause of the UPDATE statement if one is specified.
** Otherwise NULL.
** pExprList -> A list of the columns to update and the expressions to update
u8 orconf; /* OE_Rollback etc. */
Trigger *pTrig; /* The trigger that this step is a part of */
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
- char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
- SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */
+ SrcList *pSrc; /* Table to insert/update/delete */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */
IdList *pIdList; /* Column names for INSERT */
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
const char*,const char*);
- TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*,
+ TriggerStep *sqlite3TriggerInsertStep(Parse*,SrcList*, IdList*,
Select*,u8,Upsert*,
const char*,const char*);
- TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,SrcList*,ExprList*,
+ TriggerStep *sqlite3TriggerUpdateStep(Parse*,SrcList*,SrcList*,ExprList*,
Expr*, u8, const char*,const char*);
- TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*,
+ TriggerStep *sqlite3TriggerDeleteStep(Parse*,SrcList*, Expr*,
const char*,const char*);
void sqlite3DeleteTrigger(sqlite3*, Trigger*);
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
- SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*);
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
#else
# define sqlite3ParseToplevel(p) p
# define sqlite3IsToplevel(p) 1
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
-# define sqlite3TriggerStepSrc(A,B) 0
#endif
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
sqlite3SelectDelete(db, pTmp->pSelect);
sqlite3IdListDelete(db, pTmp->pIdList);
sqlite3UpsertDelete(db, pTmp->pUpsert);
- sqlite3SrcListDelete(db, pTmp->pFrom);
+ sqlite3SrcListDelete(db, pTmp->pSrc);
sqlite3DbFree(db, pTmp->zSpan);
sqlite3DbFree(db, pTmp);
if( sqlite3ReadOnlyShadowTables(db) ){
TriggerStep *pStep;
for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){
- if( pStep->zTarget!=0
- && sqlite3ShadowTableName(db, pStep->zTarget)
+ if( pStep->pSrc!=0
+ && sqlite3ShadowTableName(db, pStep->pSrc->a[0].zName)
){
sqlite3ErrorMsg(pParse,
"trigger \"%s\" may not write to shadow table \"%s\"",
- pTrig->zName, pStep->zTarget);
+ pTrig->zName, pStep->pSrc->a[0].zName);
goto triggerfinish_cleanup;
}
}
static TriggerStep *triggerStepAllocate(
Parse *pParse, /* Parser context */
u8 op, /* Trigger opcode */
- Token *pName, /* The target name */
+ SrcList *pTabList, /* Target table */
const char *zStart, /* Start of SQL text */
const char *zEnd /* End of SQL text */
){
+ Trigger *pNew = pParse->pNewTrigger;
sqlite3 *db = pParse->db;
- TriggerStep *pTriggerStep;
+ TriggerStep *pTriggerStep = 0;
- if( pParse->nErr ) return 0;
- pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
- if( pTriggerStep ){
- char *z = (char*)&pTriggerStep[1];
- memcpy(z, pName->z, pName->n);
- sqlite3Dequote(z);
- pTriggerStep->zTarget = z;
- pTriggerStep->op = op;
- pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
- if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
+ if( pParse->nErr==0 ){
+ if( pNew
+ && pNew->pSchema!=db->aDb[1].pSchema
+ && pTabList->a[0].u4.zDatabase
+ ){
+ sqlite3ErrorMsg(pParse,
+ "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
+ "statements within triggers");
+ }else{
+ pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
+ if( pTriggerStep ){
+ pTriggerStep->pSrc = sqlite3SrcListDup(db, pTabList, EXPRDUP_REDUCE);
+ pTriggerStep->op = op;
+ pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenRemap(pParse,
+ pTriggerStep->pSrc->a[0].zName,
+ pTabList->a[0].zName
+ );
+ }
+ }
}
}
+
+ sqlite3SrcListDelete(db, pTabList);
return pTriggerStep;
}
*/
TriggerStep *sqlite3TriggerInsertStep(
Parse *pParse, /* Parser */
- Token *pTableName, /* Name of the table into which we insert */
+ SrcList *pTabList, /* Table to INSERT into */
IdList *pColumn, /* List of columns in pTableName to insert into */
Select *pSelect, /* A SELECT statement that supplies values */
u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
assert(pSelect != 0 || db->mallocFailed);
- pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
+ pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTabList, zStart, zEnd);
if( pTriggerStep ){
if( IN_RENAME_OBJECT ){
pTriggerStep->pSelect = pSelect;
*/
TriggerStep *sqlite3TriggerUpdateStep(
Parse *pParse, /* Parser */
- Token *pTableName, /* Name of the table to be updated */
+ SrcList *pTabList, /* Name of the table to be updated */
SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */
ExprList *pEList, /* The SET clause: list of column and new values */
Expr *pWhere, /* The WHERE clause */
sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
- pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
+ pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTabList, zStart, zEnd);
if( pTriggerStep ){
+ SrcList *pFromDup = 0;
if( IN_RENAME_OBJECT ){
pTriggerStep->pExprList = pEList;
pTriggerStep->pWhere = pWhere;
- pTriggerStep->pFrom = pFrom;
+ pFromDup = pFrom;
pEList = 0;
pWhere = 0;
pFrom = 0;
}else{
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
- pTriggerStep->pFrom = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE);
+ pFromDup = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE);
}
pTriggerStep->orconf = orconf;
+
+ if( pFromDup && !IN_RENAME_OBJECT){
+ Select *pSub;
+ Token as = {0, 0};
+ pSub = sqlite3SelectNew(pParse, 0, pFromDup, 0,0,0,0, SF_NestedFrom, 0);
+ pFromDup = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &as, pSub ,0);
+ }
+ if( pFromDup ){
+ pTriggerStep->pSrc = sqlite3SrcListAppendList(
+ pParse, pTriggerStep->pSrc, pFromDup
+ );
+ }
}
sqlite3ExprListDelete(db, pEList);
sqlite3ExprDelete(db, pWhere);
*/
TriggerStep *sqlite3TriggerDeleteStep(
Parse *pParse, /* Parser */
- Token *pTableName, /* The table from which rows are deleted */
+ SrcList *pTabList, /* The table from which rows are deleted */
Expr *pWhere, /* The WHERE clause */
const char *zStart, /* Start of SQL text */
const char *zEnd /* End of SQL text */
sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
- pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
+ pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTabList, zStart, zEnd);
if( pTriggerStep ){
if( IN_RENAME_OBJECT ){
pTriggerStep->pWhere = pWhere;
return triggersReallyExist(pParse,pTab,op,pChanges,pMask);
}
-/*
-** Convert the pStep->zTarget string into a SrcList and return a pointer
-** to that SrcList.
-**
-** This routine adds a specific database name, if needed, to the target when
-** forming the SrcList. This prevents a trigger in one database from
-** referring to a target in another database. An exception is when the
-** trigger is in TEMP in which case it can refer to any other database it
-** wants.
-*/
-SrcList *sqlite3TriggerStepSrc(
- Parse *pParse, /* The parsing context */
- TriggerStep *pStep /* The trigger containing the target token */
-){
- sqlite3 *db = pParse->db;
- SrcList *pSrc; /* SrcList to be returned */
- char *zName = sqlite3DbStrDup(db, pStep->zTarget);
- pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
- assert( pSrc==0 || pSrc->nSrc==1 );
- assert( zName || pSrc==0 );
- if( pSrc ){
- Schema *pSchema = pStep->pTrig->pSchema;
- pSrc->a[0].zName = zName;
- if( pSchema!=db->aDb[1].pSchema ){
- assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 );
- pSrc->a[0].u4.pSchema = pSchema;
- pSrc->a[0].fg.fixedSchema = 1;
- }
- if( pStep->pFrom ){
- SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0);
- if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){
- Select *pSubquery;
- Token as;
- pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0);
- as.n = 0;
- as.z = 0;
- pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
- }
- pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup);
- }
- }else{
- sqlite3DbFree(db, zName);
- }
- return pSrc;
-}
-
/*
** Return true if the pExpr term from the RETURNING clause argument
** list is of the form "*". Raise an error if the terms if of the
switch( pStep->op ){
case TK_UPDATE: {
sqlite3Update(pParse,
- sqlite3TriggerStepSrc(pParse, pStep),
+ sqlite3SrcListDup(db, pStep->pSrc, 0),
sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3ExprDup(db, pStep->pWhere, 0),
pParse->eOrconf, 0, 0, 0
}
case TK_INSERT: {
sqlite3Insert(pParse,
- sqlite3TriggerStepSrc(pParse, pStep),
+ sqlite3SrcListDup(db, pStep->pSrc, 0),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
pParse->eOrconf,
}
case TK_DELETE: {
sqlite3DeleteFrom(pParse,
- sqlite3TriggerStepSrc(pParse, pStep),
+ sqlite3SrcListDup(db, pStep->pSrc, 0),
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
);
sqlite3VdbeAddOp0(v, OP_ResetCount);
CREATE TABLE t4(a);
CREATE TRIGGER r1 INSERT ON t1 BEGIN
- UPDATE t1 SET d='xyz' FROM t2, t3;
+ UPDATE t1 SET d='xyz' FROM t2, t3;
END;
}
# EVIDENCE-OF: R-43190-62442 In other words, the schema-name. prefix on
# the table name of the UPDATE is not allowed within triggers.
#
+# Update: Unless the trigger is in the temp schema.
+#
do_update_tests e_update-2.1 -error {
qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers
} {
UPDATE aux.t1 SET a=1, b=2;
END;
} {}
+}
- 3 {
- CREATE TRIGGER tr1 AFTER DELETE ON t4 BEGIN
- UPDATE main.t1 SET a=1, b=2;
- END;
- } {}
+# Qualified table name is allowed as t4 is a temp table.
+do_execsql_test e_update-2.1.3 {
+ CREATE TRIGGER tr1 AFTER DELETE ON t4 BEGIN
+ UPDATE main.t1 SET a=1, b=2;
+ END;
+ DROP TRIGGER tr1;
}
# EVIDENCE-OF: R-06085-13761 Unless the table to which the trigger is
} {1 error}
db2 close
+#-------------------------------------------------------------------------
+reset_db
+forcedelete test.db2
+
+do_execsql_test 7.0 {
+ CREATE TABLE m1(a, b);
+ ATTACH 'test.db2' AS aux;
+ CREATE TABLE aux.a1(c, d);
+}
+
+do_execsql_test 7.1 {
+ CREATE TEMP TRIGGER tr1 AFTER INSERT ON m1 BEGIN
+ INSERT INTO a1 VALUES(new.a, new.b);
+ END;
+
+ INSERT INTO m1 VALUES(5, 6);
+ SELECT * FROM aux.a1;
+} {5 6}
+
+do_execsql_test 7.2 {
+ CREATE TABLE a1(e, f);
+ INSERT INTO m1 VALUES(7, 8);
+}
+
+do_execsql_test 7.3.1 { SELECT * FROM main.a1 } {7 8}
+do_execsql_test 7.3.2 { SELECT * FROM aux.a1 } {5 6}
+
+do_execsql_test 7.4 {
+ DROP TRIGGER tr1;
+ CREATE TEMP TRIGGER tr1 AFTER INSERT ON m1 BEGIN
+ INSERT INTO a1 SELECT d, c FROM aux.a1;
+ END;
+
+ DELETE FROM aux.a1;
+ DELETE FROM main.a1;
+ INSERT INTO aux.a1 VALUES('hello', 'world');
+}
+
+do_execsql_test 7.5 {
+ INSERT INTO m1 VALUES(9, 10);
+ SELECT * FROM main.a1;
+} {world hello}
+
+do_catchsql_test 7.6 {
+ DROP TRIGGER tr1;
+ CREATE TRIGGER tr1 AFTER INSERT ON m1 BEGIN
+ INSERT INTO a1 SELECT d, c FROM aux.a1;
+ END;
+} {1 {trigger tr1 cannot reference objects in database aux}}
+
+#-------------------------------------------------------------------------
+# Check that temp triggers may INSERT/UPDATE/DELETE to fully qualified
+# table names.
+reset_db
+forcedelete test.db2
+do_execsql_test 8.0 {
+ ATTACH 'test.db2' AS aux;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+ CREATE TABLE aux.t1(e, f);
+ CREATE TABLE aux.t2(g, h);
+}
+
+do_catchsql_test 8.1.1 {
+ CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
+ INSERT INTO aux.t1 VALUES(new.c, new.d);
+ END;
+} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
+
+do_execsql_test 8.1.2 {
+ CREATE TEMP TRIGGER tr1 AFTER INSERT ON t2 BEGIN
+ INSERT INTO aux.t1 VALUES(new.c, new.d);
+ END;
+
+ INSERT INTO main.t2 VALUES('x', 'y');
+ SELECT * FROM aux.t1;
+} {x y}
+
+do_execsql_test 8.1.3 { SELECT * FROM t1 } {}
+
+do_catchsql_test 8.2.1 {
+ CREATE TRIGGER aux.tr2 AFTER UPDATE ON aux.t1 BEGIN
+ UPDATE main.t2 SET c=new.e, d=new.f;
+ END;
+} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
+
+do_execsql_test 8.2.2 {
+ CREATE TEMP TRIGGER tr2 AFTER UPDATE ON aux.t1 BEGIN
+ UPDATE main.t2 SET c=new.e, d=new.f;
+ END;
+
+ UPDATE aux.t1 SET e=1, f=2;
+ SELECT * FROM t2;
+} {1 2}
+
+do_execsql_test 8.2.3 { SELECT * FROM aux.t2 } {}
+
+do_catchsql_test 8.3.1 {
+ CREATE TRIGGER tr3 AFTER DELETE ON t2 BEGIN
+ DELETE FROM aux.t1;
+ END;
+} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
+
+do_execsql_test 8.3.2 {
+ INSERT INTO main.t1 VALUES('a', 'b');
+ CREATE TEMP TRIGGER tr3 AFTER DELETE ON t2 BEGIN
+ DELETE FROM aux.t1;
+ END;
+
+ DELETE FROM main.t2;
+ SELECT * FROM aux.t1;
+} {}
+
+do_execsql_test 8.3.3 { SELECT * FROM t1 } {a b}
+
finish_test