public void Can_add_and_retrieve_migrations()
        {
            var updateDatabaseOperation
                = new UpdateDatabaseOperation(new List <DbQueryCommandTree>());

            updateDatabaseOperation.AddMigration("M1", new List <MigrationOperation>());
            updateDatabaseOperation.AddMigration("M2", new List <MigrationOperation>());

            Assert.Equal(2, updateDatabaseOperation.Migrations.Count);
        }
        public void Can_add_and_retrieve_migrations()
        {
            var updateDatabaseOperation
                = new UpdateDatabaseOperation(new List<DbQueryCommandTree>());

            updateDatabaseOperation.AddMigration("M1", new List<MigrationOperation>());
            updateDatabaseOperation.AddMigration("M2", new List<MigrationOperation>());

            Assert.Equal(2, updateDatabaseOperation.Migrations.Count);
        }
        /// <summary>
        /// Produces a script to update the database.
        /// </summary>
        /// <param name="sourceMigration">
        /// The migration to update from. If null is supplied, a script to update the
        /// current database will be produced.
        /// </param>
        /// <param name="targetMigration">
        /// The migration to update to. If null is supplied,
        /// a script to update to the latest migration will be produced.
        /// </param>
        /// <returns> The generated SQL script. </returns>
        public string ScriptUpdate(string sourceMigration, string targetMigration)
        {
            _sqlBuilder.Clear();

            if (string.IsNullOrWhiteSpace(sourceMigration))
            {
                Update(targetMigration);
            }
            else
            {
                if (sourceMigration.IsAutomaticMigration())
                {
                    throw Error.AutoNotValidForScriptWindows(sourceMigration);
                }

                var sourceMigrationId = GetMigrationId(sourceMigration);
                var pendingMigrations = GetLocalMigrations().Where(m => string.CompareOrdinal(m, sourceMigrationId) > 0);

                string targetMigrationId = null;

                if (!string.IsNullOrWhiteSpace(targetMigration))
                {
                    if (targetMigration.IsAutomaticMigration())
                    {
                        throw Error.AutoNotValidForScriptWindows(targetMigration);
                    }

                    targetMigrationId = GetMigrationId(targetMigration);

                    if (string.CompareOrdinal(sourceMigrationId, targetMigrationId) > 0)
                    {
                        throw Error.DownScriptWindowsNotSupported();
                    }

                    pendingMigrations = pendingMigrations.Where(m => string.CompareOrdinal(m, targetMigrationId) <= 0);
                }

                _updateDatabaseOperation
                    = sourceMigration == DbMigrator.InitialDatabase
                          ? new UpdateDatabaseOperation(base.CreateDiscoveryQueryTrees().ToList())
                          : null;

                Upgrade(pendingMigrations, targetMigrationId, sourceMigrationId);

                if (_updateDatabaseOperation != null)
                {
                    ExecuteStatements(base.GenerateStatements(new[] { _updateDatabaseOperation }, null));
                }
            }

            return _sqlBuilder.ToString();
        }
        public void AddMigration_should_validate_preconditions()
        {
            var updateDatabaseOperation
                = new UpdateDatabaseOperation(new List <DbQueryCommandTree>());

            Assert.Equal(
                new ArgumentException(Strings.ArgumentIsNullOrWhitespace("migrationId")).Message,
                Assert.Throws <ArgumentException>(() => updateDatabaseOperation.AddMigration(null, null)).Message);

            Assert.Equal(
                "operations",
                Assert.Throws <ArgumentNullException>(() => updateDatabaseOperation.AddMigration("M", null)).ParamName);
        }
        public void AddMigration_should_validate_preconditions()
        {
            var updateDatabaseOperation
                = new UpdateDatabaseOperation(new List<DbQueryCommandTree>());

            Assert.Equal(
                new ArgumentException(Strings.ArgumentIsNullOrWhitespace("migrationId")).Message,
                Assert.Throws<ArgumentException>(() => updateDatabaseOperation.AddMigration(null, null)).Message);

            Assert.Equal(
                "operations",
                Assert.Throws<ArgumentNullException>(() => updateDatabaseOperation.AddMigration("M", null)).ParamName);
        }
示例#6
0
        public void Generate_can_handle_update_database_operations()
        {
            var migrationSqlGenerator = new SqlCeMigrationSqlGenerator();
            var providerInvariantName = ProviderRegistry.SqlCe4_ProviderInfo.ProviderInvariantName;

            var historyRepository
                = new HistoryRepository(
                    new SqlCeConnectionFactory(providerInvariantName)
                        .CreateConnection("Foo").ConnectionString,
                    DbProviderFactories.GetFactory(providerInvariantName),
                    "MyKey",
                    null);

            var updateDatabaseOperation
                = new UpdateDatabaseOperation(historyRepository.CreateDiscoveryQueryTrees().ToList());

            updateDatabaseOperation.AddMigration("M1", new []{ new DropColumnOperation("Customers", "Foo") });

            var sql = migrationSqlGenerator.Generate(new[] { updateDatabaseOperation }, "2008").Join(s => s.Sql, Environment.NewLine);

            Assert.Equal(@"ALTER TABLE [Customers] DROP COLUMN [Foo]", sql);
        }
 protected virtual IEnumerable<MigrationStatement> Generate(UpdateDatabaseOperation operation)
 {
     return GenerateStatements(operation.Migrations.SelectMany(x => x.Operations));
 }
        /// <summary>
        /// Generates the specified update database operation which represents applying a series of migrations.
        /// The generated script is idempotent, meaning it contains conditional logic to check if individual migrations 
        /// have already been applied and only apply the pending ones.
        /// </summary>
        /// <param name="updateDatabaseOperation">The update database operation.</param>
        protected virtual void Generate(UpdateDatabaseOperation updateDatabaseOperation)
        {
            Check.NotNull(updateDatabaseOperation, "updateDatabaseOperation");

            if (!updateDatabaseOperation.Migrations.Any())
            {
                return;
            }

            using (var writer = Writer())
            {
                writer.WriteLine("DECLARE @CurrentMigration [nvarchar](max)");
                writer.WriteLine();

                foreach (var historyQueryTree in updateDatabaseOperation.HistoryQueryTrees)
                {
                    HashSet<string> _;
                    var historyQuery
                        = _sqlGenerator.GenerateSql(historyQueryTree, out _);

                    writer.Write("IF object_id('");
                    writer.Write(Escape(_sqlGenerator.Targets.Single()));
                    writer.WriteLine("') IS NOT NULL");
                    writer.Indent++;
                    writer.WriteLine("SELECT @CurrentMigration =");
                    writer.Indent++;
                    writer.Write("(");
                    writer.Write(Indent(historyQuery, writer.CurrentIndentation()));
                    writer.WriteLine(")");
                    writer.Indent -= 2;
                    writer.WriteLine();
                }

                writer.WriteLine("IF @CurrentMigration IS NULL");
                writer.Indent++;
                writer.WriteLine("SET @CurrentMigration = '0'");

                Statement(writer);
            }

            var existingStatements = _statements;

            foreach (var migration in updateDatabaseOperation.Migrations)
            {
                using (var writer = Writer())
                {
                    _statements = new List<MigrationStatement>();

                    GenerateStatements(migration.Operations);

                    if (_statements.Count > 0)
                    {
                        writer.Write("IF @CurrentMigration < '");
                        writer.Write(Escape(migration.MigrationId));
                        writer.WriteLine("'");
                        writer.Write("BEGIN");

                        using (var blockWriter = Writer())
                        {
                            blockWriter.WriteLine();
                            blockWriter.Indent++;

                            foreach (var migrationStatement in _statements)
                            {
                                if (string.IsNullOrWhiteSpace(migrationStatement.BatchTerminator))
                                {
                                    migrationStatement.Sql.EachLine(blockWriter.WriteLine);
                                }
                                else
                                {
                                    blockWriter.WriteLine("EXECUTE('");
                                    blockWriter.Indent++;
                                    migrationStatement.Sql.EachLine(l => blockWriter.WriteLine(Escape(l)));
                                    blockWriter.Indent--;
                                    blockWriter.WriteLine("')");
                                }
                            }

                            writer.WriteLine(blockWriter.InnerWriter.ToString().TrimEnd());
                        }

                        writer.WriteLine("END");

                        existingStatements.Add(
                            new MigrationStatement
                                {
                                    Sql
                                        = writer.InnerWriter.ToString()
                                });
                    }
                }
            }

            _statements = existingStatements;
        }
        public void Generate_can_handle_update_database_operations()
        {
            var migrationSqlGenerator = new SqlServerMigrationSqlGenerator();

            var historyRepository
                = new HistoryRepository(
                    Mock.Of<InternalContextForMock>(),
                    new SqlConnectionFactory().CreateConnection("Foo").ConnectionString,
                    DbProviderFactories.GetFactory(ProviderRegistry.Sql2008_ProviderInfo.ProviderInvariantName),
                    "MyKey",
                    null,
                    HistoryContext.DefaultFactory,
                    schemas: new[] { "dbo", "foo", "bar" });

            var updateDatabaseOperation
                = new UpdateDatabaseOperation(historyRepository.CreateDiscoveryQueryTrees().ToList());

            updateDatabaseOperation.AddMigration(
                "V1",
                new MigrationOperation[]
                    {
                        new DropColumnOperation("Customers", "Foo"),
                        new CreateProcedureOperation("Foo", "Bar")
                    });

            updateDatabaseOperation.AddMigration(
                "V2",
                new MigrationOperation[]
                    {
                        new AddColumnOperation("Customers", new ColumnModel(PrimitiveTypeKind.String) { Name = "C" }),
                        new CreateProcedureOperation("bar", "baz")
                    });

            var sql = migrationSqlGenerator.Generate(new[] { updateDatabaseOperation }, "2008").Join(s => s.Sql, Environment.NewLine);

            Assert.Equal(@"DECLARE @CurrentMigration [nvarchar](max)

IF object_id('[dbo].[__MigrationHistory]') IS NOT NULL
    SELECT @CurrentMigration =
        (SELECT TOP (1) 
        [Project1].[MigrationId] AS [MigrationId]
        FROM ( SELECT 
        [Extent1].[MigrationId] AS [MigrationId]
        FROM [dbo].[__MigrationHistory] AS [Extent1]
        WHERE [Extent1].[ContextKey] = N'MyKey'
        )  AS [Project1]
        ORDER BY [Project1].[MigrationId] DESC)

IF object_id('[foo].[__MigrationHistory]') IS NOT NULL
    SELECT @CurrentMigration =
        (SELECT TOP (1) 
        [Project1].[MigrationId] AS [MigrationId]
        FROM ( SELECT 
        [Extent1].[MigrationId] AS [MigrationId]
        FROM [foo].[__MigrationHistory] AS [Extent1]
        WHERE [Extent1].[ContextKey] = N'MyKey'
        )  AS [Project1]
        ORDER BY [Project1].[MigrationId] DESC)

IF object_id('[bar].[__MigrationHistory]') IS NOT NULL
    SELECT @CurrentMigration =
        (SELECT TOP (1) 
        [Project1].[MigrationId] AS [MigrationId]
        FROM ( SELECT 
        [Extent1].[MigrationId] AS [MigrationId]
        FROM [bar].[__MigrationHistory] AS [Extent1]
        WHERE [Extent1].[ContextKey] = N'MyKey'
        )  AS [Project1]
        ORDER BY [Project1].[MigrationId] DESC)

IF @CurrentMigration IS NULL
    SET @CurrentMigration = '0'

IF @CurrentMigration < 'V1'
BEGIN
    DECLARE @var0 nvarchar(128)
    SELECT @var0 = name
    FROM sys.default_constraints
    WHERE parent_object_id = object_id(N'Customers')
    AND col_name(parent_object_id, parent_column_id) = 'Foo';
    IF @var0 IS NOT NULL
        EXECUTE('ALTER TABLE [Customers] DROP CONSTRAINT [' + @var0 + ']')
    ALTER TABLE [Customers] DROP COLUMN [Foo]
    EXECUTE('
        CREATE PROCEDURE [Foo]
        AS
        BEGIN
            Bar
        END
    ')
END

IF @CurrentMigration < 'V2'
BEGIN
    ALTER TABLE [Customers] ADD [C] [nvarchar](max)
    EXECUTE('
        CREATE PROCEDURE [bar]
        AS
        BEGIN
            baz
        END
    ')
END
", sql);
        }
    private MigrationStatement Generate(UpdateDatabaseOperation updateDatabaseOperation)
    {
      if (updateDatabaseOperation == null)
        throw new ArgumentNullException("UpdateDatabaseOperation");

      MigrationStatement statement = new MigrationStatement();
      StringBuilder sql = new StringBuilder();
      const string idempotentScriptName = "_idempotent_script";
      SelectGenerator generator = new SelectGenerator();

      if (!updateDatabaseOperation.Migrations.Any())
        return statement;

      sql.AppendFormat("DROP PROCEDURE IF EXISTS `{0}`;", idempotentScriptName);
      sql.AppendLine();
      sql.AppendLine();

      sql.AppendLine("DELIMITER //");
      sql.AppendLine();

      sql.AppendFormat("CREATE PROCEDURE `{0}`()", idempotentScriptName);
      sql.AppendLine();
      sql.AppendLine("BEGIN");
      sql.AppendLine("  DECLARE CurrentMigration TEXT;");
      sql.AppendLine();
      sql.AppendLine("  IF EXISTS(SELECT 1 FROM information_schema.tables ");
      sql.AppendLine("  WHERE table_name = '__MigrationHistory' ");
      sql.AppendLine("  AND table_schema = DATABASE()) THEN ");

      foreach (var historyQueryTree in updateDatabaseOperation.HistoryQueryTrees)
      {
        string historyQuery = generator.GenerateSQL(historyQueryTree);
        ReplaceParemeters(ref historyQuery, generator.Parameters);
        sql.AppendLine(@"    SET CurrentMigration = (" + historyQuery + ");");
        sql.AppendLine("  END IF;");
        sql.AppendLine();
      }

      sql.AppendLine("  IF CurrentMigration IS NULL THEN");
      sql.AppendLine("    SET CurrentMigration = '0';");
      sql.AppendLine("  END IF;");
      sql.AppendLine();

      // Migrations
      foreach (var migration in updateDatabaseOperation.Migrations)
      {
        if (migration.Operations.Count == 0)
          continue;

        sql.AppendLine("  IF CurrentMigration < '" + migration.MigrationId + "' THEN ");
        var statements = Generate(migration.Operations, _providerManifestToken);
        foreach (var migrationStatement in statements)
        {
          string sqlStatement = migrationStatement.Sql;
          if (!sqlStatement.EndsWith(";"))
            sqlStatement += ";";
          sql.AppendLine(sqlStatement);
        }
        sql.AppendLine("  END IF;");
        sql.AppendLine();
      }

      sql.AppendLine("END //");
      sql.AppendLine();
      sql.AppendLine("DELIMITER ;");
      sql.AppendLine();
      sql.AppendFormat("CALL `{0}`();", idempotentScriptName);
      sql.AppendLine();
      sql.AppendLine();
      sql.AppendFormat("DROP PROCEDURE IF EXISTS `{0}`;", idempotentScriptName);
      sql.AppendLine();

      statement.Sql = sql.ToString();

      return statement;
    }
        /// <summary>
        ///     Generates the specified update database operation which represents applying a series of migrations.
        ///     The generated script is idempotent, meaning it contains conditional logic to check if individual migrations 
        ///     have already been applied and only apply the pending ones.
        /// </summary>
        /// <param name="updateDatabaseOperation">The update database operation.</param>
        protected virtual void Generate(UpdateDatabaseOperation updateDatabaseOperation)
        {
            Check.NotNull(updateDatabaseOperation, "updateDatabaseOperation");

            GenerateStatements(updateDatabaseOperation.Migrations.SelectMany(m => m.Operations));
        }