public void Execute(IDbPlatform platform, IRefactorNode refactorNode, Environment environment)
        {
            string auditTableName = AstNodePropertyUtil.AsString(refactorNode.Properties["audit-table"].Value);
            string tableName = AstNodePropertyUtil.AsString(refactorNode.Properties["table"].Value);
            //
            // We only need columns here
            ITableDefinition tableDefinition = environment.Schema.GetTable(tableName);

            ITableDefinition auditTable = new TableDefinition(auditTableName);
            foreach(ColumnDefinition column in tableDefinition.Columns)
            {
                //
                // Keep name and type only.
                IColumnDefinition auditColumn = new ColumnDefinition(column.Name);
                
                //
                // Special handling for rowversion
                if(column.Type == DbType.Time)
                {
                    auditColumn.Length = 8;
                    auditColumn.Type = DbType.Binary;
                } // if
                else
                {
                    auditColumn.Length = column.Length;
                    auditColumn.Type = column.Type;
                } // else

                auditColumn.Nullable = true;

                auditTable.AddColumn(auditColumn);
            } // foreach

            environment.Schema.AddTable(auditTable);

            //
            // Now build an AST
            IAddTableNode addAuditTableNode = new AddTableNode(refactorNode.Parent, auditTableName);
            foreach(IColumnDefinition auditColumn in auditTable.Columns)
            {
                IAddColumnNode addAuditColumnNode = new AddColumnNode(addAuditTableNode, auditColumn.Name);
                addAuditColumnNode.Type = auditColumn.Type;
                addAuditColumnNode.Length = auditColumn.Length;
                addAuditColumnNode.Nullable = auditColumn.Nullable;

                addAuditTableNode.ChildNodes.Add(addAuditColumnNode);
            } // foreach

            addAuditTableNode.Parent.ChildNodes.Add(addAuditTableNode);
        }
        public MigrationScriptCollection CompileMigrationScripts(TextReader migrationDefinition)
        {
            Environment environment = new Environment();

            MigrationScriptsCodeGenerator migrationScriptsCodeGenerator =
                new MigrationScriptsCodeGenerator(dbPlatform, nativeSqlResourceProvider, migrationMode);
            //migrationScriptsCodeGenerator.SetEnvironment(environment);

            IMdlCompiler mdlCompiler = new MdlCompiler(migrationScriptsCodeGenerator, environment);
            mdlCompiler.AddCompilerStageAfter<AstFlattenerCompilerStage>(new DbNamingCompilerStage(dbPlatform.NamingStrategy));
            mdlCompiler.AddCompilerStageBefore<AstFlattenerCompilerStage>(new RefactoringStage(dbPlatform));

            mdlCompiler.Compile(migrationDefinition, MdlCompilationOptions.All);

            return migrationScriptsCodeGenerator.MigrationScripts;
        }
        private Schema GetSchema()
        {
            Environment environment = new Environment();

            using (Stream resourceStream =
                Assembly.GetExecutingAssembly().GetManifestResourceStream("octalforty.Wizardby.Tests.Resources.Blog.mdl"))
            {
                IMdlParser mdlParser = new MdlParser(MdlParserTestFixture.CreateScanner(
                                                         new StreamReader(resourceStream, Encoding.UTF8)));

                IMdlCompiler mdlCompiler = new MdlCompiler(new NullCodeGenerator(), environment);
                mdlCompiler.Compile(mdlParser.Parse(), MdlCompilationOptions.All);
            } // using
            
            return environment.Schema;
        }
        public void GenerateScript()
        {
            IAstNode astNode;
            using (Stream resourceStream =
                Assembly.GetExecutingAssembly().GetManifestResourceStream("octalforty.Wizardby.Tests.Resources.Blog.mdl"))
            {
                IMdlParser mdlParser = new MdlParser(MdlParserTestFixture.CreateScanner(new StreamReader(resourceStream, Encoding.UTF8)));
                astNode = mdlParser.Parse();
            } // using

            IDbPlatform platform = new SqlServer2000Platform();
            Environment environment = new Environment();
            IMdlCompiler compiler = new MdlCompiler(new NullCodeGenerator(), environment);

            compiler.RemoveCompilerStage<DowngradeGenerationStage>();
            compiler.RemoveCompilerStage<UpgradeGenerationStage>();
            compiler.AddCompilerStageAfter<AstFlattenerCompilerStage>(new DbNamingCompilerStage(platform.NamingStrategy));
            compiler.Compile(astNode, MdlCompilationOptions.All);

            DbStatementBatchWriter batchWriter = new DbStatementBatchWriter();
            IDbScriptGenerator scriptGenerator = platform.Dialect.CreateScriptGenerator(batchWriter);
            //scriptGenerator.SetEnvironment(environment);

            foreach(IVersionNode versionNode in Algorithms.Filter<IAstNode, IVersionNode>(astNode.ChildNodes))
                versionNode.Accept(scriptGenerator);

            System.Console.WriteLine(batchWriter.GetStatementBatches()[0].Clean());

            Assert.AreEqual(@"create table [SchemaInfo] (
[Version] bigint not null  
);
create table [Author] (
[ID] int not null identity ,
[FirstName] nvarchar(200) not null ,
[LastName] nvarchar(200) not null ,
[EmailAddress] nvarchar(200) not null ,
[Login] nvarchar(200) not null ,
[Password] varbinary(64) null ,
primary key (ID)
);
create table [Tag] (
[ID] int not null identity ,
[Name] nvarchar(200) not null ,
primary key (ID)
);
create table [Blog] (
[ID] int not null identity ,
[Name] nvarchar(200) not null ,
[Description] nvarchar(max) not null ,
primary key (ID)
);
create table [BlogPost] (
[ID] int not null identity ,
[Title] nvarchar(200) not null ,
[Slug] nvarchar(200) not null ,
[BlogID] int not null ,
[AuthorID] int not null ,
primary key (ID)
);
create table [BlogPostTagJunction] (
[BlogPostID] int not null,
[TagID] int not null,
);
create unique nonclustered index [UQ_Version] on [SchemaInfo] ([Version]);
create unique nonclustered index [IX_EmailAddress] on [Author] ([EmailAddress]);
create unique nonclustered index [IX_Login] on [Author] ([Login]);
alter table [BlogPost] add constraint [FK1] foreign key ([BlogID]) references [Blog] ([ID]);
alter table [BlogPost] add constraint [FK2] foreign key ([AuthorID]) references [Author] ([ID]);
alter table [BlogPostTagJunction] add constraint [FK3] foreign key ([BlogPostID]) references [BlogPost] ([ID]);
alter table [BlogPostTagJunction] add constraint [FK4] foreign key ([TagID]) references [Tag] ([ID]);
create table [BlogPostComment] (
[ID] int not null identity primary key,
[BlogPostID] int not null,
[AuthorEmailAddress] nvarchar(200) not null,
[Content] nvarchar(max) not null,
);
alter table [BlogPostComment] add constraint [FK5] foreign key ([BlogPostID]) references [BlogPost] ([ID]);
create table [Media] (
[ID] int not null identity primary key,
[TypeID] int ,
[Name] nvarchar(200) not null,
[MimeType] nvarchar(200) not null,
[Length] int ,
[BlogPostID] int null,
[BlogPostCommentID] int null,
);
create table [User] (
[ID] int not null identity primary key,
[Login] nvarchar(200) not null,
[Password] varbinary(64) not null,
);
alter table [Media] add constraint [DF_MimeType] default ('text/xml') for [MimeType];
alter table [Media] add constraint [FK10] foreign key ([BlogPostID]) references [BlogPost] ([ID]);
alter table [Media] add constraint [FK11] foreign key ([BlogPostCommentID]) references [BlogPostComment] ([ID]);
create unique nonclustered index [IX_Login] on [User] ([ID], [Login] desc);
create table [Forum] (
[ID] int not null identity primary key,
[Name] nvarchar(200) not null,
[ModeratorUserID] int not null,
);
alter table [Forum] add constraint [FK_FOO] foreign key ([ModeratorUserID]) references [User] ([ID]);
drop index [IX_Login] on [User];
create unique nonclustered index [IX_Login] on [User] ([ID], [Login] desc);
create table [BlogAuthorJunction] (
[BlogID] int not null,
[AuthorID] int not null,
);
alter table [BlogAuthorJunction] add constraint [FK12] foreign key ([BlogID]) references [Blog] ([ID]);
alter table [BlogAuthorJunction] add constraint [FK13] foreign key ([AuthorID]) references [Author] ([ID]);
alter table [Forum] add [Slug] nvarchar(200) not null;
alter table [Forum] drop column [Slug];
alter table [Forum] add [Slug] nvarchar(200) not null;
alter table [Forum] alter column [Slug] nvarchar(200) null;
alter table [Forum] drop constraint [FK_FOO];
drop table [Forum];
alter table [BlogPostTagJunction] drop constraint [FK4];
drop table [Tag];
".Clean(), batchWriter.GetStatementBatches()[0].Clean());
        }
        public void BindReferences()
        {
            Environment environment = new Environment();

            NamingCompilerStage namingCompilerStage = new NamingCompilerStage();
            namingCompilerStage.SetEnvironment(environment);

            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(environment);

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    templates:
        table template Foo

    version 1:    
        add table ""BlogPost"":
            ID type => Int32, primary-key => true

    version 2:
        remove table BlogPost

    version 3:
        add table BlogPost:            
            Version type => Int32
            ID type => Int32, primary-key => true

        alter table BlogPost:
            add column PublishedOn type => DateTime

        add table ""BlogPostApproval"":
            BlogPostID type => Int32:
                reference ""FK0"" pk-table => BlogPost
            ApprovedOn type => DateTime

            add reference FK1 fk-columns => [BlogPostID, ApprovedOn], pk-table => BlogPost, pk-column => Version
            
        add reference FK2 fk-table => BlogPostApproval, fk-columns => [ApprovedOn,BlogPostID], pk-table => BlogPost

        alter table BlogPostApproval:
            remove reference FK2
            add column Bar:
                reference pk-table => BlogPost, pk-column => ID, fk-table => BlogPost, fk-column => ID

        remove reference FK1 fk-table => BlogPostApproval
        add reference FK3 fk-table => BlogPostApproval, fk-column => BlogPostID, pk-table => BlogPost, pk-column => ID")).Parse();

            astNode.Accept(namingCompilerStage);
            astNode.Accept(bindingStage);

            IVersionNode version3Node = (IVersionNode)astNode.ChildNodes[3];
            IAddTableNode addTableBlogPostApprovalNode = (IAddTableNode)version3Node.ChildNodes[2];

            IAddReferenceNode addReferenceBlogPostNode = (IAddReferenceNode)addTableBlogPostApprovalNode.ChildNodes[0].ChildNodes[0];

            Assert.AreEqual("BlogPostApproval", addReferenceBlogPostNode.FkTable);
            Assert.AreEqual("BlogPostID", addReferenceBlogPostNode.FkColumns[0]);
            Assert.AreEqual("BlogPost", addReferenceBlogPostNode.PkTable);
            Assert.AreEqual("ID", addReferenceBlogPostNode.PkColumns[0]);

            IAddReferenceNode addReferenceFk1Node = (IAddReferenceNode)addTableBlogPostApprovalNode.ChildNodes[2];

            Assert.AreEqual("FK1", addReferenceFk1Node.Name);
            Assert.AreEqual("BlogPostApproval", addReferenceFk1Node.FkTable);
            Assert.AreEqual("BlogPostID", addReferenceFk1Node.FkColumns[0]);
            Assert.AreEqual("ApprovedOn", addReferenceFk1Node.FkColumns[1]);
            Assert.AreEqual("BlogPost", addReferenceFk1Node.PkTable);
            Assert.AreEqual("Version", addReferenceFk1Node.PkColumns[0]);

            IAddReferenceNode addReferenceFk2Node = (IAddReferenceNode)version3Node.ChildNodes[3];

            Assert.AreEqual("FK2", addReferenceFk2Node.Name);
            Assert.AreEqual("BlogPostApproval", addReferenceFk2Node.FkTable);
            Assert.AreEqual("ApprovedOn", addReferenceFk2Node.FkColumns[0]);
            Assert.AreEqual("BlogPostID", addReferenceFk2Node.FkColumns[1]);
            Assert.AreEqual("BlogPost", addReferenceFk2Node.PkTable);
            Assert.AreEqual("ID", addReferenceFk2Node.PkColumns[0]);

            IAlterTableNode alterTableBlogPostApprovalNode = (IAlterTableNode)version3Node.ChildNodes[4];
            IRemoveReferenceNode removeReferenceFk2Node =
                (IRemoveReferenceNode)alterTableBlogPostApprovalNode.ChildNodes[0];

            Assert.AreEqual("FK2", removeReferenceFk2Node.Name);
            Assert.AreEqual("BlogPostApproval", removeReferenceFk2Node.Table);

            IRemoveReferenceNode removeReferenceFk1Node = (IRemoveReferenceNode)version3Node.ChildNodes[5];

            Assert.AreEqual("FK1", removeReferenceFk1Node.Name);
            Assert.AreEqual("BlogPostApproval", removeReferenceFk1Node.Table);

            ITableDefinition blogPostTable = environment.Schema.GetTable("BlogPost");

            Assert.IsNotNull(blogPostTable);
            Assert.IsNotNull(blogPostTable.GetColumn("ID"));
            Assert.IsNotNull(blogPostTable.GetColumn("Version"));
            Assert.IsNotNull(blogPostTable.GetColumn("PublishedOn"));

            ITableDefinition blogPostApprovalTable = environment.Schema.GetTable("BlogPostApproval");

            Assert.IsNotNull(blogPostApprovalTable);
            Assert.IsNotNull(blogPostApprovalTable.GetColumn("BlogPostID"));
            Assert.IsNotNull(blogPostApprovalTable.GetColumn("ApprovedOn"));
            Assert.IsNotNull(blogPostApprovalTable.GetColumn("Bar"));

            Assert.AreEqual(3, blogPostApprovalTable.References.Count);

            IReferenceDefinition referenceFk0 = blogPostApprovalTable.GetReference("FK0");
            Assert.IsNotNull(referenceFk0);

            IReferenceDefinition referenceAnonymous = null;
            foreach (IReferenceDefinition r in blogPostApprovalTable.References)
                if(environment.IsAnonymousIdentifier(r.Name))
                {
                    referenceAnonymous = r;
                    break;
                }

            Assert.IsNotNull(referenceAnonymous);
        }
        public void BindConstraints()
        {
            Environment environment = new Environment();

            NamingCompilerStage namingStage = new NamingCompilerStage();
            namingStage.SetEnvironment(environment);

            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(environment);

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    version 1:    
        add table BlogPost:
            ID type => Int32, primary-key => true:
                constraint ""DF_ID"" default => 0
                constraint default => 1
            
            add constraint ""DFID2"" column => ID, default => 2
        remove constraint ""DFID3"" table => BlogPost
        alter table BlogPost:
            remove constraint ""DFID2""")).Parse();

            astNode.Accept(namingStage);
            astNode.Accept(bindingStage);

            IVersionNode version1Node = (IVersionNode)astNode.ChildNodes[0];
            IAddTableNode addBlogPostTableNode = (IAddTableNode)version1Node.ChildNodes[0];
            IAddConstraintNode addDfIdConstraintNode =
                (IAddConstraintNode)addBlogPostTableNode.ChildNodes[0].ChildNodes[0];

            Assert.AreEqual("DF_ID", addDfIdConstraintNode.Name);
            Assert.AreEqual("BlogPost", addDfIdConstraintNode.Table);
            Assert.AreEqual("ID", addDfIdConstraintNode.Columns[0]);
            Assert.AreEqual(0, AstNodePropertyUtil.AsInteger(addDfIdConstraintNode.Properties, "default"));

            IDefaultConstraintDefinition dfIDConstraintDefinition =
                (IDefaultConstraintDefinition)
                environment.Schema.GetTable(addDfIdConstraintNode.Table).GetConstraint(addDfIdConstraintNode.Name);
            Assert.IsNotNull(dfIDConstraintDefinition);

            IAddConstraintNode addDbID2ConstraintNode = (IAddConstraintNode)addBlogPostTableNode.ChildNodes[1];

            Assert.AreEqual("DFID2", addDbID2ConstraintNode.Name);
            Assert.AreEqual("ID", AstNodePropertyUtil.AsString(addDbID2ConstraintNode.Properties, "column"));
            Assert.AreEqual(2, AstNodePropertyUtil.AsInteger(addDbID2ConstraintNode.Properties, "default"));

            IRemoveConstraintNode removeDfId3ConstraintNode = (IRemoveConstraintNode)version1Node.ChildNodes[1];

            Assert.AreEqual("DFID3", removeDfId3ConstraintNode.Name);
            Assert.AreEqual("BlogPost", AstNodePropertyUtil.AsString(removeDfId3ConstraintNode.Properties, "table"));

            IRemoveConstraintNode removeDfId2ConstraintNode = (IRemoveConstraintNode)version1Node.ChildNodes[2].ChildNodes[0];

            Assert.AreEqual("DFID2", removeDfId2ConstraintNode.Name);
            Assert.AreEqual("BlogPost", removeDfId2ConstraintNode.Table);
        }
        public void BindIndexes()
        {
            Environment environment = new Environment();

            NamingCompilerStage namingStage = new NamingCompilerStage();
            namingStage.SetEnvironment(environment);

            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(environment);

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    version 1:    
        add table BlogPost:
            ID type => Int32, primary-key => true:
                index ""IX_ID"" unique => true, clustered => true
            Version type => Int32

        alter table BlogPost:
            add column PublishedOn type => DateTime:
                index ""IX_PublishedOn""
                index

            add index ""IX_Pub"" column => PublishedOn

        add index IX_Foo table => BlogPost, columns => [[ID, descending], PublishedOn]

        remove index IX_Foo table => BlogPost

        alter table BlogPost:
            remove index IX_Pub

        add index IX_Foo2 table => BlogPost, column => [ID, descending]")).Parse();

            astNode.Accept(namingStage);
            astNode.Accept(bindingStage);

            IVersionNode version1Node = (IVersionNode)astNode.ChildNodes[0];

            IAddIndexNode addIxIdIndexNode = (IAddIndexNode)version1Node.ChildNodes[0].ChildNodes[0].ChildNodes[0];

            Assert.AreEqual("IX_ID", addIxIdIndexNode.Name);
            Assert.AreEqual("ID", addIxIdIndexNode.Columns[0].Name);
            Assert.IsFalse(addIxIdIndexNode.Columns[0].SortDirection.HasValue);
            Assert.IsTrue(addIxIdIndexNode.Unique.Value);
            Assert.IsTrue(addIxIdIndexNode.Clustered.Value);

            IAddIndexNode addIxPubIndexNode = (IAddIndexNode)version1Node.ChildNodes[1].ChildNodes[1];

            Assert.AreEqual("IX_Pub", addIxPubIndexNode.Name);
            Assert.AreEqual("BlogPost", addIxPubIndexNode.Table);

            IAddIndexNode addIxFooIndexNode = (IAddIndexNode)version1Node.ChildNodes[2];

            Assert.AreEqual("IX_Foo", addIxFooIndexNode.Name);
            Assert.AreEqual("BlogPost", addIxFooIndexNode.Table);
            Assert.AreEqual("PublishedOn", addIxPubIndexNode.Columns[0].Name);
            Assert.IsFalse(addIxPubIndexNode.Columns[0].SortDirection.HasValue);
            
            Assert.AreEqual(2, addIxFooIndexNode.Columns.Count);

            Assert.AreEqual("ID", addIxFooIndexNode.Columns[0].Name);
            Assert.AreEqual(SortDirection.Descending, addIxFooIndexNode.Columns[0].SortDirection.Value);

            Assert.AreEqual("PublishedOn", addIxFooIndexNode.Columns[1].Name);

            IRemoveIndexNode removeIndexIxFooNode = (IRemoveIndexNode)version1Node.ChildNodes[3];

            Assert.AreEqual("IX_Foo", removeIndexIxFooNode.Name);
            Assert.AreEqual("BlogPost", removeIndexIxFooNode.Table);

            IRemoveIndexNode removeIndexIxPubNode = (IRemoveIndexNode)version1Node.ChildNodes[4].ChildNodes[0];

            Assert.AreEqual("IX_Pub", removeIndexIxPubNode.Name);
            Assert.AreEqual("BlogPost", removeIndexIxPubNode.Table);

            ITableDefinition blogPostTable = environment.Schema.GetTable("BlogPost");

            Assert.IsNotNull(blogPostTable);
            
            Assert.AreEqual(3, blogPostTable.Columns.Count);
            Assert.IsNotNull(blogPostTable.GetColumn("ID"));
            Assert.IsNotNull(blogPostTable.GetColumn("Version"));
            Assert.IsNotNull(blogPostTable.GetColumn("PublishedOn"));

            Assert.AreEqual(4, blogPostTable.Indexes.Count);
            Assert.AreEqual("IX_ID", blogPostTable.Indexes[0].Name);
            Assert.AreEqual("IX_PublishedOn", blogPostTable.Indexes[1].Name);
            Assert.IsTrue(environment.IsAnonymousIdentifier(blogPostTable.Indexes[2].Name));

            IAddIndexNode addIxFoo2IndexNode = (IAddIndexNode)version1Node.ChildNodes[5];

            Assert.AreEqual("IX_Foo2", addIxFoo2IndexNode.Name);
            Assert.AreEqual("BlogPost", addIxFoo2IndexNode.Table);
            Assert.AreEqual("ID", addIxFoo2IndexNode.Columns[0].Name);
            Assert.IsTrue(addIxFoo2IndexNode.Columns[0].SortDirection.HasValue);
            Assert.AreEqual(SortDirection.Descending, addIxFoo2IndexNode.Columns[0].SortDirection);
        }