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)); }
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)); }
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>())); }
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)); }
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)); }
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)); }
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)); }
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)); }
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)); }
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))); }
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)); }
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)); }
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)); }
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)); }
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); }
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); } }
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); } }
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))); }
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); }
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)); }
private static CardinalityEstimate EstimateJoinRelation(BoundJoinRelation relation) { return(CardinalityEstimate.Unknown); }
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); }