1 //---------------------------------------------------------------------
2 // <copyright file="ColumnMapTranslator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
12 using System.Globalization;
13 using System.Data.Query.InternalTrees;
14 using System.Data.Query.PlanCompiler;
16 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
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.
33 namespace System.Data.Query.PlanCompiler
37 /// Delegate pattern that the ColumnMapTranslator uses to find its replacement
38 /// columnMaps. Given a columnMap, return it's replacement.
40 /// <param name="columnMap"></param>
41 /// <returns></returns>
42 internal delegate ColumnMap ColumnMapTranslatorTranslationDelegate(ColumnMap columnMap);
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.
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
53 /// NOTE: over time, we should be able to modify the ColumnMaps to have more writable
56 internal class ColumnMapTranslator : ColumnMapVisitorWithResults<ColumnMap, ColumnMapTranslatorTranslationDelegate>
62 /// Singleton instance for the "public" methods to use;
64 static private ColumnMapTranslator Instance = new ColumnMapTranslator();
67 /// Constructor; no one should use this.
69 private ColumnMapTranslator()
75 #region Visitor Helpers
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.
82 /// <param name="originalVar"></param>
83 /// <param name="replacementVarMap"></param>
84 /// <returns></returns>
85 private static Var GetReplacementVar(Var originalVar, Dictionary<Var, Var> replacementVarMap)
87 // SQLBUDT #478509: Follow the chain of mapped vars, don't
88 // just stop at the first one
89 Var replacementVar = originalVar;
91 while (replacementVarMap.TryGetValue(replacementVar, out originalVar))
93 if (originalVar == replacementVar)
97 replacementVar = originalVar;
99 return replacementVar;
104 #region "Public" surface area
107 /// Bring-Your-Own-Replacement-Delegate method.
109 /// <param name="columnMap"></param>
110 /// <param name="translationDelegate"></param>
111 /// <returns></returns>
112 internal static ColumnMap Translate(ColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
114 return columnMap.Accept(ColumnMapTranslator.Instance, translationDelegate);
118 /// Replace VarRefColumnMaps with the specified ColumnMap replacement
120 /// <param name="columnMapToTranslate"></param>
121 /// <param name="varToColumnMap"></param>
122 /// <returns></returns>
123 internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary<Var, ColumnMap> varToColumnMap)
125 ColumnMap result = Translate(columnMapToTranslate,
126 delegate(ColumnMap columnMap)
128 VarRefColumnMap varRefColumnMap = columnMap as VarRefColumnMap;
129 if (null != varRefColumnMap)
131 if (varToColumnMap.TryGetValue(varRefColumnMap.Var, out columnMap))
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)
137 columnMap.Name = varRefColumnMap.Name;
142 columnMap = varRefColumnMap;
152 /// Replace VarRefColumnMaps with new VarRefColumnMaps with the specified Var
154 /// <param name="columnMapToTranslate"></param>
155 /// <param name="varToVarMap"></param>
156 /// <returns></returns>
157 internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary<Var, Var> varToVarMap)
159 ColumnMap result = Translate(columnMapToTranslate,
160 delegate(ColumnMap columnMap)
162 VarRefColumnMap varRefColumnMap = columnMap as VarRefColumnMap;
163 if (null != varRefColumnMap)
165 Var replacementVar = GetReplacementVar(varRefColumnMap.Var, varToVarMap);
166 if (varRefColumnMap.Var != replacementVar)
168 columnMap = new VarRefColumnMap(varRefColumnMap.Type, varRefColumnMap.Name, replacementVar);
179 /// Replace VarRefColumnMaps with ScalarColumnMaps referring to the command and column
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)
186 ColumnMap result = Translate(columnMapToTranslate,
187 delegate(ColumnMap columnMap)
189 VarRefColumnMap varRefColumnMap = columnMap as VarRefColumnMap;
190 if (null != varRefColumnMap)
192 KeyValuePair<int, int> commandAndColumn;
194 if (!varToCommandColumnMap.TryGetValue(varRefColumnMap.Var, out commandAndColumn))
196 throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.UnknownVar, 1, varRefColumnMap.Var.Id); // shouldn't have gotten here without having a resolveable var
198 columnMap = new ScalarColumnMap(varRefColumnMap.Type, varRefColumnMap.Name, commandAndColumn.Key, commandAndColumn.Value);
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)
206 columnMap.Name = ColumnMap.DefaultColumnName;
217 #region Visitor methods
219 #region List handling
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
230 for (int i = 0; i < tList.Length; i++)
232 tList[i] = (TResultType)tList[i].Accept(this, translationDelegate);
238 #region EntityIdentity handling
241 /// DiscriminatedEntityIdentity
243 /// <param name="entityIdentity"></param>
244 /// <param name="translationDelegate"></param>
245 /// <returns></returns>
246 protected override EntityIdentity VisitEntityIdentity(DiscriminatedEntityIdentity entityIdentity, ColumnMapTranslatorTranslationDelegate translationDelegate)
248 ColumnMap newEntitySetColumnMap = entityIdentity.EntitySetColumnMap.Accept(this, translationDelegate);
249 VisitList(entityIdentity.Keys, translationDelegate);
251 if (newEntitySetColumnMap != entityIdentity.EntitySetColumnMap)
253 entityIdentity = new DiscriminatedEntityIdentity((SimpleColumnMap)newEntitySetColumnMap, entityIdentity.EntitySetMap, entityIdentity.Keys);
255 return entityIdentity;
259 /// SimpleEntityIdentity
261 /// <param name="entityIdentity"></param>
262 /// <param name="translationDelegate"></param>
263 /// <returns></returns>
264 protected override EntityIdentity VisitEntityIdentity(SimpleEntityIdentity entityIdentity, ColumnMapTranslatorTranslationDelegate translationDelegate)
266 VisitList(entityIdentity.Keys, translationDelegate);
267 return entityIdentity;
273 /// ComplexTypeColumnMap
275 /// <param name="columnMap"></param>
276 /// <param name="translationDelegate"></param>
277 /// <returns></returns>
278 internal override ColumnMap Visit(ComplexTypeColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
280 SimpleColumnMap newNullSentinel = columnMap.NullSentinel;
281 if (null != newNullSentinel)
283 newNullSentinel = (SimpleColumnMap)translationDelegate(newNullSentinel);
286 VisitList(columnMap.Properties, translationDelegate);
288 if (columnMap.NullSentinel != newNullSentinel)
290 columnMap = new ComplexTypeColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, newNullSentinel);
292 return translationDelegate(columnMap);
296 /// DiscriminatedCollectionColumnMap
298 /// <param name="columnMap"></param>
299 /// <param name="translationDelegate"></param>
300 /// <returns></returns>
301 internal override ColumnMap Visit(DiscriminatedCollectionColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
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);
308 if (newDiscriminator != columnMap.Discriminator || newElement != columnMap.Element)
310 columnMap = new DiscriminatedCollectionColumnMap(columnMap.Type, columnMap.Name, newElement, columnMap.Keys, columnMap.ForeignKeys,(SimpleColumnMap)newDiscriminator, columnMap.DiscriminatorValue);
312 return translationDelegate(columnMap);
318 /// <param name="columnMap"></param>
319 /// <param name="translationDelegate"></param>
320 /// <returns></returns>
321 internal override ColumnMap Visit(EntityColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
323 EntityIdentity newEntityIdentity = VisitEntityIdentity(columnMap.EntityIdentity, translationDelegate);
324 VisitList(columnMap.Properties, translationDelegate);
326 if (newEntityIdentity != columnMap.EntityIdentity)
328 columnMap = new EntityColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, newEntityIdentity);
330 return translationDelegate(columnMap);
334 /// SimplePolymorphicColumnMap
336 /// <param name="columnMap"></param>
337 /// <param name="translationDelegate"></param>
338 /// <returns></returns>
339 internal override ColumnMap Visit(SimplePolymorphicColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
341 ColumnMap newTypeDiscriminator = columnMap.TypeDiscriminator.Accept(this, translationDelegate);
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)
348 TypedColumnMap newTypeChoice = (TypedColumnMap)kv.Value.Accept(this, translationDelegate);
350 if (newTypeChoice != kv.Value)
352 if (newTypeChoices == columnMap.TypeChoices)
354 newTypeChoices = new Dictionary<object, TypedColumnMap>(columnMap.TypeChoices);
356 newTypeChoices[kv.Key] = newTypeChoice;
359 VisitList(columnMap.Properties, translationDelegate);
361 if (newTypeDiscriminator != columnMap.TypeDiscriminator || newTypeChoices != columnMap.TypeChoices)
363 columnMap = new SimplePolymorphicColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, (SimpleColumnMap)newTypeDiscriminator, newTypeChoices);
365 return translationDelegate(columnMap);
369 /// MultipleDiscriminatorPolymorphicColumnMap
371 internal override ColumnMap Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
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");
382 /// <param name="columnMap"></param>
383 /// <param name="translationDelegate"></param>
384 /// <returns></returns>
385 internal override ColumnMap Visit(RecordColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
387 SimpleColumnMap newNullSentinel = columnMap.NullSentinel;
388 if (null != newNullSentinel)
390 newNullSentinel = (SimpleColumnMap)translationDelegate(newNullSentinel);
393 VisitList(columnMap.Properties, translationDelegate);
395 if (columnMap.NullSentinel != newNullSentinel)
397 columnMap = new RecordColumnMap(columnMap.Type, columnMap.Name, columnMap.Properties, newNullSentinel);
399 return translationDelegate(columnMap);
405 /// <param name="columnMap"></param>
406 /// <param name="translationDelegate"></param>
407 /// <returns></returns>
408 internal override ColumnMap Visit(RefColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
410 EntityIdentity newEntityIdentity = VisitEntityIdentity(columnMap.EntityIdentity, translationDelegate);
412 if (newEntityIdentity != columnMap.EntityIdentity)
414 columnMap = new RefColumnMap(columnMap.Type, columnMap.Name, newEntityIdentity);
416 return translationDelegate(columnMap);
422 /// <param name="columnMap"></param>
423 /// <param name="translationDelegate"></param>
424 /// <returns></returns>
425 internal override ColumnMap Visit(ScalarColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
427 return translationDelegate(columnMap);
431 /// SimpleCollectionColumnMap
433 /// <param name="columnMap"></param>
434 /// <param name="translationDelegate"></param>
435 /// <returns></returns>
436 internal override ColumnMap Visit(SimpleCollectionColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
438 VisitList(columnMap.ForeignKeys, translationDelegate);
439 VisitList(columnMap.Keys, translationDelegate);
440 ColumnMap newElement = columnMap.Element.Accept(this, translationDelegate);
442 if (newElement != columnMap.Element)
444 columnMap = new SimpleCollectionColumnMap(columnMap.Type, columnMap.Name, newElement, columnMap.Keys, columnMap.ForeignKeys);
446 return translationDelegate(columnMap);
452 /// <param name="columnMap"></param>
453 /// <param name="translationDelegate"></param>
454 /// <returns></returns>
455 internal override ColumnMap Visit(VarRefColumnMap columnMap, ColumnMapTranslatorTranslationDelegate translationDelegate)
457 return translationDelegate(columnMap);