Index: src/alter.c ================================================================== --- src/alter.c +++ src/alter.c @@ -823,13 +823,13 @@ zNew = sqlite3NameFromToken(db, pNew); if( !zNew ) goto exit_rename_column; sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " - "sql = sqlite_rename_column(sql, %d, %Q) " - "WHERE type IN ('table', 'index') AND tbl_name = %Q AND sql!=''", - zDb, MASTER_NAME, iCol, zNew, pTab->zName + "sql = sqlite_rename_column(sql, %d, %Q, %Q, %Q) " + "WHERE type = 'table' OR (type='index' AND tbl_name = %Q AND sql!='')", + zDb, MASTER_NAME, iCol, zNew, pTab->zName, zOld, pTab->zName ); /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, pTab->zName); @@ -921,10 +921,13 @@ *pp = pBest->pNext; return pBest; } +/* +** sqlite_rename_table(SQL, iCol, zNew, zTable, zOld) +*/ static void renameColumnFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv ){ @@ -932,10 +935,15 @@ struct RenameCtx sCtx; const char *zSql = sqlite3_value_text(argv[0]); int nSql = sqlite3_value_bytes(argv[0]); const char *zNew = sqlite3_value_text(argv[2]); int nNew = sqlite3_value_bytes(argv[2]); + const char *zTable = sqlite3_value_text(argv[3]); + int nTable = sqlite3_value_bytes(argv[3]); + const char *zOld = sqlite3_value_text(argv[4]); + int nOld = sqlite3_value_bytes(argv[4]); + int rc; char *zErr = 0; Parse sParse; Walker sWalker; Table *pTab; @@ -1002,29 +1010,41 @@ sWalker.pParse = &sParse; sWalker.xExprCallback = renameColumnExprCb; sWalker.u.pRename = &sCtx; if( sParse.pNewTable ){ + int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); FKey *pFKey; - sCtx.pList = renameTokenFind( - &sParse, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName - ); - sCtx.nList = 1; - sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); - for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){ - sqlite3WalkExprList(&sWalker, pIdx->aColExpr); + if( bFKOnly==0 ){ + sCtx.pList = renameTokenFind( + &sParse, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName + ); + sCtx.nList = 1; + sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); + for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){ + sqlite3WalkExprList(&sWalker, pIdx->aColExpr); + } } for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ for(i=0; inCol; i++){ - if( pFKey->aCol[i].iFrom==sCtx.iCol ){ - RenameToken *pTok = renameTokenFind(&sParse, (void*)&pFKey->aCol[i]); + RenameToken *pTok = 0; + if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){ + pTok = renameTokenFind(&sParse, (void*)&pFKey->aCol[i]); if( pTok ){ pTok->pNext = sCtx.pList; sCtx.pList = pTok; sCtx.nList++; } + } + if( 0==sqlite3_stricmp(pFKey->zTo, zTable) + && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld) + ){ + pTok = renameTokenFind(&sParse, (void*)pFKey->aCol[i].zCol); + pTok->pNext = sCtx.pList; + sCtx.pList = pTok; + sCtx.nList++; } } } }else{ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); @@ -1079,11 +1099,11 @@ ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), - FUNCTION(sqlite_rename_column, 3, 0, 0, renameColumnFunc), + FUNCTION(sqlite_rename_column, 5, 0, 0, renameColumnFunc), #ifndef SQLITE_OMIT_TRIGGER FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), #endif #ifndef SQLITE_OMIT_FOREIGN_KEY FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -2769,10 +2769,13 @@ } if( pToCol ){ for(i=0; ia[i].zName); pFKey->aCol[i].zCol = z; + if( IN_RENAME_COLUMN ){ + sqlite3MoveRenameToken(pParse, z, pToCol->a[i].zName); + } memcpy(z, pToCol->a[i].zName, n); z[n] = 0; z += n+1; } } Index: test/altercol.test ================================================================== --- test/altercol.test +++ test/altercol.test @@ -91,17 +91,73 @@ SELECT sql FROM sqlite_master WHERE tbl_name='t1' AND sql!='' } $res } #------------------------------------------------------------------------- +# do_execsql_test 2.0 { CREATE TABLE t3(a, b, c, d, e, f, g, h, i, j, k, l, m, FOREIGN KEY (b, c, d, e, f, g, h, i, j, k, l, m) REFERENCES t4); } do_execsql_test 2.1 { ALTER TABLE t3 RENAME b TO biglongname; SELECT sql FROM sqlite_master WHERE name='t3'; } {{CREATE TABLE t3(a, biglongname, c, d, e, f, g, h, i, j, k, l, m, FOREIGN KEY (biglongname, c, d, e, f, g, h, i, j, k, l, m) REFERENCES t4)}} -finish_test + +#------------------------------------------------------------------------- +# +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; + END; + INSERT INTO t4 VALUES(3, 2, 1); +} + +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 4.0 { + CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, d)); + CREATE TABLE p1(c, d, PRIMARY KEY(c, d)); + PRAGMA foreign_keys = 1; + INSERT INTO p1 VALUES(1, 2); + INSERT INTO p1 VALUES(3, 4); +} + +do_execsql_test 4.1 { + ALTER TABLE p1 RENAME d TO "silly name"; + SELECT sql FROM sqlite_master WHERE name IN ('c1', 'p1'); +} { + {CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, "silly name"))} + {CREATE TABLE p1(c, "silly name", PRIMARY KEY(c, "silly name"))} +} + +do_execsql_test 4.2 { + CREATE TABLE c2(a, b, FOREIGN KEY (a, b) REFERENCES p1); +} + +do_execsql_test 4.1 { + ALTER TABLE p1 RENAME "silly name" TO reasonable; + SELECT sql FROM sqlite_master WHERE name IN ('c1', 'c2', 'p1'); +} { + {CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, "reasonable"))} + {CREATE TABLE p1(c, "reasonable", PRIMARY KEY(c, "reasonable"))} + {CREATE TABLE c2(a, b, FOREIGN KEY (a, b) REFERENCES p1)} +} +finish_test