Have ALTER TABLE RENAME COLUMN also edit trigger and view definitions.

FossilOrigin-Name: 7908e8a4a3b9577211a5d3da9c4142c46e9d5872be4a6499ec053f2b547019b8
This commit is contained in:
dan 2018-08-18 18:01:58 +00:00
commit c23f3cb15b
11 changed files with 666 additions and 164 deletions

View File

@ -1,5 +1,5 @@
C Fix\sa\sproblem\swhen\srenaming\san\sIPK\scolumn\sthat\sis\salso\spart\sof\sa\schild\skey.
D 2018-08-14T21:03:38.735
C Have\sALTER\sTABLE\sRENAME\sCOLUMN\salso\sedit\strigger\sand\sview\sdefinitions.
D 2018-08-18T18:01:58.244
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c 35512b6c704721442f9a2b0856d139699742c85b87130c975d9dd802f327df3c
F src/alter.c 3342dba26f9023b334e6ab634ef45d0d8b333023c80070a57f22f85f64bbeabb
F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9
F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a
F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114
@ -442,7 +442,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173
F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
F src/build.c a38f9e25a34cd5a8fd46dbb42c4f7a577f4968635021ddd2ebf2dbca59fad8b3
F src/build.c a2e61e716e7d90e382d71818404472207024ecb94a44431ac9fcf1ac3b8c3066
F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c b157b01081f92442f8b0218ddb93ddce8ebddad36dbddeecfdd771561dd4f387
@ -450,7 +450,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 4aa7f26198934dbd002e69418220eae3dbc71b010bbac32bd78faf86b52ce6c3
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
F src/delete.c 107e28d3ef8bd72fd11953374ca9107cd74e8b09c3ded076a6048742d26ce7d2
F src/expr.c 4a555ff68084360c133c5b9d985ae05d2cf914125d4c8e5614496dc071d318dd
F src/expr.c 4c1e40cdb1717b42e848835caf3e2b881f748cdcfabe3498e83634d1d2db5e26
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c f59253c0be4b1e9dfcb073b6d6d6ab83090ae50c08b5c113b76013c4b157cd6a
F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f
@ -487,7 +487,7 @@ F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 76d29b8a960dcb8b67210f095899d91e4a90673a6674ea58cfd1115b705a7fb9
F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388
F src/parse.y 035b397bf1acbd3cb8293812b07c8aefce9f275d4de1442e113dbd0d2f733dce
F src/parse.y 57f533353a4945370a60f66aa7be284c8a627509baa02d707982f906e4851f7d
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
@ -496,14 +496,14 @@ F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
F src/prepare.c 117e27c6826a83f461986c0cfbb09c31fe004922ce23a61bf78d82a46b0958d9
F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c b51a48f33da36e0c2dd1ea5f0d10197c3e938a54086a69efce064ae41e2254e1
F src/resolve.c c58793195eaf2989b8d0688355cca15024cc792da155dffc44b85afae426c2d5
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04
F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f
F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
F src/sqliteInt.h a7b305c23545c052829959f71fcc1f35f823a143a54c066695d071efedabdd94
F src/sqliteInt.h 78e9b483adbdf928923a175f2c8470da89024b973b4b790486b6e8736b4c876f
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -561,9 +561,9 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3177575b587f3d0491ca5c3e937663ca3e91bf86adf4f2e8e9f7d0ab98bc66d3
F src/tokenize.c fe35cb947ec54fe9a96895359b777a9305356c2f3c2917a1d83a57603108e66c
F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f
F src/trigger.c 4ace6d1d5ba9a89822deb287317f33c810440526eafe185c2d8a48c31df1e995
F src/trigger.c f6760d78be400164c95732b4aabccab27072ec66e9db2ee33baf70e65e467af0
F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b
F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
@ -599,7 +599,7 @@ F test/alter.test b820ab9dcf85f8e3a65bc8326accb2f0c7be64ef
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3
F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b
F test/altercol.test 8380bf54ea9eb849b783cb33eab988d4efd80c04f194485ff9f695ce3d643ac0
F test/altercol.test 7e22d63b1adb3f66e695c5dfb32d102644943ceeb88bc81bf74fa6d65dca5eba
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df
@ -1756,7 +1756,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P f6d6b472713c21deace3fd52c0b0b6901d758af043c238f004fabe52b01f730c
R 8e9298efe4cb9b189013f3e469b1ce50
P ad15486022209205c65fb5ffdbe30a7b99379170451e6aff4bab6e90b549d6c7 7fa1faeaff30b74b68ee6f4b363d837f21cf313d8262361c901bda884df139a2
R 4b2d31defa56ef33764a237b1d928d0d
T +closed 7fa1faeaff30b74b68ee6f4b363d837f21cf313d8262361c901bda884df139a2
U dan
Z 288fdb9114eb61b0445f1cd0f818929a
Z 4f1d814f6f719b632fdfddd911dbafb8

View File

@ -1 +1 @@
ad15486022209205c65fb5ffdbe30a7b99379170451e6aff4bab6e90b549d6c7
7908e8a4a3b9577211a5d3da9c4142c46e9d5872be4a6499ec053f2b547019b8

View File

@ -817,13 +817,6 @@ void sqlite3AlterRenameColumn(
/* Cannot alter a system table */
if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ) goto exit_rename_column;
/* Cannot rename columns of a virtual table */
if( IsVirtual(pTab) ){
sqlite3ErrorMsg(pParse, "cannot rename columns in a virtual table (%s)",
pTab->zName);
goto exit_rename_column;
}
/* Which schema holds the table to be altered */
iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iSchema>=0 );
@ -851,11 +844,12 @@ void sqlite3AlterRenameColumn(
bQuote = sqlite3Isquote(pNew->z[0]);
sqlite3NestedParse(pParse,
"UPDATE \"%w\".%s SET "
"sql = sqlite_rename_column(sql, %d, %d, %Q, %Q, %Q) "
"WHERE name NOT LIKE 'sqlite_%%' AND ("
" type = 'table' OR (type='index' AND tbl_name = %Q)"
")",
zDb, MASTER_NAME, iCol, bQuote, zNew, pTab->zName, zOld, pTab->zName
"sql = sqlite_rename_column(sql, %Q, %Q, %d, %Q, %d) "
"WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)"
" AND sql NOT LIKE 'create virtual%%'",
zDb, MASTER_NAME,
zDb, pTab->zName, iCol, zNew, bQuote,
pTab->zName
);
/* Drop and reload the database schema. */
@ -898,10 +892,13 @@ struct RenameToken {
** The context of an ALTER TABLE RENAME COLUMN operation that gets passed
** down into the Walker.
*/
typedef struct RenameCtx RenameCtx;
struct RenameCtx {
RenameToken *pList; /* List of tokens to overwrite */
int nList; /* Number of tokens in pList */
int iCol; /* Index of column being renamed */
Table *pTab; /* Table being ALTERed */
const char *zOld; /* Old column name */
};
/*
@ -967,6 +964,14 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
}
}
/*
** This is a Walker select callback. It does nothing. It is only required
** because without a dummy callback, sqlite3WalkExpr() and similar do not
** descend into sub-select statements.
*/
static int renameColumnSelectCb(Walker *pWalker, Select *p){
return WRC_Continue;
}
/*
** This is a Walker expression callback.
@ -978,8 +983,16 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
** constructed in RenameCtx object at pWalker->u.pRename.
*/
static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
struct RenameCtx *p = pWalker->u.pRename;
if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){
RenameCtx *p = pWalker->u.pRename;
if( pExpr->op==TK_TRIGGER
&& pExpr->iColumn==p->iCol
&& pWalker->pParse->pTriggerTab==p->pTab
){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
}else if( pExpr->op==TK_COLUMN
&& pExpr->iColumn==p->iCol
&& p->pTab==pExpr->pTab
){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
}
return WRC_Continue;
@ -987,14 +1000,14 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
/*
** The RenameCtx contains a list of tokens that reference a column that
** is being renamed by an ALTER TABLE statement. Return the "first"
** is being renamed by an ALTER TABLE statement. Return the "last"
** RenameToken in the RenameCtx and remove that RenameToken from the
** RenameContext. "First" means the first RenameToken encountered when
** the input SQL from left to right. Repeated calls to this routine
** RenameContext. "Last" means the last RenameToken encountered when
** the input SQL is parsed from left to right. Repeated calls to this routine
** return all column name tokens in the order that they are encountered
** in the SQL statement.
*/
static RenameToken *renameColumnTokenNext(struct RenameCtx *pCtx){
static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
RenameToken *pBest = pCtx->pList;
RenameToken *pToken;
RenameToken **pp;
@ -1008,11 +1021,45 @@ static RenameToken *renameColumnTokenNext(struct RenameCtx *pCtx){
return pBest;
}
/*
** An error occured while parsing or otherwise processing a database
** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an
** ALTER TABLE RENAME COLUMN program. The error message emitted by the
** sub-routine is currently stored in pParse->zErrMsg. This function
** adds context to the error message and then stores it in pCtx.
*/
static void renameColumnParseError(sqlite3_context *pCtx, Parse *pParse){
const char *zT;
const char *zN;
char *zErr;
if( pParse->pNewTable ){
zT = pParse->pNewTable->pSelect ? "view" : "table";
zN = pParse->pNewTable->zName;
}else if( pParse->pNewIndex ){
zT = "index";
zN = pParse->pNewIndex->zName;
}else{
assert( pParse->pNewTrigger );
zT = "trigger";
zN = pParse->pNewTrigger->zName;
}
zErr = sqlite3_mprintf("error processing %s %s: %s", zT, zN, pParse->zErrMsg);
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
}
/*
** SQL function:
**
** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld)
**
** 0. zSql: SQL statement to rewrite
** 1. Database: Database name (e.g. "main")
** 2. Table: Table name
** 3. iCol: Index of column to rename
** 4. zNew: New column name
** 5. bQuote: True if the new column name should be quoted
**
** Do a column rename operation on the CREATE statement given in zSql.
** The iCol-th column (left-most is 0) of table zTable is renamed from zCol
** into zNew. The name should be quoted if bQuote is true.
@ -1034,14 +1081,16 @@ static void renameColumnFunc(
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
struct RenameCtx sCtx;
RenameCtx sCtx;
const char *zSql = (const char*)sqlite3_value_text(argv[0]);
int nSql = sqlite3_value_bytes(argv[0]);
int bQuote = sqlite3_value_int(argv[2]);
const char *zNew = (const char*)sqlite3_value_text(argv[3]);
int nNew = sqlite3_value_bytes(argv[3]);
const char *zTable = (const char*)sqlite3_value_text(argv[4]);
const char *zOld = (const char*)sqlite3_value_text(argv[5]);
const char *zDb = (const char*)sqlite3_value_text(argv[1]);
const char *zTable = (const char*)sqlite3_value_text(argv[2]);
int iCol = sqlite3_value_int(argv[3]);
const char *zNew = (const char*)sqlite3_value_text(argv[4]);
int nNew = sqlite3_value_bytes(argv[4]);
int bQuote = sqlite3_value_int(argv[5]);
const char *zOld;
int rc;
char *zErr = 0;
@ -1053,26 +1102,54 @@ static void renameColumnFunc(
char *zQuot = 0; /* Quoted version of zNew */
int nQuot = 0; /* Length of zQuot in bytes */
int i;
Table *pTab;
if( zSql==0 ) return;
if( zNew==0 ) return;
if( zTable==0 ) return;
if( zOld==0 ) return;
if( iCol<0 ) return;
pTab = sqlite3FindTable(db, zTable, zDb);
if( pTab==0 || iCol>=pTab->nCol ) return;
zOld = pTab->aCol[iCol].zName;
memset(&sCtx, 0, sizeof(sCtx));
sCtx.iCol = sqlite3_value_int(argv[1]);
if( sCtx.iCol<0 ) return;
sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
/* Parse the SQL statement passed as the first argument. If no error
** occurs and the parse does not result in a new table, index or
** trigger object, the database must be corrupt. */
memset(&sParse, 0, sizeof(sParse));
sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
sParse.db = db;
sParse.nQueryLoop = 1;
rc = sqlite3RunParser(&sParse, zSql, &zErr);
assert( sParse.pNewTable==0 || sParse.pNewIndex==0 );
assert( sParse.zErrMsg==0 );
assert( rc!=SQLITE_OK || zErr==0 );
assert( (!!sParse.pNewTable)+(!!sParse.pNewIndex)+(!!sParse.pNewTrigger)<2 );
sParse.zErrMsg = zErr;
if( db->mallocFailed ) rc = SQLITE_NOMEM;
if( rc==SQLITE_OK && sParse.pNewTable==0 && sParse.pNewIndex==0 ){
if( rc==SQLITE_OK
&& sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0
){
rc = SQLITE_CORRUPT_BKPT;
}
#ifdef SQLITE_DEBUG
/* Ensure that all mappings in the Parse.pRename list really do map to
** a part of the input string. */
assert( sqlite3Strlen30(zSql)==nSql );
if( rc==SQLITE_OK ){
RenameToken *pToken;
for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
}
}
#endif
/* Set zQuot to point to a buffer containing a quoted copy of the
** identifier zNew. If the corresponding identifier in the original
** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
** point to zQuot so that all substitutions are made using the
** quoted version of the new column name. */
if( rc==SQLITE_OK ){
zQuot = sqlite3_mprintf("\"%w\"", zNew);
if( zQuot==0 ){
@ -1081,72 +1158,184 @@ static void renameColumnFunc(
nQuot = sqlite3Strlen30(zQuot);
}
}
if( rc!=SQLITE_OK ){
if( zErr ){
sqlite3_result_error(context, zErr, -1);
}else{
sqlite3_result_error_code(context, rc);
}
sqlite3DbFree(db, zErr);
goto renameColumnFunc_done;
}
if( bQuote ){
zNew = zQuot;
nNew = nQuot;
}
#ifdef SQLITE_DEBUG
assert( sqlite3Strlen30(zSql)==nSql );
{
RenameToken *pToken;
for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
}
}
#endif
/* Find tokens that need to be replaced. */
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = &sParse;
sWalker.xExprCallback = renameColumnExprCb;
sWalker.xSelectCallback = renameColumnSelectCb;
sWalker.u.pRename = &sCtx;
sCtx.pTab = pTab;
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
if( sParse.pNewTable ){
int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
FKey *pFKey;
for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
for(i=0; i<pFKey->nCol; i++){
if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){
renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
Select *pSelect = sParse.pNewTable->pSelect;
if( pSelect ){
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
if( rc==SQLITE_OK ){
sqlite3WalkSelect(&sWalker, pSelect);
}
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
}else{
/* A regular table */
int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
FKey *pFKey;
assert( sParse.pNewTable->pSelect==0 );
sCtx.pTab = sParse.pNewTable;
if( bFKOnly==0 ){
renameTokenFind(
&sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName
);
if( sCtx.iCol<0 ){
renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey);
}
if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
&& 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
){
renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
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; i<pFKey->nCol; i++){
if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
}
if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
&& 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
){
renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
}
}
}
}
if( bFKOnly==0 ){
renameTokenFind(
&sParse, &sCtx, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName
);
assert( sCtx.iCol>=0 );
if( sParse.pNewTable->iPKey==sCtx.iCol ){
renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey);
sCtx.iCol = -1;
}
sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
}
}
}else{
}else if( sParse.pNewIndex ){
sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
}else{
/* A trigger */
TriggerStep *pStep;
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = &sParse;
sParse.pTriggerTab = sqlite3FindTable(db, sParse.pNewTrigger->table, zDb);
sParse.eTriggerOp = sParse.pNewTrigger->op;
/* Resolve symbols in WHEN clause */
if( 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( pStep->pUpsert ){
Upsert *pUpsert = pStep->pUpsert;
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
}
if( rc==SQLITE_OK && pUpsert->pUpsertSet){
ExprList *pUpsertSet = pUpsert->pUpsertSet;
rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
if( rc==SQLITE_OK && pTarget==pTab ){
for(i=0; i<pUpsertSet->nExpr; i++){
char *zName = pUpsertSet->a[i].zName;
if( 0==sqlite3_stricmp(zName, zOld) ){
renameTokenFind(&sParse, &sCtx, (void*)zName);
}
}
}
}
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
}
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
}
}
if( rc==SQLITE_OK && pTarget==pTab ){
if( pStep->pIdList ){
for(i=0; i<pStep->pIdList->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; i<pStep->pExprList->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; i<sParse.pNewTrigger->pColumns->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);
if( pStep->pUpsert ){
Upsert *pUpsert = pStep->pUpsert;
sqlite3WalkExprList(&sWalker, pUpsert->pUpsertTarget);
sqlite3WalkExprList(&sWalker, pUpsert->pUpsertSet);
sqlite3WalkExpr(&sWalker, pUpsert->pUpsertWhere);
sqlite3WalkExpr(&sWalker, pUpsert->pUpsertTargetWhere);
}
}
}
/* At this point sCtx.pList contains a list of RenameToken objects
** corresponding to all tokens in the input SQL that must be replaced
** with the new column name. All that remains is to construct and
** return the edited SQL string. */
assert( rc==SQLITE_OK );
assert( nQuot>=nNew );
zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
if( zOut ){
@ -1180,16 +1369,28 @@ static void renameColumnFunc(
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
sqlite3DbFree(db, zOut);
}else{
rc = SQLITE_NOMEM;
}
renameColumnFunc_done:
if( rc!=SQLITE_OK ){
if( sParse.zErrMsg ){
renameColumnParseError(context, &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
}
if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(db, sParse.pNewTable);
if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex);
sqlite3DeleteTrigger(db, sParse.pNewTrigger);
renameTokenFree(db, sParse.pRename);
renameTokenFree(db, sCtx.pList);
sqlite3DbFree(db, sParse.zErrMsg);
sqlite3ParserReset(&sParse);
sqlite3_free(zQuot);
}

View File

@ -2175,7 +2175,12 @@ void sqlite3CreateView(
** allocated rather than point to the input string - which means that
** they will persist after the current sqlite3_exec() call returns.
*/
p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
if( IN_RENAME_COLUMN ){
p->pSelect = pSelect;
pSelect = 0;
}else{
p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
}
p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
if( db->mallocFailed ) goto create_view_fail;
@ -3689,7 +3694,8 @@ void *sqlite3ArrayAllocate(
**
** A new IdList is returned, or NULL if malloc() fails.
*/
IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
sqlite3 *db = pParse->db;
int i;
if( pList==0 ){
pList = sqlite3DbMallocZero(db, sizeof(IdList) );
@ -3707,6 +3713,9 @@ IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
return 0;
}
pList->a[i].zName = sqlite3NameFromToken(db, pToken);
if( IN_RENAME_COLUMN && pList->a[i].zName ){
sqlite3RenameToken(pParse, (void*)pList->a[i].zName, pToken);
}
return pList;
}

View File

@ -1666,6 +1666,9 @@ void sqlite3ExprListSetName(
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);
}
}
}

View File

@ -908,9 +908,9 @@ insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
idlist_opt(A) ::= . {A = 0;}
idlist_opt(A) ::= LP idlist(X) RP. {A = X;}
idlist(A) ::= idlist(A) COMMA nm(Y).
{A = sqlite3IdListAppend(pParse->db,A,&Y);}
{A = sqlite3IdListAppend(pParse,A,&Y);}
idlist(A) ::= nm(Y).
{A = sqlite3IdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/}
{A = sqlite3IdListAppend(pParse,0,&Y); /*A-overwrites-Y*/}
/////////////////////////// Expression Processing /////////////////////////////
//
@ -1451,16 +1451,16 @@ tridxby ::= NOT INDEXED. {
// 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).

View File

@ -760,56 +760,58 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
}
if( 0==IN_RENAME_COLUMN ){
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
if( pDef && pDef->xValue==0 && pExpr->pWin ){
sqlite3ErrorMsg(pParse,
"%.*s() may not be used as a window function", nId, zId
);
pNC->nErr++;
}else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
|| (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin)
|| (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){
zType = "window";
}else{
zType = "aggregate";
if( pDef && pDef->xValue==0 && pExpr->pWin ){
sqlite3ErrorMsg(pParse,
"%.*s() may not be used as a window function", nId, zId
);
pNC->nErr++;
}else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
|| (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin)
|| (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){
zType = "window";
}else{
zType = "aggregate";
}
sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
pNC->nErr++;
is_agg = 0;
}
sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()", zType, nId,zId);
pNC->nErr++;
is_agg = 0;
}
#else
if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
pNC->nErr++;
is_agg = 0;
}
if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId);
pNC->nErr++;
is_agg = 0;
}
#endif
else if( no_such_func && pParse->db->init.busy==0
else if( no_such_func && pParse->db->init.busy==0 && !IN_RENAME_COLUMN
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
&& pParse->explain==0
&& pParse->explain==0
#endif
){
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
pNC->nErr++;
}else if( wrong_num_args ){
sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
nId, zId);
pNC->nErr++;
}
if( is_agg ){
){
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
pNC->nErr++;
}else if( wrong_num_args ){
sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
nId, zId);
pNC->nErr++;
}
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg);
pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg);
#else
pNC->ncFlags &= ~NC_AllowAgg;
pNC->ncFlags &= ~NC_AllowAgg;
#endif
}
}
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){

View File

@ -3887,7 +3887,7 @@ void sqlite3FreeIndex(sqlite3*, Index*);
#endif
void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
IdList *sqlite3IdListAppend(Parse*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
@ -4049,12 +4049,12 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
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*);

View File

@ -697,9 +697,11 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
*/
sqlite3DeleteTable(db, pParse->pNewTable);
}
if( !IN_RENAME_COLUMN ){
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
}
if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
sqlite3DbFree(db, pParse->pVList);
while( pParse->pAinc ){
AutoincInfo *p = pParse->pAinc;

View File

@ -181,14 +181,16 @@ void sqlite3BeginTrigger(
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
if( !IN_RENAME_COLUMN ){
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
}
goto trigger_cleanup;
}
goto trigger_cleanup;
}
/* Do not create a trigger on a system table */
@ -212,7 +214,7 @@ void sqlite3BeginTrigger(
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
if( !IN_RENAME_COLUMN ){
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int code = SQLITE_CREATE_TRIGGER;
const char *zDb = db->aDb[iTabDb].zDbSName;
@ -246,8 +248,14 @@ void sqlite3BeginTrigger(
pTrigger->pTabSchema = pTab->pSchema;
pTrigger->op = (u8)op;
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
if( IN_RENAME_COLUMN ){
pTrigger->pWhen = pWhen;
pWhen = 0;
}else{
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
}
pTrigger->pColumns = pColumns;
pColumns = 0;
assert( pParse->pNewTrigger==0 );
pParse->pNewTrigger = pTrigger;
@ -296,6 +304,14 @@ void sqlite3FinishTrigger(
goto triggerfinish_cleanup;
}
#ifndef SQLITE_OMIT_ALTERTABLE
if( IN_RENAME_COLUMN ){
assert( !db->init.busy );
pParse->pNewTrigger = pTrig;
pTrig = 0;
}else
#endif
/* if we are not initializing,
** build the sqlite_master entry
*/
@ -337,7 +353,7 @@ void sqlite3FinishTrigger(
triggerfinish_cleanup:
sqlite3DeleteTrigger(db, pTrig);
assert( !pParse->pNewTrigger );
assert( IN_RENAME_COLUMN || !pParse->pNewTrigger );
sqlite3DeleteTriggerStep(db, pStepList);
}
@ -412,7 +428,7 @@ static TriggerStep *triggerStepAllocate(
** 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 */
@ -421,13 +437,19 @@ TriggerStep *sqlite3TriggerInsertStep(
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;
@ -448,7 +470,7 @@ TriggerStep *sqlite3TriggerInsertStep(
** 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 */
@ -456,12 +478,20 @@ TriggerStep *sqlite3TriggerUpdateStep(
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);
@ -475,17 +505,23 @@ TriggerStep *sqlite3TriggerUpdateStep(
** 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);

View File

@ -26,6 +26,20 @@ ifcapable !altertable {
return
}
# Drop all the tables and views in the 'main' database of database connect
# [db]. Sort the objects by name before dropping them.
#
proc drop_all_tables_and_views {db} {
set SQL {
SELECT name, type FROM sqlite_master
WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
ORDER BY 1
}
foreach {z t} [db eval $SQL] {
db eval "DROP $t $z"
}
}
foreach {tn before after} {
1 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB)}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB)}
@ -66,6 +80,9 @@ foreach {tn before after} {
13 {CREATE TABLE t1(a, b, c, FOREIGN KEY (b) REFERENCES t2)}
{CREATE TABLE t1(a, d, c, FOREIGN KEY (d) REFERENCES t2)}
14 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b))}
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d))}
15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))}
{CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))}
@ -122,7 +139,10 @@ do_execsql_test -db db2 2.3 { SELECT biglongname FROM t3 }
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);
}
@ -132,14 +152,20 @@ do_execsql_test 3.1 {
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.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}
}
#-------------------------------------------------------------------------
#
@ -217,5 +243,227 @@ do_execsql_test 6.3 {
SELECT "where" FROM blob;
} {}
finish_test
#-------------------------------------------------------------------------
# Triggers.
#
reset_db
do_execsql_test 7.0 {
CREATE TABLE c(x);
INSERT INTO c VALUES(0);
CREATE TABLE t6("col a", "col b", "col c");
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.1 {
INSERT INTO t6 VALUES(0, 0, 0);
UPDATE t6 SET "col c" = 1;
SELECT * FROM c;
} {1}
do_execsql_test 7.1.2 {
ALTER TABLE t6 RENAME "col c" TO "col 3";
}
do_execsql_test 7.1.3 {
UPDATE t6 SET "col 3" = 0;
SELECT * FROM c;
} {2}
#-------------------------------------------------------------------------
# Views.
#
reset_db
do_execsql_test 8.0 {
CREATE TABLE a1(x INTEGER, y TEXT, z BLOB, PRIMARY KEY(x));
CREATE TABLE a2(a, b, c);
CREATE VIEW v1 AS SELECT x, y, z FROM a1;
}
do_execsql_test 8.1 {
ALTER TABLE a1 RENAME y TO yyy;
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW v1 AS SELECT x, yyy, z FROM a1}}
do_execsql_test 8.2.1 {
DROP VIEW v1;
CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2;
} {}
do_execsql_test 8.2.2 {
ALTER TABLE a1 RENAME x TO xxx;
}
do_execsql_test 8.2.3 {
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2}}
do_execsql_test 8.3.1 {
DROP TABLE a2;
DROP VIEW v2;
CREATE TABLE a2(a INTEGER PRIMARY KEY, b, c);
CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2;
} {}
do_execsql_test 8.3.2 {
ALTER TABLE a1 RENAME xxx TO x;
}
do_execsql_test 8.3.3 {
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2}}
do_execsql_test 8.4.0 {
CREATE TABLE b1(a, b, c);
CREATE TABLE b2(x, y, z);
}
do_execsql_test 8.4.1 {
CREATE VIEW vvv AS SELECT c+c || coalesce(c, c) FROM b1, b2 WHERE x=c GROUP BY c HAVING c>0;
ALTER TABLE b1 RENAME c TO "a;b";
SELECT sql FROM sqlite_master WHERE name='vvv';
} {{CREATE VIEW vvv AS SELECT "a;b"+"a;b" || coalesce("a;b", "a;b") FROM b1, b2 WHERE x="a;b" GROUP BY "a;b" HAVING "a;b">0}}
do_execsql_test 8.4.2 {
CREATE VIEW www AS SELECT b FROM b1 UNION ALL SELECT y FROM b2;
ALTER TABLE b1 RENAME b TO bbb;
SELECT sql FROM sqlite_master WHERE name='www';
} {{CREATE VIEW www AS SELECT bbb FROM b1 UNION ALL SELECT y FROM b2}}
db collate nocase {string compare}
do_execsql_test 8.4.3 {
CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT x FROM b2 ORDER BY 1 COLLATE nocase;
}
do_execsql_test 8.4.4 {
ALTER TABLE b2 RENAME x TO hello;
SELECT sql FROM sqlite_master WHERE name='xxx';
} {{CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT hello FROM b2 ORDER BY 1 COLLATE nocase}}
do_catchsql_test 8.4.5 {
CREATE VIEW zzz AS SELECT george, ringo FROM b1;
ALTER TABLE b1 RENAME a TO aaa;
} {1 {error processing view zzz: no such column: george}}
#-------------------------------------------------------------------------
# More triggers.
#
proc do_rename_column_test {tn old new lSchema} {
for {set i 0} {$i < 2} {incr i} {
drop_all_tables_and_views db
set lSorted [list]
foreach sql $lSchema {
execsql $sql
lappend lSorted [string trim $sql]
}
set lSorted [lsort $lSorted]
do_execsql_test $tn.$i.1 {
SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1
} $lSorted
if {$i==1} {
db close
sqlite3 db test.db
}
do_execsql_test $tn.$i.2 "ALTER TABLE t1 RENAME $old TO $new"
do_execsql_test $tn.$i.3 {
SELECT sql FROM sqlite_master ORDER BY 1
} [string map [list $old $new] $lSorted]
}
}
foreach {tn old new lSchema} {
1 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE TRIGGER AFTER INSERT ON t1 BEGIN
SELECT _x_ FROM t1;
END }
}
2 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE TABLE t2(c, d, e) }
{ CREATE TRIGGER ttt AFTER INSERT ON t2 BEGIN
SELECT _x_ FROM t1;
END }
}
3 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
{ CREATE TABLE t2(c, d, e) }
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
INSERT INTO t2 VALUES(new.a, new.b, new._x_);
END }
}
4 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
INSERT INTO t1 VALUES(new.a, new.b, new._x_)
ON CONFLICT (_x_) WHERE _x_>10 DO UPDATE SET _x_ = _x_+1;
END }
}
} {
do_rename_column_test 9.$tn $old $new $lSchema
}
#-------------------------------------------------------------------------
# Test that views can be edited even if there are missing collation
# sequences or user defined functions.
#
reset_db
foreach {tn old new lSchema} {
1 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIEW s1 AS SELECT a, b, _x_ FROM t1 WHERE _x_='abc' COLLATE xyz }
}
2 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE scalar(_x_) }
}
3 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_ = unicode(1, 2, 3) }
}
4 _x_ _xxx_ {
{ CREATE TABLE t1(a, b, _x_) }
{ CREATE VIRTUAL TABLE e1 USING echo(t1) }
}
} {
register_echo_module db
do_rename_column_test 10.$tn $old $new $lSchema
}
#--------------------------------------------------------------------------
# Test that if a view or trigger refers to a virtual table for which the
# module is not available, RENAME COLUMN cannot proceed.
#
reset_db
register_echo_module db
do_execsql_test 11.0 {
CREATE TABLE x1(a, b, c);
CREATE VIRTUAL TABLE e1 USING echo(x1);
}
db close
sqlite3 db test.db
do_execsql_test 11.1 {
ALTER TABLE x1 RENAME b TO bbb;
SELECT sql FROM sqlite_master;
} { {CREATE TABLE x1(a, bbb, c)} {CREATE VIRTUAL TABLE e1 USING echo(x1)} }
do_execsql_test 11.2 {
CREATE VIEW v1 AS SELECT e1.*, x1.c FROM e1, x1;
}
do_catchsql_test 11.3 {
ALTER TABLE x1 RENAME c TO ccc;
} {1 {error processing view v1: no such module: echo}}
finish_test