/// <summary> /// Выполняет изменение структуры данных /// </summary> protected UpdateSchemaResult ProcessUpdateSchema(UpdateSchemaMode mode, bool skipIfFirstTableNotExists, params DBTableEx[] tables) { // Конвертация исходных таблиц в таблицы с информацией о констрейнтах ICollection collectedTables = CollectTablesToCreate(tables); Hashtable createdTables = new Hashtable(); Hashtable recreateSafe = new Hashtable(); // Проверка существования первой таблицы if (skipIfFirstTableNotExists && collectedTables.Count > 0) { IEnumerator te = tables.GetEnumerator(); IEnumerator ce = collectedTables.GetEnumerator(); te.MoveNext(); ce.MoveNext(); if (object.ReferenceEquals(te.Current, ce.Current)) return UpdateSchemaResult.FirstTableNotExists; } // Изменение структуры данных if (CanCreateSchema) BeginTransaction(); try { // Создание таблиц if (!CanCreateSchema && collectedTables.Count > 0) { IEnumerator ce = collectedTables.GetEnumerator(); ce.MoveNext(); throw new SchemaCorrectionNeededException("Table '" + ComposeSafeTableName(((DBTable)ce.Current).Name) + "' not found"); } else { foreach (DBTableEx table in collectedTables) { if (table.IsView || table.IsCustom) continue; CreateTable(table); CreateSequence(table); CreatePrimaryKey(table); createdTables[table] = table; recreateSafe[table] = table; } } // Модификация таблиц Hashtable realTables = new Hashtable(); foreach (DBTableEx table in tables) { // Пропуск представлений, уже созданных таблиц // и таблиц с настраиваемой модификацией данных if (table.IsView || createdTables[table] != null || table.IsCustom) continue; DBTableEx realTable = new DBTableEx(table.Name); bool collectIndexes = false; bool collectFKs = false; bool collectConstraints = false; if (CanCreateSchema) { collectIndexes = table.Indexes.Count > 0; collectFKs = table.ForeignKeys.Count > 0; collectConstraints = table.Constraints.Exists(c => c is DBCriteriaConstraint); if (NeedsIndexForForeignKey) collectIndexes = collectIndexes || collectFKs; } GetTableSchema(realTable, collectIndexes, collectFKs, collectConstraints); realTables[table] = realTable; // Новые колонки foreach (DBColumn column in table.Columns) { // Создание колонки DBColumn realColumn = FindColumnByName(realTable, column); if (realColumn == null) { if (!CanCreateSchema) throw new SchemaCorrectionNeededException(string.Format( "Column '{0}' not found in table '{1}'", ComposeSafeColumnName(column.Name), ComposeSafeTableName(table.Name))); CreateColumn(table, column); recreateSafe[table] = table; } // Изменение типа колонки if (realColumn != null && GetDBTableColumnType(table, column) != realColumn.DBTypeName) { if (!CanCreateSchema) throw new SchemaCorrectionNeededException(string.Format( "Type of column '{0}' in table '{1}' is changed to {2}", ComposeSafeColumnName(column.Name), ComposeSafeTableName(table.Name), GetSqlCreateColumnType(table, column))); ChangeColumn(table, realColumn, column); recreateSafe[table] = table; } // Изменение констрейнта обязательности bool notNull = table.ColumnIsNotNull(column); if (realColumn != null && notNull != realTable.ColumnIsNotNull(realColumn)) { ChangeColumnNullable(table, column, notNull); recreateSafe[table] = table; } } // Старые колонки if (CanCreateSchema && (updateOptions & UpdateSchemaOptions.UnsafeChanges) != 0) { List<DBColumn> realColumns = new List<DBColumn>(realTable.Columns); foreach (DBColumn column in table.Columns) { // Поиск по колонкам заданной таблицы table, чтобы учесть преобразование имен колонок DBColumn realColumn = FindColumnByName(realTable, column); if (realColumn != null) realColumns.Remove(realColumn); } foreach (DBColumn realColumn in realColumns) DeleteColumn(realTable, realColumn); } // Последовательность для первичного ключа if (CanCreateSchema && GetTableSequenceColumn(table) != null && !GetSequence(table)) { CreateSequence(table); recreateSafe[table] = table; } // Первичный ключ if (CanCreateSchema && table.PrimaryKey != null && realTable.PrimaryKey == null) { CreatePrimaryKey(table); recreateSafe[table] = table; } // Невалидные или отсутствующие представления и пакеты if (recreateSafe[table] == null && !GetValidSafeObjects(table)) recreateSafe[table] = table; } // Индексы, ссылки, констрейнты if (CanCreateSchema) { foreach (DBTableEx table in tables) { // Новая или измененная таблица DBTableEx realTable = (DBTableEx)realTables[table]; if (realTable == null && createdTables[table] != null) { realTable = new DBTableEx(table.Name); realTable.Columns.AddRange(table.Columns); } if (realTable == null) continue; // Индексы foreach (DBIndex index in table.Indexes) { if (!IsIndexExists(realTable, index)) { CreateIndex(table, index); realTable.AddIndex(index); } } // Индексы ссылок if (NeedsIndexForForeignKey) { foreach (DBForeignKey fk in table.ForeignKeys) { DBIndex index = new DBIndex(fk.Columns, false); if (!IsIndexExists(realTable, index) && (table.PrimaryKey == null || (table.PrimaryKey != null && !IsColumnsEqual(table.PrimaryKey.Columns, index.Columns)))) { CreateIndex(table, index); realTable.AddIndex(index); } } } // Ссылки foreach (DBForeignKey fk in table.ForeignKeys) { if (!IsForeignKeyExists(realTable, fk)) CreateForeignKey(table, fk); } // Констрейнты foreach (DBCriteriaConstraint cons in table.Constraints.Where(c => c is DBCriteriaConstraint)) { string expression = OracleCheckConstraintGenerator.GenerateExpression(this, cons.Criteria); if (expression != null && !realTable.Constraints.Exists( c => c is DBCheckConstraint && ((DBCheckConstraint)c).Condition == expression)) CreateConstraint(table, expression); if (expression == null) //&& !IsConstraintExists - сложные констрейнты реализованы в пакетах recreateSafe[table] = table; } if (table.Constraints.Exists(c => !(c is DBCriteriaConstraint || c is DBNotNullConstraint))) recreateSafe[table] = table; } } // Представления и пакеты if (CanCreateSchema) { bool allSafeObjects = (updateOptions & UpdateSchemaOptions.AllSafeObjects) == UpdateSchemaOptions.AllSafeObjects; foreach (DBTableEx table in tables) { if (table.IsView || table.IsCustom || (!allSafeObjects && recreateSafe[table] == null)) continue; CreateOrReplaceView(table); CreateOrReplacePackage(table); } } // Завершение транзакции if (CanCreateSchema) CommitTransaction(); } catch { if (CanCreateSchema) RollbackTransaction(); throw; } return UpdateSchemaResult.SchemaExists; }
/// <contentfrom cref="ISqlDataStoreSafe.UpdateSchema" /> public UpdateSchemaResult UpdateSchema(UpdateSchemaMode mode, bool dontCreateIfFirstTableNotExist, params DBTable[] tables) { using (IDisposable c1 = new PerformanceCounters.QueueLengthCounter(PerformanceCounters.SqlDataStoreTotalRequests, PerformanceCounters.SqlDataStoreTotalQueue, PerformanceCounters.SqlDataStoreSchemaUpdateRequests, PerformanceCounters.SqlDataStoreSchemaUpdateQueue)) { lock (SyncRoot) { return ProcessUpdateSchema(mode, dontCreateIfFirstTableNotExist, tables); } } }
/// <summary> /// Выполняет изменение структуры данных с возможностью записи скрипта /// </summary> protected UpdateSchemaResult ProcessUpdateSchema(UpdateSchemaMode mode, bool skipIfFirstTableNotExists, params DBTable[] tables) { // Изменение структуры реальной базы данных SetCommandAdapterParameters(); UpdateSchemaResult result; try { result = ProcessUpdateSchema(mode, skipIfFirstTableNotExists, ConvertTables(tables)); // Небезопасные команды изменения структуры, записываемые в комментариях в отдельном блоке if (scriptMode && scriptWriter != null && scriptUnsafe != null && scriptUnsafe.Length > 0) { scriptWriter.WriteLine("/* Unsafe changes"); scriptWriter.WriteLine(scriptUnsafe.ToString()); scriptWriter.WriteLine("*/"); } } finally { FreeCommandAdapterParameters(); } if (scriptMode && scriptPath != null && scriptCommands > 0) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Please execute the script and press <Enter>."); Console.ResetColor(); Console.ReadLine(); } return result; }