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

We already know how to transform XML nodes into SQL-like rows. Now it is time to execute DQL queries. For now I will just describe operators implementation, I will handle parsing later in this series.

Today we describe basic construct: FROM.

Implementation

FROM implementation is straightforward: we assume that we already parsed the XML document (so we have strongly typed model), we have logic for building row, and we have mechanisms for extracting nodes using multi-segment identifiers. All we need to do is connect them together:

using System.Collections.Generic;
using System.Linq;
using Model;
using QueryLogic.Selectors;

namespace QueryLogic.RelationProviding
{
    public class From : IRelationProvider
    {
        public From(ISelector selector, string alias)
        {
            Selector = selector;
            Alias = alias;
        }

        public string Alias { get; private set; }
        public ISelector Selector { get; set; }

        public Relation CreateRelation(Node source)
        {
            var result = new Relation();
            IEnumerable< Node> selectedNodes = Selector.Select(source);

            result.AddRows(RowBuilder.BuildRows(selectedNodes.ToArray(), Alias));

            return result;
        }

        protected bool Equals(From other)
        {
            return string.Equals(Alias, other.Alias) && Equals(Selector, other.Selector);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((From)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((Alias != null ? Alias.GetHashCode() : 0) * 397) ^
                       (Selector != null ? Selector.GetHashCode() : 0);
            }
        }
    }
}

We create FROM, pass selector and alias and we can call CreateRelation. The missing piece is what exactly is a Relation:

using System.Collections.Generic;
using System.Linq;

namespace Model
{
    public class Relation
    {
        private readonly ISet< ColumnHeader> _columns;
        private readonly ISet< GuidHeader> _guids;
        private readonly IList< Row> _rows;

        public Relation()
        {
            _columns = new HashSet< ColumnHeader>();
            _rows = new List< Row>();
            _guids = new HashSet< GuidHeader>();
        }

        public Relation(IEnumerable< Row> rows)
            : this()
        {
            AddRows(rows);
        }

        public IEnumerable< Row> Rows
        {
            get { return _rows; }
        }

        public IEnumerable< ColumnHeader> Columns
        {
            get { return _columns; }
        }

        public IEnumerable< GuidHeader> Guids
        {
            get { return _guids; }
        }

        public void AddRow(Row row)
        {
            _rows.Add(row);
            _columns.UnionWith(row.Columns);
            _guids.UnionWith(row.Guids.Select(guid => guid.GuidHeader));
        }

        public void AddRows(IEnumerable< Row> rows)
        {
            foreach (Row row in rows)
            {
                AddRow(row);
            }
        }

        public override string ToString()
        {
            return string.Join(" ;;; ", Rows.Select(r => r.ToString()));
        }

        protected bool Equals(Relation other)
        {
            return _rows.SequenceEqual(other._rows) && _columns.SequenceEqual(other._columns);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((Relation)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((_rows != null ? _rows.GetHashCode() : 0) * 397) ^ (_columns != null ? _columns.GetHashCode() : 0);
            }
        }
    }
}

As we can see, Relation is just a bag of rows and columns, that’s all.

In the next part we will handle filtering using WHERE clause.