mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Support UPDATE...FROM statements in trigger programs.
FossilOrigin-Name: 4f6d8d0ebf40029218a1d3b05ea657c0c5953b01c6f0b6a628465aa44c67e7f3
This commit is contained in:
19
manifest
19
manifest
@@ -1,13 +1,20 @@
|
|||||||
B 5ee3c27e20d12a126fb773b428bb864102b949a5b26a8d5c523753dcedf4be10
|
B 5ee3c27e20d12a126fb773b428bb864102b949a5b26a8d5c523753dcedf4be10
|
||||||
C Add\stest\sfor\sLEFT\sJOIN\sin\sUPDATE...FROM\sstatement.
|
C Support\sUPDATE...FROM\sstatements\sin\strigger\sprograms.
|
||||||
D 2020-07-13T20:43:13.733
|
D 2020-07-14T19:51:01.128
|
||||||
|
F src/alter.c c63fb72ae0ca39175996fcf7008d44022a7ea99e03c0af3e1d968505ceff7120
|
||||||
|
F src/attach.c 0b11e00c166b622c84ec176773b1d691c61ad07d247809e3e1635d4e99e71d30
|
||||||
|
F src/parse.y ecb9abdd79ec86c8dca7cb126bbdcf322c0e282f87a6d0d734ea5f2c57ced516
|
||||||
F src/resolve.c 04f4710d3ab0070073b60b5b963158dadfbd049db55d10742d9fbd3f28a3f4b8
|
F src/resolve.c 04f4710d3ab0070073b60b5b963158dadfbd049db55d10742d9fbd3f28a3f4b8
|
||||||
|
F src/sqliteInt.h 9b73bd726b935568f081d2d77443102f663dbcef872eea4e7fbf56e697f3b53c
|
||||||
|
F src/trigger.c 6ff9c64a06e6354df8eba08ae18bc809e79931175d39dda32bf1101adee238e5
|
||||||
F src/update.c ab137d08171e2bb18a4c6af815bbd6dbe8bfb483ddd7a2b97dfbfb4d95906ee0
|
F src/update.c ab137d08171e2bb18a4c6af815bbd6dbe8bfb483ddd7a2b97dfbfb4d95906ee0
|
||||||
|
F test/altertab3.test d0d51e652aaa11e37de1f1215181d88334fefcb185f3b9bd91e06e98260c4694
|
||||||
F test/fts4upfrom.test 8df5acb6e10ad73f393d1add082b042ab1db72567888847d098152121e507b34
|
F test/fts4upfrom.test 8df5acb6e10ad73f393d1add082b042ab1db72567888847d098152121e507b34
|
||||||
|
F test/triggerupfrom.test 3645a325026619db117683ed1f9da63ec0b9313ff5857f097088c163b291d3fe
|
||||||
F test/upfrom1.tcl df984cb88010af1555812af55e9db44c4df50677395b45d1f30b69b1b6c07b73
|
F test/upfrom1.tcl df984cb88010af1555812af55e9db44c4df50677395b45d1f30b69b1b6c07b73
|
||||||
F test/upfrom1.test 2ba1ed45b4a2161fc79f234b9ce4300b0d3deba545e3f23b938fb3ebdbf8758d
|
F test/upfrom1.test 2ba1ed45b4a2161fc79f234b9ce4300b0d3deba545e3f23b938fb3ebdbf8758d
|
||||||
F test/upfrom3.test 8f107ed4dc863e75694867fa38c4f3fccd77a35939f65137ead8a55b6ee45152
|
F test/upfrom3.test 2619374c4806913414cc39514630d85bb12489a5f09a5ebdf6fad0fb4df242e2
|
||||||
P 47c87af3e52bce10fbcc2cbe832d659b0c204bfb3368d9314fa1b01120129254
|
P 4f3dff045ab90e80479960fed64cf36f23dd0e13144edbde15043913ad3faac5
|
||||||
R 65a8d9c8ed218ad0d5909deb2fd7a990
|
R e4f9685bb72c377807eea72f701df546
|
||||||
U dan
|
U dan
|
||||||
Z 74476aad6c58b82c53e5aec1a3d47ff5
|
Z c7df6a3e81fce323937b8f7723933acd
|
||||||
|
@@ -1 +1 @@
|
|||||||
4f3dff045ab90e80479960fed64cf36f23dd0e13144edbde15043913ad3faac5
|
4f6d8d0ebf40029218a1d3b05ea657c0c5953b01c6f0b6a628465aa44c67e7f3
|
32
src/alter.c
32
src/alter.c
@@ -1191,17 +1191,22 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
|||||||
if( pParse->nErr ) rc = pParse->rc;
|
if( pParse->nErr ) rc = pParse->rc;
|
||||||
}
|
}
|
||||||
if( rc==SQLITE_OK && pStep->zTarget ){
|
if( rc==SQLITE_OK && pStep->zTarget ){
|
||||||
Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
|
SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
|
||||||
if( pTarget==0 ){
|
if( pSrc ){
|
||||||
rc = SQLITE_ERROR;
|
int i;
|
||||||
}else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){
|
for(i=0; i<pSrc->nSrc; i++){
|
||||||
SrcList sSrc;
|
struct SrcList_item *p = &pSrc->a[i];
|
||||||
memset(&sSrc, 0, sizeof(sSrc));
|
p->pTab = sqlite3LocateTableItem(pParse, 0, p);
|
||||||
sSrc.nSrc = 1;
|
p->iCursor = pParse->nTab++;
|
||||||
sSrc.a[0].zName = pStep->zTarget;
|
if( p->pTab==0 ){
|
||||||
sSrc.a[0].pTab = pTarget;
|
rc = SQLITE_ERROR;
|
||||||
sNC.pSrcList = &sSrc;
|
}else{
|
||||||
if( pStep->pWhere ){
|
p->pTab->nTabRef++;
|
||||||
|
rc = sqlite3ViewGetColumnNames(pParse, p->pTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sNC.pSrcList = pSrc;
|
||||||
|
if( rc==SQLITE_OK && pStep->pWhere ){
|
||||||
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
|
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
|
||||||
}
|
}
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
@@ -1211,7 +1216,7 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
|||||||
if( pStep->pUpsert ){
|
if( pStep->pUpsert ){
|
||||||
Upsert *pUpsert = pStep->pUpsert;
|
Upsert *pUpsert = pStep->pUpsert;
|
||||||
assert( rc==SQLITE_OK );
|
assert( rc==SQLITE_OK );
|
||||||
pUpsert->pUpsertSrc = &sSrc;
|
pUpsert->pUpsertSrc = pSrc;
|
||||||
sNC.uNC.pUpsert = pUpsert;
|
sNC.uNC.pUpsert = pUpsert;
|
||||||
sNC.ncFlags = NC_UUpsert;
|
sNC.ncFlags = NC_UUpsert;
|
||||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
||||||
@@ -1228,6 +1233,9 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
|||||||
sNC.ncFlags = 0;
|
sNC.ncFlags = 0;
|
||||||
}
|
}
|
||||||
sNC.pSrcList = 0;
|
sNC.pSrcList = 0;
|
||||||
|
sqlite3SrcListDelete(db, pSrc);
|
||||||
|
}else{
|
||||||
|
rc = SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -599,6 +599,9 @@ int sqlite3FixTriggerStep(
|
|||||||
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#ifndef SQLITE_OMIT_UPSERT
|
#ifndef SQLITE_OMIT_UPSERT
|
||||||
if( pStep->pUpsert ){
|
if( pStep->pUpsert ){
|
||||||
Upsert *pUp = pStep->pUpsert;
|
Upsert *pUp = pStep->pUpsert;
|
||||||
|
@@ -1517,8 +1517,8 @@ tridxby ::= NOT INDEXED. {
|
|||||||
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
|
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
|
||||||
// UPDATE
|
// UPDATE
|
||||||
trigger_cmd(A) ::=
|
trigger_cmd(A) ::=
|
||||||
UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z) scanpt(E).
|
UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) from(F) where_opt(Z) scanpt(E).
|
||||||
{A = sqlite3TriggerUpdateStep(pParse, &X, Y, Z, R, B.z, E);}
|
{A = sqlite3TriggerUpdateStep(pParse, &X, F, Y, Z, R, B.z, E);}
|
||||||
|
|
||||||
// INSERT
|
// INSERT
|
||||||
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
|
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
|
||||||
|
@@ -3564,6 +3564,7 @@ struct TriggerStep {
|
|||||||
Trigger *pTrig; /* The trigger that this step is a part of */
|
Trigger *pTrig; /* The trigger that this step is a part of */
|
||||||
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
|
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
|
||||||
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
|
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
|
||||||
|
SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */
|
||||||
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
|
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
|
||||||
ExprList *pExprList; /* SET clause for UPDATE */
|
ExprList *pExprList; /* SET clause for UPDATE */
|
||||||
IdList *pIdList; /* Column names for INSERT */
|
IdList *pIdList; /* Column names for INSERT */
|
||||||
@@ -4402,13 +4403,14 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
|
|||||||
TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*,
|
TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*,
|
||||||
Select*,u8,Upsert*,
|
Select*,u8,Upsert*,
|
||||||
const char*,const char*);
|
const char*,const char*);
|
||||||
TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,ExprList*, Expr*, u8,
|
TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,SrcList*,ExprList*,
|
||||||
const char*,const char*);
|
Expr*, u8, const char*,const char*);
|
||||||
TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*,
|
TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*,
|
||||||
const char*,const char*);
|
const char*,const char*);
|
||||||
void sqlite3DeleteTrigger(sqlite3*, Trigger*);
|
void sqlite3DeleteTrigger(sqlite3*, Trigger*);
|
||||||
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
|
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
|
||||||
u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
|
u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
|
||||||
|
SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*);
|
||||||
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
|
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
|
||||||
# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
|
# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
|
||||||
#else
|
#else
|
||||||
@@ -4422,6 +4424,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
|
|||||||
# define sqlite3ParseToplevel(p) p
|
# define sqlite3ParseToplevel(p) p
|
||||||
# define sqlite3IsToplevel(p) 1
|
# define sqlite3IsToplevel(p) 1
|
||||||
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
|
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
|
||||||
|
# define sqlite3TriggerStepSrc(A,B) 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
|
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
|
||||||
|
@@ -26,6 +26,7 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
|
|||||||
sqlite3SelectDelete(db, pTmp->pSelect);
|
sqlite3SelectDelete(db, pTmp->pSelect);
|
||||||
sqlite3IdListDelete(db, pTmp->pIdList);
|
sqlite3IdListDelete(db, pTmp->pIdList);
|
||||||
sqlite3UpsertDelete(db, pTmp->pUpsert);
|
sqlite3UpsertDelete(db, pTmp->pUpsert);
|
||||||
|
sqlite3SrcListDelete(db, pTmp->pFrom);
|
||||||
sqlite3DbFree(db, pTmp->zSpan);
|
sqlite3DbFree(db, pTmp->zSpan);
|
||||||
|
|
||||||
sqlite3DbFree(db, pTmp);
|
sqlite3DbFree(db, pTmp);
|
||||||
@@ -486,6 +487,7 @@ TriggerStep *sqlite3TriggerInsertStep(
|
|||||||
TriggerStep *sqlite3TriggerUpdateStep(
|
TriggerStep *sqlite3TriggerUpdateStep(
|
||||||
Parse *pParse, /* Parser */
|
Parse *pParse, /* Parser */
|
||||||
Token *pTableName, /* Name of the table to be updated */
|
Token *pTableName, /* Name of the table to be updated */
|
||||||
|
SrcList *pFrom,
|
||||||
ExprList *pEList, /* The SET clause: list of column and new values */
|
ExprList *pEList, /* The SET clause: list of column and new values */
|
||||||
Expr *pWhere, /* The WHERE clause */
|
Expr *pWhere, /* The WHERE clause */
|
||||||
u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
||||||
@@ -500,16 +502,22 @@ TriggerStep *sqlite3TriggerUpdateStep(
|
|||||||
if( IN_RENAME_OBJECT ){
|
if( IN_RENAME_OBJECT ){
|
||||||
pTriggerStep->pExprList = pEList;
|
pTriggerStep->pExprList = pEList;
|
||||||
pTriggerStep->pWhere = pWhere;
|
pTriggerStep->pWhere = pWhere;
|
||||||
|
pTriggerStep->pFrom = pFrom;
|
||||||
pEList = 0;
|
pEList = 0;
|
||||||
pWhere = 0;
|
pWhere = 0;
|
||||||
|
pFrom = 0;
|
||||||
}else{
|
}else{
|
||||||
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
||||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||||
|
pTriggerStep->pFrom = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE);
|
||||||
}
|
}
|
||||||
pTriggerStep->orconf = orconf;
|
pTriggerStep->orconf = orconf;
|
||||||
|
}else{
|
||||||
|
sqlite3SrcListDelete(db, pFrom);
|
||||||
}
|
}
|
||||||
sqlite3ExprListDelete(db, pEList);
|
sqlite3ExprListDelete(db, pEList);
|
||||||
sqlite3ExprDelete(db, pWhere);
|
sqlite3ExprDelete(db, pWhere);
|
||||||
|
sqlite3SrcListDelete(db, pFrom);
|
||||||
return pTriggerStep;
|
return pTriggerStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,25 +744,28 @@ Trigger *sqlite3TriggersExist(
|
|||||||
** trigger is in TEMP in which case it can refer to any other database it
|
** trigger is in TEMP in which case it can refer to any other database it
|
||||||
** wants.
|
** wants.
|
||||||
*/
|
*/
|
||||||
static SrcList *targetSrcList(
|
SrcList *sqlite3TriggerStepSrc(
|
||||||
Parse *pParse, /* The parsing context */
|
Parse *pParse, /* The parsing context */
|
||||||
TriggerStep *pStep /* The trigger containing the target token */
|
TriggerStep *pStep /* The trigger containing the target token */
|
||||||
){
|
){
|
||||||
sqlite3 *db = pParse->db;
|
sqlite3 *db = pParse->db;
|
||||||
int iDb; /* Index of the database to use */
|
SrcList *pSrc; /* SrcList to be returned */
|
||||||
SrcList *pSrc; /* SrcList to be returned */
|
char *zName = sqlite3DbStrDup(db, pStep->zTarget);
|
||||||
|
|
||||||
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
|
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
|
||||||
|
assert( pSrc==0 || pSrc->nSrc==1 );
|
||||||
|
assert( zName || pSrc==0 );
|
||||||
if( pSrc ){
|
if( pSrc ){
|
||||||
assert( pSrc->nSrc>0 );
|
Schema *pSchema = pStep->pTrig->pSchema;
|
||||||
pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget);
|
pSrc->a[0].zName = zName;
|
||||||
iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema);
|
if( pSchema!=db->aDb[1].pSchema ){
|
||||||
if( iDb==0 || iDb>=2 ){
|
pSrc->a[0].pSchema = pSchema;
|
||||||
const char *zDb;
|
|
||||||
assert( iDb<db->nDb );
|
|
||||||
zDb = db->aDb[iDb].zDbSName;
|
|
||||||
pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, zDb);
|
|
||||||
}
|
}
|
||||||
|
if( pStep->pFrom ){
|
||||||
|
SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0);
|
||||||
|
pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
sqlite3DbFree(db, zName);
|
||||||
}
|
}
|
||||||
return pSrc;
|
return pSrc;
|
||||||
}
|
}
|
||||||
@@ -803,7 +814,7 @@ static int codeTriggerProgram(
|
|||||||
switch( pStep->op ){
|
switch( pStep->op ){
|
||||||
case TK_UPDATE: {
|
case TK_UPDATE: {
|
||||||
sqlite3Update(pParse,
|
sqlite3Update(pParse,
|
||||||
targetSrcList(pParse, pStep),
|
sqlite3TriggerStepSrc(pParse, pStep),
|
||||||
sqlite3ExprListDup(db, pStep->pExprList, 0),
|
sqlite3ExprListDup(db, pStep->pExprList, 0),
|
||||||
sqlite3ExprDup(db, pStep->pWhere, 0),
|
sqlite3ExprDup(db, pStep->pWhere, 0),
|
||||||
pParse->eOrconf, 0, 0, 0
|
pParse->eOrconf, 0, 0, 0
|
||||||
@@ -812,7 +823,7 @@ static int codeTriggerProgram(
|
|||||||
}
|
}
|
||||||
case TK_INSERT: {
|
case TK_INSERT: {
|
||||||
sqlite3Insert(pParse,
|
sqlite3Insert(pParse,
|
||||||
targetSrcList(pParse, pStep),
|
sqlite3TriggerStepSrc(pParse, pStep),
|
||||||
sqlite3SelectDup(db, pStep->pSelect, 0),
|
sqlite3SelectDup(db, pStep->pSelect, 0),
|
||||||
sqlite3IdListDup(db, pStep->pIdList),
|
sqlite3IdListDup(db, pStep->pIdList),
|
||||||
pParse->eOrconf,
|
pParse->eOrconf,
|
||||||
@@ -822,7 +833,7 @@ static int codeTriggerProgram(
|
|||||||
}
|
}
|
||||||
case TK_DELETE: {
|
case TK_DELETE: {
|
||||||
sqlite3DeleteFrom(pParse,
|
sqlite3DeleteFrom(pParse,
|
||||||
targetSrcList(pParse, pStep),
|
sqlite3TriggerStepSrc(pParse, pStep),
|
||||||
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@@ -586,5 +586,19 @@ do_execsql_test 24.4 {
|
|||||||
DELETE FROM v2;
|
DELETE FROM v2;
|
||||||
END}}
|
END}}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
reset_db
|
||||||
|
do_execsql_test 25.1 {
|
||||||
|
CREATE TABLE t1(a, b, c);
|
||||||
|
CREATE TABLE t2(a, b, c);
|
||||||
|
CREATE TRIGGER ttt AFTER INSERT ON t1 BEGIN
|
||||||
|
UPDATE t1 SET a=t2.a FROM t2 WHERE t1.a=t2.a;
|
||||||
|
END;
|
||||||
|
}
|
||||||
|
#do_execsql_test 25.2 {
|
||||||
|
# ALTER TABLE t2 RENAME COLUMN a TO aaa;
|
||||||
|
#}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
120
test/triggerupfrom.test
Normal file
120
test/triggerupfrom.test
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# 2020 July 14
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
#***********************************************************************
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set testprefix triggerupfrom
|
||||||
|
|
||||||
|
do_execsql_test 1.0 {
|
||||||
|
CREATE TABLE map(k, v);
|
||||||
|
INSERT INTO map VALUES(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four');
|
||||||
|
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||||
|
|
||||||
|
CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN
|
||||||
|
UPDATE t1 SET c = v FROM map WHERE k=new.a AND a=new.a;
|
||||||
|
END;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.1 {
|
||||||
|
INSERT INTO t1(a) VALUES(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.2 {
|
||||||
|
SELECT a, c FROM t1 ORDER BY a;
|
||||||
|
} {1 one}
|
||||||
|
|
||||||
|
do_execsql_test 1.3 {
|
||||||
|
INSERT INTO t1(a) VALUES(2), (3), (4), (5);
|
||||||
|
SELECT a, c FROM t1 ORDER BY a;
|
||||||
|
} {1 one 2 two 3 three 4 four 5 {}}
|
||||||
|
|
||||||
|
forcedelete test.db2
|
||||||
|
do_execsql_test 2.0 {
|
||||||
|
ATTACH 'test.db2' AS aux;
|
||||||
|
CREATE TABLE aux.t3(x, y);
|
||||||
|
INSERT INTO aux.t3 VALUES('x', 'y');
|
||||||
|
}
|
||||||
|
|
||||||
|
do_catchsql_test 2.1 {
|
||||||
|
CREATE TRIGGER tr2 AFTER INSERT ON t1 BEGIN
|
||||||
|
UPDATE t1 SET b = y FROM aux.t3 WHERE k=new.a;
|
||||||
|
END;
|
||||||
|
} {1 {trigger tr2 cannot reference objects in database aux}}
|
||||||
|
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
CREATE TEMP TRIGGER tr2 AFTER INSERT ON t1 BEGIN
|
||||||
|
UPDATE t1 SET b = y FROM aux.t3 WHERE a=new.a;
|
||||||
|
END;
|
||||||
|
INSERT INTO t1(a) VALUES(10), (20);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {
|
||||||
|
1 {} one
|
||||||
|
2 {} two
|
||||||
|
3 {} three
|
||||||
|
4 {} four
|
||||||
|
5 {} {}
|
||||||
|
10 y {}
|
||||||
|
20 y {}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 2.3 {
|
||||||
|
CREATE TABLE link(f, t);
|
||||||
|
INSERT INTO link VALUES(5, 2), (20, 10), (2, 1);
|
||||||
|
CREATE TRIGGER tr3 BEFORE DELETE ON t1 BEGIN
|
||||||
|
UPDATE t1 SET b=coalesce(old.b,old.c) FROM main.link WHERE a=t AND old.a=f;
|
||||||
|
END;
|
||||||
|
DELETE FROM t1 WHERE a=2;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {
|
||||||
|
1 two one
|
||||||
|
3 {} three
|
||||||
|
4 {} four
|
||||||
|
5 {} {}
|
||||||
|
10 y {}
|
||||||
|
20 y {}
|
||||||
|
}
|
||||||
|
|
||||||
|
db close
|
||||||
|
sqlite3 db ""
|
||||||
|
do_catchsql_test 2.4 {
|
||||||
|
ATTACH 'test.db' AS yyy;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {1 {malformed database schema (tr3) - trigger tr3 cannot reference objects in database main}}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
reset_db
|
||||||
|
forcedelete test.db2
|
||||||
|
do_execsql_test 3.0 {
|
||||||
|
CREATE TABLE mmm(x, y);
|
||||||
|
INSERT INTO mmm VALUES(1, 'one');
|
||||||
|
INSERT INTO mmm VALUES(2, 'two');
|
||||||
|
INSERT INTO mmm VALUES(3, 'three');
|
||||||
|
|
||||||
|
ATTACH 'test.db2' AS aux;
|
||||||
|
CREATE TABLE aux.t1(a, b);
|
||||||
|
CREATE TABLE aux.mmm(x, y);
|
||||||
|
INSERT INTO aux.mmm VALUES(1, 'ONE');
|
||||||
|
INSERT INTO aux.mmm VALUES(2, 'TWO');
|
||||||
|
INSERT INTO aux.mmm VALUES(3, 'THREE');
|
||||||
|
|
||||||
|
CREATE TRIGGER aux.ttt AFTER INSERT ON t1 BEGIN
|
||||||
|
UPDATE t1 SET b=y FROM mmm WHERE x=new.a AND a=new.a;
|
||||||
|
END;
|
||||||
|
|
||||||
|
INSERT INTO t1(a) VALUES (2);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {2 TWO}
|
||||||
|
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
@@ -14,6 +14,15 @@ set testdir [file dirname $argv0]
|
|||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
set testprefix upfrom3
|
set testprefix upfrom3
|
||||||
|
|
||||||
|
# Test plan:
|
||||||
|
#
|
||||||
|
# 1.*: Test UPDATE ... FROM statements that modify IPK fields. And that
|
||||||
|
# modify "INTEGER PRIMARY KEY" fields on WITHOUT ROWID tables.
|
||||||
|
#
|
||||||
|
# 2.*: Test UPDATE ... FROM statements that modify PK fields of WITHOUT
|
||||||
|
# ROWID tables.
|
||||||
|
#
|
||||||
|
|
||||||
foreach {tn wo} {
|
foreach {tn wo} {
|
||||||
1 ""
|
1 ""
|
||||||
2 "WITHOUT ROWID"
|
2 "WITHOUT ROWID"
|
||||||
|
Reference in New Issue
Block a user