1 //---------------------------------------------------------------------
2 // <copyright file="VarRefManager.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
12 using System.Globalization;
13 using System.Diagnostics;
14 using System.Data.Query.InternalTrees;
16 namespace System.Data.Query.PlanCompiler
19 /// This is a halper module for <see cref="JoinElimination"/>
20 /// The VarRefManager keeps track of the child-parent relationships in order to be able
21 /// to decide whether a given var is referenced by children on right-side relatives of a given node.
22 /// It is used in JoinElimination when deciding whether it is possible to eliminate the child table participating
23 /// in a left-outer join when there is a 1 - 0..1 FK relationship.
25 internal class VarRefManager
27 #region Internal State
28 private Dictionary<Node, Node> m_nodeToParentMap; //child-parent mapping
29 private Dictionary<Node, int> m_nodeToSiblingNumber; //the index of the given node among its siblings, i.e. 0 for a first child
35 /// Constructs a new VarRefManager given a command.
37 /// <param name="command"></param>
38 internal VarRefManager(Command command)
40 m_nodeToParentMap = new Dictionary<Node, Node>();
41 m_nodeToSiblingNumber = new Dictionary<Node, int>();
46 #region Public Methods
49 /// Tracks the information that the given node is a parent of its children (one level only)
51 /// <param name="parent"></param>
52 internal void AddChildren(Node parent)
54 for(int i=0; i< parent.Children.Count; i++)
56 //We do not use add on purpose, we may be updating a child's parent after join elimination in a subtree
57 m_nodeToParentMap[parent.Children[i]] = parent;
58 m_nodeToSiblingNumber[parent.Children[i]] = i;
63 /// Determines whether any var from a given list of keys is referenced by any of defining node's right relatives,
64 /// with the exception of the relatives brunching at the given targetJoinNode.
66 /// <param name="keys">A list of vars to check for</param>
67 /// <param name="definingNode">The node considered to be the defining node</param>
68 /// <param name="targetJoinNode">The relatives branching at this node are skipped</param>
69 /// <returns>False, only it can determine that not a single var from a given list of keys is referenced by any
70 /// of defining node's right relatives, with the exception of the relatives brunching at the given targetJoinNode. </returns>
71 internal bool HasKeyReferences(VarVec keys, Node definingNode, Node targetJoinNode)
73 Node currentChild = definingNode;
75 bool continueUp = true;
77 while (continueUp & m_nodeToParentMap.TryGetValue(currentChild, out parent))
79 if (parent != targetJoinNode)
82 if (HasVarReferencesShallow(parent, keys, m_nodeToSiblingNumber[currentChild], out continueUp))
87 //Check all the siblings to the right
88 for (int i = m_nodeToSiblingNumber[currentChild] + 1; i < parent.Children.Count; i++)
90 if (parent.Children[i].GetNodeInfo(m_command).ExternalReferences.Overlaps(keys))
96 currentChild = parent;
103 #region Private Methods
105 /// Checks whether the given node has references to any of the vars in the given VarVec.
106 /// It only checks the given node, not its children.
108 /// <param name="node">The node to check</param>
109 /// <param name="vars">The list of vars to check for</param>
110 /// <param name="childIndex">The index of the node's subree from which this var is coming.
111 /// This is used for SetOp-s, to be able to locate the appropriate var map that will give the
112 /// vars corresponding to the given once</param>
113 /// <param name="continueUp">If the OpType of the node's Op is such that it 'hides' the input, i.e.
114 /// the decision of whether the given vars are referenced can be made on this level, it returns true,
115 /// false otherwise</param>
116 /// <returns>True if the given node has references to any of the vars in the given VarVec, false otherwise</returns>
117 private static bool HasVarReferencesShallow(Node node, VarVec vars, int childIndex, out bool continueUp)
119 switch (node.Op.OpType)
121 case OpType.ConstrainedSort:
124 return HasVarReferences(((SortBaseOp)node.Op).Keys, vars);
126 case OpType.Distinct:
128 return HasVarReferences(((DistinctOp)node.Op).Keys, vars);
131 case OpType.Intersect:
132 case OpType.UnionAll:
134 return HasVarReferences((SetOp)node.Op, vars, childIndex);
138 return HasVarReferences(((GroupByOp)node.Op).Keys, vars);
140 case OpType.PhysicalProject:
142 return HasVarReferences(((PhysicalProjectOp)node.Op).Outputs, vars);
146 return HasVarReferences(((ProjectOp)node.Op).Outputs, vars);
155 /// Does the gvien VarList overlap with the given VarVec
157 /// <param name="listToCheck"></param>
158 /// <param name="vars"></param>
159 /// <returns></returns>
160 private static bool HasVarReferences(VarList listToCheck, VarVec vars)
162 foreach (Var var in vars)
164 if (listToCheck.Contains(var))
173 /// Do the two given varVecs overlap
175 /// <param name="listToCheck"></param>
176 /// <param name="vars"></param>
177 /// <returns></returns>
178 private static bool HasVarReferences(VarVec listToCheck, VarVec vars)
180 return listToCheck.Overlaps(vars);
184 /// Does the given list of sort keys contain a key with a var that is the given VarVec
186 /// <param name="listToCheck"></param>
187 /// <param name="vars"></param>
188 /// <returns></returns>
189 private static bool HasVarReferences(List<InternalTrees.SortKey> listToCheck, VarVec vars)
191 foreach (InternalTrees.SortKey key in listToCheck)
193 if (vars.IsSet(key.Var))
202 /// Does the list of outputs of the given SetOp contain a var
203 /// from the given VarVec defined by the SetOp's child with the given index
205 /// <param name="op"></param>
206 /// <param name="vars"></param>
207 /// <param name="index"></param>
208 /// <returns></returns>
209 private static bool HasVarReferences(SetOp op, VarVec vars, int index)
211 foreach (Var var in op.VarMap[index].Values)