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

We continue implementing the predicates. Today we will join them to construct logic expressions.

In order to be able to handle complex expressions we need to introduce logic operators. There are three of them: and, or, not. Implementation is rather straightforward:

using Model;

namespace QueryLogic.Predicates.Complex
{
    public class AndPredicate : IPredicate
    {
        public AndPredicate(IPredicate left, IPredicate right)
        {
            Left = left;
            Right = right;
        }

        public IPredicate Left { get; private set; }
        public IPredicate Right { get; private set; }

        public bool KeepRow(Row row)
        {
            bool result = Left.KeepRow(row) && Right.KeepRow(row);
            return result;
        }

        protected bool Equals(AndPredicate other)
        {
            return Equals(Left, other.Left) && Equals(Right, other.Right);
        }

        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((AndPredicate)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((Left != null ? Left.GetHashCode() : 0) * 397) ^ (Right != null ? Right.GetHashCode() : 0);
            }
        }
    }
}
using Model;

namespace QueryLogic.Predicates.Complex
{
    public class OrPredicate : IPredicate
    {
        public OrPredicate(IPredicate left, IPredicate right)
        {
            Left = left;
            Right = right;
        }

        public IPredicate Left { get; private set; }
        public IPredicate Right { get; private set; }

        public bool KeepRow(Row row)
        {
            bool result = Left.KeepRow(row) || Right.KeepRow(row);
            return result;
        }

        protected bool Equals(OrPredicate other)
        {
            return Equals(Left, other.Left) && Equals(Right, other.Right);
        }

        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((OrPredicate)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((Left != null ? Left.GetHashCode() : 0) * 397) ^ (Right != null ? Right.GetHashCode() : 0);
            }
        }
    }
}
using Model;

namespace QueryLogic.Predicates.Complex
{
    public class NotPredicate : IPredicate
    {
        public NotPredicate(IPredicate predicate)
        {
            Predicate = predicate;
        }

        public IPredicate Predicate { get; private set; }

        public bool KeepRow(Row row)
        {
            bool result = Predicate.KeepRow(row);
            return !result;
        }

        protected bool Equals(NotPredicate other)
        {
            return Equals(Predicate, other.Predicate);
        }

        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((NotPredicate)obj);
        }

        public override int GetHashCode()
        {
            return (Predicate != null ? Predicate.GetHashCode() : 0);
        }
    }
}

It is important to note that we do not consider operator precedence here, we assume, that we have operators created correctly. We will get back to this issue when we will handle parsing queries.