/// <summary> /// Получает метаданные указанной таблицы /// </summary> /// <param name="table">Таблица, в которой обновляются метаданные из базы данных</param> /// <param name="checkIndexes">Флаг сбора данных об индексах</param> /// <param name="checkForeignKeys">Флаг сбора данных о ссылках</param> /// <param name="checkConstraints">Флаг сбора данных о констрейнтах</param> public virtual void GetTableSchema(DBTableEx table, bool checkIndexes, bool checkForeignKeys, bool checkConstraints) { GetColumns(table); GetPrimaryKey(table); if (checkIndexes) GetIndexes(table); if (checkForeignKeys) GetForeignKeys(table); if (checkConstraints) GetConstraints(table); }
private void GetConstraints(DBTableEx table) { string schemaName = ComposeSafeSchemaName(table.Name); string tableName = ComposeSafeTableName(table.Name); bool allOrUser = !string.IsNullOrEmpty(schemaName); string constraintsTable = allOrUser ? "all_constraints" : "user_constraints"; string realSchemaName = allOrUser ? schemaName : CurrentUserName; Query query = new Query( // (Оба представления содержат поле owner) "select constraint_name from " + constraintsTable + " " + "where owner = :p0 and table_name = :p1 and constraint_type = 'C' and generated = 'USER NAME'", new QueryParameterCollection(new OperandValue(realSchemaName), new OperandValue(tableName)), new string[] { ":p0", ":p1" }); SelectStatementResult data = SelectData(query); foreach (SelectStatementResultRow row in data.Rows) { string name = (string)row.Values[0]; string condition = GetLong(new Query( "select search_condition from " + constraintsTable + " " + "where owner = :p0 and table_name = :p1 and constraint_name = :p2", new QueryParameterCollection(new OperandValue(realSchemaName), new OperandValue(tableName), new OperandValue(name)), new string[] { ":p0", ":p1", ":p2" })); table.AddConstraint(new DBCheckConstraint(name, condition)); } }
// Получение таблицы для объекта безопасности private SecurityObject GetSecurityTable(string tableName, string[] privs, params DBColumn[] columns) { DBTableEx table = XPDictionaryInformer.Schema.FindTable(tableName); if (table == null && (privs == null ? GetUserPrivilege(tableName) : privs.All(priv => GetUserPrivilege(priv)))) { table = new DBTableEx(tableName, columns); XPDictionaryInformer.RegisterTable(tableName, table); } if (table != null) return new SecurityObject(SecurityObjectTypes.Table, tableName); return null; }
/// <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; }
// Конвертация таблиц в таблицы с информацией о констрейнтах private DBTableEx[] ConvertTables(DBTable[] tables) { DBTableEx[] result = new DBTableEx[tables.Length]; for (int i = 0; i < tables.Length; i++) result[i] = XPDictionaryInformer.TranslateAndGet(tables[i].Name); return result; }
// Создать таблицу с дополнительными данными о констрейнтах private void CreateTable() { table = new DBTableEx(TableName, sourceInfo.Table); // Констрейнты (констрейнты базовой таблицы не наследуются) DBTableHelperEx.ProcessClassInfo(table, this); // Настраиваемая модификация данных if (IsCustomPersistent) { ICustomPersistent customPersistent = GetCustomPersistent(null); if (customPersistent == null) throw new InvalidOperationException(string.Format( "Custom persistent controller of class {0} is failed", FullName)); table.CustomPersistent = customPersistent; } }
/// <summary> /// Возвращает таблицу с дополнительными данными для ассоциации many-to-many /// </summary> /// <param name="property">Свойство, устанавливающее ассоциацию</param> /// <param name="tableName">Название таблицы</param> /// <returns>Таблица для ассоциации many-to-many</returns> public DBTableEx GetIntermediateTableEx(ReflectionPropertyInfo property, string tableName) { DBTableEx table = new DBTableEx(tableName, property.IntermediateClass.Table); if (IsCustomPersistent) { string associationName = ((AssociationAttribute)property.Attributes.First(a => a is AssociationAttribute)).Name; table.CustomPersistent = GetCustomPersistent(associationName); } return table; }
/// <summary> /// Регистрация таблицы в схеме и в трансляции имен /// </summary> /// <param name="tableName">Исходное название таблицы</param> /// <param name="table">Регистрируемая таблица</param> internal static void RegisterTable(string tableName, DBTableEx table) { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException("tableName", "Register table invalid parameter"); if (table == null) throw new ArgumentNullException("table", "Register table invalid parameter"); AddTranslation(tableName, table.Name); Schema.Add(table.Name, new Lazy<DBTableEx>(() => table)); }
/// <summary> /// Переопределяет генерацию sql-команды /// </summary> /// <returns>Sql-команда</returns> protected override string InternalGenerateSql() { if (table == null) table = XPDictionaryInformer.TranslateAndGet(Root.TableName); string rootName = table.Name; schemaName = formatter.ComposeSafeSchemaName(rootName); tableName = formatter.ComposeSafeTableName(rootName); whereCondition = null; keyProcessed = new StringCollection(); keyColumns = new StringBuilder(); parametersVars = new StringBuilder(); // Добавление if (Root is InsertStatement) { InsertStatement ins = (InsertStatement)Root; if (!ReferenceEquals(ins.IdentityParameter, null)) { identitiesByTag.ConsolidateIdentity(ins.IdentityParameter); commandParams.Add(ins.IdentityParameter); commandParamsNames.Add(formatterSequred.FormatProcedureParameter(ins.IdentityColumn)); keyProcessed.Add(ins.IdentityColumn); } PrepareParameters(false); return formatterSequred.FormatProcedureAdd(schemaName, tableName); } // Изменение else if (Root is UpdateStatement) { PrepareKey(); PrepareParameters(true); return FormatBlock(formatterSequred.FormatProcedureEdit(schemaName, tableName)); } // Удаление else if (Root is DeleteStatement) { PrepareKey(); return FormatBlock(formatterSequred.FormatProcedureDelete(schemaName, tableName)); } else new ArgumentException("Incorrect type of modification statement"); return null; }
/// <summary> /// Возвращает запрос модификации данных /// </summary> /// <param name="table">Таблица, данные которой модифицируются</param> /// <param name="node">Выражение модификации данных</param> /// <returns>Запрос модификации данных</returns> public Query GenerateSql(DBTableEx table, BaseStatement node) { this.table = table; return GenerateSql(node); }