This is the twenty third part of the SQLxD series. For your convenience you can find other parts in the table of contents in Part 1 – XML Transformation

Here go the tests for query parser:

using System;
using FluentAssertions;
using Model;
using NUnit.Framework;
using QueryLogic.Aggregates;
using QueryLogic.Expressions.CellExpressions;
using QueryLogic.Expressions.RowExpressions;
using QueryLogic.Filtering;
using QueryLogic.Grouping;
using QueryLogic.Joins.Implementation;
using QueryLogic.Ordering;
using QueryLogic.Predicates.Complex;
using QueryLogic.Predicates.Simple;
using QueryLogic.RelationProviding;
using QueryLogic.Selecting;
using QueryLogic.Selectors;
using QueryLogic.Transformers.CellTransformers;
using QueryLogic.Transformers.RowTransformers;
using QueryParser.Parsing;

namespace QueryParserTest
{
    [TestFixture]
    internal class ParserTests
    {
        [TestFixtureSetUp]
        public void Initialize()
        {
            // ReSharper disable once UnusedVariable
            var parser = new Parser();
        }

        private static void PerformTestWithAssert(string query, Select expectedModel, string[] expectedMessage)
        {
            // Arrange
            var parser = new Parser();

            // Act
            Tuple actual = parser.Parse(query);
            Select actualModel = actual.Item1;
            string[] actualMessage = actual.Item2;

            // Assert
            Console.WriteLine(string.Join("\n", actualMessage));
            actualMessage.ShouldBeEquivalentTo(expectedMessage);
            actualModel.Should().Be(expectedModel);
        }

        [Test]
        public void Parse_CrossJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t CROSS JOIN t.table2 AS t2";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var crossJoin = new CrossJoin(firstFrom, secondFrom);
            var expectedSelect = new Select(crossJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_CrossJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t CROSS JOIN table2 AS t2";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var crossJoin = new CrossJoin(firstFrom, secondFrom);
            var expectedSelect = new Select(crossJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_FromWithOneNestedTable()
        {
            // Arrange
            const string query = @"SELECT * FROM level.table AS t";

            var selector = new ChainedSelector(new TopLevelSelector("level"), new NodeSelector("table"));
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_FromWithOneTableOnTopLevel()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_FullJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t FULL JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new FullOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_FullJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t FULL JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new FullOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_FullOuterJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t FULL OUTER JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new FullOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_FullOuterJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t FULL OUTER JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new FullOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_GroupBy()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t GROUP BY t.id, t.name";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var groupBy = new GroupBy(from, new[]
            {
                new GroupingSet(new[]
                {
                    new ColumnHeader("t", "id"),
                    new ColumnHeader("t", "name")
                })
            });
            var expectedSelect = new Select(groupBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_GroupByWithOrderBy()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t GROUP BY t.id ORDER BY t.id, t.name";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            });
            var groupBy = new GroupBy(from, new[]
            {
                new GroupingSet(new[]
                {
                    new ColumnHeader("t", "id")
                })
            });
            var expectedSelect = new Select(groupBy, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_GroupByWithOrderByWithWhere()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t WHERE t.id = '5' GROUP BY t.id ORDER BY t.id, t.name";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            });
            var groupBy = new GroupBy(where, new[]
            {
                new GroupingSet(new[]
                {
                    new ColumnHeader("t", "id")
                })
            });
            var expectedSelect = new Select(groupBy, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_GroupByWithWhere()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t WHERE t.id <> '5' GROUP BY t.id, t.name";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new NotEqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var groupBy = new GroupBy(where, new[]
            {
                new GroupingSet(new[]
                {
                    new ColumnHeader("t", "id"),
                    new ColumnHeader("t", "name")
                })
            });
            var expectedSelect = new Select(groupBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_IncorrectQuery_ShouldReturnMessage()
        {
            // Arrange
            const string query = @"SELECT * FROM table";

            var expectedMessages = new[]
            {
                "Syntax error - unexpected EOF "
            };

            // Act
            // Assert
            PerformTestWithAssert(query, null, expectedMessages);
        }

        [Test]
        public void Parse_InnerJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t INNER JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var innerJoin = new InnerJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(innerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_InnerJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t INNER JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var innerJoin = new InnerJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(innerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }


        [Test]
        public void Parse_LeftJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t LEFT JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new LeftOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_LeftJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t LEFT JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new LeftOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_LeftOuterJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t LEFT OUTER JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new LeftOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_LeftOuterJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t LEFT OUTER JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new LeftOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_NaturalJoinWithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t NATURAL JOIN t.table2 AS t2";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var naturalJoin = new NaturalJoin(firstFrom, secondFrom);
            var expectedSelect = new Select(naturalJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_NaturalJoinWithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t NATURAL JOIN table2 AS t2";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var naturalJoin = new NaturalJoin(firstFrom, secondFrom);
            var expectedSelect = new Select(naturalJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_NaturalJoin_MultipleOccurences()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t NATURAL JOIN table2 AS t2 NATURAL JOIN table3 AS t3";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var thirdSelector = new TopLevelSelector("table3");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var thirdFrom = new From(thirdSelector, "t3");
            var firstJoin = new NaturalJoin(firstFrom, secondFrom);
            var secondJoin = new NaturalJoin(firstJoin, thirdFrom);
            var expectedSelect = new Select(secondJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_OrderBy()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t ORDER BY t.id, t.name";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            });
            var expectedSelect = new Select(from, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_OrderByWithDescendingColumn()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t ORDER BY t.id DESC";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id"), OrderDirection.Descending)
            });
            var expectedSelect = new Select(from, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_OrderByWithFetchCount()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t ORDER BY t.id, t.name FETCH 3";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            }, null, 3);
            var expectedSelect = new Select(from, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_OrderByWithSkipCount()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t ORDER BY t.id, t.name SKIP 3";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            }, 3);
            var expectedSelect = new Select(from, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_OrderByWithSkipCountAndFetchCount()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t ORDER BY t.id, t.name SKIP 2 FETCH 3";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            }, 2, 3);
            var expectedSelect = new Select(from, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_OrderByWithWhere()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t WHERE t.id = '5' ORDER BY t.id, t.name";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var orderBy = new OrderBy(new[]
            {
                new ColumnOrdering(new ColumnHeader("t", "id")),
                new ColumnOrdering(new ColumnHeader("t", "name"))
            });
            var expectedSelect = new Select(where, orderBy);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }


        [Test]
        public void Parse_RightJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t RIGHT JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new RightOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_RightJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t RIGHT JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new RightOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_RightOuterJoin_WithUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t RIGHT OUTER JOIN t.table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new ChainedSelector(new TopLevelSelector("table"), new NodeSelector("table2"));
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new RightOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_RightOuterJoin_WithoutUsingAliasInSelector()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t RIGHT OUTER JOIN table2 AS t2 ON t.id = t2.id";

            var firstSelector = new TopLevelSelector("table");
            var secondSelector = new TopLevelSelector("table2");
            var firstFrom = new From(firstSelector, "t");
            var secondFrom = new From(secondSelector, "t2");
            var outerJoin = new RightOuterJoin(firstFrom, secondFrom,
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new GetCellRowExpression(new ColumnHeader("t2", "id"))));
            var expectedSelect = new Select(outerJoin);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_ColumnAggregatesWithAliases()
        {
            // Arrange
            const string query = @"SELECT MIN(t.id) AS minimum, MAX(t.id) AS maximum FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddAggregate(
                new MinimumAggregate(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                    new GetOriginalCellCellExpression(new ColumnHeader("", "minimum")), "minimum"));
            expectedSelect.AddAggregate(
                new MaximumAggregate(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                    new GetOriginalCellCellExpression(new ColumnHeader("", "maximum")), "maximum"));
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[0]));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_ColumnAggregatesWithInnerFunctions()
        {
            // Arrange
            const string query = @"SELECT MIN(LEN(TRIM(t.id))) FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            var columnHeader = new ColumnHeader("t", "id");
            expectedSelect.AddAggregate(
                new MinimumAggregate(
                    new ChainedCellExpression(new GetLengthCellExpression(columnHeader),
                        new ChainedCellExpression(new GetTrimmedCellExpression(columnHeader),
                            new GetOriginalCellCellExpression(columnHeader)))
                    , new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[0]));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_ColumnAggregatesWithInnerFunctionsAndOuterFunctions()
        {
            // Arrange
            const string query = @"SELECT LEN(MIN(LEN(TRIM(t.id)))) FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            var columnHeader = new ColumnHeader("t", "id");
            expectedSelect.AddAggregate(
                new MinimumAggregate(
                    new ChainedCellExpression(new GetLengthCellExpression(columnHeader),
                        new ChainedCellExpression(new GetTrimmedCellExpression(columnHeader),
                            new GetOriginalCellCellExpression(columnHeader)))
                    ,
                    new ChainedCellExpression(new GetLengthCellExpression(new ColumnHeader("", "")),
                        new GetOriginalCellCellExpression(new ColumnHeader("", "")))));
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[0]));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }


        [Test]
        public void Parse_Select_ColumnAggregatesWithoutAliases()
        {
            // Arrange
            const string query = @"SELECT MIN(t.id), MAX(t.id), SUM(t.id), AVG(t.id), COUNT(t.id) FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddAggregate(
                new MinimumAggregate(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                    new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddAggregate(
                new MaximumAggregate(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                    new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddAggregate(new SumAggregate(
                new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddAggregate(
                new AverageAggregate(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                    new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddAggregate(
                new CountColumnAggregate(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")),
                    new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[0]));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_CountAllAggregate()
        {
            // Arrange
            const string query = @"SELECT COUNT(*) FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddAggregate(new CountAggregate(new GetOriginalCellCellExpression(new ColumnHeader("", ""))));
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[0]));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_ParseFunctionWithManyArguments()
        {
            // Arrange
            const string query = @"SELECT SUBSTR(t.id, '2', '5'), CATENATE(t.id, 'asd') FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            var columnHeader = new ColumnHeader("t", "id");
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new[]
            {
                new CellTransformer(
                    new ChainedCellExpression(new GetSubstringCellExpression(columnHeader, 2, 5),
                        new GetOriginalCellCellExpression(columnHeader)), ""),
                new CellTransformer(
                    new ChainedCellExpression(new CatenateStringCellExpression(columnHeader, "asd"),
                        new GetOriginalCellCellExpression(columnHeader)), "")
            }));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_ParseFunctions()
        {
            // Arrange
            const string query =
                @"SELECT LEN(TRIM(t.id)), YEAR(t.id), MONTH(t.id), DAY(t.id), HOUR(t.id), MINUTE(t.id), SECOND(t.id) FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            var columnHeader = new ColumnHeader("t", "id");
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new[]
            {
                new CellTransformer(new ChainedCellExpression(new GetLengthCellExpression(columnHeader),
                    new ChainedCellExpression(new GetTrimmedCellExpression(columnHeader),
                        new GetOriginalCellCellExpression(columnHeader))), ""),
                new CellTransformer(
                    new ChainedCellExpression(new GetDatePartCellExpression(columnHeader, DatePart.Year),
                        new GetOriginalCellCellExpression(columnHeader)), ""),
                new CellTransformer(
                    new ChainedCellExpression(new GetDatePartCellExpression(columnHeader, DatePart.Month),
                        new GetOriginalCellCellExpression(columnHeader)), ""),
                new CellTransformer(
                    new ChainedCellExpression(new GetDatePartCellExpression(columnHeader, DatePart.Day),
                        new GetOriginalCellCellExpression(columnHeader)), ""),
                new CellTransformer(
                    new ChainedCellExpression(new GetDatePartCellExpression(columnHeader, DatePart.Hour),
                        new GetOriginalCellCellExpression(columnHeader)), ""),
                new CellTransformer(
                    new ChainedCellExpression(new GetDatePartCellExpression(columnHeader, DatePart.Minute),
                        new GetOriginalCellCellExpression(columnHeader)), ""),
                new CellTransformer(
                    new ChainedCellExpression(new GetDatePartCellExpression(columnHeader, DatePart.Second),
                        new GetOriginalCellCellExpression(columnHeader)), "")
            }));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_SelectColumnsWithAlias()
        {
            // Arrange
            const string query = @"SELECT t.id AS ajdik, t.name as nejm FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[]
            {
                new CellTransformer(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")), "ajdik"),
                new CellTransformer(new GetOriginalCellCellExpression(new ColumnHeader("t", "name")), "nejm")
            }));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_SelectManyColumns()
        {
            // Arrange
            const string query = @"SELECT t.id, t.name FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[]
            {
                new CellTransformer(new GetOriginalCellCellExpression(new ColumnHeader("t", "id"))),
                new CellTransformer(new GetOriginalCellCellExpression(new ColumnHeader("t", "name")))
            }));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Select_SelectSingleColumn()
        {
            // Arrange
            const string query = @"SELECT t.id FROM table AS t";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var expectedSelect = new Select(from);
            expectedSelect.AddRowTransformer(new CellsRowTransformer(new ICellTransformer[]
            {
                new CellTransformer(new GetOriginalCellCellExpression(new ColumnHeader("t", "id")))
            }));

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_AndOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id = '5' AND t.name = 'JAN'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate =
                new AndPredicate(
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                        new ConstantRowExpression("5")),
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                        new ConstantRowExpression("JAN")));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_AndOrOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id = '5' AND t.name = 'JAN' OR t.name = 'GERWAZY' ";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new OrPredicate(
                new AndPredicate(
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                        new ConstantRowExpression("5")),
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                        new ConstantRowExpression("JAN"))),
                new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                    new ConstantRowExpression("GERWAZY")));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_EqualOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id = '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_GreaterEqualOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id >= '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new GreaterEqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_GreaterThanOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id > '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new GreaterThanPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_IsNullOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id IS NULL";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new IsNullPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_LessEqualOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id <= '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new LessEqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_LessThanOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id < '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new LessThanPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_LikeOperatorWithEscapeCharacter()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id LIKE 'abcd_%[a-z][0-9][^0-9]' ESCAPE '&'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new LikePredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("abcd_%[a-z][0-9][^0-9]"), '&');
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_LikeOperatorWithoutEscapeCharacter()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id LIKE 'abcd_%[a-z][^0-9]'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new LikePredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("abcd_%[a-z][^0-9]"), '\0');
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_NotEqualOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id <> '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new NotEqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5"));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_NotOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where NOT (t.id >= '5')";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate =
                new NotPredicate(new GreaterEqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                    new ConstantRowExpression("5")));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_OrAndOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.name = 'GERWAZY' OR t.name = 'JAN' AND t.id = '5'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new OrPredicate(new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                new ConstantRowExpression("GERWAZY")),
                new AndPredicate(
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                        new ConstantRowExpression("JAN")),
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                        new ConstantRowExpression("5"))));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_OrOperator()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id = '5' OR t.name = 'JAN'";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate =
                new OrPredicate(
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                        new ConstantRowExpression("5")),
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                        new ConstantRowExpression("JAN")));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }

        [Test]
        public void Parse_Where_Parenthesis()
        {
            // Arrange
            const string query = @"SELECT * FROM table AS t where t.id = '5' AND (t.name = 'JAN' OR t.name = 'GERWAZY')";

            var selector = new TopLevelSelector("table");
            var from = new From(selector, "t");
            var predicate = new AndPredicate(new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "id")),
                new ConstantRowExpression("5")),
                new OrPredicate(
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                        new ConstantRowExpression("JAN")),
                    new EqualPredicate(new GetCellRowExpression(new ColumnHeader("t", "name")),
                        new ConstantRowExpression("GERWAZY"))));
            var where = new Where(from, predicate);
            var expectedSelect = new Select(where);
            expectedSelect.AddRowTransformer(new IdentityRowTransformer());

            var expectedMessages = new string[0];

            // Act
            // Assert
            PerformTestWithAssert(query, expectedSelect, expectedMessages);
        }
    }
}

If you followed this series then this whole code should be very easy.

Summary

We now have engine for parsing XML documents, transforming them, and executing queries. We can easily transform rows, perform set operations, implement functions. There are things in SQL-92 which we miss, e.g., nested queries. However, we can easily extend our language and engine to handle them. For now the job is done but if you want you can easily extend the SQLxD database even more.