#include "sqliteInt.h"
#ifndef SQLITE_OMIT_ALTERTABLE
static int isAlterableTable(Parse *pParse, Table *pTab){
if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
#ifndef SQLITE_OMIT_VIRTUALTABLE
|| (pTab->tabFlags & TF_Eponymous)!=0
|| ( (pTab->tabFlags & TF_Shadow)!=0
&& sqlite3ReadOnlyShadowTables(pParse->db)
)
#endif
){
sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName);
return 1;
}
return 0;
}
static void renameTestSchema(
Parse *pParse,
const char *zDb,
int bTemp,
const char *zWhen,
int bNoDQS
){
pParse->colNamesSet = 1;
sqlite3NestedParse(pParse,
"SELECT 1 "
"FROM \"%w\"." LEGACY_SCHEMA_TABLE " "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
" AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ",
zDb,
zDb, bTemp, zWhen, bNoDQS
);
if( bTemp==0 ){
sqlite3NestedParse(pParse,
"SELECT 1 "
"FROM temp." LEGACY_SCHEMA_TABLE " "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
" AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ",
zDb, zWhen, bNoDQS
);
}
}
static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE
" SET sql = sqlite_rename_quotefix(%Q, sql)"
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'" , zDb, zDb
);
if( bTemp==0 ){
sqlite3NestedParse(pParse,
"UPDATE temp." LEGACY_SCHEMA_TABLE
" SET sql = sqlite_rename_quotefix('temp', sql)"
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
);
}
}
static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){
Vdbe *v = pParse->pVdbe;
if( v ){
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5);
if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5);
}
}
void sqlite3AlterRenameTable(
Parse *pParse,
SrcList *pSrc,
Token *pName
){
int iDb;
char *zDb;
Table *pTab;
char *zName = 0;
sqlite3 *db = pParse->db;
int nTabName;
const char *zTabName;
Vdbe *v;
VTable *pVTab = 0;
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
assert( pSrc->nSrc==1 );
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zDbSName;
zName = sqlite3NameFromToken(db, pName);
if( !zName ) goto exit_rename_table;
if( sqlite3FindTable(db, zName, zDb)
|| sqlite3FindIndex(db, zName, zDb)
|| sqlite3IsShadowTableOf(db, pTab, zName)
){
sqlite3ErrorMsg(pParse,
"there is already another table or index with this name: %s", zName);
goto exit_rename_table;
}
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
goto exit_rename_table;
}
if( SQLITE_OK!=sqlite3CheckObjectName(pParse,zName,"table",zName) ){
goto exit_rename_table;
}
#ifndef SQLITE_OMIT_VIEW
if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName);
goto exit_rename_table;
}
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
goto exit_rename_table;
}
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto exit_rename_table;
}
if( IsVirtual(pTab) ){
pVTab = sqlite3GetVTable(db, pTab);
if( pVTab->pVtab->pModule->xRename==0 ){
pVTab = 0;
}
}
#endif
v = sqlite3GetVdbe(pParse);
if( v==0 ){
goto exit_rename_table;
}
sqlite3MayAbort(pParse);
zTabName = pTab->zName;
nTabName = sqlite3Utf8CharLen(zTabName, -1);
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
"WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
"AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
, zDb, zDb, zTabName, zName, (iDb==1), zTabName
);
sqlite3NestedParse(pParse,
"UPDATE %Q." LEGACY_SCHEMA_TABLE " SET "
"tbl_name = %Q, "
"name = CASE "
"WHEN type='table' THEN %Q "
"WHEN name LIKE 'sqliteX_autoindex%%' ESCAPE 'X' "
" AND type='index' THEN "
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
"ELSE name END "
"WHERE tbl_name=%Q COLLATE nocase AND "
"(type='table' OR type='index' OR type='trigger');",
zDb,
zName, zName, zName,
nTabName, zTabName
);
#ifndef SQLITE_OMIT_AUTOINCREMENT
if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){
sqlite3NestedParse(pParse,
"UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q",
zDb, zName, pTab->zName);
}
#endif
if( iDb!=1 ){
sqlite3NestedParse(pParse,
"UPDATE sqlite_temp_schema SET "
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
"tbl_name = "
"CASE WHEN tbl_name=%Q COLLATE nocase AND "
" sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) "
"THEN %Q ELSE tbl_name END "
"WHERE type IN ('view', 'trigger')"
, zDb, zTabName, zName, zTabName, zDb, zName);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pVTab ){
int i = ++pParse->nMem;
sqlite3VdbeLoadString(v, i, zName);
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
}
#endif
renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
renameTestSchema(pParse, zDb, iDb==1, "after rename", 0);
exit_rename_table:
sqlite3SrcListDelete(db, pSrc);
sqlite3DbFree(db, zName);
}
static void sqlite3ErrorIfNotEmpty(
Parse *pParse,
const char *zDb,
const char *zTab,
const char *zErr
){
sqlite3NestedParse(pParse,
"SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
zErr, zDb, zTab
);
}
void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
Table *pNew;
Table *pTab;
int iDb;
const char *zDb;
const char *zTab;
char *zCol;
Column *pCol;
Expr *pDflt;
sqlite3 *db;
Vdbe *v;
int r1;
db = pParse->db;
assert( db->pParse==pParse );
if( pParse->nErr ) return;
assert( db->mallocFailed==0 );
pNew = pParse->pNewTable;
assert( pNew );
assert( sqlite3BtreeHoldsAllMutexes(db) );
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
zDb = db->aDb[iDb].zDbSName;
zTab = &pNew->zName[16];
pCol = &pNew->aCol[pNew->nCol-1];
pDflt = sqlite3ColumnExpr(pNew, pCol);
pTab = sqlite3FindTable(db, zTab, zDb);
assert( pTab );
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
return;
}
#endif
if( pCol->colFlags & COLFLAG_PRIMKEY ){
sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
return;
}
if( pNew->pIndex ){
sqlite3ErrorMsg(pParse,
"Cannot add a UNIQUE column");
return;
}
if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
assert( pDflt==0 || pDflt->op==TK_SPAN );
if( pDflt && pDflt->pLeft->op==TK_NULL ){
pDflt = 0;
}
assert( IsOrdinaryTable(pNew) );
if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
"Cannot add a REFERENCES column with non-NULL default value");
}
if( pCol->notNull && !pDflt ){
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
"Cannot add a NOT NULL column with default value NULL");
}
if( pDflt ){
sqlite3_value *pVal = 0;
int rc;
rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
if( rc!=SQLITE_OK ){
assert( db->mallocFailed == 1 );
return;
}
if( !pVal ){
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
"Cannot add a column with non-constant default");
}
sqlite3ValueFree(pVal);
}
}else if( pCol->colFlags & COLFLAG_STORED ){
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
}
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
if( zCol ){
char *zEnd = &zCol[pColDef->n-1];
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
*zEnd-- = '\0';
}
assert( IsOrdinaryTable(pTab) );
assert( IsOrdinaryTable(pNew) );
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = printf('%%.%ds, ',sql) || %Q"
" || substr(sql,1+length(printf('%%.%ds',sql))) "
"WHERE type = 'table' AND name = %Q",
zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset,
zTab
);
sqlite3DbFree(db, zCol);
}
v = sqlite3GetVdbe(pParse);
if( v ){
r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
sqlite3VdbeUsesBtree(v, iDb);
sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
sqlite3ReleaseTempReg(pParse, r1);
renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd);
if( pNew->pCheck!=0
|| (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0)
){
sqlite3NestedParse(pParse,
"SELECT CASE WHEN quick_check GLOB 'CHECK*'"
" THEN raise(ABORT,'CHECK constraint failed')"
" ELSE raise(ABORT,'NOT NULL constraint failed')"
" END"
" FROM pragma_quick_check(%Q,%Q)"
" WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'",
zTab, zDb
);
}
}
}
void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
Table *pNew;
Table *pTab;
int iDb;
int i;
int nAlloc;
sqlite3 *db = pParse->db;
assert( pParse->pNewTable==0 );
assert( sqlite3BtreeHoldsAllMutexes(db) );
if( db->mallocFailed ) goto exit_begin_add_column;
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_begin_add_column;
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
sqlite3ErrorMsg(pParse, "virtual tables may not be altered");
goto exit_begin_add_column;
}
#endif
if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
goto exit_begin_add_column;
}
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
goto exit_begin_add_column;
}
sqlite3MayAbort(pParse);
assert( IsOrdinaryTable(pTab) );
assert( pTab->u.tab.addColOffset>0 );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
if( !pNew ) goto exit_begin_add_column;
pParse->pNewTable = pNew;
pNew->nTabRef = 1;
pNew->nCol = pTab->nCol;
assert( pNew->nCol>0 );
nAlloc = (((pNew->nCol-1)/8)*8)+8;
assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
if( !pNew->aCol || !pNew->zName ){
assert( db->mallocFailed );
goto exit_begin_add_column;
}
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
for(i=0; i<pNew->nCol; i++){
Column *pCol = &pNew->aCol[i];
pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName);
pCol->hName = sqlite3StrIHash(pCol->zCnName);
}
assert( IsOrdinaryTable(pNew) );
pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0);
pNew->pSchema = db->aDb[iDb].pSchema;
pNew->u.tab.addColOffset = pTab->u.tab.addColOffset;
pNew->nTabRef = 1;
exit_begin_add_column:
sqlite3SrcListDelete(db, pSrc);
return;
}
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
const char *zType = 0;
#ifndef SQLITE_OMIT_VIEW
if( IsView(pTab) ){
zType = "view";
}
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
zType = "virtual table";
}
#endif
if( zType ){
sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
(bDrop ? "drop column from" : "rename columns of"),
zType, pTab->zName
);
return 1;
}
return 0;
}
#else
# define isRealTable(x,y,z) (0)
#endif
void sqlite3AlterRenameColumn(
Parse *pParse,
SrcList *pSrc,
Token *pOld,
Token *pNew
){
sqlite3 *db = pParse->db;
Table *pTab;
int iCol;
char *zOld = 0;
char *zNew = 0;
const char *zDb;
int iSchema;
int bQuote;
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_rename_column;
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column;
if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column;
iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iSchema>=0 );
zDb = db->aDb[iSchema].zDbSName;
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){
goto exit_rename_column;
}
#endif
zOld = sqlite3NameFromToken(db, pOld);
if( !zOld ) goto exit_rename_column;
for(iCol=0; iCol<pTab->nCol; iCol++){
if( 0==sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break;
}
if( iCol==pTab->nCol ){
sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld);
goto exit_rename_column;
}
renameTestSchema(pParse, zDb, iSchema==1, "", 0);
renameFixQuotes(pParse, zDb, iSchema==1);
sqlite3MayAbort(pParse);
zNew = sqlite3NameFromToken(db, pNew);
if( !zNew ) goto exit_rename_column;
assert( pNew->n>0 );
bQuote = sqlite3Isquote(pNew->z[0]);
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
" AND (type != 'index' OR tbl_name = %Q)",
zDb,
zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
pTab->zName
);
sqlite3NestedParse(pParse,
"UPDATE temp." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) "
"WHERE type IN ('trigger', 'view')",
zDb, pTab->zName, iCol, zNew, bQuote
);
renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1);
exit_rename_column:
sqlite3SrcListDelete(db, pSrc);
sqlite3DbFree(db, zOld);
sqlite3DbFree(db, zNew);
return;
}
struct RenameToken {
const void *p;
Token t;
RenameToken *pNext;
};
typedef struct RenameCtx RenameCtx;
struct RenameCtx {
RenameToken *pList;
int nList;
int iCol;
Table *pTab;
const char *zOld;
};
#ifdef SQLITE_DEBUG
static void renameTokenCheckAll(Parse *pParse, const void *pPtr){
assert( pParse==pParse->db->pParse );
assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
if( pParse->nErr==0 ){
const RenameToken *p;
u32 i = 1;
for(p=pParse->pRename; p; p=p->pNext){
if( p->p ){
assert( p->p!=pPtr );
i += *(u8*)(p->p) | 1;
}
}
assert( i>0 );
}
}
#else
# define renameTokenCheckAll(x,y)
#endif
const void *sqlite3RenameTokenMap(
Parse *pParse,
const void *pPtr,
const Token *pToken
){
RenameToken *pNew;
assert( pPtr || pParse->db->mallocFailed );
renameTokenCheckAll(pParse, pPtr);
if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
if( pNew ){
pNew->p = pPtr;
pNew->t = *pToken;
pNew->pNext = pParse->pRename;
pParse->pRename = pNew;
}
}
return pPtr;
}
void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){
RenameToken *p;
renameTokenCheckAll(pParse, pTo);
for(p=pParse->pRename; p; p=p->pNext){
if( p->p==pFrom ){
p->p = pTo;
break;
}
}
}
static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
Parse *pParse = pWalker->pParse;
sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr);
if( ExprUseYTab(pExpr) ){
sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab);
}
return WRC_Continue;
}
static void renameWalkWith(Walker *pWalker, Select *pSelect){
With *pWith = pSelect->pWith;
if( pWith ){
Parse *pParse = pWalker->pParse;
int i;
With *pCopy = 0;
assert( pWith->nCte>0 );
if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){
pCopy = sqlite3WithDup(pParse->db, pWith);
pCopy = sqlite3WithPush(pParse, pCopy, 1);
}
for(i=0; i<pWith->nCte; i++){
Select *p = pWith->a[i].pSelect;
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC);
if( sNC.pParse->db->mallocFailed ) return;
sqlite3WalkSelect(pWalker, p);
sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols);
}
if( pCopy && pParse->pWith==pCopy ){
pParse->pWith = pCopy->pOuter;
}
}
}
static void unmapColumnIdlistNames(
Parse *pParse,
const IdList *pIdList
){
int ii;
assert( pIdList!=0 );
for(ii=0; ii<pIdList->nId; ii++){
sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName);
}
}
static int renameUnmapSelectCb(Walker *pWalker, Select *p){
Parse *pParse = pWalker->pParse;
int i;
if( pParse->nErr ) return WRC_Abort;
testcase( p->selFlags & SF_View );
testcase( p->selFlags & SF_CopyCte );
if( p->selFlags & (SF_View|SF_CopyCte) ){
return WRC_Prune;
}
if( ALWAYS(p->pEList) ){
ExprList *pList = p->pEList;
for(i=0; i<pList->nExpr; i++){
if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){
sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName);
}
}
}
if( ALWAYS(p->pSrc) ){
SrcList *pSrc = p->pSrc;
for(i=0; i<pSrc->nSrc; i++){
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
if( pSrc->a[i].fg.isUsing==0 ){
sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn);
}else{
unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing);
}
}
}
renameWalkWith(pWalker, p);
return WRC_Continue;
}
void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
u8 eMode = pParse->eParseMode;
Walker sWalker;
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = pParse;
sWalker.xExprCallback = renameUnmapExprCb;
sWalker.xSelectCallback = renameUnmapSelectCb;
pParse->eParseMode = PARSE_MODE_UNMAP;
sqlite3WalkExpr(&sWalker, pExpr);
pParse->eParseMode = eMode;
}
void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){
if( pEList ){
int i;
Walker sWalker;
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = pParse;
sWalker.xExprCallback = renameUnmapExprCb;
sqlite3WalkExprList(&sWalker, pEList);
for(i=0; i<pEList->nExpr; i++){
if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){
sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName);
}
}
}
}
static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
RenameToken *pNext;
RenameToken *p;
for(p=pToken; p; p=pNext){
pNext = p->pNext;
sqlite3DbFree(db, p);
}
}
static RenameToken *renameTokenFind(
Parse *pParse,
struct RenameCtx *pCtx,
const void *pPtr
){
RenameToken **pp;
if( NEVER(pPtr==0) ){
return 0;
}
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
if( (*pp)->p==pPtr ){
RenameToken *pToken = *pp;
if( pCtx ){
*pp = pToken->pNext;
pToken->pNext = pCtx->pList;
pCtx->pList = pToken;
pCtx->nList++;
}
return pToken;
}
}
return 0;
}
static int renameColumnSelectCb(Walker *pWalker, Select *p){
if( p->selFlags & (SF_View|SF_CopyCte) ){
testcase( p->selFlags & SF_View );
testcase( p->selFlags & SF_CopyCte );
return WRC_Prune;
}
renameWalkWith(pWalker, p);
return WRC_Continue;
}
static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
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
&& ALWAYS(ExprUseYTab(pExpr))
&& p->pTab==pExpr->y.pTab
){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
}
return WRC_Continue;
}
static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
RenameToken *pBest = pCtx->pList;
RenameToken *pToken;
RenameToken **pp;
for(pToken=pBest->pNext; pToken; pToken=pToken->pNext){
if( pToken->t.z>pBest->t.z ) pBest = pToken;
}
for(pp=&pCtx->pList; *pp!=pBest; pp=&(*pp)->pNext);
*pp = pBest->pNext;
return pBest;
}
static void renameColumnParseError(
sqlite3_context *pCtx,
const char *zWhen,
sqlite3_value *pType,
sqlite3_value *pObject,
Parse *pParse
){
const char *zT = (const char*)sqlite3_value_text(pType);
const char *zN = (const char*)sqlite3_value_text(pObject);
char *zErr;
zErr = sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s",
zT, zN, (zWhen[0] ? " " : ""), zWhen,
pParse->zErrMsg
);
sqlite3_result_error(pCtx, zErr, -1);
sqlite3DbFree(pParse->db, zErr);
}
static void renameColumnElistNames(
Parse *pParse,
RenameCtx *pCtx,
const ExprList *pEList,
const char *zOld
){
if( pEList ){
int i;
for(i=0; i<pEList->nExpr; i++){
const char *zName = pEList->a[i].zEName;
if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME)
&& ALWAYS(zName!=0)
&& 0==sqlite3_stricmp(zName, zOld)
){
renameTokenFind(pParse, pCtx, (const void*)zName);
}
}
}
}
static void renameColumnIdlistNames(
Parse *pParse,
RenameCtx *pCtx,
const IdList *pIdList,
const char *zOld
){
if( pIdList ){
int i;
for(i=0; i<pIdList->nId; i++){
const char *zName = pIdList->a[i].zName;
if( 0==sqlite3_stricmp(zName, zOld) ){
renameTokenFind(pParse, pCtx, (const void*)zName);
}
}
}
}
static int renameParseSql(
Parse *p,
const char *zDb,
sqlite3 *db,
const char *zSql,
int bTemp
){
int rc;
sqlite3ParseObjectInit(p, db);
if( zSql==0 ){
return SQLITE_NOMEM;
}
if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){
return SQLITE_CORRUPT_BKPT;
}
db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
p->eParseMode = PARSE_MODE_RENAME;
p->db = db;
p->nQueryLoop = 1;
rc = sqlite3RunParser(p, zSql);
if( db->mallocFailed ) rc = SQLITE_NOMEM;
if( rc==SQLITE_OK
&& NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
){
rc = SQLITE_CORRUPT_BKPT;
}
#ifdef SQLITE_DEBUG
if( rc==SQLITE_OK ){
int nSql = sqlite3Strlen30(zSql);
RenameToken *pToken;
for(pToken=p->pRename; pToken; pToken=pToken->pNext){
assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
}
}
#endif
db->init.iDb = 0;
return rc;
}
static int renameEditSql(
sqlite3_context *pCtx,
RenameCtx *pRename,
const char *zSql,
const char *zNew,
int bQuote
){
i64 nNew = sqlite3Strlen30(zNew);
i64 nSql = sqlite3Strlen30(zSql);
sqlite3 *db = sqlite3_context_db_handle(pCtx);
int rc = SQLITE_OK;
char *zQuot = 0;
char *zOut;
i64 nQuot = 0;
char *zBuf1 = 0;
char *zBuf2 = 0;
if( zNew ){
zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew);
if( zQuot==0 ){
return SQLITE_NOMEM;
}else{
nQuot = sqlite3Strlen30(zQuot)-1;
}
assert( nQuot>=nNew );
zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
}else{
zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3);
if( zOut ){
zBuf1 = &zOut[nSql*2+1];
zBuf2 = &zOut[nSql*4+2];
}
}
if( zOut ){
int nOut = nSql;
memcpy(zOut, zSql, nSql);
while( pRename->pList ){
int iOff;
u32 nReplace;
const char *zReplace;
RenameToken *pBest = renameColumnTokenNext(pRename);
if( zNew ){
if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){
nReplace = nNew;
zReplace = zNew;
}else{
nReplace = nQuot;
zReplace = zQuot;
if( pBest->t.z[pBest->t.n]=='"' ) nReplace++;
}
}else{
memcpy(zBuf1, pBest->t.z, pBest->t.n);
zBuf1[pBest->t.n] = 0;
sqlite3Dequote(zBuf1);
sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1,
pBest->t.z[pBest->t.n]=='\'' ? " " : ""
);
zReplace = zBuf2;
nReplace = sqlite3Strlen30(zReplace);
}
iOff = pBest->t.z - zSql;
if( pBest->t.n!=nReplace ){
memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
nOut - (iOff + pBest->t.n)
);
nOut += nReplace - pBest->t.n;
zOut[nOut] = '\0';
}
memcpy(&zOut[iOff], zReplace, nReplace);
sqlite3DbFree(db, pBest);
}
sqlite3_result_text(pCtx, zOut, -1, SQLITE_TRANSIENT);
sqlite3DbFree(db, zOut);
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zQuot);
return rc;
}
static void renameSetENames(ExprList *pEList, int val){
if( pEList ){
int i;
for(i=0; i<pEList->nExpr; i++){
assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME );
pEList->a[i].fg.eEName = val;
}
}
}
static int renameResolveTrigger(Parse *pParse){
sqlite3 *db = pParse->db;
Trigger *pNew = pParse->pNewTrigger;
TriggerStep *pStep;
NameContext sNC;
int rc = SQLITE_OK;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
assert( pNew->pTabSchema );
pParse->pTriggerTab = sqlite3FindTable(db, pNew->table,
db->aDb[sqlite3SchemaToIndex(db, pNew->pTabSchema)].zDbSName
);
pParse->eTriggerOp = pNew->op;
if( ALWAYS(pParse->pTriggerTab) ){
rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab);
}
if( rc==SQLITE_OK && pNew->pWhen ){
rc = sqlite3ResolveExprNames(&sNC, pNew->pWhen);
}
for(pStep=pNew->step_list; rc==SQLITE_OK && pStep; pStep=pStep->pNext){
if( pStep->pSelect ){
sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
if( pParse->nErr ) rc = pParse->rc;
}
if( rc==SQLITE_OK && pStep->zTarget ){
SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
if( pSrc ){
Select *pSel = sqlite3SelectNew(
pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
);
if( pSel==0 ){
pStep->pExprList = 0;
pSrc = 0;
rc = SQLITE_NOMEM;
}else{
renameSetENames(pStep->pExprList, ENAME_SPAN);
sqlite3SelectPrep(pParse, pSel, 0);
renameSetENames(pStep->pExprList, ENAME_NAME);
rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList );
assert( pSrc==pSel->pSrc );
if( pStep->pExprList ) pSel->pEList = 0;
pSel->pSrc = 0;
sqlite3SelectDelete(db, pSel);
}
if( pStep->pFrom ){
int i;
for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
SrcItem *p = &pStep->pFrom->a[i];
if( p->pSelect ){
sqlite3SelectPrep(pParse, p->pSelect, 0);
}
}
}
if( db->mallocFailed ){
rc = SQLITE_NOMEM;
}
sNC.pSrcList = pSrc;
if( rc==SQLITE_OK && pStep->pWhere ){
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
}
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
}
assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
if( pStep->pUpsert && rc==SQLITE_OK ){
Upsert *pUpsert = pStep->pUpsert;
pUpsert->pUpsertSrc = pSrc;
sNC.uNC.pUpsert = pUpsert;
sNC.ncFlags = NC_UUpsert;
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
if( rc==SQLITE_OK ){
ExprList *pUpsertSet = pUpsert->pUpsertSet;
rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
}
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
}
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
}
sNC.ncFlags = 0;
}
sNC.pSrcList = 0;
sqlite3SrcListDelete(db, pSrc);
}else{
rc = SQLITE_NOMEM;
}
}
}
return rc;
}
static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
TriggerStep *pStep;
sqlite3WalkExpr(pWalker, pTrigger->pWhen);
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
sqlite3WalkSelect(pWalker, pStep->pSelect);
sqlite3WalkExpr(pWalker, pStep->pWhere);
sqlite3WalkExprList(pWalker, pStep->pExprList);
if( pStep->pUpsert ){
Upsert *pUpsert = pStep->pUpsert;
sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget);
sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet);
sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
}
if( pStep->pFrom ){
int i;
for(i=0; i<pStep->pFrom->nSrc; i++){
sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].pSelect);
}
}
}
}
static void renameParseCleanup(Parse *pParse){
sqlite3 *db = pParse->db;
Index *pIdx;
if( pParse->pVdbe ){
sqlite3VdbeFinalize(pParse->pVdbe);
}
sqlite3DeleteTable(db, pParse->pNewTable);
while( (pIdx = pParse->pNewIndex)!=0 ){
pParse->pNewIndex = pIdx->pNext;
sqlite3FreeIndex(db, pIdx);
}
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
sqlite3DbFree(db, pParse->zErrMsg);
renameTokenFree(db, pParse->pRename);
sqlite3ParseObjectReset(pParse);
}
static void renameColumnFunc(
sqlite3_context *context,
int NotUsed,
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
RenameCtx sCtx;
const char *zSql = (const char*)sqlite3_value_text(argv[0]);
const char *zDb = (const char*)sqlite3_value_text(argv[3]);
const char *zTable = (const char*)sqlite3_value_text(argv[4]);
int iCol = sqlite3_value_int(argv[5]);
const char *zNew = (const char*)sqlite3_value_text(argv[6]);
int bQuote = sqlite3_value_int(argv[7]);
int bTemp = sqlite3_value_int(argv[8]);
const char *zOld;
int rc;
Parse sParse;
Walker sWalker;
Index *pIdx;
int i;
Table *pTab;
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
#endif
UNUSED_PARAMETER(NotUsed);
if( zSql==0 ) return;
if( zTable==0 ) return;
if( zNew==0 ) return;
if( iCol<0 ) return;
sqlite3BtreeEnterAll(db);
pTab = sqlite3FindTable(db, zTable, zDb);
if( pTab==0 || iCol>=pTab->nCol ){
sqlite3BtreeLeaveAll(db);
return;
}
zOld = pTab->aCol[iCol].zCnName;
memset(&sCtx, 0, sizeof(sCtx));
sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = 0;
#endif
rc = renameParseSql(&sParse, zDb, db, zSql, bTemp);
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 ){
if( IsView(sParse.pNewTable) ){
Select *pSelect = sParse.pNewTable->u.view.pSelect;
pSelect->selFlags &= ~SF_View;
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
if( rc==SQLITE_OK ){
sqlite3WalkSelect(&sWalker, pSelect);
}
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
}else if( IsOrdinaryTable(sParse.pNewTable) ){
int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
FKey *pFKey;
sCtx.pTab = sParse.pNewTable;
if( bFKOnly==0 ){
if( iCol<sParse.pNewTable->nCol ){
renameTokenFind(
&sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName
);
}
if( sCtx.iCol<0 ){
renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey);
}
sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
}
for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
}
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
for(i=0; i<sParse.pNewTable->nCol; i++){
Expr *pExpr = sqlite3ColumnExpr(sParse.pNewTable,
&sParse.pNewTable->aCol[i]);
sqlite3WalkExpr(&sWalker, pExpr);
}
#endif
}
assert( IsOrdinaryTable(sParse.pNewTable) );
for(pFKey=sParse.pNewTable->u.tab.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 if( sParse.pNewIndex ){
sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
}else{
TriggerStep *pStep;
rc = renameResolveTrigger(&sParse);
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
if( pStep->zTarget ){
Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
if( pTarget==pTab ){
if( pStep->pUpsert ){
ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
}
renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
}
}
}
if( sParse.pTriggerTab==pTab ){
renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
}
renameWalkTrigger(&sWalker, sParse.pNewTrigger);
}
assert( rc==SQLITE_OK );
rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote);
renameColumnFunc_done:
if( rc!=SQLITE_OK ){
if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){
sqlite3_result_value(context, argv[0]);
}else if( sParse.zErrMsg ){
renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
}
renameParseCleanup(&sParse);
renameTokenFree(db, sCtx.pList);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif
sqlite3BtreeLeaveAll(db);
}
static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
RenameCtx *p = pWalker->u.pRename;
if( pExpr->op==TK_COLUMN
&& ALWAYS(ExprUseYTab(pExpr))
&& p->pTab==pExpr->y.pTab
){
renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab);
}
return WRC_Continue;
}
static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
int i;
RenameCtx *p = pWalker->u.pRename;
SrcList *pSrc = pSelect->pSrc;
if( pSelect->selFlags & (SF_View|SF_CopyCte) ){
testcase( pSelect->selFlags & SF_View );
testcase( pSelect->selFlags & SF_CopyCte );
return WRC_Prune;
}
if( NEVER(pSrc==0) ){
assert( pWalker->pParse->db->mallocFailed );
return WRC_Abort;
}
for(i=0; i<pSrc->nSrc; i++){
SrcItem *pItem = &pSrc->a[i];
if( pItem->pTab==p->pTab ){
renameTokenFind(pWalker->pParse, p, pItem->zName);
}
}
renameWalkWith(pWalker, pSelect);
return WRC_Continue;
}
static void renameTableFunc(
sqlite3_context *context,
int NotUsed,
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
const char *zDb = (const char*)sqlite3_value_text(argv[0]);
const char *zInput = (const char*)sqlite3_value_text(argv[3]);
const char *zOld = (const char*)sqlite3_value_text(argv[4]);
const char *zNew = (const char*)sqlite3_value_text(argv[5]);
int bTemp = sqlite3_value_int(argv[6]);
UNUSED_PARAMETER(NotUsed);
if( zInput && zOld && zNew ){
Parse sParse;
int rc;
int bQuote = 1;
RenameCtx sCtx;
Walker sWalker;
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
db->xAuth = 0;
#endif
sqlite3BtreeEnterAll(db);
memset(&sCtx, 0, sizeof(RenameCtx));
sCtx.pTab = sqlite3FindTable(db, zOld, zDb);
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = &sParse;
sWalker.xExprCallback = renameTableExprCb;
sWalker.xSelectCallback = renameTableSelectCb;
sWalker.u.pRename = &sCtx;
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
if( rc==SQLITE_OK ){
int isLegacy = (db->flags & SQLITE_LegacyAlter);
if( sParse.pNewTable ){
Table *pTab = sParse.pNewTable;
if( IsView(pTab) ){
if( isLegacy==0 ){
Select *pSelect = pTab->u.view.pSelect;
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = &sParse;
assert( pSelect->selFlags & SF_View );
pSelect->selFlags &= ~SF_View;
sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
if( sParse.nErr ){
rc = sParse.rc;
}else{
sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect);
}
}
}else{
#ifndef SQLITE_OMIT_FOREIGN_KEY
if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys))
&& !IsVirtual(pTab)
){
FKey *pFKey;
assert( IsOrdinaryTable(pTab) );
for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
}
}
}
#endif
if( sqlite3_stricmp(zOld, pTab->zName)==0 ){
sCtx.pTab = pTab;
if( isLegacy==0 ){
sqlite3WalkExprList(&sWalker, pTab->pCheck);
}
renameTokenFind(&sParse, &sCtx, pTab->zName);
}
}
}
else if( sParse.pNewIndex ){
renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName);
if( isLegacy==0 ){
sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
}
}
#ifndef SQLITE_OMIT_TRIGGER
else{
Trigger *pTrigger = sParse.pNewTrigger;
TriggerStep *pStep;
if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld)
&& sCtx.pTab->pSchema==pTrigger->pTabSchema
){
renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table);
}
if( isLegacy==0 ){
rc = renameResolveTrigger(&sParse);
if( rc==SQLITE_OK ){
renameWalkTrigger(&sWalker, pTrigger);
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
renameTokenFind(&sParse, &sCtx, pStep->zTarget);
}
if( pStep->pFrom ){
int i;
for(i=0; i<pStep->pFrom->nSrc; i++){
SrcItem *pItem = &pStep->pFrom->a[i];
if( 0==sqlite3_stricmp(pItem->zName, zOld) ){
renameTokenFind(&sParse, &sCtx, pItem->zName);
}
}
}
}
}
}
}
#endif
}
if( rc==SQLITE_OK ){
rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote);
}
if( rc!=SQLITE_OK ){
if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){
sqlite3_result_value(context, argv[3]);
}else if( sParse.zErrMsg ){
renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
}
renameParseCleanup(&sParse);
renameTokenFree(db, sCtx.pList);
sqlite3BtreeLeaveAll(db);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif
}
return;
}
static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){
renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr);
}
return WRC_Continue;
}
static void renameQuotefixFunc(
sqlite3_context *context,
int NotUsed,
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
char const *zDb = (const char*)sqlite3_value_text(argv[0]);
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
db->xAuth = 0;
#endif
sqlite3BtreeEnterAll(db);
UNUSED_PARAMETER(NotUsed);
if( zDb && zInput ){
int rc;
Parse sParse;
rc = renameParseSql(&sParse, zDb, db, zInput, 0);
if( rc==SQLITE_OK ){
RenameCtx sCtx;
Walker sWalker;
memset(&sCtx, 0, sizeof(RenameCtx));
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = &sParse;
sWalker.xExprCallback = renameQuotefixExprCb;
sWalker.xSelectCallback = renameColumnSelectCb;
sWalker.u.pRename = &sCtx;
if( sParse.pNewTable ){
if( IsView(sParse.pNewTable) ){
Select *pSelect = sParse.pNewTable->u.view.pSelect;
pSelect->selFlags &= ~SF_View;
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
if( rc==SQLITE_OK ){
sqlite3WalkSelect(&sWalker, pSelect);
}
}else{
int i;
sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
for(i=0; i<sParse.pNewTable->nCol; i++){
sqlite3WalkExpr(&sWalker,
sqlite3ColumnExpr(sParse.pNewTable,
&sParse.pNewTable->aCol[i]));
}
#endif
}
}else if( sParse.pNewIndex ){
sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
}else{
#ifndef SQLITE_OMIT_TRIGGER
rc = renameResolveTrigger(&sParse);
if( rc==SQLITE_OK ){
renameWalkTrigger(&sWalker, sParse.pNewTrigger);
}
#endif
}
if( rc==SQLITE_OK ){
rc = renameEditSql(context, &sCtx, zInput, 0, 0);
}
renameTokenFree(db, sCtx.pList);
}
if( rc!=SQLITE_OK ){
if( sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){
sqlite3_result_value(context, argv[1]);
}else{
sqlite3_result_error_code(context, rc);
}
}
renameParseCleanup(&sParse);
}
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif
sqlite3BtreeLeaveAll(db);
}
static void renameTableTest(
sqlite3_context *context,
int NotUsed,
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
char const *zDb = (const char*)sqlite3_value_text(argv[0]);
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
int bTemp = sqlite3_value_int(argv[4]);
int isLegacy = (db->flags & SQLITE_LegacyAlter);
char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
int bNoDQS = sqlite3_value_int(argv[6]);
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
db->xAuth = 0;
#endif
UNUSED_PARAMETER(NotUsed);
if( zDb && zInput ){
int rc;
Parse sParse;
int flags = db->flags;
if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
if( rc==SQLITE_OK ){
if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = &sParse;
sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC);
if( sParse.nErr ) rc = sParse.rc;
}
else if( sParse.pNewTrigger ){
if( isLegacy==0 ){
rc = renameResolveTrigger(&sParse);
}
if( rc==SQLITE_OK ){
int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema);
int i2 = sqlite3FindDbName(db, zDb);
if( i1==i2 ){
sqlite3_result_int(context, 1);
}
}
}
}
if( rc!=SQLITE_OK && zWhen && !sqlite3WritableSchema(db) ){
renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse);
}
renameParseCleanup(&sParse);
}
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif
}
static void dropColumnFunc(
sqlite3_context *context,
int NotUsed,
sqlite3_value **argv
){
sqlite3 *db = sqlite3_context_db_handle(context);
int iSchema = sqlite3_value_int(argv[0]);
const char *zSql = (const char*)sqlite3_value_text(argv[1]);
int iCol = sqlite3_value_int(argv[2]);
const char *zDb = db->aDb[iSchema].zDbSName;
int rc;
Parse sParse;
RenameToken *pCol;
Table *pTab;
const char *zEnd;
char *zNew = 0;
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
db->xAuth = 0;
#endif
UNUSED_PARAMETER(NotUsed);
rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
if( rc!=SQLITE_OK ) goto drop_column_done;
pTab = sParse.pNewTable;
if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
rc = SQLITE_CORRUPT_BKPT;
goto drop_column_done;
}
pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName);
if( iCol<pTab->nCol-1 ){
RenameToken *pEnd;
pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName);
zEnd = (const char*)pEnd->t.z;
}else{
assert( IsOrdinaryTable(pTab) );
zEnd = (const char*)&zSql[pTab->u.tab.addColOffset];
while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
}
zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd);
sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT);
sqlite3_free(zNew);
drop_column_done:
renameParseCleanup(&sParse);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif
if( rc!=SQLITE_OK ){
sqlite3_result_error_code(context, rc);
}
}
void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){
sqlite3 *db = pParse->db;
Table *pTab;
int iDb;
const char *zDb;
char *zCol = 0;
int iCol;
assert( pParse->pNewTable==0 );
assert( sqlite3BtreeHoldsAllMutexes(db) );
if( NEVER(db->mallocFailed) ) goto exit_drop_column;
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_drop_column;
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column;
if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column;
zCol = sqlite3NameFromToken(db, pName);
if( zCol==0 ){
assert( db->mallocFailed );
goto exit_drop_column;
}
iCol = sqlite3ColumnIndex(pTab, zCol);
if( iCol<0 ){
sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName);
goto exit_drop_column;
}
if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){
sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"",
(pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE",
zCol
);
goto exit_drop_column;
}
if( pTab->nCol<=1 ){
sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol);
goto exit_drop_column;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 );
zDb = db->aDb[iDb].zDbSName;
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){
goto exit_drop_column;
}
#endif
renameTestSchema(pParse, zDb, iDb==1, "", 0);
renameFixQuotes(pParse, zDb, iDb==1);
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_drop_column(%d, sql, %d) "
"WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
, zDb, iDb, iCol, pTab->zName
);
renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1);
if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
int i;
int addr;
int reg;
int regRec;
Index *pPk = 0;
int nField = 0;
int iCur;
Vdbe *v = sqlite3GetVdbe(pParse);
iCur = pParse->nTab++;
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
reg = ++pParse->nMem;
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg);
pParse->nMem += pTab->nCol;
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
pParse->nMem += pPk->nColumn;
for(i=0; i<pPk->nKeyCol; i++){
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1);
}
nField = pPk->nKeyCol;
}
regRec = ++pParse->nMem;
for(i=0; i<pTab->nCol; i++){
if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
int regOut;
if( pPk ){
int iPos = sqlite3TableColumnToIndex(pPk, i);
int iColPos = sqlite3TableColumnToIndex(pPk, iCol);
if( iPos<pPk->nKeyCol ) continue;
regOut = reg+1+iPos-(iPos>iColPos);
}else{
regOut = reg+1+nField;
}
if( i==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regOut);
}else{
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
}
nField++;
}
}
if( nField==0 ){
pParse->nMem++;
sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1);
nField = 1;
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec);
if( pPk ){
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol);
}else{
sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg);
}
sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION);
sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr);
}
exit_drop_column:
sqlite3DbFree(db, zCol);
sqlite3SrcListDelete(db, pSrc);
}
void sqlite3AlterFunctions(void){
static FuncDef aAlterTableFuncs[] = {
INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest),
INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
#endif