Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / PlanCompiler / ColumnMapTranslator.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ColumnMapTranslator.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.Globalization;
13 using System.Data.Query.InternalTrees;
14 using System.Data.Query.PlanCompiler;
15 using System.Linq;
16 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
17
18 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
19 // to prevent from simple mistakes during development (e.g. method argument validation 
20 // in cases where it was you who created the variables or the variables had already been validated or 
21 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
22 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
23 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
24 // the shipped product. 
25 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
26 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
27 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
28 // or the tree was built/rewritten not the way we thought it was.
29 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
30 // PlanCompiler.Assert.
31
32
33 namespace System.Data.Query.PlanCompiler
34 {
35
36     /// <summary>
37     /// Delegate pattern that the ColumnMapTranslator uses to find its replacement
38     /// columnMaps.  Given a columnMap, return it's replacement.
39     /// </summary>
40     /// <param name="columnMap"></param>
41     /// <returns></returns>
42     internal delegate ColumnMap ColumnMapTranslatorTranslationDelegate(ColumnMap columnMap);
43
44     /// <summary>
45     /// ColumnMapTranslator visits the ColumnMap hiearchy and runs the translation delegate
46     /// you specify;  There are some static methods to perform common translations, but you
47     /// can bring your own translation if you desire.
48     /// 
49     /// This visitor only creates new ColumnMap objects when necessary; it attempts to 
50     /// replace-in-place, except when that is not possible because the field is not
51     /// writable.
52     /// 
53     /// NOTE: over time, we should be able to modify the ColumnMaps to have more writable
54     ///       fields;
55     /// </summary>
56     internal class ColumnMapTranslator : ColumnMapVisitorWithResults<ColumnMap, ColumnMapTranslatorTranslationDelegate>
57     {
58
59         #region Constructors
60
61         /// <summary>
62         /// Singleton instance for the "public" methods to use;
63         /// </summary>
64         static private ColumnMapTranslator Instance = new ColumnMapTranslator();
65
66         /// <summary>
67         /// Constructor; no one should use this.
68         /// </summary>
69         private ColumnMapTranslator()
70         {
71         }
72
73         #endregion
74
75         #region Visitor Helpers
76
77         /// <summary>
78         /// Returns the var to use in the copy, either the original or the
79         /// replacement.  Note that we will follow the chain of replacements, in
80         /// case the replacement was also replaced.
81         /// </summary>
82         /// <param name="originalVar"></param>
83         /// <param name="replacementVarMap"></param>
84         /// <returns></returns>
85         private static Var GetReplacementVar(Var originalVar, Dictionary<Var, Var> replacementVarMap)
86         {
87             // SQLBUDT #478509: Follow the chain of mapped vars, don't
88             //                  just stop at the first one
89             Var replacementVar = originalVar;
90
91             while (replacementVarMap.TryGetValue(replacementVar, out originalVar))
92             {
93                 if (originalVar == replacementVar)
94                 {
95                     break;
96                 }
97                 replacementVar = originalVar;
98             }
99             return replacementVar;
100         }
101
102         #endregion
103
104         #region "Public" surface area
105
106         /// <summary>
107         /// Bring-Your-Own-Replacement-Delegate method.
108         /// </summary>
109         /// <param name="columnMap"></param>
110         /// <param name="translationDelegate"></param>
111         /// <returns></returns>
112         internal static ColumnMap Translate(ColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
113         {
114             return columnMap.Accept(ColumnMapTranslator.Instance, translationDelegate);
115         }
116         
117         /// <summary>
118         /// Replace VarRefColumnMaps with the specified ColumnMap replacement
119         /// </summary>
120         /// <param name="columnMapToTranslate"></param>
121         /// <param name="varToColumnMap"></param>
122         /// <returns></returns>
123         internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary<Var, ColumnMap> varToColumnMap)
124         {
125             ColumnMap result = Translate(columnMapToTranslate,
126                                             delegate(ColumnMap columnMap)
127                                             {
128                                                 VarRefColumnMap varRefColumnMap = columnMap as VarRefColumnMap;
129                                                 if (null != varRefColumnMap)
130                                                 {
131                                                     if (varToColumnMap.TryGetValue(varRefColumnMap.Var, out columnMap))
132                                                     {
133                                                         // perform fixups; only allow name changes when the replacement isn't
134                                                         // already named (and the original is named...)
135                                                         if (!columnMap.IsNamed && varRefColumnMap.IsNamed)
136                                                         {
137                                                             columnMap.Name = varRefColumnMap.Name;
138                                                         }
139                                                     }
140                                                     else 
141                                                     {
142                                                         columnMap = varRefColumnMap;
143                                                     }
144                                                 }
145                                                 return columnMap;
146                                             }
147                                             );
148             return result;
149         }
150
151         /// <summary>
152         /// Replace VarRefColumnMaps with new VarRefColumnMaps with the specified Var
153         /// </summary>
154         /// <param name="columnMapToTranslate"></param>
155         /// <param name="varToVarMap"></param>
156         /// <returns></returns>
157         internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary<Var, Var> varToVarMap)
158         {
159             ColumnMap result = Translate(columnMapToTranslate,
160                                             delegate(ColumnMap columnMap)
161                                             {
162                                                 VarRefColumnMap varRefColumnMap = columnMap as VarRefColumnMap;
163                                                 if (null != varRefColumnMap)
164                                                 {
165                                                     Var replacementVar = GetReplacementVar(varRefColumnMap.Var, varToVarMap);
166                                                     if (varRefColumnMap.Var != replacementVar)
167                                                     {
168                                                         columnMap = new VarRefColumnMap(varRefColumnMap.Type, varRefColumnMap.Name, replacementVar);
169                                                     }
170                                                 }
171                                                 return columnMap;
172                                             }
173                                             );
174
175             return result;
176         }
177
178         /// <summary>
179         /// Replace VarRefColumnMaps with ScalarColumnMaps referring to the command and column
180         /// </summary>
181         /// <param name="columnMapToTranslate"></param>
182         /// <param name="varToCommandColumnMap"></param>
183         /// <returns></returns>
184         internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary<Var, KeyValuePair<int, int>> varToCommandColumnMap)
185         {
186             ColumnMap result = Translate(columnMapToTranslate,
187                                             delegate(ColumnMap columnMap)
188                                             {
189                                                 VarRefColumnMap varRefColumnMap = columnMap as VarRefColumnMap;
190                                                 if (null != varRefColumnMap)
191                                                 {
192                                                     KeyValuePair<int, int> commandAndColumn;
193
194                                                     if (!varToCommandColumnMap.TryGetValue(varRefColumnMap.Var, out commandAndColumn))
195                                                     {
196                                                         throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownVar, 1, varRefColumnMap.Var.Id); // shouldn't have gotten here without having a resolveable var
197                                                     }
198                                                     columnMap = new ScalarColumnMap(varRefColumnMap.Type, varRefColumnMap.Name, commandAndColumn.Key, commandAndColumn.Value);
199                                                 }
200
201                                                 // While we're at it, we ensure that all columnMaps are named; we wait
202                                                 // until this point, because we don't want to assign names until after
203                                                 // we've gone through the transformations; 
204                                                 if (!columnMap.IsNamed)
205                                                 {
206                                                     columnMap.Name = ColumnMap.DefaultColumnName;
207                                                 }
208                                                 return columnMap;
209                                             }
210                                             );
211
212             return result;
213         }
214
215         #endregion
216
217         #region Visitor methods
218
219         #region List handling
220
221         /// <summary>
222         /// List(ColumnMap)
223         /// </summary>
224         /// <typeparam name="TResultType"></typeparam>
225         /// <param name="tList"></param>
226         /// <param name="translationDelegate"></param>
227         private void VisitList<TResultType>(TResultType[] tList, ColumnMapTranslatorTranslationDelegate translationDelegate)
228                 where TResultType : ColumnMap
229         {
230             for (int i = 0; i < tList.Length; i++)
231             {
232                 tList[i] = (TResultType)tList[i].Accept(this, translationDelegate);
233             }
234         }
235
236         #endregion
237
238         #region EntityIdentity handling
239
240         /// <summary>
241         /// DiscriminatedEntityIdentity
242         /// </summary>
243         /// <param name="entityIdentity"></param>
244         /// <param name="translationDelegate"></param>
245         /// <returns></returns>
246         protected override EntityIdentity VisitEntityIdentity(DiscriminatedEntityIdentity entityIdentity, ColumnMapTranslatorTranslationDelegate translationDelegate)
247         {
248             ColumnMap newEntitySetColumnMap = entityIdentity.EntitySetColumnMap.Accept(this, translationDelegate);
249             VisitList(entityIdentity.Keys, translationDelegate);
250
251             if (newEntitySetColumnMap != entityIdentity.EntitySetColumnMap)
252             {
253                 entityIdentity = new DiscriminatedEntityIdentity((SimpleColumnMap)newEntitySetColumnMap, entityIdentity.EntitySetMap, entityIdentity.Keys);
254             }
255             return entityIdentity;
256         }
257
258         /// <summary>
259         /// SimpleEntityIdentity
260         /// </summary>
261         /// <param name="entityIdentity"></param>
262         /// <param name="translationDelegate"></param>
263         /// <returns></returns>
264         protected override EntityIdentity VisitEntityIdentity(SimpleEntityIdentity entityIdentity, ColumnMapTranslatorTranslationDelegate translationDelegate)
265         {
266             VisitList(entityIdentity.Keys, translationDelegate);
267             return entityIdentity;
268         }
269
270         #endregion
271
272         /// <summary>
273         /// ComplexTypeColumnMap
274         /// </summary>
275         /// <param name="columnMap"></param>
276         /// <param name="translationDelegate"></param>
277         /// <returns></returns>
278         internal override ColumnMap Visit(ComplexTypeColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
279         {
280             SimpleColumnMap newNullSentinel = columnMap.NullSentinel;
281             if (null != newNullSentinel) 
282             {
283                 newNullSentinel = (SimpleColumnMap)translationDelegate(newNullSentinel);
284             }
285
286             VisitList(columnMap.Properties, translationDelegate);
287
288             if (columnMap.NullSentinel != newNullSentinel)
289             {
290                 columnMap = new ComplexTypeColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, newNullSentinel);
291             }
292             return translationDelegate(columnMap);
293         }
294
295         /// <summary>
296         /// DiscriminatedCollectionColumnMap
297         /// </summary>
298         /// <param name="columnMap"></param>
299         /// <param name="translationDelegate"></param>
300         /// <returns></returns>
301         internal override ColumnMap Visit(DiscriminatedCollectionColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
302         {
303             ColumnMap newDiscriminator = columnMap.Discriminator.Accept(this, translationDelegate);
304             VisitList(columnMap.ForeignKeys, translationDelegate);
305             VisitList(columnMap.Keys, translationDelegate);
306             ColumnMap newElement = columnMap.Element.Accept(this, translationDelegate);
307
308             if (newDiscriminator != columnMap.Discriminator || newElement != columnMap.Element)
309             {
310                 columnMap = new DiscriminatedCollectionColumnMap(columnMap.Type, columnMap.Name, newElement, columnMap.Keys, columnMap.ForeignKeys,(SimpleColumnMap)newDiscriminator, columnMap.DiscriminatorValue);
311             }
312             return translationDelegate(columnMap);
313         }
314
315         /// <summary>
316         /// EntityColumnMap
317         /// </summary>
318         /// <param name="columnMap"></param>
319         /// <param name="translationDelegate"></param>
320         /// <returns></returns>
321         internal override ColumnMap Visit(EntityColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
322         {
323             EntityIdentity newEntityIdentity = VisitEntityIdentity(columnMap.EntityIdentity, translationDelegate);
324             VisitList(columnMap.Properties, translationDelegate);
325
326             if (newEntityIdentity != columnMap.EntityIdentity)
327             {
328                 columnMap = new EntityColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, newEntityIdentity);
329             }
330             return translationDelegate(columnMap);
331         }
332
333         /// <summary>
334         /// SimplePolymorphicColumnMap
335         /// </summary>
336         /// <param name="columnMap"></param>
337         /// <param name="translationDelegate"></param>
338         /// <returns></returns>
339         internal override ColumnMap Visit(SimplePolymorphicColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
340         {
341             ColumnMap newTypeDiscriminator = columnMap.TypeDiscriminator.Accept(this, translationDelegate);
342
343             // NOTE: we're using Copy-On-Write logic to avoid allocation if we don't 
344             //       need to change things.
345             Dictionary<object, TypedColumnMap> newTypeChoices = columnMap.TypeChoices;
346             foreach (KeyValuePair<object, TypedColumnMap> kv in columnMap.TypeChoices)
347             {
348                 TypedColumnMap newTypeChoice = (TypedColumnMap)kv.Value.Accept(this, translationDelegate);
349
350                 if (newTypeChoice != kv.Value)
351                 {
352                     if (newTypeChoices == columnMap.TypeChoices)
353                     {
354                         newTypeChoices = new Dictionary<object, TypedColumnMap>(columnMap.TypeChoices);
355                     }
356                     newTypeChoices[kv.Key] = newTypeChoice;
357                 }
358             }
359             VisitList(columnMap.Properties, translationDelegate);
360
361             if (newTypeDiscriminator != columnMap.TypeDiscriminator || newTypeChoices != columnMap.TypeChoices) 
362             {
363                 columnMap = new SimplePolymorphicColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, (SimpleColumnMap)newTypeDiscriminator, newTypeChoices);
364             }
365             return translationDelegate(columnMap);
366         }
367
368         /// <summary>
369         /// MultipleDiscriminatorPolymorphicColumnMap
370         /// </summary>
371         internal override ColumnMap Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
372         {
373             // At this time, we shouldn't ever see this type here; it's for SPROCS which don't use
374             // the plan compiler.
375             System.Data.Query.PlanCompiler.PlanCompiler.Assert(false, "unexpected MultipleDiscriminatorPolymorphicColumnMap in ColumnMapTranslator");
376             return null;
377         }
378
379         /// <summary>
380         /// RecordColumnMap
381         /// </summary>
382         /// <param name="columnMap"></param>
383         /// <param name="translationDelegate"></param>
384         /// <returns></returns>
385         internal override ColumnMap Visit(RecordColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
386         {
387             SimpleColumnMap newNullSentinel = columnMap.NullSentinel;
388             if (null != newNullSentinel) 
389             {
390                 newNullSentinel = (SimpleColumnMap)translationDelegate(newNullSentinel);
391             }
392
393             VisitList(columnMap.Properties, translationDelegate);
394
395             if (columnMap.NullSentinel != newNullSentinel)
396             {
397                 columnMap = new RecordColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, newNullSentinel);
398             }
399             return translationDelegate(columnMap);
400         }
401
402         /// <summary>
403         /// RefColumnMap
404         /// </summary>
405         /// <param name="columnMap"></param>
406         /// <param name="translationDelegate"></param>
407         /// <returns></returns>
408         internal override ColumnMap Visit(RefColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
409         {
410             EntityIdentity newEntityIdentity = VisitEntityIdentity(columnMap.EntityIdentity, translationDelegate);
411
412             if (newEntityIdentity != columnMap.EntityIdentity)
413             {
414                 columnMap = new RefColumnMap(columnMap.Type, columnMap.Name, newEntityIdentity);
415             }
416             return translationDelegate(columnMap);
417         }
418
419         /// <summary>
420         /// ScalarColumnMap
421         /// </summary>
422         /// <param name="columnMap"></param>
423         /// <param name="translationDelegate"></param>
424         /// <returns></returns>
425         internal override ColumnMap Visit(ScalarColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
426         {
427             return translationDelegate(columnMap);
428         }
429
430         /// <summary>
431         /// SimpleCollectionColumnMap
432         /// </summary>
433         /// <param name="columnMap"></param>
434         /// <param name="translationDelegate"></param>
435         /// <returns></returns>
436         internal override ColumnMap Visit(SimpleCollectionColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
437         {
438             VisitList(columnMap.ForeignKeys, translationDelegate);
439             VisitList(columnMap.Keys, translationDelegate);
440             ColumnMap newElement = columnMap.Element.Accept(this, translationDelegate);
441
442             if (newElement != columnMap.Element)
443             {
444                 columnMap = new SimpleCollectionColumnMap(columnMap.Type, columnMap.Name, newElement, columnMap.Keys, columnMap.ForeignKeys);
445             }
446             return translationDelegate(columnMap);
447         }
448
449         /// <summary>
450         /// VarRefColumnMap
451         /// </summary>
452         /// <param name="columnMap"></param>
453         /// <param name="translationDelegate"></param>
454         /// <returns></returns>
455         internal override ColumnMap Visit(VarRefColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
456         {
457             return translationDelegate(columnMap);
458         }
459
460         #endregion
461     }
462 }