コード例 #1
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            // A <IJ> (B <J> C) --> (A <IJ> B) <J> C

            if (node.JoinType != BoundJoinType.Inner)
            {
                return(base.RewriteJoinRelation(node));
            }

            var rightSide = node.Right as BoundJoinRelation;

            if (rightSide == null || rightSide.Probe != null || rightSide.PassthruPredicate != null)
            {
                return(base.RewriteJoinRelation(node));
            }

            var leftSideLeft  = RewriteRelation(node.Left);
            var leftSideRight = rightSide.Left;
            var left          = new BoundJoinRelation(node.JoinType, leftSideLeft, leftSideRight, null, null, null);
            var right         = rightSide.Right;
            var condition     = Expression.And(node.Condition, rightSide.Condition);
            var result        = new BoundJoinRelation(rightSide.JoinType, left, right, condition, null, null);

            return(RewriteRelation(result));
        }
コード例 #2
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            node = (BoundJoinRelation)base.RewriteJoinRelation(node);

            var needsRewriting = NeedsRewriting(node);

            if (!needsRewriting)
            {
                return(node);
            }

            var node1 = (BoundJoinRelation)Instatiator.Instantiate(node);
            var node2 = (BoundJoinRelation)Instatiator.Instantiate(node);

            var leftOuterJoin    = node1.Update(BoundJoinType.LeftOuter, node1.Left, node1.Right, node1.Condition, null, null);
            var leftAntiSemiJoin = node1.Update(BoundJoinType.LeftAntiSemi, node2.Right, node2.Left, node2.Condition, null, null);

            var computedValueSlots = node.Left.GetOutputValues().Select(v => v.Duplicate()).ToImmutableArray();
            var computedValues     = computedValueSlots.Select(v => new BoundComputedValue(Expression.Null(), v));
            var compute            = new BoundComputeRelation(leftAntiSemiJoin, computedValues);
            var project            = new BoundProjectRelation(compute, computedValueSlots.Concat(leftAntiSemiJoin.GetOutputValues()));

            var concatValueSlots = node.GetOutputValues().ToImmutableArray();
            var firstOutputs     = leftOuterJoin.GetOutputValues().ToImmutableArray();
            var secondOutputs    = project.GetOutputValues().ToImmutableArray();
            var unifiedValues    = new BoundUnifiedValue[concatValueSlots.Length];

            for (var i = 0; i < unifiedValues.Length; i++)
            {
                unifiedValues[i] = new BoundUnifiedValue(concatValueSlots[i], new[] { firstOutputs[i], secondOutputs[i] });
            }

            return(new BoundConcatenationRelation(new BoundRelation[] { leftOuterJoin, project }, unifiedValues));
        }
コード例 #3
0
        private static BoundRelation RewriteDisjunctions(BoundExpression condition)
        {
            var scalarPredicates     = new List <BoundExpression>();
            var relationalPredicates = new List <BoundRelation>();

            foreach (var disjunction in Expression.SplitDisjunctions(condition))
            {
                BoundExistsSubselect exists;
                bool isNegated;
                if (TryGetExistsSubselect(disjunction, out exists, out isNegated))
                {
                    if (!isNegated)
                    {
                        relationalPredicates.Add(exists.Relation);
                    }
                    else
                    {
                        var constantRelation = new BoundConstantRelation();
                        var predicate        = new BoundJoinRelation(BoundJoinType.LeftAntiSemi, constantRelation, exists.Relation, null, null, null);
                        relationalPredicates.Add(predicate);
                    }
                }
                else if (Expression.IsConjunction(disjunction))
                {
                    var constantRelation = new BoundConstantRelation();
                    var output           = RewriteConjunctions(constantRelation, disjunction);
                    if (output == null)
                    {
                        scalarPredicates.Add(disjunction);
                    }
                    else
                    {
                        var predicate = new BoundJoinRelation(BoundJoinType.LeftSemi, constantRelation, output, null, null, null);
                        relationalPredicates.Add(predicate);
                    }
                }
                else
                {
                    scalarPredicates.Add(disjunction);
                }
            }

            if (relationalPredicates.Count == 0)
            {
                return(null);
            }

            if (scalarPredicates.Count > 0)
            {
                var constantRelation = new BoundConstantRelation();
                var predicate        = Expression.Or(scalarPredicates);
                var filter           = new BoundFilterRelation(constantRelation, predicate);
                relationalPredicates.Insert(0, filter);
            }

            return(new BoundConcatenationRelation(relationalPredicates, Enumerable.Empty <BoundUnifiedValue>()));
        }
コード例 #4
0
        private static BoundRelation RewriteConjunctions(BoundRelation input, BoundExpression condition)
        {
            var current          = input;
            var scalarPredicates = new List <BoundExpression>();
            var conjunctions     = Expression.SplitConjunctions(condition);

            foreach (var conjunction in conjunctions)
            {
                BoundExistsSubselect exists;
                bool isNegated;
                if (TryGetExistsSubselect(conjunction, out exists, out isNegated))
                {
                    var joinType = isNegated
                        ? BoundJoinType.LeftAntiSemi
                        : BoundJoinType.LeftSemi;
                    current = new BoundJoinRelation(joinType, current, exists.Relation, null, null, null);
                }
                else if (Expression.IsDisjunction(conjunction))
                {
                    var relation = RewriteDisjunctions(conjunction);
                    if (relation != null)
                    {
                        current = new BoundJoinRelation(BoundJoinType.LeftSemi, current, relation, null, null, null);
                    }
                    else
                    {
                        scalarPredicates.Add(conjunction);
                    }
                }
                else
                {
                    scalarPredicates.Add(conjunction);
                }
            }

            // If we haven't done anything, simply return null to indicate to our
            // caller that the condition only contained scalars.

            if (current == input)
            {
                return(null);
            }

            // If we have no scalar predicates left, it means the condition only
            // contained EXISTs queries, so we can return the current node.

            if (scalarPredicates.Count == 0)
            {
                return(current);
            }

            // Othwerwise We add a filter for the scalars.

            var predicate = Expression.And(scalarPredicates);

            return(new BoundFilterRelation(current, predicate));
        }
コード例 #5
0
        public static IEnumerable <ValueSlot> GetOuterReferences(BoundJoinRelation node)
        {
            var valueSlotDependencyFinder = new ValueSlotDependencyFinder();

            valueSlotDependencyFinder.VisitRelation(node.Right);
            var usedValueSlots = valueSlotDependencyFinder.ValueSlots;

            return(node.Left.GetDefinedValues().Where(usedValueSlots.Contains));
        }
コード例 #6
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            if (node.JoinType == BoundJoinType.RightOuter)
            {
                var newLeft  = node.Right;
                var newRight = node.Left;
                node = node.Update(BoundJoinType.LeftOuter, newLeft, newRight, node.Condition, node.Probe, node.PassthruPredicate);
            }

            return(base.RewriteJoinRelation(node));
        }
コード例 #7
0
        private static ShowPlanNode BuildJoin(BoundJoinRelation node)
        {
            var outerReferences = GetOuterReferences(node);
            var probe           = node.Probe == null ? string.Empty : $", ProbeColumn := {node.Probe}";
            var passthru        = node.PassthruPredicate == null ? string.Empty : $", Passthru := {node.PassthruPredicate}";
            var name            = $"{node.JoinType}Join{outerReferences}{probe}{passthru}";
            var properties      = Enumerable.Empty <KeyValuePair <string, string> >();
            var leftAndRight    = new[] { Build(node.Left), Build(node.Right) };
            var children        = node.Condition == null
                               ? leftAndRight
                               : leftAndRight.Concat(new[] { Build(node.Condition) });

            return(new ShowPlanNode(name, properties, children));
        }
コード例 #8
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            var newLeft = RewriteRelation(node.Left);

            bool semiJoinContext;

            semiJoinContext = (node.JoinType == BoundJoinType.LeftSemi ||
                               node.JoinType == BoundJoinType.LeftAntiSemi);
            _semiJoinContextFlagStack.Push(semiJoinContext);
            var newRight = RewriteRelation(node.Right);

            _semiJoinContextFlagStack.Pop();

            return(node.Update(node.JoinType, newLeft, newRight, node.Condition, node.Probe, node.PassthruPredicate));
        }
コード例 #9
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            if (node.Condition != null)
            {
                _recorder.Record(node.Condition);
            }

            if (node.PassthruPredicate != null)
            {
                _recorder.Record(node.PassthruPredicate);
            }

            if (node.Probe != null)
            {
                _recorder.Record(node.Probe);
            }

            return(base.RewriteJoinRelation(node));
        }
コード例 #10
0
        private static bool NeedsRewriting(BoundJoinRelation node)
        {
            if (node.JoinType != BoundJoinType.FullOuter)
            {
                return(false);
            }

            if (SubqueryChecker.ContainsSubquery(node.Condition))
            {
                return(true);
            }

            var left  = node.Left.GetOutputValues().ToImmutableArray();
            var right = node.Right.GetOutputValues().ToImmutableArray();

            var conjunctions = Expression.SplitConjunctions(node.Condition);

            return(!conjunctions.Any(c => CanBeUsedForHashMatch(left, right, c)));
        }
コード例 #11
0
ファイル: SelectionPusher.cs プロジェクト: jawn/nquery-vnext
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            if (node.Condition != null && node.Probe == null)
            {
                BoundExpression pushedLeft  = null;
                BoundExpression pushedRight = null;
                BoundExpression remainder   = null;

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

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

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

                var newNode = node.Update(node.JoinType,
                                          RewriteRelation(newLeft),
                                          RewriteRelation(newRight),
                                          remainder,
                                          RewriteValueSlot(node.Probe),
                                          RewriteExpression(node.PassthruPredicate));
                return(newNode);
            }

            return(base.RewriteJoinRelation(node));
        }
コード例 #12
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            var op = GetLogicalOperator(node.JoinType);

            if (op == null)
            {
                return(base.RewriteJoinRelation(node));
            }

            // The optimizer already made sure that left usually contains the bigger
            // relations.
            //
            // For a hash match, the left side is the one we're indexing. Hence, it's
            // beneficial to make sure the we use the smaller relation as the left side.
            //
            // Hence, we simply swap left and right. However, this transformation is
            // only valid if it's an inner join -- for the other join types it would
            // change the semantics of the join.

            var swapLeftRight = op == BoundHashMatchOperator.Inner;

            var left        = RewriteRelation(swapLeftRight ? node.Right : node.Left);
            var leftOutputs = left.GetOutputValues().ToImmutableArray();

            var right        = RewriteRelation(swapLeftRight ? node.Left : node.Right);
            var rightOutputs = right.GetOutputValues().ToImmutableArray();

            var equalPredicatesInfo = GetEqualPredicatesInfo(node.Condition, leftOutputs, rightOutputs);

            if (equalPredicatesInfo.Predicates.Any())
            {
                var equalPredicate  = equalPredicatesInfo.Predicates.First();
                var otherPredicates = Expression.And(equalPredicatesInfo.Predicates.Skip(1).Select(p => p.Condition));
                var remainder       = Expression.And(equalPredicatesInfo.Remainder, otherPredicates);
                return(new BoundHashMatchRelation(op.Value, left, right, equalPredicate.Left, equalPredicate.Right, remainder));
            }

            return(base.RewriteJoinRelation(node));
        }
コード例 #13
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            var left  = RewriteRelation(node.Left);
            var right = RewriteRelation(node.Right);

            var rewrittenRight = RewriteConjunctions(right, node.Condition);

            if (rewrittenRight != null)
            {
                // We might have residual subqueries that couldn't be
                // converted to semi joins.
                return(RewriteRelation(node.Update(node.JoinType, left, rewrittenRight, null, node.Probe, node.PassthruPredicate)));
            }

            // There were no subqueries that could be expressed as semi joins.
            // However, there might still exist subqueries, so we need to visit
            // the expression that will convert them to probing semi joins.

            var condition       = RewriteExpression(node.Condition);
            var rightWithProbes = RewriteInputWithSubqueries(right);

            return(node.Update(node.JoinType, left, rightWithProbes, condition, node.Probe, node.PassthruPredicate));
        }
コード例 #14
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            BoundRelation otherSide;

            if (IsInstantiatedCommonTableExpression(node.Left))
            {
                otherSide = RewriteRelation(node.Right);
            }
            else if (IsInstantiatedCommonTableExpression(node.Right))
            {
                otherSide = RewriteRelation(node.Left);
            }
            else
            {
                return(base.RewriteJoinRelation(node));
            }

            Debug.Assert(node.JoinType == BoundJoinType.Inner);

            return(node.Condition == null
                ? otherSide
                : new BoundFilterRelation(otherSide, node.Condition));
        }
コード例 #15
0
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            // Extract relations and predicates

            List <BoundRelation>   relations;
            List <BoundExpression> predicates;

            ExtractRelationsAndPredicates(node, out relations, out predicates);

            // Build a graph where the nodes are relations and edges are the predicates
            // connecting them.

            ICollection <JoinNode> nodes;
            ICollection <JoinEdge> edges;

            BuildGraph(relations, predicates, out nodes, out edges);

            // Given the graph, compute a join order that uses the predicates.

            var result = AssembleJoin(nodes, edges);

            return(result);
        }
コード例 #16
0
        private Iterator BuildJoin(BoundJoinRelation relation)
        {
            var left = BuildRelation(relation.Left);

            _outerRowBufferAllocation = BuildRowBufferAllocation(relation.Left, left.RowBuffer);

            var right = BuildRelation(relation.Right);
            var combinedAllocation = BuildRowBufferAllocation(relation.Right, right.RowBuffer);

            _outerRowBufferAllocation = _outerRowBufferAllocation.Parent;

            var predicate = BuildPredicate(relation.Condition, true, combinedAllocation);

            var passthruPredicate = BuildPredicate(relation.PassthruPredicate, false, combinedAllocation);

            switch (relation.JoinType)
            {
            case BoundJoinType.Inner:
                Debug.Assert(relation.Probe == null);
                return(new InnerNestedLoopsIterator(left, right, predicate, passthruPredicate));

            case BoundJoinType.LeftSemi:
                return(relation.Probe == null
                        ? (Iterator) new LeftSemiNestedLoopsIterator(left, right, predicate, passthruPredicate)
                        : new ProbingLeftSemiNestedLoopsIterator(left, right, predicate));

            case BoundJoinType.LeftAntiSemi:
                Debug.Assert(relation.Probe == null);
                return(new LeftAntiSemiNestedLoopsIterator(left, right, predicate, passthruPredicate));

            case BoundJoinType.LeftOuter:
                return(new LeftOuterNestedLoopsIterator(left, right, predicate, passthruPredicate));

            default:
                throw ExceptionBuilder.UnexpectedValue(relation.JoinType);
            }
        }
コード例 #17
0
ファイル: SelectionPusher.cs プロジェクト: jawn/nquery-vnext
        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);
            }
        }
コード例 #18
0
        private BoundRelation InstantiateRecursiveCommonTableExpression(ImmutableArray <ValueSlot> outputValues, CommonTableExpressionSymbol symbol)
        {
            // TableSpoolPusher
            //     Concat
            //         Compute (Recursion := 0)
            //             <Anchor>
            //         Assert (Recursion <= 100)
            //             Inner Join
            //                 Compute (Recursion := Recursion + 1)
            //                     TableSpoolPopper
            //                 Concat
            //                      Filter <RecursiveJoinPredicate1>
            //                          <RecursiveMember1>
            //                      Filter <RecursiveJoinPredicate2>
            //                          <RecursiveMember2>

            var valueSlotFactory = outputValues.First().Factory;

            // Create output values

            var unionRecusionSlot = valueSlotFactory.CreateTemporary(typeof(int));
            var concatValueSlots  = outputValues.Add(unionRecusionSlot);

            // Create anchor

            var anchor       = RewriteRelation(Instatiator.Instantiate(symbol.Anchor.Relation));
            var anchorValues = anchor.GetOutputValues().ToImmutableArray();

            var initRecursionSlot       = valueSlotFactory.CreateTemporary(typeof(int));
            var initRecursionDefinition = new BoundComputedValue(Expression.Literal(0), initRecursionSlot);
            var initRecursion           = new BoundComputeRelation(anchor, ImmutableArray.Create(initRecursionDefinition));

            var initRecursionOutputs = anchorValues.Add(initRecursionSlot);

            // Create TableSpoolPopper

            var tableSpoolPopperSlots = initRecursionOutputs.Select(v => v.Duplicate()).ToImmutableArray();

            var tableSpoolPopper = new BoundTableSpoolPopper(tableSpoolPopperSlots);

            var anchorRecursionCounter = tableSpoolPopperSlots.Last();
            var inc = Expression.Plus(Expression.Value(anchorRecursionCounter), Expression.Literal(1));
            var incRecursionSlot       = valueSlotFactory.CreateTemporary(typeof(int));
            var incRecursionDefinition = new BoundComputedValue(inc, incRecursionSlot);
            var incRecursion           = new BoundComputeRelation(tableSpoolPopper, ImmutableArray.Create(incRecursionDefinition));

            // Create recursive members

            var recursiveRewriter      = new CommonTableExpressionInstantiator(symbol);
            var recursiveMembers       = new List <BoundRelation>(symbol.RecursiveMembers.Length);
            var recursiveMemberOutputs = new List <ImmutableArray <ValueSlot> >(symbol.RecursiveMembers.Length);

            var anchorReferences = tableSpoolPopperSlots.RemoveAt(tableSpoolPopperSlots.Length - 1);

            foreach (var recursiveMember in symbol.RecursiveMembers)
            {
                var recursivePrototype       = recursiveMember.Relation;
                var mapping                  = CreateRecursiveMemberInstanceValueSlotMapping(symbol, anchorReferences, recursivePrototype);
                var recursiveInstance        = Instatiator.Instantiate(recursivePrototype, mapping);
                var recursiveRelation        = recursiveRewriter.RewriteRelation(recursiveInstance);
                var recursiveRelationOutputs = recursiveRelation.GetOutputValues().ToImmutableArray();
                recursiveMembers.Add(recursiveRelation);
                recursiveMemberOutputs.Add(recursiveRelationOutputs);
            }

            // Concatenate recursive members

            var recursiveConcatValues = Enumerable
                                        .Range(0, concatValueSlots.Length - 1)
                                        .Select(i =>
            {
                var slot = valueSlotFactory.CreateTemporary(concatValueSlots[i].Type);
                return(new BoundUnifiedValue(slot, recursiveMemberOutputs.Select(o => o[i])));
            })
                                        .ToImmutableArray();

            var hasSingleRecursiveMember = recursiveMembers.Count == 1;

            var recursiveConcat  = hasSingleRecursiveMember ? recursiveMembers.Single() : new BoundConcatenationRelation(recursiveMembers, recursiveConcatValues);
            var recursionOutputs = hasSingleRecursiveMember ? recursiveMemberOutputs.Single() : recursiveConcatValues.Select(u => u.ValueSlot).ToImmutableArray();

            // Create inner join

            var join                = new BoundJoinRelation(BoundJoinType.Inner, incRecursion, recursiveConcat, null, null, null);
            var joinOutputs         = recursionOutputs.Add(incRecursionSlot);
            var recursiveProjection = new BoundProjectRelation(join, joinOutputs);

            // Create assert

            var assertCondition = Expression.LessThan(Expression.Value(incRecursionSlot), Expression.Literal(100));
            var assert          = new BoundAssertRelation(recursiveProjection, assertCondition, Resources.MaximumRecursionLevelExceeded);

            // Create top level concat

            var concatValues = concatValueSlots.Select((v, i) =>
            {
                var slots = new[]
                {
                    initRecursionOutputs[i],
                    joinOutputs[i]
                };

                return(new BoundUnifiedValue(v, slots));
            });

            var concatInputs = new BoundRelation[] { initRecursion, assert };
            var concat       = new BoundConcatenationRelation(concatInputs, concatValues);

            var tableSpoolPusher = new BoundTableSpoolPusher(concat);

            return(new BoundProjectRelation(tableSpoolPusher, concatValueSlots.Take(concatValueSlots.Length - 1)));
        }
コード例 #19
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);
        }
コード例 #20
0
ファイル: OuterJoinRemover.cs プロジェクト: jawn/nquery-vnext
        protected override BoundRelation RewriteJoinRelation(BoundJoinRelation node)
        {
            // Get declared tables of left and right

            var leftDefinedValues  = node.Left.GetDefinedValues().ToImmutableArray();
            var rightDefinedValues = node.Right.GetDefinedValues().ToImmutableArray();

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

            if (node.JoinType == BoundJoinType.RightOuter ||
                node.JoinType == BoundJoinType.FullOuter)
            {
                if (IsAnyNullRejected(leftDefinedValues))
                {
                    var newType = node.JoinType == BoundJoinType.RightOuter
                        ? BoundJoinType.Inner
                        : BoundJoinType.LeftOuter;

                    node = node.Update(newType, node.Left, node.Right, node.Condition, node.Probe, node.PassthruPredicate);
                }
            }

            if (node.JoinType == BoundJoinType.LeftOuter ||
                node.JoinType == BoundJoinType.FullOuter)
            {
                if (IsAnyNullRejected(rightDefinedValues))
                {
                    var newType = node.JoinType == BoundJoinType.LeftOuter
                        ? BoundJoinType.Inner
                        : BoundJoinType.RightOuter;

                    node = node.Update(newType, node.Left, node.Right, node.Condition, node.Probe, node.PassthruPredicate);
                }
            }

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

            if (node.JoinType == BoundJoinType.Inner)
            {
                if (node.Left is BoundConstantRelation && !node.Left.GetDefinedValues().Any())
                {
                    return(RewriteRelation(WrapWithFilter(node.Right, node.Condition)));
                }

                if (node.Right is BoundConstantRelation && !node.Right.GetDefinedValues().Any())
                {
                    return(RewriteRelation(WrapWithFilter(node.Left, node.Condition)));
                }
            }

            // Analyze AND-parts of Condition

            if (node.JoinType != BoundJoinType.FullOuter)
            {
                var dependencyFinder = new ValueSlotDependencyFinder();

                foreach (var conjunction in Expression.SplitConjunctions(node.Condition))
                {
                    // Check if we can derive from this conjunction that a table it depends on
                    // is null-rejected.

                    dependencyFinder.ValueSlots.Clear();
                    dependencyFinder.VisitExpression(conjunction);

                    var slots             = dependencyFinder.ValueSlots;
                    var nullRejectedSlots = slots.Where(v => NullRejection.IsRejectingNull(conjunction, v));

                    foreach (var valueSlot in nullRejectedSlots)
                    {
                        if (node.JoinType != BoundJoinType.LeftOuter && leftDefinedValues.Contains(valueSlot))
                        {
                            AddNullRejectedTable(valueSlot);
                        }
                        else if (node.JoinType != BoundJoinType.RightOuter && rightDefinedValues.Contains(valueSlot))
                        {
                            AddNullRejectedTable(valueSlot);
                        }
                    }
                }
            }

            // Visit children

            return(base.RewriteJoinRelation(node));
        }
コード例 #21
0
 private static CardinalityEstimate EstimateJoinRelation(BoundJoinRelation relation)
 {
     return(CardinalityEstimate.Unknown);
 }
コード例 #22
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);
        }