/// <summary> /// Compares <paramref name="sourceSchema"/> and <paramref name="targetSchema"/>. /// </summary> /// <param name="sourceSchema">The source schema.</param> /// <param name="targetSchema">The target schema.</param> /// <param name="schemaHints">The upgrade hints.</param> /// <param name="upgradeHints"><see cref="UpgradeHint"/>s to be applied.</param> /// <param name="schemaUpgradeMode">A <see cref="SchemaUpgradeMode"/> being used.</param> /// <param name="model">A <see cref="DomainModel"/> of a storage.</param> /// <param name="briefExceptionFormat">Indicates whether brief or full exception format should be used.</param> /// <param name="upgradeStage">A current <see cref="UpgradeStage"/>.</param> /// <returns>Comparison result.</returns> public static SchemaComparisonResult Compare( StorageModel sourceSchema, StorageModel targetSchema, HintSet schemaHints, SetSlim <UpgradeHint> upgradeHints, SchemaUpgradeMode schemaUpgradeMode, DomainModel model, bool briefExceptionFormat, UpgradeStage upgradeStage) { if (schemaHints == null) { schemaHints = new HintSet(sourceSchema, targetSchema); } var comparer = new Comparer(); var difference = comparer.Compare(sourceSchema, targetSchema, schemaHints); var actions = GetUpgradeActions(comparer, difference, schemaHints); var actionList = actions.Flatten().ToList(); var comparisonStatus = GetComparisonStatus(actionList, schemaUpgradeMode); var unsafeActions = GetUnsafeActions(actionList, upgradeHints); var columnTypeChangeActions = actionList.OfType <PropertyChangeAction>().Where(IsTypeChangeAction).ToList(); if (schemaUpgradeMode != SchemaUpgradeMode.ValidateLegacy) { return(new SchemaComparisonResult( comparisonStatus, columnTypeChangeActions.Count > 0, null, schemaHints, difference, actions, unsafeActions)); } // Legacy comparison var systemTablesSequence = model.Types.Where(type => type.IsSystem).Select(type => type.MappingName); var systemTables = new HashSet <string>(systemTablesSequence, Comparer); var createTableActions = actionList .OfType <CreateNodeAction>() .Where( action => { var table = action.Difference.Target as TableInfo; return(table != null && !systemTables.Contains(table.Name)); }) .ToList(); var createColumnActions = actionList .OfType <CreateNodeAction>() .Where( action => { var column = action.Difference.Target as StorageColumnInfo; return(column != null && !systemTables.Contains(column.Parent.Name)); }) .ToList(); columnTypeChangeActions = columnTypeChangeActions .Where( action => { var sourceType = action.Difference.Source as StorageTypeInfo; var targetType = action.Difference.Target as StorageTypeInfo; return(sourceType == null || targetType == null || sourceType.IsTypeUndefined || sourceType.Type.ToNullable() != targetType.Type.ToNullable()); }) .ToList(); var isCompatibleInLegacyMode = createTableActions.Count == 0 && createColumnActions.Count == 0 && columnTypeChangeActions.Count == 0; if (briefExceptionFormat) { unsafeActions = createTableActions.Cast <NodeAction>() .Concat(createColumnActions.Cast <NodeAction>()) .Concat(columnTypeChangeActions.Cast <NodeAction>()) .ToList(); } return(new SchemaComparisonResult( comparisonStatus, columnTypeChangeActions.Count > 0, isCompatibleInLegacyMode, schemaHints, difference, actions, unsafeActions)); }
private void SynchronizeSchema( Domain domain, SchemaUpgrader upgrader, SchemaExtractor extractor, SchemaUpgradeMode schemaUpgradeMode) { using (UpgradeLog.InfoRegion(Strings.LogSynchronizingSchemaInXMode, schemaUpgradeMode)) { StorageModel targetSchema = null; if (schemaUpgradeMode == SchemaUpgradeMode.Skip) { if (context.ParentDomain == null) { //If we build main domain we should log target model. //Log of Storage Node target model is not necessary //because storage target model exactly the same. targetSchema = GetTargetModel(domain); context.TargetStorageModel = targetSchema; if (UpgradeLog.IsLogged(LogLevel.Info)) { UpgradeLog.Info(Strings.LogTargetSchema); targetSchema.Dump(); } } var builder = ExtractedModelBuilderFactory.GetBuilder(context); context.ExtractedSqlModelCache = builder.Run(); OnSchemaReady(); return; // Skipping comparison completely } var extractedSchema = extractor.GetSchema(); // Hints var triplet = BuildTargetModelAndHints(extractedSchema); var hintProcessingResult = triplet.Third; targetSchema = triplet.First; context.TargetStorageModel = targetSchema; var hints = triplet.Second; if (UpgradeLog.IsLogged(LogLevel.Info)) { UpgradeLog.Info(Strings.LogExtractedSchema); extractedSchema.Dump(); UpgradeLog.Info(Strings.LogTargetSchema); targetSchema.Dump(); } OnSchemaReady(); var breifExceptionFormat = domain.Configuration.SchemaSyncExceptionFormat == SchemaSyncExceptionFormat.Brief; var result = SchemaComparer.Compare(extractedSchema, targetSchema, hints, context.Hints, schemaUpgradeMode, domain.Model, breifExceptionFormat, context.Stage); var shouldDumpSchema = !schemaUpgradeMode.In( SchemaUpgradeMode.Skip, SchemaUpgradeMode.ValidateCompatible, SchemaUpgradeMode.Recreate); if (shouldDumpSchema && UpgradeLog.IsLogged(LogLevel.Info)) { UpgradeLog.Info(result.ToString()); } if (UpgradeLog.IsLogged(LogLevel.Info)) { UpgradeLog.Info(Strings.LogComparisonResultX, result); } context.SchemaDifference = (NodeDifference)result.Difference; context.SchemaUpgradeActions = result.UpgradeActions; switch (schemaUpgradeMode) { case SchemaUpgradeMode.ValidateExact: if (result.SchemaComparisonStatus != SchemaComparisonStatus.Equal || result.HasColumnTypeChanges) { throw new SchemaSynchronizationException(result); } if (!hintProcessingResult.AreAllTypesMapped() && hintProcessingResult.SuspiciousTypes.Any()) { throw new SchemaSynchronizationException(Strings.ExExtractedAndTargetSchemasAreEqualButThereAreChangesInTypeIdentifiersSet); } break; case SchemaUpgradeMode.ValidateCompatible: if (result.SchemaComparisonStatus != SchemaComparisonStatus.Equal && result.SchemaComparisonStatus != SchemaComparisonStatus.TargetIsSubset) { throw new SchemaSynchronizationException(result); } break; case SchemaUpgradeMode.PerformSafely: if (result.HasUnsafeActions) { throw new SchemaSynchronizationException(result); } goto case SchemaUpgradeMode.Perform; case SchemaUpgradeMode.Recreate: case SchemaUpgradeMode.Perform: upgrader.UpgradeSchema(extractor.GetSqlSchema(), extractedSchema, targetSchema, result.UpgradeActions); if (result.UpgradeActions.Any()) { extractor.ClearCache(); } break; case SchemaUpgradeMode.ValidateLegacy: if (result.IsCompatibleInLegacyMode != true) { throw new SchemaSynchronizationException(result); } break; default: throw new ArgumentOutOfRangeException("schemaUpgradeMode"); } } }
private static SchemaComparisonStatus GetComparisonStatus(ICollection <NodeAction> actions, SchemaUpgradeMode schemaUpgradeMode) { var filter = schemaUpgradeMode != SchemaUpgradeMode.ValidateCompatible ? (Func <Type, bool>)(targetType => true) : targetType => targetType.In(WellKnownUpgradeTypes.TableInfo, WellKnownUpgradeTypes.StorageColumnInfo); var hasCreateActions = actions .OfType <CreateNodeAction>() .Select(action => action.Difference.Target.GetType()) .Any(filter); var hasRemoveActions = actions .OfType <RemoveNodeAction>() .Select(action => action.Difference.Source.GetType()) .Any(sourceType => sourceType.In(WellKnownUpgradeTypes.TableInfo, WellKnownUpgradeTypes.StorageColumnInfo)); if (hasCreateActions && hasRemoveActions) { return(SchemaComparisonStatus.NotEqual); } if (hasCreateActions) { return(SchemaComparisonStatus.TargetIsSuperset); } if (hasRemoveActions) { return(SchemaComparisonStatus.TargetIsSubset); } return(SchemaComparisonStatus.Equal); }