Index: src/alter.c ================================================================== --- src/alter.c +++ src/alter.c @@ -980,10 +980,14 @@ ** RenameToken object to the list of RenameToken objects being ** constructed in RenameCtx object at pWalker->u.pRename. */ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; + if( pExpr->op==TK_TRIGGER && pExpr->iColumn==p->iCol ){ + renameTokenFind(pWalker->pParse, p, (void*)pExpr); + }else + if( p->zOld && pExpr->op==TK_DOT ){ Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; assert( pLeft->op==TK_ID && pRight->op==TK_ID ); if( 0==sqlite3_stricmp(pLeft->u.zToken, "old") @@ -1143,11 +1147,11 @@ sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0); rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); if( rc==SQLITE_OK ){ sqlite3WalkSelect(&sWalker, pSelect); }else if( rc==SQLITE_ERROR ){ - /* Failed to resolve all symboles in the view. This is not an + /* Failed to resolve all symbols in the view. This is not an ** error, but it will not be edited. */ sqlite3DbFree(db, sParse.zErrMsg); sParse.zErrMsg = 0; rc = SQLITE_OK; } @@ -1185,20 +1189,90 @@ } }else if( sParse.pNewIndex ){ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); }else{ - sCtx.zOld = zOld; - sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen); - if( sParse.pNewTrigger->pColumns ){ + /* A trigger */ + TriggerStep *pStep; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; + sParse.pTriggerTab = pTab; + sParse.eTriggerOp = sParse.pNewTrigger->op; + + /* Resolve symbols in WHEN clause */ + if( sParse.pTriggerTab==pTab && sParse.pNewTrigger->pWhen ){ + rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen); + } + + for(pStep=sParse.pNewTrigger->step_list; + rc==SQLITE_OK && pStep; + pStep=pStep->pNext + ){ + if( pStep->pSelect ) sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC); + if( pStep->zTarget ){ + Table *pTarget = sqlite3FindTable(db, pStep->zTarget, zDb); + if( pTarget==0 ){ + rc = SQLITE_ERROR; + }else{ + SrcList sSrc; + memset(&sSrc, 0, sizeof(sSrc)); + sSrc.nSrc = 1; + sSrc.a[0].zName = pStep->zTarget; + sSrc.a[0].pTab = pTarget; + sNC.pSrcList = &sSrc; + if( pStep->pWhere ){ + rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); + } + + if( rc==SQLITE_OK && pTarget==pTab ){ + if( pStep->pIdList ){ + for(i=0; ipIdList->nId; i++){ + char *zName = pStep->pIdList->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, (void*)zName); + } + } + } + if( pStep->op==TK_UPDATE ){ + assert( pStep->pExprList ); + for(i=0; ipExprList->nExpr; i++){ + char *zName = pStep->pExprList->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, (void*)zName); + } + } + } + } + } + } + } + + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; + + /* Find tokens to edit in UPDATE OF clause */ + if( sParse.pTriggerTab==pTab && sParse.pNewTrigger->pColumns ){ for(i=0; ipColumns->nId; i++){ char *zName = sParse.pNewTrigger->pColumns->a[i].zName; if( 0==sqlite3_stricmp(zName, zOld) ){ renameTokenFind(&sParse, &sCtx, (void*)zName); } } } + + /* Find tokens to edit in WHEN clause */ + sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen); + + /* Find tokens to edit in trigger steps */ + for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){ + sqlite3WalkSelect(&sWalker, pStep->pSelect); + sqlite3WalkExpr(&sWalker, pStep->pWhere); + sqlite3WalkExprList(&sWalker, pStep->pExprList); + } } assert( rc==SQLITE_OK ); assert( nQuot>=nNew ); zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1); Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -1664,10 +1664,13 @@ assert( pList->nExpr>0 ); pItem = &pList->a[pList->nExpr-1]; assert( pItem->zName==0 ); pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); if( dequote ) sqlite3Dequote(pItem->zName); + if( IN_RENAME_COLUMN ){ + sqlite3RenameToken(pParse, (void*)pItem->zName, pName); + } } } /* ** Set the ExprList.a[].zSpan element of the most recently added item Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -1449,20 +1449,20 @@ %type trigger_cmd {TriggerStep*} %destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);} // UPDATE trigger_cmd(A) ::= UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z) scanpt(E). - {A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R, B.z, E);} + {A = sqlite3TriggerUpdateStep(pParse, &X, 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->db,&X,F,S,R,U,B,Z);/*A-overwrites-R*/ + 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->db, &X, Y, B.z, E);} + {A = sqlite3TriggerDeleteStep(pParse, &X, Y, B.z, E);} // SELECT trigger_cmd(A) ::= scanpt(B) select(X) scanpt(E). {A = sqlite3TriggerSelectStep(pParse->db, X, B, E); /*A-overwrites-X*/} Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -4047,16 +4047,16 @@ void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*, const char*,const char*); - TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, + TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*, Select*,u8,Upsert*, const char*,const char*); - TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8, + TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,ExprList*, Expr*, u8, const char*,const char*); - TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*, + TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*, const char*,const char*); void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int); # define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) Index: src/trigger.c ================================================================== --- src/trigger.c +++ src/trigger.c @@ -426,26 +426,32 @@ ** ** The parser calls this routine when it sees an INSERT inside the ** body of a trigger. */ TriggerStep *sqlite3TriggerInsertStep( - sqlite3 *db, /* The database connection */ + Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table into which we insert */ 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.) */ Upsert *pUpsert, /* ON CONFLICT clauses for upsert */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; assert(pSelect != 0 || db->mallocFailed); pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd); if( pTriggerStep ){ - pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + pTriggerStep->pSelect = pSelect; + pSelect = 0; + }else{ + pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + } pTriggerStep->pIdList = pColumn; pTriggerStep->pUpsert = pUpsert; pTriggerStep->orconf = orconf; }else{ testcase( pColumn ); @@ -462,24 +468,32 @@ ** Construct a trigger step that implements an UPDATE statement and return ** a pointer to that trigger step. The parser calls this routine when it ** sees an UPDATE statement inside the body of a CREATE TRIGGER. */ TriggerStep *sqlite3TriggerUpdateStep( - sqlite3 *db, /* The database connection */ + Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd); if( pTriggerStep ){ - pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + pTriggerStep->pExprList = pEList; + pTriggerStep->pWhere = pWhere; + pEList = 0; + pWhere = 0; + }else{ + pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); + pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + } pTriggerStep->orconf = orconf; } sqlite3ExprListDelete(db, pEList); sqlite3ExprDelete(db, pWhere); return pTriggerStep; @@ -489,21 +503,27 @@ ** Construct a trigger step that implements a DELETE statement and return ** a pointer to that trigger step. The parser calls this routine when it ** sees a DELETE statement inside the body of a CREATE TRIGGER. */ TriggerStep *sqlite3TriggerDeleteStep( - sqlite3 *db, /* Database connection */ + Parse *pParse, /* Parser */ Token *pTableName, /* 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(db, TK_DELETE, pTableName, zStart, zEnd); if( pTriggerStep ){ - pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + pTriggerStep->pWhere = pWhere; + pWhere = 0; + }else{ + pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + } pTriggerStep->orconf = OE_Default; } sqlite3ExprDelete(db, pWhere); return pTriggerStep; } Index: test/altercol.test ================================================================== --- test/altercol.test +++ test/altercol.test @@ -123,28 +123,38 @@ #------------------------------------------------------------------------- # do_execsql_test 3.0 { CREATE TABLE t4(x, y, z); CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.y<0 BEGIN - SELECT 1, 2, 3, 4; + SELECT x, y, z FROM t4; + DELETE FROM t4 WHERE y=32; + UPDATE t4 SET x=y+1, y=0 WHERE y=32; + INSERT INTO t4(x, y, z) SELECT 4, 5, 6 WHERE 0; END; INSERT INTO t4 VALUES(3, 2, 1); } +breakpoint do_execsql_test 3.1 { ALTER TABLE t4 RENAME y TO abc; SELECT sql FROM sqlite_master WHERE name='t4'; } {{CREATE TABLE t4(x, abc, z)}} -db close -sqlite3 db test.db - do_execsql_test 3.2 { SELECT * FROM t4; } {3 2 1} do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {} + +do_execsql_test 3.4 { SELECT sql FROM sqlite_master WHERE type='trigger' } { +{CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.abc<0 BEGIN + SELECT x, abc, z FROM t4; + DELETE FROM t4 WHERE abc=32; + UPDATE t4 SET x=abc+1, abc=0 WHERE abc=32; + INSERT INTO t4(x, abc, z) SELECT 4, 5, 6 WHERE 0; + END} +} #------------------------------------------------------------------------- # do_execsql_test 4.0 { CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, d)); @@ -231,21 +241,21 @@ CREATE TRIGGER zzz AFTER UPDATE OF "col a", "col c" ON t6 BEGIN UPDATE c SET x=x+1; END; } -do_execsql_test 7.1 { +do_execsql_test 7.1.1 { INSERT INTO t6 VALUES(0, 0, 0); UPDATE t6 SET "col c" = 1; SELECT * FROM c; } {1} -do_execsql_test 7.2 { +do_execsql_test 7.1.2 { ALTER TABLE t6 RENAME "col c" TO "col 3"; } -do_execsql_test 7.3 { +do_execsql_test 7.1.3 { UPDATE t6 SET "col 3" = 0; SELECT * FROM c; } {2} #-------------------------------------------------------------------------