private static void runSteps(SchemaConnection connection, IOutput output, SchemaChangeOptions options, string phaseName, IEnumerable <ISchemaProvider> providers, ref DatabaseState currentState, ref ImmutableHashSet <ObjectType> objectTypes, ref StateStorage storage) { using (var progress = output.ProgressBlock()) { output.Message(phaseName + ": Starting..."); // Get object types, get steps, get desired state, get current state, everything else var chunks = progress.GetChunks(1, 1, 5, 5, 1, 50).ToList(); var oldTypes = objectTypes; using (chunks[0].Start()) { objectTypes = (from provider in providers from objectType in provider.GetObjectTypes(connection.SchemaDriver) select objectType).ToImmutableHashSet(); output.Verbose(phaseName + ": Object types: " + string.Join(", ", objectTypes)); } // Get all the steps and organize them into order IImmutableList <StepBase> steps; using (chunks[1].Start()) { steps = sortSteps(from objectType in objectTypes from step in objectType.Steps select step); output.Verbose(phaseName + ": Steps: " + string.Join(", ", steps)); } // Get the desired state DatabaseState desiredState; using (chunks[2].Start()) { output.Verbose(phaseName + ": Determining desired database state..."); desiredState = DatabaseState.Create(from provider in providers from state in provider.GetDesiredState(connection, chunks[2]) select state); foreach (var overrideProvider in providers.OfType <ISchemaOverrideProvider>()) { desiredState = overrideProvider.ApplyOverrides(connection, desiredState); } output.Message(phaseName + ": " + counted(desiredState.Count, "object") + " desired."); } ObjectTypeHelper helper; using (chunks[3].Start()) { // Get the current state output.Verbose(phaseName + ": Determining current database state..."); helper = new ObjectTypeHelper(objectTypes, storage != null, connection.DbDriver); currentState = currentState.With(from objectType in objectTypes where !oldTypes.Contains(objectType) from state in objectType.GetExistingObjects(connection, helper) select state); output.Verbose(phaseName + ": " + counted(currentState.Count, "object") + " existing."); } // Filter to only things that are relevant here using (chunks[4].Start()) { output.Verbose(phaseName + ": Filtering..."); if (storage != null) { var priorKnownRoots = storage.GetKnownRoots(helper); var priorKnownSubs = storage.GetKnownSubs(helper); // Anything that exists in *both* current and desired is treated as known and assumed to be in-scope for consideration var knownRoots = priorKnownRoots.Union(from root in currentState.RootObjects where desiredState.Contains(root) select root.Identifier); foreach (var extra in knownRoots.Except(priorKnownRoots)) { storage.PutRoot(extra); } // FIXME: remove from storage anything that's of a type that's under consideration but is neither current nor desired var knownSubs = priorKnownSubs.Union(from sub in currentState.AllChildren where desiredState.Contains(sub) select Tuple.Create(sub.ParentIdentifier, sub.Identifier)); foreach (var extra in knownSubs.Except(priorKnownSubs)) { storage.PutSub(extra.Item1, extra.Item2); } // FIXME: remove from storage anything that's of a type that's under consideration but is neither current nor desired var _storage = storage; var initialState = currentState; currentState = currentState.WithFilters((root) => desiredState.ContainsRoot(root) || Objects.Tables.TableRenameType.ContainsRenameTo(desiredState, root) || Objects.Tables.PendingReorderTableType.IsTablePendingReorder(initialState, root) || (options.PreserveUnknownObjects ? !initialState.ContainsRoot(root) : _storage.ContainsRoot(root)), null); } else { // FIXME this doesn't allow for any flexibility in whether the nrdo_ tables live in dbo currentState = currentState.WithFilters(root => root.Name.StartsWith("dbo.nrdo_", StringComparison.OrdinalIgnoreCase), null); } output.Message(phaseName + ": " + counted(currentState.Count, "applicable existing object") + "."); } var isFullRun = storage != null; if (isFullRun) { // Sanity check for any before statements that will be skipped due to the associated step not existing var stepSet = new HashSet <string>(from step in steps select step.Identifier, Nstring.DBEquivalentComparer); var current = currentState; // Can't use ref parameter directly inside linq query var unknownBefores = from stmt in BeforeStatementType.AllFrom(desiredState) where !stepSet.Contains(stmt.State.Step) && !current.Contains(stmt) select stmt; foreach (var stmt in unknownBefores) { output.Warning(stmt.Name + " on " + stmt.ParentIdentifier + " will not be executed because there is no such step as " + stmt.State.Step); } } var changes = SchemaChanges.Create(currentState, desiredState, connection, chunks[5], options, storage); changes.PerformSteps(steps, phaseName, isFullRun); currentState = changes.Current.WithoutFilters(); storage = changes.Storage; if (changes.HasFailed) { throw new ApplicationException("Schema update failed"); } output.Message(phaseName + ": Completed."); output.Message(phaseName + ": " + counted(changes.StatementCount, "statement") + " executed."); output.Message(phaseName + ": " + counted(currentState.Count, "object") + " existing."); } }