1 //---------------------------------------------------------------------
2 // <copyright file="AggregatePushdown.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
12 using System.Data.Common;
13 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
15 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
16 // to prevent from simple mistakes during development (e.g. method argument validation
17 // in cases where it was you who created the variables or the variables had already been validated or
18 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default
19 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are
20 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in
21 // the shipped product.
22 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions
23 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
24 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct
25 // or the tree was built/rewritten not the way we thought it was.
26 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
27 // PlanCompiler.Assert.
29 using System.Globalization;
32 using System.Data.Metadata.Edm;
33 using System.Data.Query.InternalTrees;
35 namespace System.Data.Query.PlanCompiler
37 internal delegate bool TryGetValue(Node key, out Node value);
39 #region Helper Classes
41 /// Helper class to track the aggregate nodes that are candidates to be
42 /// pushed into the definingGroupByNode.
44 internal class GroupAggregateVarInfo
46 #region Private Fields
47 private readonly Node _definingGroupByNode;
48 private HashSet<KeyValuePair<Node, Node>> _candidateAggregateNodes;
49 private readonly Var _groupAggregateVar;
54 /// Public constructor
56 /// <param name="defingingGroupNode">The GroupIntoOp node</param>
57 /// <param name="groupAggregateVar"></param>
58 internal GroupAggregateVarInfo(Node defingingGroupNode, Var groupAggregateVar)
60 _definingGroupByNode = defingingGroupNode;
61 _groupAggregateVar = groupAggregateVar;
65 #region 'Public' Properties
67 /// Each key value pair represents a candidate aggregate.
68 /// The key is the function aggregate subtree and the value is a 'template' of translation of the
69 /// function aggregate's argument over the var representing the group aggregate.
70 /// A valid candidate has an argument that does not have any external references
71 /// except for the group aggregate corresponding to the DefiningGroupNode.
73 internal HashSet<KeyValuePair<Node, Node>> CandidateAggregateNodes
77 if (_candidateAggregateNodes == null)
79 _candidateAggregateNodes = new HashSet<KeyValuePair<Node, Node>>();
81 return _candidateAggregateNodes;
86 /// Are there are agregates that are candidates to be pushed into the DefiningGroupNode
88 internal bool HasCandidateAggregateNodes
92 return (_candidateAggregateNodes != null && _candidateAggregateNodes.Count != 0);
97 /// The GroupIntoOp node that this GroupAggregateVarInfo represents
99 internal Node DefiningGroupNode
101 get { return _definingGroupByNode; }
104 internal Var GroupAggregateVar
106 get { return _groupAggregateVar; }
112 /// Helper class to track usage of GroupAggregateVarInfo
113 /// It represents the usage of a single GroupAggregateVar.
114 /// The usage is defined by the computation, it should be a subree whose only
115 /// external reference is the group var represented by the GroupAggregateVarInfo.
117 internal class GroupAggregateVarRefInfo
119 #region Private fields
120 private readonly Node _computation;
121 private readonly GroupAggregateVarInfo _groupAggregateVarInfo;
122 private readonly bool _isUnnested;
127 /// Public constructor
129 /// <param name="groupAggregateVarInfo"></param>
130 /// <param name="computation"></param>
131 internal GroupAggregateVarRefInfo(GroupAggregateVarInfo groupAggregateVarInfo, Node computation, bool isUnnested)
133 this._groupAggregateVarInfo = groupAggregateVarInfo;
134 this._computation = computation;
135 this._isUnnested = isUnnested;
140 #region 'Public' Properties
142 /// Subtree whose only external reference is
143 /// the group var represented by the GroupAggregateVarInfo
145 internal Node Computation
147 get { return _computation; }
151 /// The GroupAggregateVarInfo (possibly) referenced by the computation
153 internal GroupAggregateVarInfo GroupAggregateVarInfo
155 get { return _groupAggregateVarInfo; }
159 /// Is the computation over unnested group aggregate var
161 internal bool IsUnnested
163 get { return _isUnnested; }
169 /// Manages refereces to groupAggregate variables.
171 internal class GroupAggregateVarInfoManager
173 #region Private state
174 private readonly Dictionary<Var, GroupAggregateVarRefInfo> _groupAggregateVarRelatedVarToInfo = new Dictionary<Var, GroupAggregateVarRefInfo>();
175 private Dictionary<Var, Dictionary<EdmMember, GroupAggregateVarRefInfo>> _groupAggregateVarRelatedVarPropertyToInfo;
176 private HashSet<GroupAggregateVarInfo> _groupAggregateVarInfos = new HashSet<GroupAggregateVarInfo>();
179 #region Public Surface
181 /// Get all the groupAggregateVarInfos
183 internal IEnumerable<GroupAggregateVarInfo> GroupAggregateVarInfos
187 return _groupAggregateVarInfos;
192 /// Add an entry that var is a computation represented by the computationTemplate
193 /// over the var represented by the given groupAggregateVarInfo
195 /// <param name="var"></param>
196 /// <param name="groupAggregateVarInfo"></param>
197 /// <param name="computationTemplate"></param>
198 /// <param name="isUnnested"></param>
199 internal void Add(Var var, GroupAggregateVarInfo groupAggregateVarInfo, Node computationTemplate, bool isUnnested)
201 this._groupAggregateVarRelatedVarToInfo.Add(var, new GroupAggregateVarRefInfo(groupAggregateVarInfo, computationTemplate, isUnnested));
202 _groupAggregateVarInfos.Add(groupAggregateVarInfo);
206 /// Add an entry that the given property of the given var is a computation represented
207 /// by the computationTemplate over the var represented by the given groupAggregateVarInfo
209 /// <param name="var"></param>
210 /// <param name="groupAggregateVarInfo"></param>
211 /// <param name="computationTemplate"></param>
212 /// <param name="isUnnested"></param>
213 /// <param name="property"></param>
214 internal void Add(Var var, GroupAggregateVarInfo groupAggregateVarInfo, Node computationTemplate, bool isUnnested, EdmMember property)
216 if (property == null)
218 Add(var, groupAggregateVarInfo, computationTemplate, isUnnested);
221 if (this._groupAggregateVarRelatedVarPropertyToInfo == null)
223 this._groupAggregateVarRelatedVarPropertyToInfo = new Dictionary<Var, Dictionary<System.Data.Metadata.Edm.EdmMember, GroupAggregateVarRefInfo>>();
225 Dictionary<EdmMember, GroupAggregateVarRefInfo> varPropertyDictionary;
226 if (!_groupAggregateVarRelatedVarPropertyToInfo.TryGetValue(var, out varPropertyDictionary))
228 varPropertyDictionary = new Dictionary<System.Data.Metadata.Edm.EdmMember, GroupAggregateVarRefInfo>();
229 _groupAggregateVarRelatedVarPropertyToInfo.Add(var, varPropertyDictionary);
231 varPropertyDictionary.Add(property, new GroupAggregateVarRefInfo(groupAggregateVarInfo, computationTemplate, isUnnested));
233 // Note: The following line is not necessary with the current usage pattern, this method is
234 // never called with a new groupAggregateVarInfo thus it is a no-op.
235 _groupAggregateVarInfos.Add(groupAggregateVarInfo);
239 /// Gets the groupAggregateVarRefInfo representing the definition of the given var over
240 /// a group aggregate var if any.
242 /// <param name="var"></param>
243 /// <param name="groupAggregateVarRefInfo"></param>
244 /// <returns></returns>
245 internal bool TryGetReferencedGroupAggregateVarInfo(Var var, out GroupAggregateVarRefInfo groupAggregateVarRefInfo)
247 return this._groupAggregateVarRelatedVarToInfo.TryGetValue(var, out groupAggregateVarRefInfo);
251 /// Gets the groupAggregateVarRefInfo representing the definition of the given property of the given
252 /// var over a group aggregate var if any.
254 /// <param name="var"></param>
255 /// <param name="property"></param>
256 /// <param name="groupAggregateVarRefInfo"></param>
257 /// <returns></returns>
258 internal bool TryGetReferencedGroupAggregateVarInfo(Var var, EdmMember property, out GroupAggregateVarRefInfo groupAggregateVarRefInfo)
260 if (property == null)
262 return TryGetReferencedGroupAggregateVarInfo(var, out groupAggregateVarRefInfo);
265 Dictionary<EdmMember, GroupAggregateVarRefInfo> varPropertyDictionary;
266 if (_groupAggregateVarRelatedVarPropertyToInfo == null || !_groupAggregateVarRelatedVarPropertyToInfo.TryGetValue(var, out varPropertyDictionary))
268 groupAggregateVarRefInfo = null;
271 return varPropertyDictionary.TryGetValue(property, out groupAggregateVarRefInfo);
278 /// Utility class that tries to produce an equivalent tree to the input tree over
279 /// a single group aggregate variable and no other external references
281 internal class GroupAggregateVarComputationTranslator : BasicOpVisitorOfNode
283 #region Private State
284 private GroupAggregateVarInfo _targetGroupAggregateVarInfo;
285 private bool _isUnnested;
286 private readonly Command _command;
287 private readonly GroupAggregateVarInfoManager _groupAggregateVarInfoManager;
292 /// Private constructor
294 /// <param name="command"></param>
295 /// <param name="groupAggregateVarInfoManager"></param>
296 private GroupAggregateVarComputationTranslator(
298 GroupAggregateVarInfoManager groupAggregateVarInfoManager)
300 this._command = command;
301 this._groupAggregateVarInfoManager = groupAggregateVarInfoManager;
305 #region 'Public' Surface
307 /// Try to produce an equivalent tree to the input subtree, over a single group aggregate variable.
308 /// Such translation can only be produced if all external references of the input subtree are to a
309 /// single group aggregate var, or to vars that are can be translated over that single group
312 /// <param name="subtree">The input subtree</param>
313 /// <param name="isVarDefinition"></param>
314 /// <param name="command"></param>
315 /// <param name="groupAggregateVarInfoManager"></param>
316 /// <param name="groupAggregateVarInfo">The groupAggregateVarInfo over which the input subtree can be translated </param>
317 /// <param name="templateNode">A tree that is equvalent to the input tree, but over the group aggregate variable
318 /// represented by the groupAggregetVarInfo</param>
319 /// <param name="isUnnested"></param>
320 /// <returns>True, if the translation can be done, false otherwise</returns>
321 public static bool TryTranslateOverGroupAggregateVar(
323 bool isVarDefinition,
325 GroupAggregateVarInfoManager groupAggregateVarInfoManager,
326 out GroupAggregateVarInfo groupAggregateVarInfo,
327 out Node templateNode,
330 GroupAggregateVarComputationTranslator handler = new GroupAggregateVarComputationTranslator(command, groupAggregateVarInfoManager);
332 Node inputNode = subtree;
333 SoftCastOp softCastOp = null;
335 if (inputNode.Op.OpType == OpType.SoftCast)
337 softCastOp = (SoftCastOp)inputNode.Op;
338 inputNode = inputNode.Child0;
341 if (inputNode.Op.OpType == OpType.Collect)
343 templateNode = handler.VisitCollect(inputNode);
348 templateNode = handler.VisitNode(inputNode);
352 groupAggregateVarInfo = handler._targetGroupAggregateVarInfo;
353 isUnnested = handler._isUnnested;
355 if (handler._targetGroupAggregateVarInfo == null || templateNode == null)
359 if (softCastOp != null)
361 SoftCastOp newSoftCastOp;
363 // The type needs to be fixed only if the unnesting happened during this translation.
364 // That can be recognized by these two cases:
365 // 1) if the input node was a collect, or
366 // 2) if the input did not represent a var definition, but a function aggregate argument and
367 // the template is VarRef of a group aggregate var.
369 if (isCollect || !isVarDefinition && AggregatePushdownUtil.IsVarRefOverGivenVar(templateNode, handler._targetGroupAggregateVarInfo.GroupAggregateVar))
371 newSoftCastOp = command.CreateSoftCastOp(TypeHelpers.GetEdmType<CollectionType>(softCastOp.Type).TypeUsage);
375 newSoftCastOp = softCastOp;
377 templateNode = command.CreateNode(newSoftCastOp, templateNode);
383 #region Visitor Methods
385 /// See <cref="TryTranslateOverGroupAggregateVar"/>
387 /// <param name="op"></param>
388 /// <param name="n"></param>
389 /// <returns></returns>
390 public override Node Visit(VarRefOp op, Node n)
392 return TranslateOverGroupAggregateVar(op.Var, null);
396 /// If the child is VarRef check if the subtree PropertyOp(VarRef) is reference to a
397 /// group aggregate var.
398 /// Otherwise do default processing
400 /// <param name="op"></param>
401 /// <param name="n"></param>
402 /// <returns></returns>
403 public override Node Visit(PropertyOp op, Node n)
405 if (n.Child0.Op.OpType != OpType.VarRef)
407 return base.Visit(op, n);
409 VarRefOp varRefOp = (VarRefOp)n.Child0.Op;
410 return TranslateOverGroupAggregateVar(varRefOp.Var, op.PropertyInfo);
414 /// If the Subtree rooted at the collect is of the following structure:
416 /// PhysicalProject(outputVar)
422 /// where the unnest is over the group aggregate var and the output var
423 /// is either a reference to the group aggregate var or to a constant, it returns the
424 /// translation of the ouput var.
426 /// <param name="n"></param>
427 /// <returns></returns>
428 private Node VisitCollect(Node n)
430 //Make sure the only children are projects over unnest
431 Node currentNode = n.Child0;
432 Dictionary<Var, Node> constantDefinitions = new Dictionary<Var, Node>();
433 while (currentNode.Child0.Op.OpType == OpType.Project)
435 currentNode = currentNode.Child0;
436 //Visit the VarDefListOp child
437 if (VisitDefault(currentNode.Child1) == null)
441 foreach (Node definitionNode in currentNode.Child1.Children)
443 if (IsConstant(definitionNode.Child0))
445 constantDefinitions.Add(((VarDefOp)definitionNode.Op).Var, definitionNode.Child0);
450 if (currentNode.Child0.Op.OpType != OpType.Unnest)
456 UnnestOp unnestOp = (UnnestOp)currentNode.Child0.Op;
457 GroupAggregateVarRefInfo groupAggregateVarRefInfo;
458 if (_groupAggregateVarInfoManager.TryGetReferencedGroupAggregateVarInfo(unnestOp.Var, out groupAggregateVarRefInfo))
460 if (_targetGroupAggregateVarInfo == null)
462 _targetGroupAggregateVarInfo = groupAggregateVarRefInfo.GroupAggregateVarInfo;
464 else if (_targetGroupAggregateVarInfo != groupAggregateVarRefInfo.GroupAggregateVarInfo)
478 PhysicalProjectOp physicalProjectOp = (PhysicalProjectOp)n.Child0.Op;
479 PlanCompiler.Assert(physicalProjectOp.Outputs.Count == 1, "PhysicalProject should only have one output at this stage");
480 Var outputVar = physicalProjectOp.Outputs[0];
482 Node computationTemplate = TranslateOverGroupAggregateVar(outputVar, null);
483 if (computationTemplate != null)
486 return computationTemplate;
489 Node constantDefinitionNode;
490 if (constantDefinitions.TryGetValue(outputVar, out constantDefinitionNode))
493 return constantDefinitionNode;
499 /// Determines whether the given Node is a constant subtree
500 /// It only recognizes any of the constant base ops
501 /// and possibly casts over these nodes.
503 /// <param name="node"></param>
504 /// <returns></returns>
505 private static bool IsConstant(Node node)
507 Node currentNode = node;
508 while (currentNode.Op.OpType == OpType.Cast)
510 currentNode = currentNode.Child0;
512 return PlanCompilerUtil.IsConstantBaseOp(currentNode.Op.OpType);
516 /// (1) If the given var or the given property of the given var are defined over a group aggregate var,
517 /// (2) and if that group aggregate var matches the var represented by represented by _targetGroupAggregateVarInfo
520 /// it returns the corresponding translation over the group aggregate var. Also, if _targetGroupAggregateVarInfo
521 /// is not set, it sets it to the group aggregate var representing the referenced var.
523 /// <param name="var"></param>
524 /// <param name="property"></param>
525 /// <returns></returns>
526 private Node TranslateOverGroupAggregateVar(Var var, EdmMember property)
528 GroupAggregateVarRefInfo groupAggregateVarRefInfo;
529 EdmMember localProperty;
530 if (_groupAggregateVarInfoManager.TryGetReferencedGroupAggregateVarInfo(var, out groupAggregateVarRefInfo))
532 localProperty = property;
534 else if (_groupAggregateVarInfoManager.TryGetReferencedGroupAggregateVarInfo(var, property, out groupAggregateVarRefInfo))
536 localProperty = null;
543 if (_targetGroupAggregateVarInfo == null)
545 _targetGroupAggregateVarInfo = groupAggregateVarRefInfo.GroupAggregateVarInfo;
546 _isUnnested = groupAggregateVarRefInfo.IsUnnested;
548 else if (_targetGroupAggregateVarInfo != groupAggregateVarRefInfo.GroupAggregateVarInfo || _isUnnested != groupAggregateVarRefInfo.IsUnnested)
553 Node computationTemplate = groupAggregateVarRefInfo.Computation;
554 if (localProperty != null)
556 computationTemplate = this._command.CreateNode(this._command.CreatePropertyOp(localProperty), computationTemplate);
558 return computationTemplate;
562 /// Default processing for nodes.
563 /// Visits the children and if any child has changed it creates a new node
565 /// If the reference of the child node did not change, the child node did not change either,
566 /// this is because a node can only be reused "as is" when building a template.
568 /// <param name="n"></param>
569 /// <returns></returns>
570 protected override Node VisitDefault(Node n)
572 List<Node> newChildren = new List<Node>(n.Children.Count);
573 bool anyChildChanged = false;
574 for (int i = 0; i < n.Children.Count; i++)
576 Node processedChild = VisitNode(n.Children[i]);
577 if (processedChild == null)
581 if (!anyChildChanged && !Object.ReferenceEquals(n.Children[i], processedChild))
583 anyChildChanged = true;
585 newChildren.Add(processedChild);
588 if (!anyChildChanged)
594 return _command.CreateNode(n.Op, newChildren);
598 #region Unsupported node types
600 protected override Node VisitRelOpDefault(RelOp op, Node n)
605 public override Node Visit(AggregateOp op, Node n)
610 public override Node Visit(CollectOp op, Node n)
615 public override Node Visit(ElementOp op, Node n)
626 /// A visitor that collects all group aggregates and the corresponding function aggregates
627 /// that are defined over them, referred to as 'candidate aggregates'. The candidate aggregates are aggregates
628 /// that have an argument that has the corresponding group aggregate as the only external reference
630 internal class GroupAggregateRefComputingVisitor : BasicOpVisitor
632 #region private state
633 private readonly Command _command;
634 private readonly GroupAggregateVarInfoManager _groupAggregateVarInfoManager = new GroupAggregateVarInfoManager();
635 private readonly Dictionary<Node, Node> _childToParent = new Dictionary<Node, Node>();
640 /// Produces a list of all GroupAggregateVarInfos, each of which represents a single group aggregate
641 /// and it candidate function aggregates. It also produces a delegate that given a child node returns the parent node
643 /// <param name="itree"></param>
644 /// <param name="tryGetParent"></param>
645 /// <returns></returns>
646 internal static IEnumerable<GroupAggregateVarInfo> Process(Command itree, out TryGetValue tryGetParent)
648 GroupAggregateRefComputingVisitor groupRefComputingVisitor = new GroupAggregateRefComputingVisitor(itree);
649 groupRefComputingVisitor.VisitNode(itree.Root);
650 tryGetParent = groupRefComputingVisitor._childToParent.TryGetValue;
652 return groupRefComputingVisitor._groupAggregateVarInfoManager.GroupAggregateVarInfos;
656 #region Private Constructor
658 /// Private constructor
660 /// <param name="itree"></param>
661 private GroupAggregateRefComputingVisitor(Command itree)
663 this._command = itree;
667 #region Visitor Methods
671 /// Determines whether the var or a property of the var (if the var is defined as a NewRecord)
672 /// is defined exclusively over a single group aggregate. If so, it registers it as such with the
673 /// group aggregate var info manager.
675 /// <param name="op"></param>
676 /// <param name="n"></param>
677 public override void Visit(VarDefOp op, Node n)
681 Node definingNode = n.Child0;
682 Op definingNodeOp = definingNode.Op;
684 GroupAggregateVarInfo referencedVarInfo;
687 if (GroupAggregateVarComputationTranslator.TryTranslateOverGroupAggregateVar(definingNode, true, this._command, this._groupAggregateVarInfoManager, out referencedVarInfo, out templateNode, out isUnnested))
689 _groupAggregateVarInfoManager.Add(op.Var, referencedVarInfo, templateNode, isUnnested);
691 else if (definingNodeOp.OpType == OpType.NewRecord)
693 NewRecordOp newRecordOp = (NewRecordOp)definingNodeOp;
694 for (int i = 0; i < definingNode.Children.Count; i++)
696 Node argumentNode = definingNode.Children[i];
697 if (GroupAggregateVarComputationTranslator.TryTranslateOverGroupAggregateVar(argumentNode, true, this._command, this._groupAggregateVarInfoManager, out referencedVarInfo, out templateNode, out isUnnested))
699 _groupAggregateVarInfoManager.Add(op.Var, referencedVarInfo, templateNode, isUnnested, newRecordOp.Properties[i]);
707 #region RelOp Visitors
709 /// Registers the group aggregate var with the group aggregate var info manager
711 /// <param name="op"></param>
712 /// <param name="n"></param>
713 public override void Visit(GroupByIntoOp op, Node n)
715 VisitGroupByOp(op, n);
716 foreach (Node child in n.Child3.Children)
718 Var groupAggregateVar = ((VarDefOp)child.Op).Var;
719 GroupAggregateVarRefInfo groupAggregateVarRefInfo;
720 // If the group by is over a group, it may be already tracked as referencing a group var
721 // An optimization would be to separately track this groupAggregateVar too, for the cases when the aggregate can
722 // not be pushed to the group by node over which this one is defined but can be propagated to this group by node.
723 if (!_groupAggregateVarInfoManager.TryGetReferencedGroupAggregateVarInfo(groupAggregateVar, out groupAggregateVarRefInfo))
725 _groupAggregateVarInfoManager.Add(groupAggregateVar, new GroupAggregateVarInfo(n, groupAggregateVar), this._command.CreateNode(this._command.CreateVarRefOp(groupAggregateVar)), false);
731 /// If the unnestOp's var is defined as a reference of a group aggregate var,
732 /// then the columns it produces should be registered too, but as 'unnested' references
734 /// <param name="op">the unnestOp</param>
735 /// <param name="n">current subtree</param>
736 /// <returns>modified subtree</returns>
737 public override void Visit(UnnestOp op, Node n)
740 GroupAggregateVarRefInfo groupAggregateVarRefInfo;
741 if (_groupAggregateVarInfoManager.TryGetReferencedGroupAggregateVarInfo(op.Var, out groupAggregateVarRefInfo))
743 PlanCompiler.Assert(op.Table.Columns.Count == 1, "Expected one column before NTE");
744 _groupAggregateVarInfoManager.Add(op.Table.Columns[0], groupAggregateVarRefInfo.GroupAggregateVarInfo, groupAggregateVarRefInfo.Computation, true);
750 #region ScalarOps Visitors
752 /// If the op is a collection aggregate function it checks whether its arguement can be translated over
753 /// a single group aggregate var. If so, it is tracked as a candidate to be pushed into that
754 /// group by into node.
756 /// <param name="op"></param>
757 /// <param name="n"></param>
758 public override void Visit(FunctionOp op, Node n)
761 if (!PlanCompilerUtil.IsCollectionAggregateFunction(op, n))
765 PlanCompiler.Assert(n.Children.Count == 1, "Aggregate Function must have one argument");
767 Node argumentNode = n.Child0;
769 GroupAggregateVarInfo referencedGroupAggregateVarInfo;
772 if (GroupAggregateVarComputationTranslator.TryTranslateOverGroupAggregateVar(n.Child0, false, _command, _groupAggregateVarInfoManager, out referencedGroupAggregateVarInfo, out templateNode, out isUnnested)
773 && (isUnnested || AggregatePushdownUtil.IsVarRefOverGivenVar(templateNode, referencedGroupAggregateVarInfo.GroupAggregateVar)))
775 referencedGroupAggregateVarInfo.CandidateAggregateNodes.Add(new KeyValuePair<Node, Node>(n, templateNode));
782 /// Default visitor for nodes.
783 /// It tracks the child-parent relationship.
785 /// <param name="n"></param>
786 protected override void VisitDefault(Node n)
789 foreach (Node child in n.Children)
791 //No need to track terminal nodes, plus some of these may be reused.
792 if (child.Op.Arity != 0)
794 _childToParent.Add(child, n);
803 /// Utility class to gather helper methods used by more than one class in the Aggregate Pushdown feature.
805 internal static class AggregatePushdownUtil
808 /// Determines whether the given node is a VarRef over the given var
810 /// <param name="node"></param>
811 /// <param name="var"></param>
812 /// <returns></returns>
813 internal static bool IsVarRefOverGivenVar(Node node, Var var)
815 if (node.Op.OpType != OpType.VarRef)
819 return ((VarRefOp)node.Op).Var == var;
824 /// The Aggregate Pushdown feature tries to identify function aggregates defined over a
825 /// group aggregate and push their definitions in the group by into node corresponding to
826 /// the group aggregate.
828 internal class AggregatePushdown
830 #region Private fields
831 private readonly Command m_command;
832 private TryGetValue m_tryGetParent;
835 #region Private Constructor
836 private AggregatePushdown(Command command)
838 this.m_command = command;
842 #region 'Public' Surface
844 /// Apply Aggregate Pushdown over the tree in the given plan complier state.
846 /// <param name="planCompilerState"></param>
847 internal static void Process(PlanCompiler planCompilerState)
849 AggregatePushdown aggregatePushdown = new AggregatePushdown(planCompilerState.Command);
850 aggregatePushdown.Process();
854 #region Private Methods
859 private void Process()
861 IEnumerable<GroupAggregateVarInfo> groupAggregateVarInfos = GroupAggregateRefComputingVisitor.Process(m_command, out m_tryGetParent);
862 foreach (GroupAggregateVarInfo groupAggregateVarInfo in groupAggregateVarInfos)
864 if (groupAggregateVarInfo.HasCandidateAggregateNodes)
866 foreach (KeyValuePair<Node, Node> candidate in groupAggregateVarInfo.CandidateAggregateNodes)
868 TryProcessCandidate(candidate, groupAggregateVarInfo);
875 /// Try to push the given function aggregate candidate to the corresponding group into node.
876 /// The candidate can be pushed if all ancestors of the group into node up to the least common
877 /// ancestor between the group into node and the function aggregate have one of the following node op types:
882 /// <param name="command"></param>
883 /// <param name="candidate"></param>
884 /// <param name="groupAggregateVarInfo"></param>
885 /// <param name="m_childToParent"></param>
886 private void TryProcessCandidate(
887 KeyValuePair<Node, Node> candidate,
888 GroupAggregateVarInfo groupAggregateVarInfo)
890 IList<Node> functionAncestors;
891 IList<Node> groupByAncestors;
892 Node definingGroupNode = groupAggregateVarInfo.DefiningGroupNode;
893 FindPathsToLeastCommonAncestor(candidate.Key, definingGroupNode, out functionAncestors, out groupByAncestors);
895 //Check whether all ancestors of the GroupByInto node are of type that we support propagating through
896 if (!AreAllNodesSupportedForPropagation(groupByAncestors))
901 //Add the function to the group by node
902 GroupByIntoOp definingGroupOp = (GroupByIntoOp)definingGroupNode.Op;
903 PlanCompiler.Assert(definingGroupOp.Inputs.Count == 1, "There should be one input var to GroupByInto at this stage");
904 Var inputVar = definingGroupOp.Inputs.First;
905 FunctionOp functionOp = (FunctionOp)candidate.Key.Op;
908 // Remap the template from referencing the groupAggregate var to reference the input to
911 Node argumentNode = OpCopier.Copy(m_command, candidate.Value);
912 Dictionary<Var, Var> dictionary = new Dictionary<Var, Var>(1);
913 dictionary.Add(groupAggregateVarInfo.GroupAggregateVar, inputVar);
914 VarRemapper remapper = new VarRemapper(m_command, dictionary);
915 remapper.RemapSubtree(argumentNode);
917 Node newFunctionDefiningNode = m_command.CreateNode(
918 m_command.CreateAggregateOp(functionOp.Function, false),
922 Node varDefNode = m_command.CreateVarDefNode(newFunctionDefiningNode, out newFunctionVar);
924 // Add the new aggregate to the list of aggregates
925 definingGroupNode.Child2.Children.Add(varDefNode);
926 GroupByIntoOp groupByOp = (GroupByIntoOp)definingGroupNode.Op;
927 groupByOp.Outputs.Set(newFunctionVar);
929 //Propagate the new var throught the ancestors of the GroupByInto
930 for (int i = 0; i < groupByAncestors.Count; i++)
932 Node groupByAncestor = groupByAncestors[i];
933 if (groupByAncestor.Op.OpType == OpType.Project)
935 ProjectOp ancestorProjectOp = (ProjectOp)groupByAncestor.Op;
936 ancestorProjectOp.Outputs.Set(newFunctionVar);
940 //Update the functionNode
941 candidate.Key.Op = m_command.CreateVarRefOp(newFunctionVar);
942 candidate.Key.Children.Clear();
946 /// Check whether all nodes in the given list of nodes are of types
947 /// that we know how to propagate an aggregate through
949 /// <param name="nodes"></param>
950 /// <returns></returns>
951 private static bool AreAllNodesSupportedForPropagation(IList<Node> nodes)
953 foreach (Node node in nodes)
955 if (node.Op.OpType != OpType.Project
956 && node.Op.OpType != OpType.Filter
957 && node.Op.OpType != OpType.ConstrainedSort
967 /// Finds the paths from each of node1 and node2 to their least common ancestor
969 /// <param name="node1"></param>
970 /// <param name="node2"></param>
971 /// <param name="ancestors1"></param>
972 /// <param name="ancestors2"></param>
973 private void FindPathsToLeastCommonAncestor(Node node1, Node node2, out IList<Node> ancestors1, out IList<Node> ancestors2)
975 ancestors1 = FindAncestors(node1);
976 ancestors2 = FindAncestors(node2);
978 int currentIndex1 = ancestors1.Count - 1;
979 int currentIndex2 = ancestors2.Count - 1;
980 while (ancestors1[currentIndex1] == ancestors2[currentIndex2])
986 for (int i = ancestors1.Count - 1; i > currentIndex1; i--)
988 ancestors1.RemoveAt(i);
990 for (int i = ancestors2.Count - 1; i > currentIndex2; i--)
992 ancestors2.RemoveAt(i);
997 /// Finds all ancestors of the given node.
999 /// <param name="node"></param>
1000 /// <returns>An ordered list of the all the ancestors of the given node starting from the immediate parent
1001 /// to the root of the tree</returns>
1002 private IList<Node> FindAncestors(Node node)
1004 List<Node> ancestors = new List<Node>();
1005 Node currentNode = node;
1007 while (m_tryGetParent(currentNode, out ancestor))
1009 ancestors.Add(ancestor);
1010 currentNode = ancestor;