| /* |
| ** 2005 February 15 |
| ** |
| ** The author disclaims copyright to this source code. In place of |
| ** a legal notice, here is a blessing: |
| ** |
| ** May you do good and not evil. |
| ** May you find forgiveness for yourself and forgive others. |
| ** May you share freely, never taking more than you give. |
| ** |
| ************************************************************************* |
| ** This file contains C code routines that used to generate VDBE code |
| ** that implements the ALTER TABLE command. |
| */ |
| #include "sqliteInt.h" |
| |
| /* |
| ** The code in this file only exists if we are not omitting the |
| ** ALTER TABLE logic from the build. |
| */ |
| #ifndef SQLITE_OMIT_ALTERTABLE |
| |
| /* |
| ** Parameter zName is the name of a table that is about to be altered |
| ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). |
| ** If the table is a system table, this function leaves an error message |
| ** in pParse->zErr (system tables may not be altered) and returns non-zero. |
| ** |
| ** Or, if zName is not a system table, zero is returned. |
| */ |
| static int isAlterableTable(Parse *pParse, Table *pTab){ |
| if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) |
| #ifndef SQLITE_OMIT_VIRTUALTABLE |
| || ( (pTab->tabFlags & TF_Shadow) |
| && (pParse->db->flags & SQLITE_Defensive) |
| && pParse->db->nVdbeExec==0 |
| ) |
| #endif |
| ){ |
| sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* |
| ** Generate code to verify that the schemas of database zDb and, if |
| ** bTemp is not true, database "temp", can still be parsed. This is |
| ** called at the end of the generation of an ALTER TABLE ... RENAME ... |
| ** statement to ensure that the operation has not rendered any schema |
| ** objects unusable. |
| */ |
| static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ |
| sqlite3NestedParse(pParse, |
| "SELECT 1 " |
| "FROM \"%w\".%s " |
| "WHERE name NOT LIKE 'sqlite_%%'" |
| " AND sql NOT LIKE 'create virtual%%'" |
| " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ", |
| zDb, MASTER_NAME, |
| zDb, bTemp |
| ); |
| |
| if( bTemp==0 ){ |
| sqlite3NestedParse(pParse, |
| "SELECT 1 " |
| "FROM temp.%s " |
| "WHERE name NOT LIKE 'sqlite_%%'" |
| " AND sql NOT LIKE 'create virtual%%'" |
| " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ", |
| MASTER_NAME, zDb |
| ); |
| } |
| } |
| |
| /* |
| ** Generate code to reload the schema for database iDb. And, if iDb!=1, for |
| ** the temp database as well. |
| */ |
| static void renameReloadSchema(Parse *pParse, int iDb){ |
| Vdbe *v = pParse->pVdbe; |
| if( v ){ |
| sqlite3ChangeCookie(pParse, iDb); |
| sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); |
| if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); |
| } |
| } |
| |
| /* |
| ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" |
| ** command. |
| */ |
| void sqlite3AlterRenameTable( |
| Parse *pParse, /* Parser context. */ |
| SrcList *pSrc, /* The table to rename. */ |
| Token *pName /* The new table name. */ |
| ){ |
| int iDb; /* Database that contains the table */ |
| char *zDb; /* Name of database iDb */ |
| Table *pTab; /* Table being renamed */ |
| char *zName = 0; /* NULL-terminated version of pName */ |
| sqlite3 *db = pParse->db; /* Database connection */ |
| int nTabName; /* Number of UTF-8 characters in zTabName */ |
| const char *zTabName; /* Original name of the table */ |
| Vdbe *v; |
| VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ |
| u32 savedDbFlags; /* Saved value of db->mDbFlags */ |
| |
| savedDbFlags = db->mDbFlags; |
| 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; |
| db->mDbFlags |= DBFLAG_PreferBuiltin; |
| |
| /* Get a NULL terminated version of the new table name. */ |
| zName = sqlite3NameFromToken(db, pName); |
| if( !zName ) goto exit_rename_table; |
| |
| /* Check that a table or index named 'zName' does not already exist |
| ** in database iDb. If so, this is an error. |
| */ |
| if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ |
| sqlite3ErrorMsg(pParse, |
| "there is already another table or index with this name: %s", zName); |
| goto exit_rename_table; |
| } |
| |
| /* Make sure it is not a system table being altered, or a reserved name |
| ** that the table is being renamed to. |
| */ |
| if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ |
| goto exit_rename_table; |
| } |
| if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto |
| exit_rename_table; |
| } |
| |
| #ifndef SQLITE_OMIT_VIEW |
| if( pTab->pSelect ){ |
| sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); |
| goto exit_rename_table; |
| } |
| #endif |
| |
| #ifndef SQLITE_OMIT_AUTHORIZATION |
| /* Invoke the authorization callback. */ |
| 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 |
| |
| /* Begin a transaction for database iDb. Then modify the schema cookie |
| ** (since the ALTER TABLE modifies the schema). Call sqlite3MayAbort(), |
| ** as the scalar functions (e.g. sqlite_rename_table()) invoked by the |
| ** nested SQL may raise an exception. */ |
| v = sqlite3GetVdbe(pParse); |
| if( v==0 ){ |
| goto exit_rename_table; |
| } |
| sqlite3MayAbort(pParse); |
| |
| /* figure out how many UTF-8 characters are in zName */ |
| zTabName = pTab->zName; |
| nTabName = sqlite3Utf8CharLen(zTabName, -1); |
| |
| /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in |
| ** the schema to use the new table name. */ |
| sqlite3NestedParse(pParse, |
| "UPDATE \"%w\".%s 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 'sqlite_%%'" |
| , zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName |
| ); |
| |
| /* Update the tbl_name and name columns of the sqlite_master table |
| ** as required. */ |
| sqlite3NestedParse(pParse, |
| "UPDATE %Q.%s SET " |
| "tbl_name = %Q, " |
| "name = CASE " |
| "WHEN type='table' THEN %Q " |
| "WHEN name LIKE 'sqlite_autoindex%%' 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, MASTER_NAME, |
| zName, zName, zName, |
| nTabName, zTabName |
| ); |
| |
| #ifndef SQLITE_OMIT_AUTOINCREMENT |
| /* If the sqlite_sequence table exists in this database, then update |
| ** it with the new table name. |
| */ |
| if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){ |
| sqlite3NestedParse(pParse, |
| "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", |
| zDb, zName, pTab->zName); |
| } |
| #endif |
| |
| /* If the table being renamed is not itself part of the temp database, |
| ** edit view and trigger definitions within the temp database |
| ** as required. */ |
| if( iDb!=1 ){ |
| sqlite3NestedParse(pParse, |
| "UPDATE sqlite_temp_master 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) " |
| "THEN %Q ELSE tbl_name END " |
| "WHERE type IN ('view', 'trigger')" |
| , zDb, zTabName, zName, zTabName, zDb, zName); |
| } |
| |
| /* If this is a virtual table, invoke the xRename() function if |
| ** one is defined. The xRename() callback will modify the names |
| ** of any resources used by the v-table implementation (including other |
| ** SQLite tables) that are identified by the name of the virtual table. |
| */ |
| #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); |
| renameTestSchema(pParse, zDb, iDb==1); |
| |
| exit_rename_table: |
| sqlite3SrcListDelete(db, pSrc); |
| sqlite3DbFree(db, zName); |
| db->mDbFlags = savedDbFlags; |
| } |
| |
| /* |
| ** This function is called after an "ALTER TABLE ... ADD" statement |
| ** has been parsed. Argument pColDef contains the text of the new |
| ** column definition. |
| ** |
| ** The Table structure pParse->pNewTable was extended to include |
| ** the new column during parsing. |
| */ |
| void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ |
| Table *pNew; /* Copy of pParse->pNewTable */ |
| Table *pTab; /* Table being altered */ |
| int iDb; /* Database number */ |
| const char *zDb; /* Database name */ |
| const char *zTab; /* Table name */ |
| char *zCol; /* Null-terminated column definition */ |
| Column *pCol; /* The new column */ |
| Expr *pDflt; /* Default value for the new column */ |
| sqlite3 *db; /* The database connection; */ |
| Vdbe *v; /* The prepared statement under construction */ |
| int r1; /* Temporary registers */ |
| |
| db = pParse->db; |
| if( pParse->nErr || db->mallocFailed ) return; |
| pNew = pParse->pNewTable; |
| assert( pNew ); |
| |
| assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| iDb = sqlite3SchemaToIndex(db, pNew->pSchema); |
| zDb = db->aDb[iDb].zDbSName; |
| zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ |
| pCol = &pNew->aCol[pNew->nCol-1]; |
| pDflt = pCol->pDflt; |
| pTab = sqlite3FindTable(db, zTab, zDb); |
| assert( pTab ); |
| |
| #ifndef SQLITE_OMIT_AUTHORIZATION |
| /* Invoke the authorization callback. */ |
| if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| return; |
| } |
| #endif |
| |
| /* If the default value for the new column was specified with a |
| ** literal NULL, then set pDflt to 0. This simplifies checking |
| ** for an SQL NULL default below. |
| */ |
| assert( pDflt==0 || pDflt->op==TK_SPAN ); |
| if( pDflt && pDflt->pLeft->op==TK_NULL ){ |
| pDflt = 0; |
| } |
| |
| /* Check that the new column is not specified as PRIMARY KEY or UNIQUE. |
| ** If there is a NOT NULL constraint, then the default value for the |
| ** column must not be NULL. |
| */ |
| 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( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ |
| sqlite3ErrorMsg(pParse, |
| "Cannot add a REFERENCES column with non-NULL default value"); |
| return; |
| } |
| if( pCol->notNull && !pDflt ){ |
| sqlite3ErrorMsg(pParse, |
| "Cannot add a NOT NULL column with default value NULL"); |
| return; |
| } |
| |
| /* Ensure the default expression is something that sqlite3ValueFromExpr() |
| ** can handle (i.e. not CURRENT_TIME etc.) |
| */ |
| 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 ){ |
| sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default"); |
| return; |
| } |
| sqlite3ValueFree(pVal); |
| } |
| |
| /* Modify the CREATE TABLE statement. */ |
| zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); |
| if( zCol ){ |
| char *zEnd = &zCol[pColDef->n-1]; |
| u32 savedDbFlags = db->mDbFlags; |
| while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ |
| *zEnd-- = '\0'; |
| } |
| db->mDbFlags |= DBFLAG_PreferBuiltin; |
| sqlite3NestedParse(pParse, |
| "UPDATE \"%w\".%s SET " |
| "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " |
| "WHERE type = 'table' AND name = %Q", |
| zDb, MASTER_NAME, pNew->addColOffset, zCol, pNew->addColOffset+1, |
| zTab |
| ); |
| sqlite3DbFree(db, zCol); |
| db->mDbFlags = savedDbFlags; |
| } |
| |
| /* Make sure the schema version is at least 3. But do not upgrade |
| ** from less than 3 to 4, as that will corrupt any preexisting DESC |
| ** index. |
| */ |
| 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); |
| } |
| |
| /* Reload the table definition */ |
| renameReloadSchema(pParse, iDb); |
| } |
| |
| /* |
| ** This function is called by the parser after the table-name in |
| ** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument |
| ** pSrc is the full-name of the table being altered. |
| ** |
| ** This routine makes a (partial) copy of the Table structure |
| ** for the table being altered and sets Parse.pNewTable to point |
| ** to it. Routines called by the parser as the column definition |
| ** is parsed (i.e. sqlite3AddColumn()) add the new Column data to |
| ** the copy. The copy of the Table structure is deleted by tokenize.c |
| ** after parsing is finished. |
| ** |
| ** Routine sqlite3AlterFinishAddColumn() will be called to complete |
| ** coding the "ALTER TABLE ... ADD" statement. |
| */ |
| void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ |
| Table *pNew; |
| Table *pTab; |
| int iDb; |
| int i; |
| int nAlloc; |
| sqlite3 *db = pParse->db; |
| |
| /* Look up the table being altered. */ |
| 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 |
| |
| /* Make sure this is not an attempt to ALTER a view. */ |
| if( pTab->pSelect ){ |
| 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; |
| } |
| |
| assert( pTab->addColOffset>0 ); |
| iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| |
| /* Put a copy of the Table struct in Parse.pNewTable for the |
| ** sqlite3AddColumn() function and friends to modify. But modify |
| ** the name by adding an "sqlite_altertab_" prefix. By adding this |
| ** prefix, we insure that the name will not collide with an existing |
| ** table because user table are not allowed to have the "sqlite_" |
| ** prefix on their name. |
| */ |
| 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->zName = sqlite3DbStrDup(db, pCol->zName); |
| pCol->zColl = 0; |
| pCol->pDflt = 0; |
| } |
| pNew->pSchema = db->aDb[iDb].pSchema; |
| pNew->addColOffset = pTab->addColOffset; |
| pNew->nTabRef = 1; |
| |
| exit_begin_add_column: |
| sqlite3SrcListDelete(db, pSrc); |
| return; |
| } |
| |
| /* |
| ** Parameter pTab is the subject of an ALTER TABLE ... RENAME COLUMN |
| ** command. This function checks if the table is a view or virtual |
| ** table (columns of views or virtual tables may not be renamed). If so, |
| ** it loads an error message into pParse and returns non-zero. |
| ** |
| ** Or, if pTab is not a view or virtual table, zero is returned. |
| */ |
| #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) |
| static int isRealTable(Parse *pParse, Table *pTab){ |
| const char *zType = 0; |
| #ifndef SQLITE_OMIT_VIEW |
| if( pTab->pSelect ){ |
| zType = "view"; |
| } |
| #endif |
| #ifndef SQLITE_OMIT_VIRTUALTABLE |
| if( IsVirtual(pTab) ){ |
| zType = "virtual table"; |
| } |
| #endif |
| if( zType ){ |
| sqlite3ErrorMsg( |
| pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName |
| ); |
| return 1; |
| } |
| return 0; |
| } |
| #else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */ |
| # define isRealTable(x,y) (0) |
| #endif |
| |
| /* |
| ** Handles the following parser reduction: |
| ** |
| ** cmd ::= ALTER TABLE pSrc RENAME COLUMN pOld TO pNew |
| */ |
| void sqlite3AlterRenameColumn( |
| Parse *pParse, /* Parsing context */ |
| SrcList *pSrc, /* Table being altered. pSrc->nSrc==1 */ |
| Token *pOld, /* Name of column being changed */ |
| Token *pNew /* New column name */ |
| ){ |
| sqlite3 *db = pParse->db; /* Database connection */ |
| Table *pTab; /* Table being updated */ |
| int iCol; /* Index of column being renamed */ |
| char *zOld = 0; /* Old column name */ |
| char *zNew = 0; /* New column name */ |
| const char *zDb; /* Name of schema containing the table */ |
| int iSchema; /* Index of the schema */ |
| int bQuote; /* True to quote the new name */ |
| |
| /* Locate the table to be altered */ |
| pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); |
| if( !pTab ) goto exit_rename_column; |
| |
| /* Cannot alter a system table */ |
| if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; |
| if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; |
| |
| /* Which schema holds the table to be altered */ |
| iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); |
| assert( iSchema>=0 ); |
| zDb = db->aDb[iSchema].zDbSName; |
| |
| #ifndef SQLITE_OMIT_AUTHORIZATION |
| /* Invoke the authorization callback. */ |
| if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| goto exit_rename_column; |
| } |
| #endif |
| |
| /* Make sure the old name really is a column name in the table to be |
| ** altered. Set iCol to be the index of the column being renamed */ |
| zOld = sqlite3NameFromToken(db, pOld); |
| if( !zOld ) goto exit_rename_column; |
| for(iCol=0; iCol<pTab->nCol; iCol++){ |
| if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break; |
| } |
| if( iCol==pTab->nCol ){ |
| sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); |
| goto exit_rename_column; |
| } |
| |
| /* Do the rename operation using a recursive UPDATE statement that |
| ** uses the sqlite_rename_column() SQL function to compute the new |
| ** CREATE statement text for the sqlite_master table. |
| */ |
| 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\".%s SET " |
| "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %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, iSchema==1, |
| pTab->zName |
| ); |
| |
| sqlite3NestedParse(pParse, |
| "UPDATE temp.%s SET " |
| "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) " |
| "WHERE type IN ('trigger', 'view')", |
| MASTER_NAME, |
| zDb, pTab->zName, iCol, zNew, bQuote |
| ); |
| |
| /* Drop and reload the database schema. */ |
| renameReloadSchema(pParse, iSchema); |
| renameTestSchema(pParse, zDb, iSchema==1); |
| |
| exit_rename_column: |
| sqlite3SrcListDelete(db, pSrc); |
| sqlite3DbFree(db, zOld); |
| sqlite3DbFree(db, zNew); |
| return; |
| } |
| |
| /* |
| ** Each RenameToken object maps an element of the parse tree into |
| ** the token that generated that element. The parse tree element |
| ** might be one of: |
| ** |
| ** * A pointer to an Expr that represents an ID |
| ** * The name of a table column in Column.zName |
| ** |
| ** A list of RenameToken objects can be constructed during parsing. |
| ** Each new object is created by sqlite3RenameTokenMap(). |
| ** As the parse tree is transformed, the sqlite3RenameTokenRemap() |
| ** routine is used to keep the mapping current. |
| ** |
| ** After the parse finishes, renameTokenFind() routine can be used |
| ** to look up the actual token value that created some element in |
| ** the parse tree. |
| */ |
| struct RenameToken { |
| void *p; /* Parse tree element created by token t */ |
| Token t; /* The token that created parse tree element p */ |
| RenameToken *pNext; /* Next is a list of all RenameToken objects */ |
| }; |
| |
| /* |
| ** 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 */ |
| }; |
| |
| #ifdef SQLITE_DEBUG |
| /* |
| ** This function is only for debugging. It performs two tasks: |
| ** |
| ** 1. Checks that pointer pPtr does not already appear in the |
| ** rename-token list. |
| ** |
| ** 2. Dereferences each pointer in the rename-token list. |
| ** |
| ** The second is most effective when debugging under valgrind or |
| ** address-sanitizer or similar. If any of these pointers no longer |
| ** point to valid objects, an exception is raised by the memory-checking |
| ** tool. |
| ** |
| ** The point of this is to prevent comparisons of invalid pointer values. |
| ** Even though this always seems to work, it is undefined according to the |
| ** C standard. Example of undefined comparison: |
| ** |
| ** sqlite3_free(x); |
| ** if( x==y ) ... |
| ** |
| ** Technically, as x no longer points into a valid object or to the byte |
| ** following a valid object, it may not be used in comparison operations. |
| */ |
| static void renameTokenCheckAll(Parse *pParse, void *pPtr){ |
| if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ |
| RenameToken *p; |
| u8 i = 0; |
| for(p=pParse->pRename; p; p=p->pNext){ |
| if( p->p ){ |
| assert( p->p!=pPtr ); |
| i += *(u8*)(p->p); |
| } |
| } |
| } |
| } |
| #else |
| # define renameTokenCheckAll(x,y) |
| #endif |
| |
| /* |
| ** Remember that the parser tree element pPtr was created using |
| ** the token pToken. |
| ** |
| ** In other words, construct a new RenameToken object and add it |
| ** to the list of RenameToken objects currently being built up |
| ** in pParse->pRename. |
| ** |
| ** The pPtr argument is returned so that this routine can be used |
| ** with tail recursion in tokenExpr() routine, for a small performance |
| ** improvement. |
| */ |
| void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ |
| RenameToken *pNew; |
| assert( pPtr || pParse->db->mallocFailed ); |
| renameTokenCheckAll(pParse, pPtr); |
| pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); |
| if( pNew ){ |
| pNew->p = pPtr; |
| pNew->t = *pToken; |
| pNew->pNext = pParse->pRename; |
| pParse->pRename = pNew; |
| } |
| |
| return pPtr; |
| } |
| |
| /* |
| ** It is assumed that there is already a RenameToken object associated |
| ** with parse tree element pFrom. This function remaps the associated token |
| ** to parse tree element pTo. |
| */ |
| void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ |
| RenameToken *p; |
| renameTokenCheckAll(pParse, pTo); |
| for(p=pParse->pRename; p; p=p->pNext){ |
| if( p->p==pFrom ){ |
| p->p = pTo; |
| break; |
| } |
| } |
| } |
| |
| /* |
| ** Walker callback used by sqlite3RenameExprUnmap(). |
| */ |
| static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ |
| Parse *pParse = pWalker->pParse; |
| sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); |
| return WRC_Continue; |
| } |
| |
| /* |
| ** Remove all nodes that are part of expression pExpr from the rename list. |
| */ |
| void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){ |
| Walker sWalker; |
| memset(&sWalker, 0, sizeof(Walker)); |
| sWalker.pParse = pParse; |
| sWalker.xExprCallback = renameUnmapExprCb; |
| sqlite3WalkExpr(&sWalker, pExpr); |
| } |
| |
| /* |
| ** Remove all nodes that are part of expression-list pEList from the |
| ** rename list. |
| */ |
| 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++){ |
| sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zName); |
| } |
| } |
| } |
| |
| /* |
| ** Free the list of RenameToken objects given in the second argument |
| */ |
| static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ |
| RenameToken *pNext; |
| RenameToken *p; |
| for(p=pToken; p; p=pNext){ |
| pNext = p->pNext; |
| sqlite3DbFree(db, p); |
| } |
| } |
| |
| /* |
| ** Search the Parse object passed as the first argument for a RenameToken |
| ** object associated with parse tree element pPtr. If found, remove it |
| ** from the Parse object and add it to the list maintained by the |
| ** RenameCtx object passed as the second argument. |
| */ |
| static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ |
| RenameToken **pp; |
| assert( pPtr!=0 ); |
| for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ |
| if( (*pp)->p==pPtr ){ |
| RenameToken *pToken = *pp; |
| *pp = pToken->pNext; |
| pToken->pNext = pCtx->pList; |
| pCtx->pList = pToken; |
| pCtx->nList++; |
| break; |
| } |
| } |
| } |
| |
| /* |
| ** Iterate through the Select objects that are part of WITH clauses attached |
| ** to select statement pSelect. |
| */ |
| static void renameWalkWith(Walker *pWalker, Select *pSelect){ |
| if( pSelect->pWith ){ |
| int i; |
| for(i=0; i<pSelect->pWith->nCte; i++){ |
| Select *p = pSelect->pWith->a[i].pSelect; |
| NameContext sNC; |
| memset(&sNC, 0, sizeof(sNC)); |
| sNC.pParse = pWalker->pParse; |
| sqlite3SelectPrep(sNC.pParse, p, &sNC); |
| sqlite3WalkSelect(pWalker, p); |
| } |
| } |
| } |
| |
| /* |
| ** 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){ |
| renameWalkWith(pWalker, p); |
| return WRC_Continue; |
| } |
| |
| /* |
| ** This is a Walker expression callback. |
| ** |
| ** For every TK_COLUMN node in the expression tree, search to see |
| ** if the column being references is the column being renamed by an |
| ** ALTER TABLE statement. If it is, then attach its associated |
| ** RenameToken object to the list of RenameToken objects being |
| ** constructed in RenameCtx object at pWalker->u.pRename. |
| */ |
| static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ |
| RenameCtx *p = pWalker->u.pRename; |
| if( pExpr->op==TK_TRIGGER |
| && pExpr->iColumn==p->iCol |
| && pWalker->pParse->pTriggerTab==p->pTab |
| ){ |
| renameTokenFind(pWalker->pParse, p, (void*)pExpr); |
| }else if( pExpr->op==TK_COLUMN |
| && pExpr->iColumn==p->iCol |
| && p->pTab==pExpr->y.pTab |
| ){ |
| renameTokenFind(pWalker->pParse, p, (void*)pExpr); |
| } |
| return WRC_Continue; |
| } |
| |
| /* |
| ** The RenameCtx contains a list of tokens that reference a column that |
| ** is being renamed by an ALTER TABLE statement. Return the "last" |
| ** RenameToken in the RenameCtx and remove that RenameToken from the |
| ** 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(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; |
| } |
| |
| /* |
| ** 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, |
| int bPost, |
| 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 = sqlite3_mprintf("error in %s %s%s: %s", |
| zT, zN, (bPost ? " after rename" : ""), |
| pParse->zErrMsg |
| ); |
| sqlite3_result_error(pCtx, zErr, -1); |
| sqlite3_free(zErr); |
| } |
| |
| /* |
| ** For each name in the the expression-list pEList (i.e. each |
| ** pEList->a[i].zName) that matches the string in zOld, extract the |
| ** corresponding rename-token from Parse object pParse and add it |
| ** to the RenameCtx pCtx. |
| */ |
| static void renameColumnElistNames( |
| Parse *pParse, |
| RenameCtx *pCtx, |
| ExprList *pEList, |
| const char *zOld |
| ){ |
| if( pEList ){ |
| int i; |
| for(i=0; i<pEList->nExpr; i++){ |
| char *zName = pEList->a[i].zName; |
| if( 0==sqlite3_stricmp(zName, zOld) ){ |
| renameTokenFind(pParse, pCtx, (void*)zName); |
| } |
| } |
| } |
| } |
| |
| /* |
| ** For each name in the the id-list pIdList (i.e. each pIdList->a[i].zName) |
| ** that matches the string in zOld, extract the corresponding rename-token |
| ** from Parse object pParse and add it to the RenameCtx pCtx. |
| */ |
| static void renameColumnIdlistNames( |
| Parse *pParse, |
| RenameCtx *pCtx, |
| IdList *pIdList, |
| const char *zOld |
| ){ |
| if( pIdList ){ |
| int i; |
| for(i=0; i<pIdList->nId; i++){ |
| char *zName = pIdList->a[i].zName; |
| if( 0==sqlite3_stricmp(zName, zOld) ){ |
| renameTokenFind(pParse, pCtx, (void*)zName); |
| } |
| } |
| } |
| } |
| |
| /* |
| ** Parse the SQL statement zSql using Parse object (*p). The Parse object |
| ** is initialized by this function before it is used. |
| */ |
| static int renameParseSql( |
| Parse *p, /* Memory to use for Parse object */ |
| const char *zDb, /* Name of schema SQL belongs to */ |
| int bTable, /* 1 -> RENAME TABLE, 0 -> RENAME COLUMN */ |
| sqlite3 *db, /* Database handle */ |
| const char *zSql, /* SQL to parse */ |
| int bTemp /* True if SQL is from temp schema */ |
| ){ |
| int rc; |
| char *zErr = 0; |
| |
| db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); |
| |
| /* 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(p, 0, sizeof(Parse)); |
| p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN); |
| p->db = db; |
| p->nQueryLoop = 1; |
| rc = sqlite3RunParser(p, zSql, &zErr); |
| assert( p->zErrMsg==0 ); |
| assert( rc!=SQLITE_OK || zErr==0 ); |
| p->zErrMsg = zErr; |
| if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| if( rc==SQLITE_OK |
| && p->pNewTable==0 && p->pNewIndex==0 && p->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. */ |
| 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; |
| } |
| |
| /* |
| ** This function edits SQL statement zSql, replacing each token identified |
| ** by the linked list pRename with the text of zNew. If argument bQuote is |
| ** true, then zNew is always quoted first. If no error occurs, the result |
| ** is loaded into context object pCtx as the result. |
| ** |
| ** Or, if an error occurs (i.e. an OOM condition), an error is left in |
| ** pCtx and an SQLite error code returned. |
| */ |
| static int renameEditSql( |
| sqlite3_context *pCtx, /* Return result here */ |
| RenameCtx *pRename, /* Rename context */ |
| const char *zSql, /* SQL statement to edit */ |
| const char *zNew, /* New token text */ |
| int bQuote /* True to always quote token */ |
| ){ |
| int nNew = sqlite3Strlen30(zNew); |
| int nSql = sqlite3Strlen30(zSql); |
| sqlite3 *db = sqlite3_context_db_handle(pCtx); |
| int rc = SQLITE_OK; |
| char *zQuot; |
| char *zOut; |
| int nQuot; |
| |
| /* 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. */ |
| zQuot = sqlite3MPrintf(db, "\"%w\"", zNew); |
| if( zQuot==0 ){ |
| return SQLITE_NOMEM; |
| }else{ |
| nQuot = sqlite3Strlen30(zQuot); |
| } |
| if( bQuote ){ |
| zNew = zQuot; |
| nNew = nQuot; |
| } |
| |
| /* At this point pRename->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( nQuot>=nNew ); |
| zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); |
| if( zOut ){ |
| int nOut = nSql; |
| memcpy(zOut, zSql, nSql); |
| while( pRename->pList ){ |
| int iOff; /* Offset of token to replace in zOut */ |
| RenameToken *pBest = renameColumnTokenNext(pRename); |
| |
| u32 nReplace; |
| const char *zReplace; |
| if( sqlite3IsIdChar(*pBest->t.z) ){ |
| nReplace = nNew; |
| zReplace = zNew; |
| }else{ |
| nReplace = nQuot; |
| zReplace = zQuot; |
| } |
| |
| 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; |
| } |
| |
| /* |
| ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming |
| ** it was read from the schema of database zDb. Return SQLITE_OK if |
| ** successful. Otherwise, return an SQLite error code and leave an error |
| ** message in the Parse object. |
| */ |
| static int renameResolveTrigger(Parse *pParse, const char *zDb){ |
| 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; |
| /* ALWAYS() because if the table of the trigger does not exist, the |
| ** error would have been hit before this point */ |
| if( ALWAYS(pParse->pTriggerTab) ){ |
| rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab); |
| } |
| |
| /* Resolve symbols in WHEN clause */ |
| 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 ){ |
| Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb); |
| if( pTarget==0 ){ |
| rc = SQLITE_ERROR; |
| }else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){ |
| 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); |
| } |
| assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); |
| if( pStep->pUpsert ){ |
| Upsert *pUpsert = pStep->pUpsert; |
| assert( rc==SQLITE_OK ); |
| pUpsert->pUpsertSrc = &sSrc; |
| 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; |
| } |
| } |
| } |
| return rc; |
| } |
| |
| /* |
| ** Invoke sqlite3WalkExpr() or sqlite3WalkSelect() on all Select or Expr |
| ** objects that are part of the trigger passed as the second argument. |
| */ |
| static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ |
| TriggerStep *pStep; |
| |
| /* Find tokens to edit in WHEN clause */ |
| sqlite3WalkExpr(pWalker, pTrigger->pWhen); |
| |
| /* Find tokens to edit in trigger steps */ |
| 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); |
| } |
| } |
| } |
| |
| /* |
| ** Free the contents of Parse object (*pParse). Do not free the memory |
| ** occupied by the Parse object itself. |
| */ |
| 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); |
| sqlite3ParserReset(pParse); |
| } |
| |
| /* |
| ** SQL function: |
| ** |
| ** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) |
| ** |
| ** 0. zSql: SQL statement to rewrite |
| ** 1. type: Type of object ("table", "view" etc.) |
| ** 2. object: Name of object |
| ** 3. Database: Database name (e.g. "main") |
| ** 4. Table: Table name |
| ** 5. iCol: Index of column to rename |
| ** 6. zNew: New column name |
| ** 7. bQuote: Non-zero if the new column name should be quoted. |
| ** 8. bTemp: True if zSql comes from temp schema |
| ** |
| ** 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. |
| ** |
| ** This function is used internally by the ALTER TABLE RENAME COLUMN command. |
| ** It is only accessible to SQL created using sqlite3NestedParse(). It is |
| ** not reachable from ordinary SQL passed into sqlite3_prepare(). |
| */ |
| 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].zName; |
| memset(&sCtx, 0, sizeof(sCtx)); |
| sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); |
| |
| #ifndef SQLITE_OMIT_AUTHORIZATION |
| db->xAuth = 0; |
| #endif |
| rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp); |
| |
| /* 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 ){ |
| 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); |
| } |
| 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); |
| } |
| } |
| |
| 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 if( sParse.pNewIndex ){ |
| sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); |
| sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); |
| }else{ |
| /* A trigger */ |
| TriggerStep *pStep; |
| rc = renameResolveTrigger(&sParse, (bTemp ? 0 : zDb)); |
| 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); |
| } |
| } |
| } |
| |
| |
| /* Find tokens to edit in UPDATE OF clause */ |
| if( sParse.pTriggerTab==pTab ){ |
| renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld); |
| } |
| |
| /* Find tokens to edit in various expressions and selects */ |
| renameWalkTrigger(&sWalker, sParse.pNewTrigger); |
| } |
| |
| assert( rc==SQLITE_OK ); |
| rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote); |
| |
| renameColumnFunc_done: |
| if( rc!=SQLITE_OK ){ |
| if( sParse.zErrMsg ){ |
| renameColumnParseError(context, 0, 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); |
| } |
| |
| /* |
| ** Walker expression callback used by "RENAME TABLE". |
| */ |
| static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ |
| RenameCtx *p = pWalker->u.pRename; |
| if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){ |
| renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); |
| } |
| return WRC_Continue; |
| } |
| |
| /* |
| ** Walker select callback used by "RENAME TABLE". |
| */ |
| static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ |
| int i; |
| RenameCtx *p = pWalker->u.pRename; |
| SrcList *pSrc = pSelect->pSrc; |
| if( pSrc==0 ){ |
| assert( pWalker->pParse->db->mallocFailed ); |
| return WRC_Abort; |
| } |
| for(i=0; i<pSrc->nSrc; i++){ |
| struct SrcList_item *pItem = &pSrc->a[i]; |
| if( pItem->pTab==p->pTab ){ |
| renameTokenFind(pWalker->pParse, p, pItem->zName); |
| } |
| } |
| renameWalkWith(pWalker, pSelect); |
| |
| return WRC_Continue; |
| } |
| |
| |
| /* |
| ** This C function implements an SQL user function that is used by SQL code |
| ** generated by the ALTER TABLE ... RENAME command to modify the definition |
| ** of any foreign key constraints that use the table being renamed as the |
| ** parent table. It is passed three arguments: |
| ** |
| ** 0: The database containing the table being renamed. |
| ** 1. type: Type of object ("table", "view" etc.) |
| ** 2. object: Name of object |
| ** 3: The complete text of the schema statement being modified, |
| ** 4: The old name of the table being renamed, and |
| ** 5: The new name of the table being renamed. |
| ** 6: True if the schema statement comes from the temp db. |
| ** |
| ** It returns the new schema statement. For example: |
| ** |
| ** sqlite_rename_table('main', 'CREATE TABLE t1(a REFERENCES t2)','t2','t3',0) |
| ** -> 'CREATE TABLE t1(a REFERENCES t3)' |
| */ |
| 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, 1, db, zInput, bTemp); |
| |
| if( rc==SQLITE_OK ){ |
| int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| if( sParse.pNewTable ){ |
| Table *pTab = sParse.pNewTable; |
| |
| if( pTab->pSelect ){ |
| if( isLegacy==0 ){ |
| NameContext sNC; |
| memset(&sNC, 0, sizeof(sNC)); |
| sNC.pParse = &sParse; |
| |
| sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); |
| if( sParse.nErr ) rc = sParse.rc; |
| sqlite3WalkSelect(&sWalker, pTab->pSelect); |
| } |
| }else{ |
| /* Modify any FK definitions to point to the new table. */ |
| #ifndef SQLITE_OMIT_FOREIGN_KEY |
| if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){ |
| FKey *pFKey; |
| for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
| if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ |
| renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); |
| } |
| } |
| } |
| #endif |
| |
| /* If this is the table being altered, fix any table refs in CHECK |
| ** expressions. Also update the name that appears right after the |
| ** "CREATE [VIRTUAL] TABLE" bit. */ |
| 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, bTemp ? 0 : zDb); |
| 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); |
| } |
| } |
| } |
| } |
| } |
| #endif |
| } |
| |
| if( rc==SQLITE_OK ){ |
| rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); |
| } |
| if( rc!=SQLITE_OK ){ |
| if( sParse.zErrMsg ){ |
| renameColumnParseError(context, 0, 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; |
| } |
| |
| /* |
| ** An SQL user function that checks that there are no parse or symbol |
| ** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement. |
| ** After an ALTER TABLE .. RENAME operation is performed and the schema |
| ** reloaded, this function is called on each SQL statement in the schema |
| ** to ensure that it is still usable. |
| ** |
| ** 0: Database name ("main", "temp" etc.). |
| ** 1: SQL statement. |
| ** 2: Object type ("view", "table", "trigger" or "index"). |
| ** 3: Object name. |
| ** 4: True if object is from temp schema. |
| ** |
| ** Unless it finds an error, this function normally returns NULL. However, it |
| ** returns integer value 1 if: |
| ** |
| ** * the SQL argument creates a trigger, and |
| ** * the table that the trigger is attached to is in database zDb. |
| */ |
| 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); |
| |
| #ifndef SQLITE_OMIT_AUTHORIZATION |
| sqlite3_xauth xAuth = db->xAuth; |
| db->xAuth = 0; |
| #endif |
| |
| UNUSED_PARAMETER(NotUsed); |
| if( zDb && zInput ){ |
| int rc; |
| Parse sParse; |
| rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp); |
| if( rc==SQLITE_OK ){ |
| if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ |
| NameContext sNC; |
| memset(&sNC, 0, sizeof(sNC)); |
| sNC.pParse = &sParse; |
| sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); |
| if( sParse.nErr ) rc = sParse.rc; |
| } |
| |
| else if( sParse.pNewTrigger ){ |
| if( isLegacy==0 ){ |
| rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); |
| } |
| 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 ){ |
| renameColumnParseError(context, 1, argv[2], argv[3], &sParse); |
| } |
| renameParseCleanup(&sParse); |
| } |
| |
| #ifndef SQLITE_OMIT_AUTHORIZATION |
| db->xAuth = xAuth; |
| #endif |
| } |
| |
| /* |
| ** Register built-in functions used to help implement ALTER TABLE |
| */ |
| 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, 5, renameTableTest), |
| }; |
| sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); |
| } |
| #endif /* SQLITE_ALTER_TABLE */ |