Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / PlanCompiler / VarRemapper.cs
1 //---------------------------------------------------------------------
2 // <copyright file="VarRemapper.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.Collections.Generic;
12 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
13
14 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
15 // to prevent from simple mistakes during development (e.g. method argument validation 
16 // in cases where it was you who created the variables or the variables had already been validated or 
17 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
18 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
19 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
20 // the shipped product. 
21 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
22 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
23 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
24 // or the tree was built/rewritten not the way we thought it was.
25 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
26 // PlanCompiler.Assert.
27
28 using System.Data.Query.InternalTrees;
29
30 namespace System.Data.Query.PlanCompiler
31 {
32     /// <summary>
33     /// The VarRemapper is a utility class that can be used to "remap" Var references
34     /// in a node, or a subtree. 
35     /// </summary>
36     internal class VarRemapper : BasicOpVisitor
37     {
38         #region Private state
39         private readonly Dictionary<Var, Var> m_varMap;
40         protected readonly Command m_command;
41         #endregion
42
43         #region Constructors
44         /// <summary>
45         /// Internal constructor
46         /// </summary>
47         /// <param name="command">Current iqt command</param>
48         internal VarRemapper(Command command)
49             :this(command, new Dictionary<Var,Var>())
50         {
51         }
52
53         /// <summary>
54         /// Internal constructor
55         /// </summary>
56         /// <param name="command">Current iqt command</param>
57         /// <param name="varMap">Var map to be used</param>
58         internal VarRemapper(Command command, Dictionary<Var, Var> varMap)
59         {
60             m_command = command;
61             m_varMap = varMap;
62         }
63         #endregion
64
65         #region Public surface
66         /// <summary>
67         /// Add a mapping for "oldVar" - when the replace methods are invoked, they
68         /// will replace all references to "oldVar" by "newVar"
69         /// </summary>
70         /// <param name="oldVar">var to replace</param>
71         /// <param name="newVar">the replacement var</param>
72         internal void AddMapping(Var oldVar, Var newVar)
73         {
74             m_varMap[oldVar] = newVar;
75         }
76
77         /// <summary>
78         /// Update vars in just this node (and not the entire subtree) 
79         /// Does *not* recompute the nodeinfo - there are at least some consumers of this
80         /// function that do not want the recomputation - transformation rules, for example
81         /// </summary>
82         /// <param name="node">current node</param>
83         internal virtual void RemapNode(Node node)
84         {
85             if (m_varMap.Count == 0)
86             {
87                 return;
88             }
89             VisitNode(node);
90         }
91
92         /// <summary>
93         /// Update vars in this subtree. Recompute the nodeinfo along the way
94         /// </summary>
95         /// <param name="subTree">subtree to "remap"</param>
96         internal virtual void RemapSubtree(Node subTree)
97         {
98             if (m_varMap.Count == 0)
99             {
100                 return;
101             }
102
103             foreach (Node chi in subTree.Children)
104             {
105                 RemapSubtree(chi);
106             }
107
108             RemapNode(subTree);
109             m_command.RecomputeNodeInfo(subTree);
110         }
111
112         /// <summary>
113         /// Produce a a new remapped varList
114         /// </summary>
115         /// <param name="varList"></param>
116         /// <returns>remapped varList</returns>
117         internal VarList RemapVarList(VarList varList)
118         {
119             return Command.CreateVarList(MapVars(varList));
120         }
121
122         /// <summary>
123         /// Remap the given varList using the given varMap
124         /// </summary>
125         /// <param name="command"></param>
126         /// <param name="varMap"></param>
127         /// <param name="varList"></param>
128         internal static VarList RemapVarList(Command command, Dictionary<Var, Var> varMap, VarList varList)
129         {
130             VarRemapper varRemapper = new VarRemapper(command, varMap);
131             return varRemapper.RemapVarList(varList);
132         }
133         #endregion
134
135         #region Private methods
136         /// <summary>
137         /// Get the mapping for a Var - returns the var itself, mapping was found
138         /// </summary>
139         /// <param name="v"></param>
140         /// <returns></returns>
141         private Var Map(Var v)
142         {
143             Var newVar;
144             while (true)
145             {
146                 if (!m_varMap.TryGetValue(v, out newVar))
147                 {
148                     return v;
149                 }
150                 v = newVar;
151             }
152         }
153
154         private IEnumerable<Var> MapVars(IEnumerable<Var> vars)
155         {
156             foreach (Var v in vars)
157             {
158                 yield return Map(v);
159             }
160         }
161
162         private void Map(VarVec vec)
163         {
164             VarVec newVec = m_command.CreateVarVec(MapVars(vec));
165             vec.InitFrom(newVec);
166         }
167
168         private void Map(VarList varList)
169         {
170             VarList newList = Command.CreateVarList(MapVars(varList));
171             varList.Clear();
172             varList.AddRange(newList);
173         }
174
175         private void Map(VarMap varMap)
176         {
177             VarMap newVarMap = new VarMap();
178             foreach (KeyValuePair<Var, Var> kv in varMap)
179             {
180                 Var newVar = Map(kv.Value);
181                 newVarMap.Add(kv.Key, newVar);
182             }
183             varMap.Clear();
184             foreach (KeyValuePair<Var, Var> kv in newVarMap)
185             {
186                 varMap.Add(kv.Key, kv.Value);
187             }
188         }
189         private void Map(List<InternalTrees.SortKey> sortKeys)
190         {
191             VarVec sortVars = m_command.CreateVarVec();
192             bool hasDuplicates = false;
193
194             // 
195             // Map each var in the sort list. Remapping may introduce duplicates, and
196             // we should get rid of duplicates, since sql doesn't like them
197             //
198             foreach (InternalTrees.SortKey sk in sortKeys)
199             {
200                 sk.Var = Map(sk.Var);
201                 if (sortVars.IsSet(sk.Var))
202                 {
203                     hasDuplicates = true;
204                 }
205                 sortVars.Set(sk.Var);
206             }
207
208             //
209             // Get rid of any duplicates
210             //
211             if (hasDuplicates)
212             {
213                 List<InternalTrees.SortKey> newSortKeys = new List<SortKey>(sortKeys);
214                 sortKeys.Clear();
215                 sortVars.Clear();
216                 foreach (InternalTrees.SortKey sk in newSortKeys)
217                 {
218                     if (!sortVars.IsSet(sk.Var))
219                     {
220                         sortKeys.Add(sk);
221                     }
222                     sortVars.Set(sk.Var);
223                 }
224             }
225         }
226
227         #region VisitorMethods
228         /// <summary>
229         ///  Default visitor for a node - does not visit the children 
230         /// The reason we have this method is because the default VisitDefault
231         /// actually visits the children, and we don't want to do that
232         /// </summary>
233         /// <param name="n"></param>
234         protected override void VisitDefault(Node n)
235         {
236             // Do nothing. 
237         }
238
239         #region ScalarOps
240         public override void Visit(VarRefOp op, Node n)
241         {
242             VisitScalarOpDefault(op, n);
243             Var newVar = Map(op.Var);
244             if (newVar != op.Var)
245             {
246                 n.Op = m_command.CreateVarRefOp(newVar);
247             }
248         }
249         #endregion
250
251         #region AncillaryOps
252         #endregion
253
254         #region PhysicalOps
255         protected override void VisitNestOp(NestBaseOp op, Node n)
256         {
257             throw EntityUtil.NotSupported();
258         }
259
260         public override void Visit(PhysicalProjectOp op, Node n)
261         {
262             VisitPhysicalOpDefault(op, n);
263             Map(op.Outputs);
264
265             SimpleCollectionColumnMap newColumnMap = (SimpleCollectionColumnMap)ColumnMapTranslator.Translate(op.ColumnMap, m_varMap);
266             n.Op = m_command.CreatePhysicalProjectOp(op.Outputs, newColumnMap);
267         }
268         #endregion
269
270         #region RelOps
271         protected override void VisitGroupByOp(GroupByBaseOp op, Node n)
272         {
273             VisitRelOpDefault(op, n);
274             Map(op.Outputs);
275             Map(op.Keys);
276         }
277         public override void Visit(GroupByIntoOp op, Node n)
278         {
279             VisitGroupByOp(op, n);
280             Map(op.Inputs);
281         }
282         public override void Visit(DistinctOp op, Node n)
283         {
284             VisitRelOpDefault(op, n);
285             Map(op.Keys);
286         }
287         public override void Visit(ProjectOp op, Node n)
288         {
289             VisitRelOpDefault(op, n);
290             Map(op.Outputs);
291         }
292         public override void Visit(UnnestOp op, Node n)
293         {
294             VisitRelOpDefault(op, n);
295             Var newVar = Map(op.Var);
296             if (newVar != op.Var)
297             {
298                 n.Op = m_command.CreateUnnestOp(newVar, op.Table);
299             }
300         }
301         protected override void VisitSetOp(SetOp op, Node n)
302         {
303             VisitRelOpDefault(op, n);
304             Map(op.VarMap[0]);
305             Map(op.VarMap[1]);
306         }
307         protected override void VisitSortOp(SortBaseOp op, Node n)
308         {
309             VisitRelOpDefault(op, n);
310             Map(op.Keys);
311         }
312         #endregion
313
314         #endregion
315
316         #endregion
317     }
318 }