Пример #1
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in FieldType.AllFrom(changes.Current))
            {
                var desired = FieldType.GetDesired(changes, current);
                if (desired == null)
                {
                    continue;
                }

                // If we can't alter sequenced keys then no point trying!
                if (!changes.SchemaDriver.IsAlterSequencedFieldSupported &&
                    (current.State.IsSequencedPkey || desired.State.IsSequencedPkey))
                {
                    continue;
                }

                // If the fields are the same by datatype and sequenced-ness - or the sequenced-ness is irrelevant at the field level -
                // then there's nothing to change
                if (changes.SchemaDriver.DbDriver.StringEquals(current.State.DataType, desired.State.DataType) &&
                    (current.State.IsSequencedPkey == desired.State.IsSequencedPkey || !changes.SchemaDriver.IsSequencedPartOfFieldDeclaration))
                {
                    continue;
                }

                // Try the alter statement. If it doesn't work, the field will be dropped and added.
                var altered  = current.With(s => s.WithTypeChange(desired.State.DataType, desired.State.IsSequencedPkey, desired.State.SequenceName));
                var alterSql = changes.SchemaDriver.GetAlterFieldTypeSql(altered.ParentName, altered.Name, altered.State.DataType, altered.State.IsNullable, altered.State.IsSequencedPkey, altered.State.SequenceName);
                changes.Put(alterSql, ErrorResponse.Ignore, altered);
            }
        }
Пример #2
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            // This isn't really anything to do with pre-upgrade-hooks but there's no other good place to give this warning!
            foreach (var query in QueryType.AllFrom(changes.Current))
            {
                if (query.State != null && query.State.Body.StartsWith("<FAIL>"))
                {
                    output.Warning(query.State.Body.Substring(6));
                }
            }

            foreach (var hook in PreUpgradeHookType.AllFrom(changes.Current))
            {
                var proc = QueryType.GetFrom(changes.Current, hook.ParentIdentifier);
                if (proc.State == null || proc.State is QueryType.FunctionState || proc.State.Parameters.Any())
                {
                    output.Warning("Cannot execute pre-upgrade hook " + proc.Name + ": stored procedure does not exist or has parameters.");
                }

                changes.Put(changes.SchemaDriver.GetExecuteProcSql(hook.ParentName));

                if (changes.Desired.Get(hook) == null)
                {
                    changes.Remove(null, hook);
                }
            }
        }
Пример #3
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            var fieldsToBeAdded = from field in FieldType.AllFrom(changes.Desired)
                                  where !changes.Current.Contains(field)
                                  orderby field.State.OrdinalPosition
                                  select field;

            // FIXME It's a little weird to be ordering by OrdinalPosition independent of table, because it means we jump from table to table adding all
            // fifth columns, then all sixth columns, etc, but it doesn't hurt anything.

            foreach (var desired in fieldsToBeAdded)
            {
                // We create all fields as initially nullable...
                var fieldToCreate = desired.With(s => s.WithNullable(true));

                // ... except for sequenced fields on databases that don't support altering sequenced fields to notnull later.
                if (desired.State.IsSequencedPkey && !changes.SchemaDriver.IsAlterSequencedFieldSupported)
                {
                    fieldToCreate = desired;
                }

                // Add the field
                changes.Put(changes.SchemaDriver.GetAddFieldSql(desired.ParentName, desired.Name, desired.State.DataType, desired.State.IsSequencedPkey, desired.State.SequenceName),
                            fieldToCreate);
            }
        }
Пример #4
0
        public static bool TriggerHasChanged(SchemaChanges changes, SubObjectState <TriggerType, State> current)
        {
            if (current == null)
            {
                return(true);
            }

            var desired = changes.Desired.Get(current);

            // The trigger doesn't exist in the desired schema at all
            if (desired == null)
            {
                return(true);
            }

            // The trigger's contents are different
            if (current.State.Timing != desired.State.Timing ||
                current.State.Events != desired.State.Events ||
                !Nstring.DBEquivalentComparer.Equals(current.State.Body, desired.State.Body))
            {
                return(true);
            }

            // The table might need to be dropped entirely to reorder the columns
            if (FieldType.IsFieldReorderPossiblyNeeded(changes, current.ParentIdentifier))
            {
                return(true);
            }

            return(false);
        }
Пример #5
0
        public static bool FkeyHasChanged(SchemaChanges changes, SubObjectState <FkeyType, FkeyType.State> current)
        {
            if (current == null)
            {
                return(true);
            }

            var desired = changes.Desired.Get(current);

            // The foreign key doesn't exist in the desired schema at all
            if (desired == null)
            {
                return(true);
            }

            // The destination table has changed
            if (desired.State.ToTable != current.State.ToTable)
            {
                return(true);
            }

            // Either the from or to table might need to be dropped entirely to reorder fields
            if (FieldType.IsFieldReorderPossiblyNeeded(changes, current.ParentIdentifier) ||
                FieldType.IsFieldReorderPossiblyNeeded(changes, current.State.ToTable))
            {
                return(true);
            }

            // It's changed from cascading to not or vice versa
            if (desired.State.IsCascadeDelete != current.State.IsCascadeDelete)
            {
                return(true);
            }

            // The sequence of field name pairs don't match
            if (!Enumerable.SequenceEqual(desired.State.Joins, current.State.Joins, FieldPair.GetComparer(changes.DbDriver)))
            {
                return(true);
            }

            // Any of the fields in either table are changing
            if (current.State.Joins.Any(field =>
                                        FieldType.FieldHasChanged(changes, current.ParentIdentifier, FieldType.Identifier(field.FromFieldName)) ||
                                        FieldType.FieldHasChanged(changes, current.State.ToTable, FieldType.Identifier(field.ToFieldName))))
            {
                return(true);
            }

            // The unique index on the destination table that corresponds to the destination fields is changing
            var ukey = UniqueIndexType.ChildrenFrom(changes.Current, current.State.ToTable)
                       .SingleOrDefault(key => new HashSet <Identifier>(key.State.IndexState.Fields).SetEquals(from field in current.State.Joins
                                                                                                               select FieldType.Identifier(field.ToFieldName)));

            if (ukey != null && UniqueIndexType.IndexHasChanged(changes, ukey))
            {
                return(true);
            }

            return(false);
        }
Пример #6
0
        internal static bool IndexStateHasChanged(SchemaChanges changes, Identifier table, IndexState current, IndexState desired)
        {
            // The table might need to be dropped entirely to reorder the columns
            if (FieldType.IsFieldReorderPossiblyNeeded(changes, table))
            {
                return(true);
            }

            // The sequence of field names don't match
            if (!Enumerable.SequenceEqual(desired.FieldNames, current.FieldNames, changes.DbDriver.DbStringComparer))
            {
                return(true);
            }

            // Something custom about the index has changed
            if (!changes.SchemaDriver.IsIndexCustomStateEqual(current.CustomState, desired.CustomState))
            {
                return(true);
            }

            // Any of the fields in the index are changing
            if (current.Fields.Any(field => FieldType.FieldHasChanged(changes, table, field)))
            {
                return(true);
            }

            return(false);
        }
Пример #7
0
        public static bool IndexHasChanged(SchemaChanges changes, SubObjectState <UniqueIndexType, UniqueIndexType.State> current)
        {
            if (current == null)
            {
                return(true);
            }

            var desired = changes.Desired.Get(current);

            // The index doesn't exist in the desired schema at all
            if (desired == null)
            {
                return(true);
            }

            // It's changed from primary key to not or vice versa
            if (desired.State.IsPrimaryKey != current.State.IsPrimaryKey)
            {
                return(true);
            }

            // The index fields are different
            if (IndexState.IndexStateHasChanged(changes, current.ParentIdentifier, current.State.IndexState, desired.State.IndexState))
            {
                return(true);
            }

            return(false);
        }
Пример #8
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            // This step covers both unique keys (including primary keys) and non-unique indexes
            foreach (var desired in UniqueIndexType.AllFrom(changes.Desired))
            {
                if (changes.Current.Contains(desired))
                {
                    continue;
                }

                var sql = changes.SchemaDriver.GetCreateUniqueConstraintSql(desired.ParentName, desired.State.IsPrimaryKey, desired.Name,
                                                                            desired.State.IndexState.FieldNames, desired.State.IndexState.CustomState);
                changes.Put(sql, desired);
            }
            foreach (var desired in NonUniqueIndexType.AllFrom(changes.Desired))
            {
                if (changes.Current.Contains(desired))
                {
                    continue;
                }

                var sql = changes.SchemaDriver.GetCreateIndexSql(desired.ParentName, desired.Name, desired.State.FieldNames, desired.State.CustomState);
                changes.Put(sql, desired);
            }
        }
Пример #9
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var pending in PendingReorderTableType.AllFrom(changes.Current))
            {
                var originalTable = TableType.Identifier(pending.Name);
                var tempTable     = pending.ParentIdentifier;

                if (object.Equals(originalTable, tempTable))
                {
                    // If the database fails at precisely the wrong time, the rename can be succeed without dropping the PendingReorder,
                    // so the table thinks it's to be renamed to itself. In that case we need to drop the PendingReorder, NOT drop the table!
                    output.Verbose("Cleaning up after completed reorder of columns in " + originalTable);
                    changes.Remove(null, pending);
                }
                else if (changes.Current.ContainsRoot(originalTable))
                {
                    // If the original table still exists, we drop the temp table so we can start over (in case the data in the table has changed since the temp table
                    // was created, or the reordering has somehow become unnecessary).
                    output.Verbose("Clearing pending reorder of columns in " + originalTable);
                    changes.Remove(changes.SchemaDriver.GetDropTableSql(pending.ParentName), changes.Current.GetRoot(pending.ParentIdentifier));
                }
                else
                {
                    // The original table has been dropped so we need to recreate it by renaming the temp table.
                    output.Verbose("Completing pending reordering of columns in " + originalTable);
                    changes.Rename(changes.SchemaDriver.GetRenameTableSql(tempTable.Name, originalTable.Name), tempTable, originalTable);

                    // And forget about the pending rename.
                    changes.Remove(null, pending.WithParent(originalTable));
                }
            }
        }
Пример #10
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            if (changes.Options.FieldDropBehavior == DropBehavior.SkipDrop)
            {
                return;
            }
            var errorResponse = changes.Options.FieldDropBehavior == DropBehavior.TryDrop ? ErrorResponse.Ignore : ErrorResponse.Fail;

            foreach (var current in FieldType.AllFrom(changes.Current))
            {
                if (changes.Desired.Contains(current))
                {
                    continue;
                }

                // If we're going to drop the whole table then don't bother dropping the field
                if (!changes.Desired.ContainsRoot(current.ParentIdentifier))
                {
                    continue;
                }

                if (!changes.AllowDropWithPossibleDataLoss(current, changes.Options.FieldDropBehavior))
                {
                    continue;
                }

                changes.Remove(changes.SchemaDriver.GetDropFieldSql(current.ParentName, current.Name), current);
            }
        }
Пример #11
0
        public static bool FieldHasChanged(SchemaChanges changes, Identifier table, Identifier field)
        {
            var current = FieldType.GetFrom(changes.Current, table, field);
            var desired = FieldType.GetFrom(changes.Desired, table, field);

            return(current == null || desired == null || !FieldType.IsEqual(current.State, desired.State, changes.SchemaDriver.IsSequencedPartOfFieldDeclaration));
        }
Пример #12
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in FieldType.AllFrom(changes.Current))
            {
                // Don't need to set null if it's already null!
                if (current.State.IsNullable)
                {
                    continue;
                }

                // Don't need to set null if nullability is already correct
                var desired = FieldType.GetDesired(changes, current);
                if (desired != null && !desired.State.IsNullable)
                {
                    continue;
                }

                // Sequenced pkey fields can't be made nullable
                if (current.State.IsSequencedPkey && !changes.SchemaDriver.IsAlterSequencedFieldSupported)
                {
                    continue;
                }

                // The field is either being dropped (desired == null) or set to nullable
                // Either way we set it to null now
                // We know to specify "false, null" for sequenced key field because the desired state can't be nullable if it's sequenced
                var setNullSql = changes.SchemaDriver.GetSetFieldNullSql(current.ParentName, current.Name, current.State.DataType, false, null);
                changes.Put(setNullSql, current.With(state => state.WithNullable(true)));
            }
        }
Пример #13
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            var completion = CompletionType.AllFrom(changes.Desired).Single();

            if (!changes.Current.Contains(completion))
            {
                changes.Put(null, completion);
            }
        }
Пример #14
0
 private bool isSequencedPkeyToBeDropped(SchemaChanges changes, SubObjectState <FieldType, FieldType.State> current)
 {
     if (current.State.IsSequencedPkey)
     {
         var desiredTable = TableType.GetDesiredIdentifier(changes, current.ParentIdentifier);
         return(desiredTable != null && FieldType.ChildrenFrom(changes.Desired, desiredTable).Any(field => field.State.IsSequencedPkey));
     }
     return(false);
 }
Пример #15
0
        public static SubObjectState <FieldType, State> GetDesired(SchemaChanges changes, SubObjectState <FieldType, State> current)
        {
            var desiredTable = TableType.GetDesiredIdentifier(changes, current.ParentIdentifier);

            if (desiredTable == null)
            {
                return(null);
            }

            return(GetFrom(changes.Desired, desiredTable, current.Identifier));
        }
Пример #16
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in FulltextCatalogType.AllFrom(changes.Current))
            {
                if (changes.Desired.Contains(current))
                {
                    continue;
                }

                changes.RemoveWithoutTransaction(changes.SchemaDriver.GetDropFulltextCatalogSql(current.Name), current);
            }
        }
Пример #17
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in SequenceType.AllFrom(changes.Current))
            {
                if (changes.Desired.Contains(current))
                {
                    continue;
                }

                changes.Remove(changes.SchemaDriver.GetDropSequenceSql(current.Name), current);
            }
        }
Пример #18
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in TriggerType.AllFrom(changes.Current))
            {
                if (!TriggerType.TriggerHasChanged(changes, current))
                {
                    continue;
                }

                changes.Remove(changes.SchemaDriver.GetDropTriggerSql(current.ParentName, current.Name), current);
            }
        }
Пример #19
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in FulltextIndexType.AllFrom(changes.Current))
            {
                if (!FulltextIndexType.FulltextIndexHasChanged(changes, current))
                {
                    continue;
                }

                changes.RemoveWithoutTransaction(changes.SchemaDriver.GetDropFulltextIndexSql(current.Name), current);
            }
        }
Пример #20
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var desired in SequenceType.AllFrom(changes.Desired))
            {
                if (changes.Current.Contains(desired))
                {
                    continue;
                }

                changes.Put(changes.SchemaDriver.GetCreateSequenceSql(desired.Name), desired);
            }
        }
Пример #21
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var desired in FkeyType.AllFrom(changes.Desired))
            {
                if (changes.Current.Contains(desired))
                {
                    continue;
                }

                changes.Put(changes.SchemaDriver.GetAddFkeySql(desired.ParentName, desired.State.ToTableName, desired.Name, desired.State.IsCascadeDelete,
                                                               desired.State.Joins), desired);
            }
        }
Пример #22
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in ViewType.AllFrom(changes.Current))
            {
                var desired = changes.Desired.Get(current);
                if (desired != null && ViewType.IsEqual(current.State, desired.State, changes.DbDriver))
                {
                    continue;
                }

                changes.Remove(changes.SchemaDriver.GetDropViewSql(current.Name), current);
            }
        }
Пример #23
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var desired in FulltextIndexType.AllFrom(changes.Desired))
            {
                if (changes.Current.Contains(desired))
                {
                    continue;
                }

                changes.PutWithoutTransaction(changes.SchemaDriver.GetCreateFulltextIndexSql(desired.Name, desired.ParentName, desired.State.KeyName, desired.State.Columns),
                                              desired);
            }
        }
Пример #24
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var desired in TriggerType.AllFrom(changes.Desired))
            {
                if (changes.Current.Contains(desired))
                {
                    continue;
                }

                var table = desired.ParentIdentifier;

                changes.Put(changes.SchemaDriver.GetCreateTriggerSql(table.Name, desired.Name, desired.State.Timing, desired.State.Events, desired.State.Body), desired);
            }
        }
Пример #25
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var desired in QueryType.AllFrom(changes.Desired))
            {
                var current = changes.Current.Get(desired);
                if (current != null && QueryType.IsEqual(current.State, desired.State, changes.DbDriver))
                {
                    continue;
                }

                if (desired.State != null)
                {
                    var    body       = desired.State.Body;
                    var    parameters = desired.State.Parameters;
                    string createSql;

                    var functionState = desired.State as QueryType.FunctionState;
                    if (functionState != null)
                    {
                        createSql = changes.SchemaDriver.GetCreateFunctionSql(desired.Name, parameters, functionState.ReturnType, body);
                    }
                    else
                    {
                        createSql = changes.SchemaDriver.GetCreateProcSql(desired.Name, parameters, body);
                    }

                    changes.Put(createSql, desired);
                }
                else
                {
                    if (current == null)
                    {
                        changes.Put(null, desired);
                    }
                }
            }

            // Restore all pre-upgrade-hooks corresponding to procedures that have been successfully updated.
            foreach (var upgradeHook in PreUpgradeHookType.AllFrom(changes.Desired))
            {
                var desiredProc = QueryType.GetFrom(changes.Desired, upgradeHook.ParentIdentifier);

                var currentProc = changes.Current.Get(desiredProc);
                if (currentProc != null && QueryType.IsEqual(currentProc.State, desiredProc.State, changes.DbDriver))
                {
                    changes.Put(null, upgradeHook);
                }
            }
        }
Пример #26
0
        public override void Perform(SchemaChanges changes, IOutput output)
        {
            foreach (var current in QueryType.AllFrom(changes.Current))
            {
                // Queries that are neither stored proc or stored function in the current schema don't need to be dropped!
                if (current.State == null)
                {
                    continue;
                }

                var desired = changes.Desired.Get(current);
                if (desired != null)
                {
                    if (changes.SchemaDriver.IsCreateOrReplaceProcSupported)
                    {
                        // If CREATE OR REPLACE PROCEDURE is supported in the current database, there's no need to drop it unless
                        // it's changed from function to procedure or vice versa.
                        if (QueryType.IsTypeEqual(current.State, desired.State))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // Even if CREATE OR REPLACE PROCEDURE is not supported, we don't need to drop it if it's
                        // not changed at all.
                        if (QueryType.IsEqual(current.State, desired.State, changes.DbDriver))
                        {
                            continue;
                        }
                    }
                }

                var dropSql = current.State is QueryType.FunctionState
                    ? changes.SchemaDriver.GetDropFunctionSql(current.Name)
                    : changes.SchemaDriver.GetDropProcSql(current.Name);

                if (desired != null)
                {
                    // The query isn't going away entirely so we don't want to lose its associated before statements
                    changes.Put(dropSql, QueryType.CreateUnstored(current.Name));
                }
                else
                {
                    changes.Remove(dropSql, current);
                }
            }
        }
Пример #27
0
        private void renameTempTables(SchemaChanges changes, IOutput output)
        {
            // For any PendingReorderTables where the original table doesn't exist, rename the table to the target name and remove the pending rename.
            foreach (var pending in PendingReorderTableType.AllFrom(changes.Current))
            {
                var originalTable = TableType.Identifier(pending.Name);
                var tempTable     = pending.ParentIdentifier;

                if (changes.Current.ContainsRoot(originalTable))
                {
                    continue;
                }

                changes.Rename(changes.SchemaDriver.GetRenameTableSql(tempTable.Name, originalTable.Name), tempTable, originalTable);
                changes.Remove(null, pending.WithParent(originalTable));
            }
        }
Пример #28
0
        private void dropOriginalTables(SchemaChanges changes, IOutput output)
        {
            // For any tables that are the target of existing PendingReorderTables, drop them (without prompting).
            foreach (var pending in PendingReorderTableType.AllFrom(changes.Current))
            {
                var originalTable = TableType.Identifier(pending.Name);
                var tempTable     = pending.ParentIdentifier;

                if (!changes.Current.ContainsRoot(originalTable))
                {
                    continue;
                }

                // FIXME: (Consider sanity checks before doing this - anything from "confirm all the right column names exist" to "confirm the right number of rows exist" to
                // "confirm all the data is identical in all the rows")
                changes.Remove(changes.SchemaDriver.GetDropTableSql(originalTable.Name), changes.Current.GetRoot(originalTable));
            }
        }
Пример #29
0
        public static bool IndexHasChanged(SchemaChanges changes, SubObjectState <NonUniqueIndexType, IndexState> current)
        {
            var desired = changes.Desired.Get(current);

            // The index doesn't exist in the desired schema at all
            if (desired == null)
            {
                return(true);
            }

            // The index fields are different
            if (IndexState.IndexStateHasChanged(changes, current.ParentIdentifier, current.State, desired.State))
            {
                return(true);
            }

            return(false);
        }
Пример #30
0
        public static Identifier GetDesiredIdentifier(SchemaChanges changes, Identifier ident)
        {
            CheckType(ident);

            if (changes.Desired.ContainsRoot(ident))
            {
                return(ident);
            }

            var rename = TableRenameType.GetFrom(changes.Desired, TableRenameType.Identifier(ident.Name));

            if (rename != null)
            {
                return(rename.State.ToTable);
            }

            return(null);
        }
Пример #31
0
 internal SchemaChange(SchemaChanges change, string name, string dbDataType)
 {
     Change = change;
     Name = name;
     DbDataType = dbDataType;
 }