Beispiel #1
0
        public override AlgebraNode VisitJoinAlgebraNode(JoinAlgebraNode node)
        {
            // Get declared tables of left and right

            RowBufferEntry[] leftDefinedValues  = AstUtil.GetDefinedValueEntries(node.Left);
            RowBufferEntry[] rightDefinedValues = AstUtil.GetDefinedValueEntries(node.Right);

            // Analyze AND-parts of Condition

            if (node.Predicate != null)
            {
                List <ExpressionNode> leftAndParts      = new List <ExpressionNode>();
                List <ExpressionNode> rightAndParts     = new List <ExpressionNode>();
                List <ExpressionNode> remainingAndParts = new List <ExpressionNode>();

                foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
                {
                    // Check if we can push this AND-part down.

                    if (AstUtil.AllowsLeftPushDown(node.Op) && AstUtil.ExpressionDoesNotReference(andPart, rightDefinedValues))
                    {
                        leftAndParts.Add(andPart);
                    }
                    else if (AstUtil.AllowsRightPushDown(node.Op) && AstUtil.ExpressionDoesNotReference(andPart, leftDefinedValues))
                    {
                        rightAndParts.Add(andPart);
                    }
                    else
                    {
                        remainingAndParts.Add(andPart);
                    }
                }

                if (leftAndParts.Count > 0)
                {
                    node.Left = GetFilterFromAndParts(leftAndParts, node.Left);
                }

                if (rightAndParts.Count > 0)
                {
                    node.Right = GetFilterFromAndParts(rightAndParts, node.Right);
                }

                node.Predicate = AstUtil.CombineConditions(LogicalOperator.And, remainingAndParts);
            }

            // Visit children

            node.Left  = VisitAlgebraNode(node.Left);
            node.Right = VisitAlgebraNode(node.Right);

            return(node);
        }
Beispiel #2
0
        private AlgebraNode PushOverValueDefininingUnary(IEnumerable <ValueDefinition> definedValues, FilterAlgebraNode node)
        {
            UnaryAlgebraNode      inputNode            = (UnaryAlgebraNode)node.Input;
            List <ExpressionNode> nonDependingAndParts = new List <ExpressionNode>();
            List <ExpressionNode> dependingAndParts    = new List <ExpressionNode>();

            foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
            {
                RowBufferEntry[] rowBufferEntries = AstUtil.GetRowBufferEntryReferences(andPart);

                bool dependsOnDefinedValue = false;
                foreach (ValueDefinition definedValue in definedValues)
                {
                    if (ArrayHelpers.Contains(rowBufferEntries, definedValue.Target))
                    {
                        dependsOnDefinedValue = true;
                        break;
                    }
                }

                if (dependsOnDefinedValue)
                {
                    dependingAndParts.Add(andPart);
                }
                else
                {
                    nonDependingAndParts.Add(andPart);
                }
            }

            if (nonDependingAndParts.Count > 0)
            {
                node.Predicate  = AstUtil.CombineConditions(LogicalOperator.And, dependingAndParts);
                inputNode.Input = GetFilterFromAndParts(nonDependingAndParts, inputNode.Input);

                if (node.Predicate == null)
                {
                    node.Input      = inputNode.Input;
                    inputNode.Input = VisitAlgebraNode(node);
                    return(inputNode);
                }
            }

            node.Input = VisitAlgebraNode(node.Input);
            return(node);
        }
        public override AlgebraNode VisitFilterAlgebraNode(FilterAlgebraNode node)
        {
            // Check for null rejecting conditions.

            foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
            {
                RowBufferEntry[] rowBufferEntries = AstUtil.GetRowBufferEntryReferences(andPart);

                foreach (RowBufferEntry rowBufferEntry in rowBufferEntries)
                {
                    if (AstUtil.ExpressionYieldsNullOrFalseIfRowBufferEntryNull(andPart, rowBufferEntry))
                    {
                        AddNullRejectedTable(rowBufferEntry);
                    }
                }
            }

            return(base.VisitFilterAlgebraNode(node));
        }
Beispiel #4
0
        public override AlgebraNode VisitJoinAlgebraNode(JoinAlgebraNode node)
        {
            // Check if node only consists of INNER join nodes and simple table reference nodes.
            // This algorithm assumes that the table references have been lineraized so that
            //
            // - JoinedTableReference appear on the LHS only (the last one must be NamedTableReference ovbiviously)
            // - NamedTableReference appear on the RHS only
            //
            // While scanning the node's children we create a list of all JoinedTableReferences and
            // NamedTableReferences.

            InnerJoinTableExtractor innerJoinTableExtractor = new InnerJoinTableExtractor();

            innerJoinTableExtractor.Visit(node);

            if (!innerJoinTableExtractor.ConsistsOnlyOfInnerJoinsFiltersAndTables)
            {
                node.Left  = VisitAlgebraNode(node.Left);
                node.Right = VisitAlgebraNode(node.Right);
                return(node);
            }
            else
            {
                TableAlgebraNode[] algebraNodes = innerJoinTableExtractor.GetTableNodes();
                Dictionary <TableRefBinding, TableAlgebraNode> tableRefToNodeDictionary = new Dictionary <TableRefBinding, TableAlgebraNode>();
                List <TableRefBinding> tableList = new List <TableRefBinding>();
                foreach (TableAlgebraNode algebraNode in algebraNodes)
                {
                    tableRefToNodeDictionary.Add(algebraNode.TableRefBinding, algebraNode);
                    tableList.Add(algebraNode.TableRefBinding);
                }

                // Create a mapping RowBufferEntry -> ColumnRefBinding

                Dictionary <RowBufferEntry, ColumnRefBinding> rowBufferColumnDictionary = new Dictionary <RowBufferEntry, ColumnRefBinding>();
                foreach (TableRefBinding tableRefBinding in tableList)
                {
                    foreach (ColumnRefBinding columnRefBinding in tableRefBinding.ColumnRefs)
                    {
                        rowBufferColumnDictionary.Add(columnRefBinding.ValueDefinition.Target, columnRefBinding);
                    }
                }

                // Create list of all possible join conditions and remaining AND-parts.

                List <JoinCondition>  joinConditionList = new List <JoinCondition>();
                List <ExpressionNode> andPartList       = new List <ExpressionNode>();

                ExpressionNode filter = AstUtil.CombineConditions(LogicalOperator.And, innerJoinTableExtractor.GetFilters());

                foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, filter))
                {
                    JoinCondition joinCondition = ConvertToJoinCondition(rowBufferColumnDictionary, andPart);

                    if (joinCondition != null)
                    {
                        joinConditionList.Add(joinCondition);
                    }
                    else
                    {
                        andPartList.Add(andPart);
                    }
                }

                // After creating the list of all join conditions and AND-parts we have all we need to create
                // an optimimal join order between all tables of this part of the table tree.
                JoinOrder bestJoinOrder = GetBestJoinOrder(tableList.ToArray(), joinConditionList.ToArray(), andPartList.ToArray());

                // Get all tables that are introduced by this join order

                Dictionary <RowBufferEntry, ColumnValueDefinition> introducedColumns = GetIntroducedColumns(bestJoinOrder);

                // Combine AND-part list with all unused join conditions.

                andPartList.AddRange(bestJoinOrder.UnusedConditions);

                // Now we will re-create this part of the tree using the this join order.

                AlgebraNode lastAlgebraNode = null;
                for (int joinIndex = 0; joinIndex < bestJoinOrder.Joins.Length; joinIndex++)
                {
                    Join join = bestJoinOrder.Joins[joinIndex];

                    AlgebraNode      tableInput;
                    TableAlgebraNode tableNode = tableRefToNodeDictionary[join.TableRefBinding];

                    ExpressionNode tableFilter = ExtractConditionsApplicableToTable(introducedColumns, andPartList, join.TableRefBinding);
                    if (tableFilter == null)
                    {
                        tableInput = tableNode;
                    }
                    else
                    {
                        FilterAlgebraNode filterAlgebraNode = new FilterAlgebraNode();
                        filterAlgebraNode.Input     = tableNode;
                        filterAlgebraNode.Predicate = tableFilter;
                        tableInput = filterAlgebraNode;
                    }

                    if (lastAlgebraNode == null)
                    {
                        // This was the first one.
                        lastAlgebraNode = tableInput;
                    }
                    else
                    {
                        // Not the first one, we can create a join with the current table reference
                        // and last table reference.

                        // Get all AND-parts that can be applied to the tables already joined.
                        // This expression is merged to one condition.
                        ExpressionNode[] applicableAndParts = GetAndPartsApplicableToJoin(introducedColumns, bestJoinOrder, joinIndex, andPartList, true);
                        ExpressionNode   condition          = AstUtil.CombineConditions(LogicalOperator.And, applicableAndParts);

                        ExpressionNode joinCondition;
                        if (join.JoinCondition == null)
                        {
                            joinCondition = null;
                        }
                        else
                        {
                            joinCondition = join.JoinCondition.ToExpression();
                        }

                        ExpressionNode completeCondition = AstUtil.CombineConditions(LogicalOperator.And, condition, joinCondition);

                        JoinAlgebraNode joinAlgebraNode = new JoinAlgebraNode();
                        joinAlgebraNode.Op        = JoinAlgebraNode.JoinOperator.InnerJoin;
                        joinAlgebraNode.Left      = lastAlgebraNode;
                        joinAlgebraNode.Right     = tableInput;
                        joinAlgebraNode.Predicate = completeCondition;

                        // Next time this newly created join is the last table reference.
                        lastAlgebraNode = joinAlgebraNode;
                    }
                }

                return(lastAlgebraNode);
            }
        }
        public override AlgebraNode VisitJoinAlgebraNode(JoinAlgebraNode node)
        {
            // Get declared tables of left and right

            RowBufferEntry[] leftDefinedValues  = AstUtil.GetDefinedValueEntries(node.Left);
            RowBufferEntry[] rightDefinedValues = AstUtil.GetDefinedValueEntries(node.Right);

            // Replace outer joins by left-/right-/inner joins

            if (node.Op == JoinAlgebraNode.JoinOperator.RightOuterJoin ||
                node.Op == JoinAlgebraNode.JoinOperator.FullOuterJoin)
            {
                if (IsAnyNullRejected(leftDefinedValues))
                {
                    if (node.Op == JoinAlgebraNode.JoinOperator.RightOuterJoin)
                    {
                        node.Op = JoinAlgebraNode.JoinOperator.InnerJoin;
                    }
                    else
                    {
                        node.Op = JoinAlgebraNode.JoinOperator.LeftOuterJoin;
                    }
                }
            }

            if (node.Op == JoinAlgebraNode.JoinOperator.LeftOuterJoin ||
                node.Op == JoinAlgebraNode.JoinOperator.FullOuterJoin)
            {
                if (IsAnyNullRejected(rightDefinedValues))
                {
                    if (node.Op == JoinAlgebraNode.JoinOperator.LeftOuterJoin)
                    {
                        node.Op = JoinAlgebraNode.JoinOperator.InnerJoin;
                    }
                    else
                    {
                        node.Op = JoinAlgebraNode.JoinOperator.RightOuterJoin;
                    }
                }
            }

            // After converting an outer join to an inner one we can
            // sometimes eliminate the whole join.

            if (node.Op == JoinAlgebraNode.JoinOperator.InnerJoin)
            {
                // TODO: There is a problem. If the constant scan defines values this does not work. Acutally,
                //       this is currently no problem as the only way to create such a plan is using derived
                //       tables and in this phase the child will be a ResultNode.

                if (node.Left is ConstantScanAlgebraNode)
                {
                    return(VisitAlgebraNode(WrapWithFilter(node.Right, node.Predicate)));
                }

                if (node.Right is ConstantScanAlgebraNode)
                {
                    return(VisitAlgebraNode(WrapWithFilter(node.Left, node.Predicate)));
                }
            }

            // Analyze AND-parts of Condition

            if (node.Predicate == null)
            {
                // TODO: This does not work as the children are not yet rearranged.
                if (node.Op == JoinAlgebraNode.JoinOperator.LeftOuterJoin ||
                    node.Op == JoinAlgebraNode.JoinOperator.RightOuterJoin)
                {
                    bool hasOuterReferences = AstUtil.GetOuterReferences(node).Length == 0;
                    if (!hasOuterReferences)
                    {
                        if (node.Op == JoinAlgebraNode.JoinOperator.LeftOuterJoin && AstUtil.WillProduceAtLeastOneRow(node.Right) ||
                            node.Op == JoinAlgebraNode.JoinOperator.RightOuterJoin && AstUtil.WillProduceAtLeastOneRow(node.Left))
                        {
                            node.Op = JoinAlgebraNode.JoinOperator.InnerJoin;
                            return(VisitAlgebraNode(node));
                        }
                    }
                }
            }
            else
            {
                foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
                {
                    if (node.Op != JoinAlgebraNode.JoinOperator.FullOuterJoin)
                    {
                        // Check if we can derive from this AND-part that a table it depends on
                        // is null-rejected.

                        RowBufferEntry[] rowBufferEntries = AstUtil.GetRowBufferEntryReferences(andPart);
                        foreach (RowBufferEntry rowBufferEntry in rowBufferEntries)
                        {
                            if (AstUtil.ExpressionYieldsNullOrFalseIfRowBufferEntryNull(andPart, rowBufferEntry))
                            {
                                if (ArrayHelpers.Contains(leftDefinedValues, rowBufferEntry) &&
                                    node.Op != JoinAlgebraNode.JoinOperator.LeftOuterJoin)
                                {
                                    AddNullRejectedTable(rowBufferEntry);
                                }
                                else if (ArrayHelpers.Contains(rightDefinedValues, rowBufferEntry) &&
                                         node.Op != JoinAlgebraNode.JoinOperator.RightOuterJoin)
                                {
                                    AddNullRejectedTable(rowBufferEntry);
                                }
                            }
                        }
                    }
                }
            }

            // Visit children

            node.Left  = VisitAlgebraNode(node.Left);
            node.Right = VisitAlgebraNode(node.Right);

            return(node);
        }
Beispiel #6
0
        public override AlgebraNode VisitJoinAlgebraNode(JoinAlgebraNode node)
        {
            node.Left  = VisitAlgebraNode(node.Left);
            node.Right = VisitAlgebraNode(node.Right);

            // Get defined values of left and right

            RowBufferEntry[] leftDefinedValues  = AstUtil.GetDefinedValueEntries(node.Left);
            RowBufferEntry[] rightDefinedValues = AstUtil.GetDefinedValueEntries(node.Right);

            List <ExpressionNode> andPartsWithinJoin = new List <ExpressionNode>();

            // Try to pull up AND-parts that contain outer references from a left sided filter and combine
            // them with the join predicate.
            //
            // NOTE: This is only possible if the join is not a LEFT OUTER or FULL OUTER JOIN since this
            // operation would change the join's semantic.

            if (node.Op != JoinAlgebraNode.JoinOperator.LeftOuterJoin &&
                node.Op != JoinAlgebraNode.JoinOperator.FullOuterJoin)
            {
                FilterAlgebraNode leftAsFilter = node.Left as FilterAlgebraNode;
                if (leftAsFilter != null)
                {
                    List <ExpressionNode> remainingAndParts = new List <ExpressionNode>();
                    foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, leftAsFilter.Predicate))
                    {
                        if (AndPartHasOuterReference(andPart, leftDefinedValues))
                        {
                            andPartsWithinJoin.Add(andPart);
                        }
                        else
                        {
                            remainingAndParts.Add(andPart);
                        }
                    }

                    leftAsFilter.Predicate = AstUtil.CombineConditions(LogicalOperator.And, remainingAndParts);
                    if (leftAsFilter.Predicate == null)
                    {
                        node.Left = leftAsFilter.Input;
                    }
                }
            }

            // Try to pull up AND-parts that contain outer references from a right sided filter and combine
            // them with the join predicate.
            //
            // NOTE: This is only possible if the join is not a RIGHT OUTER or FULL OUTER JOIN since this
            // operation would change the join's semantic.

            if (node.Op != JoinAlgebraNode.JoinOperator.RightOuterJoin &&
                node.Op != JoinAlgebraNode.JoinOperator.FullOuterJoin)
            {
                FilterAlgebraNode rightAsFilter = node.Right as FilterAlgebraNode;
                if (rightAsFilter != null)
                {
                    List <ExpressionNode> remainingAndParts = new List <ExpressionNode>();
                    foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, rightAsFilter.Predicate))
                    {
                        if (AndPartHasOuterReference(andPart, rightDefinedValues))
                        {
                            andPartsWithinJoin.Add(andPart);
                        }
                        else
                        {
                            remainingAndParts.Add(andPart);
                        }
                    }

                    rightAsFilter.Predicate = AstUtil.CombineConditions(LogicalOperator.And, remainingAndParts);
                    if (rightAsFilter.Predicate == null)
                    {
                        node.Right = rightAsFilter.Input;
                    }
                }
            }

            // If we found any AND-parts that could be pulled up, merge them with the join predicate.

            if (andPartsWithinJoin.Count > 0)
            {
                node.Predicate = AstUtil.CombineConditions(LogicalOperator.And, node.Predicate, AstUtil.CombineConditions(LogicalOperator.And, andPartsWithinJoin));
            }

            // Now we try to extract AND-parts that contain outer references from the join predicate itself.
            //
            // NOTE: This is only possible if the node is not an OUTER JOIN. If the node is a SEMI JOIN the
            // operation is only legal if the AND-part does not reference any columns from the side that is
            // is used as filter criteria (i.e. for LSJ this is the right side, for RSJ this is the left
            // side).

            if (node.Op != JoinAlgebraNode.JoinOperator.LeftOuterJoin &&
                node.Op != JoinAlgebraNode.JoinOperator.RightOuterJoin &&
                node.Op != JoinAlgebraNode.JoinOperator.FullOuterJoin &&
                node.Predicate != null)
            {
                List <ExpressionNode> andPartsAboveJoin = new List <ExpressionNode>();
                List <ExpressionNode> remainingAndParts = new List <ExpressionNode>();

                foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
                {
                    if (AndPartHasOuterReference(andPart, leftDefinedValues, rightDefinedValues) &&
                        SemiJoinDoesNotDependOn(node.Op, andPart, leftDefinedValues, rightDefinedValues))
                    {
                        andPartsAboveJoin.Add(andPart);
                    }
                    else
                    {
                        remainingAndParts.Add(andPart);
                    }
                }

                node.Predicate = AstUtil.CombineConditions(LogicalOperator.And, remainingAndParts);

                if (andPartsAboveJoin.Count > 0)
                {
                    FilterAlgebraNode filterAlgebraNode = new FilterAlgebraNode();
                    filterAlgebraNode.Predicate = AstUtil.CombineConditions(LogicalOperator.And, andPartsAboveJoin);
                    filterAlgebraNode.Input     = node;
                    return(filterAlgebraNode);
                }
            }

            return(node);
        }
        public override ExpressionNode VisitBinaryExpression(BinaryExpression expression)
        {
            if (expression.Op != BinaryOperator.LogicalAnd &&
                expression.Op != BinaryOperator.LogicalOr)
            {
                return(base.VisitBinaryExpression(expression));
            }

            if (expression.Op == BinaryOperator.LogicalAnd)
            {
                // AND

                expression.Left  = VisitExpression(expression.Left);
                expression.Right = VisitExpression(expression.Right);

                return(expression);
            }
            else
            {
                // OR

                AlgebraNode input = GetAndResetLastNode();
                _probingEnabledStack.Push(false);

                List <ExpressionNode> scalarOrParts     = new List <ExpressionNode>();
                List <AlgebraNode>    algebrizedOrParts = new List <AlgebraNode>();
                foreach (ExpressionNode orPart in AstUtil.SplitCondition(LogicalOperator.Or, expression))
                {
                    if (!AstUtil.ContainsSubselect(orPart))
                    {
                        scalarOrParts.Add(orPart);
                    }
                    else
                    {
                        ExpressionNode    replacedOrPart    = VisitExpression(orPart);
                        FilterAlgebraNode filterAlgebraNode = new FilterAlgebraNode();
                        filterAlgebraNode.Input     = GetAndResetLastNode();
                        filterAlgebraNode.Predicate = replacedOrPart;
                        algebrizedOrParts.Add(filterAlgebraNode);
                    }
                }

                if (scalarOrParts.Count > 0)
                {
                    FilterAlgebraNode filterAlgebraNode = new FilterAlgebraNode();
                    filterAlgebraNode.Predicate = AstUtil.CombineConditions(LogicalOperator.Or, scalarOrParts);
                    filterAlgebraNode.Input     = CreateConstantScan();
                    algebrizedOrParts.Insert(0, filterAlgebraNode);
                }

                _probingEnabledStack.Pop();

                ConcatAlgebraNode concat = new ConcatAlgebraNode();
                concat.DefinedValues = new UnitedValueDefinition[0];
                concat.Inputs        = algebrizedOrParts.ToArray();

                RowBufferEntry  probeColumn = CreateProbeColumn();
                JoinAlgebraNode leftSemiJoinBetweenInputAndConcat = new JoinAlgebraNode();
                leftSemiJoinBetweenInputAndConcat.Op = JoinAlgebraNode.JoinOperator.LeftSemiJoin;
                leftSemiJoinBetweenInputAndConcat.PassthruPredicate = CurrentPassthruPredicate;
                leftSemiJoinBetweenInputAndConcat.ProbeBufferEntry  = probeColumn;
                leftSemiJoinBetweenInputAndConcat.Left  = input;
                leftSemiJoinBetweenInputAndConcat.Right = concat;
                SetLastAlgebraNode(leftSemiJoinBetweenInputAndConcat);
                return(CreateProbeColumnRef(probeColumn));
            }
        }
Beispiel #8
0
        private AlgebraNode PushOverJoin(FilterAlgebraNode node)
        {
            JoinAlgebraNode inputNode = (JoinAlgebraNode)node.Input;

            // Get declared tables of left and right

            RowBufferEntry[] leftDefinedValues  = AstUtil.GetDefinedValueEntries(inputNode.Left);
            RowBufferEntry[] rightDefinedValues = AstUtil.GetDefinedValueEntries(inputNode.Right);

            // Obviously, we cannot merge the filter with the join if the join is an outer join
            // (since it would change the join's semantics).
            //
            // Another less obvious restriction is that we cannot merge a filter with the join if
            // the join has a passthru predicate. In case the passthru predicte evaluates to true
            // the filter would not be applied. However, we are allowed to push the filter the over
            // join.

            bool canMerge = inputNode.Op != JoinAlgebraNode.JoinOperator.FullOuterJoin &&
                            inputNode.Op != JoinAlgebraNode.JoinOperator.LeftOuterJoin &&
                            inputNode.Op != JoinAlgebraNode.JoinOperator.RightOuterJoin &&
                            inputNode.PassthruPredicate == null;

            if (canMerge)
            {
                // We can merge the filter with the condition of the join.
                //
                // However, we have to make sure that the predicate does not reference the probe column.
                // Since not having a probe column is the most common case, we don't always split the
                // predicate into conjuncts.

                if (inputNode.ProbeBufferEntry == null || !ArrayHelpers.Contains(AstUtil.GetRowBufferEntryReferences(node.Predicate), inputNode.ProbeBufferEntry))
                {
                    // Either there is no probe column defined or the filter does not reference it. That means
                    // no splitting necessary, we can just merge the whole predicate with the join predicate.
                    inputNode.Predicate = AstUtil.CombineConditions(LogicalOperator.And, inputNode.Predicate, node.Predicate);
                    return(VisitAlgebraNode(inputNode));
                }
                else
                {
                    // Unfortunately, the filter references the probe column. Now let's check whether we can merge
                    // conjuncts of the predicate.

                    List <ExpressionNode> remainingAndParts = new List <ExpressionNode>();
                    List <ExpressionNode> mergableAndParts  = new List <ExpressionNode>();

                    foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
                    {
                        bool andPartReferencesProbeColumn = ArrayHelpers.Contains(AstUtil.GetRowBufferEntryReferences(andPart), inputNode.ProbeBufferEntry);

                        if (andPartReferencesProbeColumn)
                        {
                            remainingAndParts.Add(andPart);
                        }
                        else
                        {
                            mergableAndParts.Add(andPart);
                        }
                    }

                    if (mergableAndParts.Count > 0)
                    {
                        ExpressionNode combinedMergableAndParts = AstUtil.CombineConditions(LogicalOperator.And, mergableAndParts.ToArray());
                        inputNode.Predicate = AstUtil.CombineConditions(LogicalOperator.And, inputNode.Predicate, combinedMergableAndParts);
                        node.Predicate      = AstUtil.CombineConditions(LogicalOperator.And, remainingAndParts);

                        if (node.Predicate == null)
                        {
                            return(VisitAlgebraNode(inputNode));
                        }
                    }
                }
            }
            else
            {
                // The condition cannot be merged. Now we try to push AND-parts over the join.

                List <ExpressionNode> leftAndParts      = new List <ExpressionNode>();
                List <ExpressionNode> rightAndParts     = new List <ExpressionNode>();
                List <ExpressionNode> remainingAndParts = new List <ExpressionNode>();

                foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, node.Predicate))
                {
                    bool andPartReferencesProbeColumn = inputNode.ProbeBufferEntry != null &&
                                                        ArrayHelpers.Contains(AstUtil.GetRowBufferEntryReferences(andPart), inputNode.ProbeBufferEntry);

                    if (!andPartReferencesProbeColumn && AstUtil.AllowsLeftPushOver(inputNode.Op) && AstUtil.ExpressionDoesNotReference(andPart, rightDefinedValues))
                    {
                        // The AND-part depends only on the LHS and the join is inner/left.
                        // So we are allowed to push this AND-part down.
                        leftAndParts.Add(andPart);
                    }
                    else if (!andPartReferencesProbeColumn && AstUtil.AllowsRightPushOver(inputNode.Op) && AstUtil.ExpressionDoesNotReference(andPart, leftDefinedValues))
                    {
                        // The AND-part depends only on the RHS and the join is inner/right.
                        // So we are allowed to push this AND-part down.
                        rightAndParts.Add(andPart);
                    }
                    else
                    {
                        remainingAndParts.Add(andPart);
                    }
                }

                if (leftAndParts.Count > 0)
                {
                    inputNode.Left = GetFilterFromAndParts(leftAndParts, inputNode.Left);
                }

                if (rightAndParts.Count > 0)
                {
                    inputNode.Right = GetFilterFromAndParts(rightAndParts, inputNode.Right);
                }

                node.Predicate = AstUtil.CombineConditions(LogicalOperator.And, remainingAndParts);
                if (node.Predicate == null)
                {
                    return(VisitAlgebraNode(inputNode));
                }
            }

            node.Input = VisitAlgebraNode(node.Input);
            return(node);
        }