diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alter.c | 180 | ||||
-rw-r--r-- | src/build.c | 13 | ||||
-rw-r--r-- | src/parse.y | 4 | ||||
-rw-r--r-- | src/sqliteInt.h | 2 | ||||
-rw-r--r-- | src/tokenize.c | 4 | ||||
-rw-r--r-- | src/trigger.c | 38 |
6 files changed, 166 insertions, 75 deletions
diff --git a/src/alter.c b/src/alter.c index a07c248b3..89be503fc 100644 --- a/src/alter.c +++ b/src/alter.c @@ -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,14 @@ void sqlite3AlterRenameColumn( bQuote = sqlite3Isquote(pNew->z[0]); sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " - "sql = sqlite_rename_column(sql, %d, %d, %Q, %Q, %Q) " + "sql = sqlite_rename_column(sql, %Q, %Q, %d, %Q, %d) " "WHERE name NOT LIKE 'sqlite_%%' AND (" - " type = 'table' OR (type='index' AND tbl_name = %Q)" + " type IN ('table', 'view') " + " OR (type IN ('index', 'trigger') AND tbl_name = %Q)" ")", - zDb, MASTER_NAME, iCol, bQuote, zNew, pTab->zName, zOld, pTab->zName + zDb, MASTER_NAME, + zDb, pTab->zName, iCol, zNew, bQuote, + pTab->zName ); /* Drop and reload the database schema. */ @@ -898,10 +894,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 +966,10 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ } } +static int renameColumnSelectCb(Walker *pWalker, Select *p){ + return WRC_Continue; +} + /* ** This is a Walker expression callback. @@ -978,8 +981,21 @@ 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( 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") + || 0==sqlite3_stricmp(pLeft->u.zToken, "new") + ){ + if( 0==sqlite3_stricmp(pRight->u.zToken, p->zOld) ){ + renameTokenFind(pWalker->pParse, p, (void*)pRight); + } + } + }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol + && (p->pTab==0 || p->pTab==pExpr->pTab) + ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); } return WRC_Continue; @@ -994,7 +1010,7 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ ** 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; @@ -1013,6 +1029,13 @@ static RenameToken *renameColumnTokenNext(struct RenameCtx *pCtx){ ** ** 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 +1057,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,14 +1078,17 @@ 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); memset(&sParse, 0, sizeof(sParse)); sParse.eParseMode = PARSE_MODE_RENAME_COLUMN; @@ -1069,7 +1097,9 @@ static void renameColumnFunc( rc = sqlite3RunParser(&sParse, zSql, &zErr); assert( sParse.pNewTable==0 || sParse.pNewIndex==0 ); 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; } @@ -1082,16 +1112,6 @@ static void renameColumnFunc( } } - 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; @@ -1099,7 +1119,7 @@ static void renameColumnFunc( #ifdef SQLITE_DEBUG 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] ); @@ -1111,42 +1131,75 @@ static void renameColumnFunc( memset(&sWalker, 0, sizeof(Walker)); sWalker.pParse = &sParse; sWalker.xExprCallback = renameColumnExprCb; + sWalker.xSelectCallback = renameColumnSelectCb; sWalker.u.pRename = &sCtx; + 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 ){ + sCtx.pTab = pTab; + sParse.rc = SQLITE_OK; + 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 + ** error, but it will not be edited. */ + sqlite3DbFree(db, sParse.zErrMsg); + sParse.zErrMsg = 0; + rc = SQLITE_OK; + } + 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 ); + 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); } } - } - 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); + + 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); + } + } } } - }else{ + }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 ){ + 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); + } + } + } } + assert( rc==SQLITE_OK ); assert( nQuot>=nNew ); zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1); if( zOut ){ @@ -1180,14 +1233,25 @@ 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( zErr ){ + sqlite3_result_error(context, zErr, -1); + }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); sqlite3ParserReset(&sParse); diff --git a/src/build.c b/src/build.c index 31ef2e17d..68fecd103 100644 --- a/src/build.c +++ b/src/build.c @@ -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; } diff --git a/src/parse.y b/src/parse.y index 47e73feb8..76e966a4d 100644 --- a/src/parse.y +++ b/src/parse.y @@ -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 ///////////////////////////// // diff --git a/src/sqliteInt.h b/src/sqliteInt.h index e7d86ba3b..f2e6121de 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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*); diff --git a/src/tokenize.c b/src/tokenize.c index 685d10b25..02d21f1f5 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -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; diff --git a/src/trigger.c b/src/trigger.c index 044f256ae..1df88fd80 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -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); } |