Пример #1
0
        private static BoundRelation AssembleJoin(ICollection <JoinNode> nodes, ICollection <JoinEdge> edges)
        {
            var nodeComparer = Comparer <JoinNode> .Create(CompareNode);

            var remainingNodes = new HashSet <JoinNode>(nodes);
            var remainingEdges = new HashSet <JoinEdge>(edges);
            var candidateNodes = new HashSet <JoinNode>();

            BoundRelation result = null;

            while (remainingNodes.Count > 0)
            {
                var start = remainingNodes.OrderBy(n => n, nodeComparer).First();
                remainingNodes.Remove(start);

                var relation = start.Relation;

                candidateNodes.UnionWith(start.Edges.Select(e => e.Other(start)));

                while (candidateNodes.Count > 0)
                {
                    var usableEdges = candidateNodes.SelectMany(n => n.Edges)
                                      .Where(e => remainingEdges.Contains(e))
                                      .Where(n => !remainingNodes.Contains(n.Left) || !remainingNodes.Contains(n.Right))
                                      .Where(e => e.Conditions.Any(IsRelation))
                                      .ToImmutableArray();

                    if (!usableEdges.Any())
                    {
                        usableEdges = candidateNodes.SelectMany(n => n.Edges)
                                      .Where(e => remainingEdges.Contains(e))
                                      .Where(n => !remainingNodes.Contains(n.Left) || !remainingNodes.Contains(n.Right))
                                      .ToImmutableArray();
                    }

                    var nextNode = usableEdges.SelectMany(e => new[] { e.Left, e.Right })
                                   .Where(candidateNodes.Contains)
                                   .OrderBy(n => n, nodeComparer)
                                   .FirstOrDefault();

                    if (nextNode != null)
                    {
                        candidateNodes.Remove(nextNode);
                        candidateNodes.UnionWith(nextNode.Edges.Select(e => e.Other(nextNode)).Where(n => remainingNodes.Contains(n)));

                        var edge = usableEdges.First(e => !remainingNodes.Contains(e.Left) && e.Right == nextNode ||
                                                     !remainingNodes.Contains(e.Right) && e.Left == nextNode);

                        remainingNodes.Remove(nextNode);
                        remainingEdges.Remove(edge);

                        var left      = relation;
                        var right     = nextNode.Relation;
                        var condition = Expression.And(edge.Conditions);
                        relation = new BoundJoinRelation(BoundJoinType.Inner, left, right, condition, null, null);
                    }
                }

                result = result == null
                    ? relation
                    : new BoundJoinRelation(BoundJoinType.Inner, result, relation, null, null, null);
            }

            Debug.Assert(remainingNodes.Count == 0, @"Found remaining nodes");

            // Add filter for remaining predicates

            var remainingPredicates = remainingEdges.SelectMany(e => e.Conditions);
            var remainingCondition  = Expression.And(remainingPredicates);

            if (remainingCondition != null)
            {
                result = new BoundFilterRelation(result, remainingCondition);
            }

            return(result);
        }
Пример #2
0
        private BoundRelation MergeWithOrPushOverJoin(BoundFilterRelation node, BoundJoinRelation input)
        {
            // TODO: Right now, we're not pushing over a join if it has any probing.
            //
            //       That might be too restristive. It should be OK to push a over
            //       a join to the side that isn't affecting the probe column.
            //
            //       In other words:
            //
            //       * pushing to the left is OK if it's a left (anti) semi join
            //       * pushing to the right is OK if it's a right (anti) semi join

            if (input.Probe != null)
            {
                return(node.Update(RewriteRelation(input), node.Condition));
            }

            if (AllowsMerge(input.JoinType))
            {
                var newCondition = Expression.And(input.Condition, node.Condition);
                var newInput     = input.Update(input.JoinType, input.Left, input.Right, newCondition, null, null);
                return(RewriteRelation(newInput));
            }
            else
            {
                BoundExpression pushedLeft  = null;
                BoundExpression pushedRight = null;
                BoundExpression remainder   = null;

                foreach (var conjunction in Expression.SplitConjunctions(node.Condition))
                {
                    if (AllowsLeftPushOver(input.JoinType) && !conjunction.DependsOnAny(input.Right.GetOutputValues()))
                    {
                        pushedLeft = Expression.And(pushedLeft, conjunction);
                    }
                    else if (AllowsRightPushOver(input.JoinType) && !conjunction.DependsOnAny(input.Left.GetOutputValues()))
                    {
                        pushedRight = Expression.And(pushedRight, conjunction);
                    }
                    else
                    {
                        remainder = Expression.And(remainder, conjunction);
                    }
                }

                var newLeft = pushedLeft == null
                    ? input.Left
                    : new BoundFilterRelation(input.Left, pushedLeft);

                var newRight = pushedRight == null
                    ? input.Right
                    : new BoundFilterRelation(input.Right, pushedRight);

                var newInput = input.Update(input.JoinType,
                                            RewriteRelation(newLeft),
                                            RewriteRelation(newRight),
                                            input.Condition,
                                            RewriteValueSlot(input.Probe),
                                            RewriteExpression(input.PassthruPredicate));

                var newNode = remainder == null
                    ? (BoundRelation)newInput
                    : node.Update(newInput, remainder);

                return(newNode);
            }
        }
Пример #3
0
        protected override BoundRelation RewriteFilterRelation(BoundFilterRelation node)
        {
            _recorder.Record(node.Condition);

            return(base.RewriteFilterRelation(node));
        }
Пример #4
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            // First, let's rewrite our inputs

            node = (BoundJoinRelation)base.RewriteJoinRelation(node);

            // Get defined values of left and right

            var leftDefinedValues  = new HashSet <ValueSlot>(node.Left.GetDefinedValues());
            var rightDefinedValues = new HashSet <ValueSlot>(node.Right.GetDefinedValues());

            var extractedConjunctions = new List <BoundExpression>();

            // Try to pull up conjunctions 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.JoinType != BoundJoinType.LeftOuter &&
                node.JoinType != BoundJoinType.FullOuter)
            {
                if (node.Left is BoundFilterRelation leftAsFilter)
                {
                    var anythingExtracted     = false;
                    var remainingConjunctions = new List <BoundExpression>();

                    foreach (var conjunction in Expression.SplitConjunctions(leftAsFilter.Condition))
                    {
                        if (!ConjunctionHasOuterReference(leftDefinedValues, conjunction))
                        {
                            remainingConjunctions.Add(conjunction);
                        }
                        else
                        {
                            anythingExtracted = true;
                            extractedConjunctions.Add(conjunction);
                        }
                    }

                    if (!anythingExtracted)
                    {
                        // We haven't extracted any conjunctions.
                        //
                        // In order to avoid creating new node, we just do nothing.
                    }
                    else
                    {
                        var newCondition = Expression.And(remainingConjunctions);
                        if (newCondition == null)
                        {
                            node = node.WithLeft(leftAsFilter.Input);
                        }
                        else
                        {
                            leftAsFilter = leftAsFilter.WithCondition(newCondition);
                            node         = node.WithLeft(leftAsFilter);
                        }
                    }
                }
            }

            // Try to pull up conjunctions 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.JoinType != BoundJoinType.RightOuter &&
                node.JoinType != BoundJoinType.FullOuter)
            {
                if (node.Right is BoundFilterRelation rightAsFilter)
                {
                    var anythingExtracted     = false;
                    var remainingConjunctions = new List <BoundExpression>();

                    foreach (var conjunction in Expression.SplitConjunctions(rightAsFilter.Condition))
                    {
                        if (!ConjunctionHasOuterReference(rightDefinedValues, conjunction))
                        {
                            remainingConjunctions.Add(conjunction);
                        }
                        else
                        {
                            anythingExtracted = true;
                            extractedConjunctions.Add(conjunction);
                        }
                    }

                    if (!anythingExtracted)
                    {
                        // We haven't extracted any conjunctions.
                        //
                        // In order to avoid creating new node, we just do nothing.
                    }
                    else
                    {
                        var newCondition = Expression.And(remainingConjunctions);
                        if (newCondition == null)
                        {
                            node = node.WithRight(rightAsFilter.Input);
                        }
                        else
                        {
                            rightAsFilter = rightAsFilter.WithCondition(newCondition);
                            node          = node.WithRight(rightAsFilter);
                        }
                    }
                }
            }

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

            if (extractedConjunctions.Any())
            {
                var newCondition = Expression.And(new[] { node.Condition }.Concat(extractedConjunctions));
                node = node.WithCondition(newCondition);
            }

            // Now we try to extract conjunctions 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 conjunction does not reference any
            // columns from the side that is used as filter criteria (i.e. for LSJ this is the
            // right side, for RSJ this is the left side).

            if (node.JoinType != BoundJoinType.LeftOuter &&
                node.JoinType != BoundJoinType.RightOuter &&
                node.JoinType != BoundJoinType.FullOuter &&
                node.Condition != null)
            {
                var conjunctionsAboveJoin = new List <BoundExpression>();
                var remainingConjunctions = new List <BoundExpression>();
                var definedValues         = new HashSet <ValueSlot>(leftDefinedValues.Concat(rightDefinedValues));

                foreach (var conjunction in Expression.SplitConjunctions(node.Condition))
                {
                    if (ConjunctionHasOuterReference(definedValues, conjunction) && SemiJoinDoesNotDependOn(node.JoinType, conjunction, rightDefinedValues))
                    {
                        conjunctionsAboveJoin.Add(conjunction);
                    }
                    else
                    {
                        remainingConjunctions.Add(conjunction);
                    }
                }

                if (conjunctionsAboveJoin.Any())
                {
                    var newCondition = Expression.And(remainingConjunctions);
                    node = node.WithCondition(newCondition);

                    var filterCondition = Expression.And(conjunctionsAboveJoin);
                    var filter          = new BoundFilterRelation(node, filterCondition);
                    return(filter);
                }
            }

            return(node);
        }