public void BindExecuteNativeSql()
        {
            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(new Environment());

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
                @"execute native-sql upgrade-resource => ""Upgrade.sql"", downgrade-resource => ""Downgrade.sql""")).Parse();

            astNode.Accept(bindingStage);

            Assert.IsInstanceOfType(typeof(IExecuteNativeSqlNode), astNode);

            IExecuteNativeSqlNode executeNativeSqlNode = (IExecuteNativeSqlNode)astNode;

            Assert.AreEqual("Upgrade.sql", executeNativeSqlNode.UpgradeResource);
            Assert.AreEqual("Downgrade.sql", executeNativeSqlNode.DowngradeResource);
        }
        public void BindReferencesThrowsMdlCompilerExceptionOnMissingFkColumn()
        {
            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(new Environment());

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    version 1:    
        add table BlogPost:
            ID type => Int32, primary-key => true
        
        add reference FK1 pk-table => BlogPost, fk-table => BlogPost")).Parse();

            astNode.Accept(bindingStage);
        }
        public void BindColumnProperties()
        {
            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(new Environment());

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    version 1:    
        add table BlogPost:
            ID type => Int32, primary-key => true, nullable => true, length => 8, scale => 20, precision => 10, identity => true, default => ""0""")).Parse();

            astNode.Accept(bindingStage);

            IAddColumnNode addColumnNode = (IAddColumnNode)astNode.ChildNodes[0].ChildNodes[0].ChildNodes[0];

            Assert.AreEqual(DbType.Int32, addColumnNode.Type);
            Assert.AreEqual(true, addColumnNode.Identity);
            Assert.AreEqual(true, addColumnNode.PrimaryKey);
            Assert.AreEqual(true, addColumnNode.Nullable);
            Assert.AreEqual(8, addColumnNode.Length.Value);
            Assert.AreEqual(20, addColumnNode.Scale.Value);
            Assert.AreEqual(10, addColumnNode.Precision.Value);
            Assert.AreEqual("BlogPost", addColumnNode.Table);
        }
        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 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 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);
        }
        public void InferLowercasedType()
        {
            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(new Environment());

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    version 1:    
        add table BlogPost:
            ID type => int32")).Parse();

            astNode.Accept(bindingStage);
        }
        public void InferUnknownType()
        {
            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(new Environment());

            IAstNode astNode = new MdlParser(MdlParserTestFixture.CreateScanner(
@"migration ""Waffle"" revision => 1:
    version 1:    
        add table BlogPost:
            ID type => Int128, primary-key => true")).Parse();

            astNode.Accept(bindingStage);
        }
        public void InferTypes()
        {
            IMdlCompilerStage bindingStage = new BindingCompilerStage();
            bindingStage.SetEnvironment(new Environment());

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

    version 2:
        remove table BlogPost

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

        alter table BlogPost:
            add column PublishedOn type => DateTime

        add table BlogPostApproval:
            BlogPostID nullable => true:
                reference ""FK0"" pk-table => BlogPost
            ApprovedOn type => DateTime
            BlogPostID2: 
                reference ""FK1"" pk-table => BlogPost")).Parse();

            astNode.Accept(bindingStage);

            IVersionNode version3Node = (IVersionNode)astNode.ChildNodes[2];
            IAddTableNode addTableBlogPostApprovalNode = (IAddTableNode)version3Node.ChildNodes[2];
            IAddColumnNode addColumnBlogPostIDNode = (IAddColumnNode)addTableBlogPostApprovalNode.ChildNodes[0];

            Assert.AreEqual(DbType.Int32, addColumnBlogPostIDNode.Type);
            Assert.AreEqual(true, addColumnBlogPostIDNode.Nullable);

            IAddColumnNode addColumnBlogPostID2Node = (IAddColumnNode)addTableBlogPostApprovalNode.ChildNodes[2];

            Assert.AreEqual(DbType.Int32, addColumnBlogPostID2Node.Type);
            Assert.AreEqual(false, addColumnBlogPostID2Node.Nullable);
        }
        private IAstNode GetAstNode()
        {
            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

            //
            // Process type aliases
            astNode.Accept(new TypeAliasResolutionCompilerStage());

            //
            // Resolve PKs
            astNode.Accept(new PrimaryKeyResolutionCompilerStage());

            //
            // Bind stuff
            BindingCompilerStage bindingCompilerStage = new BindingCompilerStage();
            bindingCompilerStage.SetEnvironment(new Wizardby.Core.Compiler.Environment());

            astNode.Accept(bindingCompilerStage);

            //
            // Flatten AST
            astNode.Accept(new AstFlattenerCompilerStage());

            //
            // We also need to generate upgrade since that's what downgrade generator is expecting
            IMdlCompilerStage upgradeGenerationStage = new UpgradeGenerationStage();
            astNode.Accept(upgradeGenerationStage);

            IMdlCompilerStage downgradeGenerationStage = new DowngradeGenerationStage();
            downgradeGenerationStage.SetEnvironment(new Wizardby.Core.Compiler.Environment());
            
            astNode.Accept(downgradeGenerationStage);
            
            return astNode;
        }