public static void BeginAddColumn(Parse parse, SrcList src) { Context ctx = parse.Ctx; // Look up the table being altered. Debug.Assert(parse.NewTable == null); Debug.Assert(Btree.HoldsAllMutexes(ctx)); //if (ctx.MallocFailed) goto exit_begin_add_column; Table table = parse.LocateTableItem(false, src.Ids[0]); if (table == null) { goto exit_begin_add_column; } #if !OMIT_VIRTUALTABLE if (IsVirtual(table)) { parse.ErrorMsg("virtual tables may not be altered"); goto exit_begin_add_column; } #endif // Make sure this is not an attempt to ALTER a view. if (table.Select != null) { parse.ErrorMsg("Cannot add a column to a view"); goto exit_begin_add_column; } if (IsSystemTable(parse, table.Name)) { goto exit_begin_add_column; } Debug.Assert(table.AddColOffset > 0); int db = Prepare.SchemaToIndex(ctx, table.Schema); // 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. Table newTable = new Table(); if (newTable == null) { goto exit_begin_add_column; } parse.NewTable = newTable; newTable.Refs = 1; newTable.Cols.length = table.Cols.length; Debug.Assert(newTable.Cols.length > 0); int allocs = (((newTable.Cols.length - 1) / 8) * 8) + 8; Debug.Assert(allocs >= newTable.Cols.length && allocs % 8 == 0 && allocs - newTable.Cols.length < 8); newTable.Cols.data = new Column[allocs]; newTable.Name = C._mtagprintf(ctx, "sqlite_altertab_%s", table.Name); if (newTable.Cols.data == null || newTable.Name == null) { ctx.MallocFailed = true; goto exit_begin_add_column; } for (int i = 0; i < newTable.Cols.length; i++) { Column col = table.Cols[i].memcpy(); col.Coll = null; col.Type = null; col.Dflt = null; col.Dflt = null; newTable.Cols[i] = col; } newTable.Schema = ctx.DBs[db].Schema; newTable.AddColOffset = table.AddColOffset; newTable.Refs = 1; // Begin a transaction and increment the schema cookie. parse.BeginWriteOperation(0, db); Vdbe v = parse.V; if (v == null) { goto exit_begin_add_column; } parse.ChangeCookie(db); exit_begin_add_column: sqlite3SrcListDelete(ctx, ref src); return; }
public static void BeginAddColumn(Parse parse, SrcList src) { Context ctx = parse.Ctx; // Look up the table being altered. Debug.Assert(parse.NewTable == null); Debug.Assert(Btree.HoldsAllMutexes(ctx)); //if (ctx.MallocFailed) goto exit_begin_add_column; Table table = parse.LocateTableItem(false, src.Ids[0]); if (table == null) goto exit_begin_add_column; #if !OMIT_VIRTUALTABLE if (IsVirtual(table)) { parse.ErrorMsg("virtual tables may not be altered"); goto exit_begin_add_column; } #endif // Make sure this is not an attempt to ALTER a view. if (table.Select != null) { parse.ErrorMsg("Cannot add a column to a view"); goto exit_begin_add_column; } if (IsSystemTable(parse, table.Name)) goto exit_begin_add_column; Debug.Assert(table.AddColOffset > 0); int db = Prepare.SchemaToIndex(ctx, table.Schema); // 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. Table newTable = new Table(); if (newTable == null) goto exit_begin_add_column; parse.NewTable = newTable; newTable.Refs = 1; newTable.Cols.length = table.Cols.length; Debug.Assert(newTable.Cols.length > 0); int allocs = (((newTable.Cols.length - 1) / 8) * 8) + 8; Debug.Assert(allocs >= newTable.Cols.length && allocs % 8 == 0 && allocs - newTable.Cols.length < 8); newTable.Cols.data = new Column[allocs]; newTable.Name = C._mtagprintf(ctx, "sqlite_altertab_%s", table.Name); if (newTable.Cols.data == null || newTable.Name == null) { ctx.MallocFailed = true; goto exit_begin_add_column; } for (int i = 0; i < newTable.Cols.length; i++) { Column col = table.Cols[i].memcpy(); col.Coll = null; col.Type = null; col.Dflt = null; col.Dflt = null; newTable.Cols[i] = col; } newTable.Schema = ctx.DBs[db].Schema; newTable.AddColOffset = table.AddColOffset; newTable.Refs = 1; // Begin a transaction and increment the schema cookie. parse.BeginWriteOperation(0, db); Vdbe v = parse.V; if (v == null) goto exit_begin_add_column; parse.ChangeCookie(db); exit_begin_add_column: sqlite3SrcListDelete(ctx, ref src); return; }
public static void RenameTable(Parse parse, SrcList src, Token name) { Context ctx = parse.Ctx; // Database connection Context.FLAG savedDbFlags = ctx.Flags; // Saved value of db->flags //if (C._NEVER(ctx.MallocFailed)) goto exit_rename_table; Debug.Assert(src.Srcs == 1); Debug.Assert(Btree.HoldsAllMutexes(ctx)); Table table = parse.LocateTableItem(false, src.Ids[0]); // Table being renamed if (table == null) { goto exit_rename_table; } int db = Prepare.SchemaToIndex(ctx, table.Schema); // Database that contains the table string dbName = ctx.DBs[db].Name; // Name of database iDb ctx.Flags |= Context.FLAG.PreferBuiltin; // Get a NULL terminated version of the new table name. string nameAsString = Parse.NameFromToken(ctx, name); // NULL-terminated version of pName if (nameAsString == null) { 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 (Parse.FindTable(ctx, nameAsString, dbName) != null || Parse.FindIndex(ctx, nameAsString, dbName) != null) { parse.ErrorMsg("there is already another table or index with this name: %s", nameAsString); 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 (IsSystemTable(parse, table.Name) || parse->CheckObjectName(nameAsString) != RC.OK) { goto exit_rename_table; } #if !OMIT_VIEW if (table.Select != null) { parse.ErrorMsg("view %s may not be altered", table.Name); goto exit_rename_table; } #endif #if !OMIT_AUTHORIZATION // Invoke the authorization callback. if (Auth.Check(parse, AUTH.ALTER_TABLE, dbName, table.Name, null)) { goto exit_rename_table; } #endif VTable vtable = null; // Non-zero if this is a v-tab with an xRename() #if !OMIT_VIRTUALTABLE if (parse->ViewGetColumnNames(table) != 0) { goto exit_rename_table; } if (E.IsVirtual(table)) { vtable = VTable.GetVTable(ctx, table); if (vtable.IVTable.IModule.Rename == null) { vtable = null; } } #endif // Begin a transaction and code the VerifyCookie for database iDb. Then modify the schema cookie (since the ALTER TABLE modifies the // schema). Open a statement transaction if the table is a virtual table. Vdbe v = parse.GetVdbe(); if (v == null) { goto exit_rename_table; } parse.BeginWriteOperation((vtable != null ? 1 : 0), db); parse.ChangeCookie(db); // 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. #if !OMIT_VIRTUALTABLE if (vtable != null) { int i = ++parse.Mems; v.AddOp4(OP.String8, 0, i, 0, nameAsString, 0); v.AddOp4(OP.VRename, i, 0, 0, vtable, Vdbe.P4T.VTAB); parse.MayAbort(); } #endif // figure out how many UTF-8 characters are in zName string tableName = table.Name; // Original name of the table int tableNameLength = C._utf8charlength(tableName, -1); // Number of UTF-8 characters in zTabName #if !OMIT_TRIGGER string where_ = string.Empty; // Where clause to locate temp triggers #endif #if !OMIT_FOREIGN_KEY && !OMIT_TRIGGER if ((ctx.Flags & Context.FLAG.ForeignKeys) != 0) { // If foreign-key support is enabled, rewrite the CREATE TABLE statements corresponding to all child tables of foreign key constraints // for which the renamed table is the parent table. if ((where_ = WhereForeignKeys(parse, table)) != null) { parse.NestedParse( "UPDATE \"%w\".%s SET " + "sql = sqlite_rename_parent(sql, %Q, %Q) " + "WHERE %s;", dbName, E.SCHEMA_TABLE(db), tableName, nameAsString, where_); C._tagfree(ctx, ref where_); } } #endif // Modify the sqlite_master table to use the new table name. parse.NestedParse( "UPDATE %Q.%s SET " + #if OMIT_TRIGGER "sql = sqlite_rename_table(sql, %Q), " + #else "sql = CASE " + "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" + "ELSE sqlite_rename_table(sql, %Q) END, " + #endif "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 AND " + "(type='table' OR type='index' OR type='trigger');", dbName, SCHEMA_TABLE(db), nameAsString, nameAsString, nameAsString, #if !OMIT_TRIGGER nameAsString, #endif nameAsString, tableNameLength, tableName); #if !OMIT_AUTOINCREMENT // If the sqlite_sequence table exists in this database, then update it with the new table name. if (Parse.FindTable(ctx, "sqlite_sequence", dbName) != null) { parse.NestedParse( "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", dbName, nameAsString, table.Name); } #endif #if !OMIT_TRIGGER // If there are TEMP triggers on this table, modify the sqlite_temp_master table. Don't do this if the table being ALTERed is itself located in the temp database. if ((where_ = WhereTempTriggers(parse, table)) != "") { parse.NestedParse( "UPDATE sqlite_temp_master SET " + "sql = sqlite_rename_trigger(sql, %Q), " + "tbl_name = %Q " + "WHERE %s;", nameAsString, nameAsString, where_); C._tagfree(ctx, ref where_); } #endif #if !(OMIT_FOREIGN_KEY) && !(OMIT_TRIGGER) if ((ctx.Flags & Context.FLAG.ForeignKeys) != 0) { for (FKey p = Parse.FkReferences(table); p != null; p = p.NextTo) { Table from = p.From; if (from != table) { ReloadTableSchema(parse, p.From, from.Name); } } } #endif // Drop and reload the internal table schema. ReloadTableSchema(parse, table, nameAsString); exit_rename_table: Expr.SrcListDelete(ctx, ref src); C._tagfree(ctx, ref nameAsString); ctx.Flags = savedDbFlags; }
public static void RenameTable(Parse parse, SrcList src, Token name) { Context ctx = parse.Ctx; // Database connection Context.FLAG savedDbFlags = ctx.Flags; // Saved value of db->flags //if (C._NEVER(ctx.MallocFailed)) goto exit_rename_table; Debug.Assert(src.Srcs == 1); Debug.Assert(Btree.HoldsAllMutexes(ctx)); Table table = parse.LocateTableItem(false, src.Ids[0]); // Table being renamed if (table == null) goto exit_rename_table; int db = Prepare.SchemaToIndex(ctx, table.Schema); // Database that contains the table string dbName = ctx.DBs[db].Name; // Name of database iDb ctx.Flags |= Context.FLAG.PreferBuiltin; // Get a NULL terminated version of the new table name. string nameAsString = Parse.NameFromToken(ctx, name); // NULL-terminated version of pName if (nameAsString == null) 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 (Parse.FindTable(ctx, nameAsString, dbName) != null || Parse.FindIndex(ctx, nameAsString, dbName) != null) { parse.ErrorMsg("there is already another table or index with this name: %s", nameAsString); 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 (IsSystemTable(parse, table.Name) || parse->CheckObjectName(nameAsString) != RC.OK) goto exit_rename_table; #if !OMIT_VIEW if (table.Select != null) { parse.ErrorMsg("view %s may not be altered", table.Name); goto exit_rename_table; } #endif #if !OMIT_AUTHORIZATION // Invoke the authorization callback. if (Auth.Check(parse, AUTH.ALTER_TABLE, dbName, table.Name, null)) goto exit_rename_table; #endif VTable vtable = null; // Non-zero if this is a v-tab with an xRename() #if !OMIT_VIRTUALTABLE if (parse->ViewGetColumnNames(table) != 0) goto exit_rename_table; if (E.IsVirtual(table)) { vtable = VTable.GetVTable(ctx, table); if (vtable.IVTable.IModule.Rename == null) vtable = null; } #endif // Begin a transaction and code the VerifyCookie for database iDb. Then modify the schema cookie (since the ALTER TABLE modifies the // schema). Open a statement transaction if the table is a virtual table. Vdbe v = parse.GetVdbe(); if (v == null) goto exit_rename_table; parse.BeginWriteOperation((vtable != null ? 1 : 0), db); parse.ChangeCookie(db); // 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. #if !OMIT_VIRTUALTABLE if (vtable != null) { int i = ++parse.Mems; v.AddOp4(OP.String8, 0, i, 0, nameAsString, 0); v.AddOp4(OP.VRename, i, 0, 0, vtable, Vdbe.P4T.VTAB); parse.MayAbort(); } #endif // figure out how many UTF-8 characters are in zName string tableName = table.Name; // Original name of the table int tableNameLength = C._utf8charlength(tableName, -1); // Number of UTF-8 characters in zTabName #if !OMIT_TRIGGER string where_ = string.Empty; // Where clause to locate temp triggers #endif #if !OMIT_FOREIGN_KEY && !OMIT_TRIGGER if ((ctx.Flags & Context.FLAG.ForeignKeys) != 0) { // If foreign-key support is enabled, rewrite the CREATE TABLE statements corresponding to all child tables of foreign key constraints // for which the renamed table is the parent table. if ((where_ = WhereForeignKeys(parse, table)) != null) { parse.NestedParse( "UPDATE \"%w\".%s SET " + "sql = sqlite_rename_parent(sql, %Q, %Q) " + "WHERE %s;", dbName, E.SCHEMA_TABLE(db), tableName, nameAsString, where_); C._tagfree(ctx, ref where_); } } #endif // Modify the sqlite_master table to use the new table name. parse.NestedParse( "UPDATE %Q.%s SET " + #if OMIT_TRIGGER "sql = sqlite_rename_table(sql, %Q), " + #else "sql = CASE " + "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" + "ELSE sqlite_rename_table(sql, %Q) END, " + #endif "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 AND " + "(type='table' OR type='index' OR type='trigger');", dbName, SCHEMA_TABLE(db), nameAsString, nameAsString, nameAsString, #if !OMIT_TRIGGER nameAsString, #endif nameAsString, tableNameLength, tableName); #if !OMIT_AUTOINCREMENT // If the sqlite_sequence table exists in this database, then update it with the new table name. if (Parse.FindTable(ctx, "sqlite_sequence", dbName) != null) parse.NestedParse( "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", dbName, nameAsString, table.Name); #endif #if !OMIT_TRIGGER // If there are TEMP triggers on this table, modify the sqlite_temp_master table. Don't do this if the table being ALTERed is itself located in the temp database. if ((where_ = WhereTempTriggers(parse, table)) != "") { parse.NestedParse( "UPDATE sqlite_temp_master SET " + "sql = sqlite_rename_trigger(sql, %Q), " + "tbl_name = %Q " + "WHERE %s;", nameAsString, nameAsString, where_); C._tagfree(ctx, ref where_); } #endif #if !(OMIT_FOREIGN_KEY) && !(OMIT_TRIGGER) if ((ctx.Flags & Context.FLAG.ForeignKeys) != 0) { for (FKey p = Parse.FkReferences(table); p != null; p = p.NextTo) { Table from = p.From; if (from != table) ReloadTableSchema(parse, p.From, from.Name); } } #endif // Drop and reload the internal table schema. ReloadTableSchema(parse, table, nameAsString); exit_rename_table: Expr.SrcListDelete(ctx, ref src); C._tagfree(ctx, ref nameAsString); ctx.Flags = savedDbFlags; }