private void TestUpdate(Action <Server, Server, HintSet> update, bool useHints) { var s1 = Clone(srv); var s2 = Clone(srv); var hints = new HintSet(s1, s2); update.Invoke(s1, s2, hints); if (!useHints) { hints = new HintSet(s1, s2); } TestLog.Info("Update test ({0} hints)", useHints ? "with" : "without"); // s1.Dump(); // s2.Dump(); s1.Validate(); s2.Validate(); // Comparing different models TestLog.Info("Comparing models:"); var comparer = new Comparer(); var diff = comparer.Compare(s1, s2, hints); TestLog.Info("\r\nDifference:\r\n{0}", diff); var actions = new ActionSequence() { new Upgrader().GetUpgradeSequence(diff, hints, comparer) }; TestLog.Info("\r\nActions:\r\n{0}", actions); }
public void BaseComparisonTest() { var source = new Server("Source"); var target = srv; target.Validate(); TestLog.Info("Source model:"); // source.Dump(); TestLog.Info("Target model:"); // target.Dump(); var comparer = new Comparer(); var hints = new HintSet(source, target) { new RenameHint("", "") }; Difference diff = comparer.Compare(source, target, hints); TestLog.Info("Difference: \r\n{0}", diff); var actions = new ActionSequence { new Upgrader().GetUpgradeSequence(diff, hints, comparer) }; TestLog.Info("Actions: \r\n{0}", actions); TestLog.Info("Applying actions..."); actions.Apply(source); TestLog.Info("Updated Model 1:"); // source.Dump(); }
private static void TestUpdate(StorageInfo origin, Action <StorageInfo, StorageInfo, HintSet> mutator, Action <Difference, ActionSequence> validator, bool useHints) { var s1 = Clone(origin); var s2 = Clone(origin); var hints = new HintSet(s1, s2); mutator.Invoke(s1, s2, hints); if (!useHints) { hints = new HintSet(s1, s2); } TestLog.Info("Update test ({0} hints)", useHints ? "with" : "without"); s1.Dump(); s2.Dump(); s1.Validate(); s2.Validate(); // Comparing different models TestLog.Info("Comparing models:"); var comparer = new Comparer(); var diff = comparer.Compare(s1, s2, hints); TestLog.Info("\r\nDifference:\r\n{0}", diff); var actions = new ActionSequence() { new Upgrader().GetUpgradeSequence(diff, hints, comparer) }; TestLog.Info("\r\nActions:\r\n{0}", actions); if (validator != null) { validator.Invoke(diff, actions); } }
/// <inheritdoc/> /// <exception cref="ArgumentOutOfRangeException"><c>hints.SourceModel</c> or <c>hints.TargetModel</c> /// is out of range.</exception> public Difference Compare(IModel source, IModel target, HintSet hints) { Source = source; Target = target; Hints = hints ?? new HintSet(Source, Target); if (Hints.SourceModel != Source) { throw new ArgumentOutOfRangeException("hints.SourceModel"); } if (Hints.TargetModel != Target) { throw new ArgumentOutOfRangeException("hints.TargetModel"); } var previous = currentAsync.Value; currentAsync.Value = this; Results = new Dictionary <object, Difference>(); try { Stage = ComparisonStage.BaseComparison; Visit(Source, Target); CoreLog.Info("Base comparison complete."); Stage = ComparisonStage.ReferenceComparison; return(Visit(Source, Target)); } finally { currentAsync.Value = previous; Results = null; } }
private void UpdateHints() { var originalHints = Hints; Hints = new HintSet(CurrentModel, TargetModel); // Process RenameHints foreach (var renameHint in originalHints.OfType <RenameHint>()) { if (TemporaryRenames.TryGetValue(renameHint.SourcePath, out var sourceNode)) { Hints.Add(new RenameHint(sourceNode.Path, renameHint.TargetPath)); } else { Hints.Add(renameHint); } } // Process CopyDataHints foreach (var copyDataHint in originalHints.OfType <CopyDataHint>()) { var sourceTablePath = GetActualPath(copyDataHint.SourceTablePath); var identities = copyDataHint.Identities.Select(pair => new IdentityPair(GetActualPath(pair.Source), pair.Target, pair.IsIdentifiedByConstant)) .ToList(); var copiedColumns = copyDataHint.CopiedColumns.Select(pair => new Pair <string>(GetActualPath(pair.First), pair.Second)) .ToList(); var newCopyDataHint = new CopyDataHint(sourceTablePath, identities, copiedColumns); Hints.Add(newCopyDataHint); } // Process DeleteDataHints foreach (var deleteDataHint in originalHints.OfType <DeleteDataHint>()) { if (!deleteDataHint.PostCopy) { continue; // It's not necessary to copy this hint } var sourceTablePath = GetActualPath(deleteDataHint.SourceTablePath); var identities = deleteDataHint.Identities.Select(pair => new IdentityPair(GetActualPath(pair.Source), pair.Target, pair.IsIdentifiedByConstant)) .ToList(); var newDeleteDataHint = new DeleteDataHint(sourceTablePath, identities, true); Hints.Add(newDeleteDataHint); } // Process IgnoreHints foreach (var ignoreHint in originalHints.OfType <IgnoreHint>()) { Hints.Add(ignoreHint); } }
// Constructors /// <summary> /// Initializes a new instance of this class. /// </summary> /// <param name="schemaComparisonStatus">The comparison status.</param> /// <param name="hasColumnTypeChanges">Indicates whether there are column type changes.</param> /// <param name="isCompatibleInLegacyMode">Indicates whether schemes are compatible in legacy mode.</param> /// <param name="hints">The upgrade hints.</param> /// <param name="difference">The difference.</param> /// <param name="upgradeActions">The upgrade actions.</param> /// <param name="unsafeActions">The unsafe (breaking) actions.</param> public SchemaComparisonResult( SchemaComparisonStatus schemaComparisonStatus, bool hasColumnTypeChanges, bool?isCompatibleInLegacyMode, HintSet hints, Difference difference, ActionSequence upgradeActions, IList <NodeAction> unsafeActions) { SchemaComparisonStatus = schemaComparisonStatus; IsCompatibleInLegacyMode = isCompatibleInLegacyMode; HasColumnTypeChanges = hasColumnTypeChanges; Hints = hints; Difference = difference; UpgradeActions = upgradeActions; UnsafeActions = unsafeActions != null ? new ReadOnlyList <NodeAction>(unsafeActions) : new ReadOnlyList <NodeAction>(new List <NodeAction>()); HasUnsafeActions = UnsafeActions.Any(); }
/// <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 static ActionSequence GetUpgradeActions(Comparer comparer, Difference difference, HintSet hints) { var actions = difference != null ? new Upgrader().GetUpgradeSequence(difference, hints, comparer) : EnumerableUtils <NodeAction> .Empty; return(new ActionSequence { actions }); }
/// <inheritdoc/> /// <exception cref="ArgumentOutOfRangeException"><c>hints.SourceModel</c> or <c>hints.TargetModel</c> /// is out of range.</exception> /// <exception cref="InvalidOperationException">Upgrade sequence validation has failed.</exception> public ReadOnlyList <NodeAction> GetUpgradeSequence(Difference difference, HintSet hints, IComparer comparer) { ArgumentValidator.EnsureArgumentNotNull(hints, nameof(hints)); ArgumentValidator.EnsureArgumentNotNull(comparer, nameof(comparer)); if (difference == null) { return(new ReadOnlyList <NodeAction>(Enumerable.Empty <NodeAction>().ToList())); } TemporaryRenames = new Dictionary <string, Node>(StringComparer.OrdinalIgnoreCase); SourceModel = (IModel)difference.Source; TargetModel = (IModel)difference.Target; Hints = hints ?? new HintSet(SourceModel, TargetModel); Comparer = comparer; if (Hints.SourceModel != SourceModel) { throw new ArgumentOutOfRangeException("hints.SourceModel"); } if (Hints.TargetModel != TargetModel) { throw new ArgumentOutOfRangeException("hints.TargetModel"); } CurrentModel = (IModel)SourceModel.Clone(null, SourceModel.Name); Difference = difference; var previous = currentAsync.Value; currentAsync.Value = this; using (NullActionHandler.Instance.Activate()) { try { var actions = new GroupingNodeAction(); ProcessStage(UpgradeStage.CleanupData, actions); ProcessStage(UpgradeStage.Prepare, actions); ProcessStage(UpgradeStage.TemporaryRename, actions); ProcessStage(UpgradeStage.Upgrade, actions); ProcessStage(UpgradeStage.CopyData, actions); ProcessStage(UpgradeStage.PostCopyData, actions); ProcessStage(UpgradeStage.Cleanup, actions); var validationHints = new HintSet(CurrentModel, TargetModel); Hints.OfType <IgnoreHint>() .Where(h => CurrentModel.Resolve(h.Path, false) != null && SourceModel.Resolve(h.Path, false) != null) .ForEach(validationHints.Add); var diff = comparer.Compare(CurrentModel, TargetModel, validationHints); if (diff != null) { CoreLog.InfoRegion(Strings.LogAutomaticUpgradeSequenceValidation); CoreLog.Info(Strings.LogValidationFailed); CoreLog.Info(Strings.LogItemFormat, Strings.Difference); CoreLog.Info("{0}", diff); CoreLog.Info(Strings.LogItemFormat + "\r\n{1}", Strings.UpgradeSequence, new ActionSequence() { actions }); CoreLog.Info(Strings.LogItemFormat, Strings.ExpectedTargetModel); TargetModel.Dump(); CoreLog.Info(Strings.LogItemFormat, Strings.ActualTargetModel); CurrentModel.Dump(); throw new InvalidOperationException(Strings.ExUpgradeSequenceValidationFailure); } return(new ReadOnlyList <NodeAction>(actions.Actions, true)); } finally { currentAsync.Value = previous; } } }
/// <inheritdoc/> /// <exception cref="ArgumentOutOfRangeException"><c>hints.SourceModel</c> or <c>hints.TargetModel</c> /// is out of range.</exception> public ReadOnlyList <NodeAction> GetUpgradeSequence(Difference difference, HintSet hints) => GetUpgradeSequence(difference, hints, new Comparer());