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); }
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); } }
protected override BoundRelation RewriteFilterRelation(BoundFilterRelation node) { _recorder.Record(node.Condition); return(base.RewriteFilterRelation(node)); }
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); }