d87b08c64e0b797e493a3487b45230425a12d0cc
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / PlanCompiler / TypeInfo.cs
1 //---------------------------------------------------------------------
2 // <copyright file="TypeInfo.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.Globalization;
29
30 using System.Data.Common;
31 using md = System.Data.Metadata.Edm;
32 using System.Data.Query.InternalTrees;
33
34 namespace System.Data.Query.PlanCompiler
35 {
36
37     /// <summary>
38     /// The kind of type-id in use
39     /// </summary>
40     internal enum TypeIdKind
41     {
42         UserSpecified = 0,
43         Generated
44     }
45
46     /// <summary>
47     /// The TypeInfo class encapsulates various pieces of information about a type.
48     /// The most important of these include the "flattened" record type - corresponding
49     /// to the type, and the TypeId field for nominal types
50     /// </summary>
51     internal class TypeInfo
52     {
53
54         #region private state
55         private readonly md.TypeUsage m_type;    // the type
56         private object m_typeId;            // the type's Id, assigned by the StructuredTypeInfo processing.
57         private List<TypeInfo> m_immediateSubTypes;  // the list of children below this type in it's type hierarchy.
58         private readonly TypeInfo m_superType;       // the type one level up in this types type hierarchy -- the base type.
59         private readonly RootTypeInfo m_rootType;    // the top-most type in this types type hierarchy
60         #endregion
61
62         #region Constructors and factory methods
63
64         /// <summary>
65         /// Creates type information for a type
66         /// </summary>
67         /// <param name="type"></param>
68         /// <param name="superTypeInfo"></param>
69         /// <returns></returns>
70         internal static TypeInfo Create(md.TypeUsage type, TypeInfo superTypeInfo, ExplicitDiscriminatorMap discriminatorMap)
71         {
72             TypeInfo result;
73             if (superTypeInfo == null)
74             {
75                 result = new RootTypeInfo(type, discriminatorMap);
76             }
77             else
78             {
79                 result = new TypeInfo(type, superTypeInfo);
80             }
81             return result;
82         }
83
84         protected TypeInfo(md.TypeUsage type, TypeInfo superType)
85         {
86             m_type = type;
87             m_immediateSubTypes = new List<TypeInfo>();
88             m_superType = superType;
89             if (superType != null)
90             {
91                 // Add myself to my supertype's list of subtypes
92                 superType.m_immediateSubTypes.Add(this);
93                 // my supertype's root type is mine as well
94                 m_rootType = superType.RootType;
95             }
96         }
97
98         #endregion
99
100         #region "public" properties for all types
101
102         /// <summary>
103         /// Is this the root type?
104         /// True for entity, complex types and ref types, if this is the root of the
105         /// hierarchy. 
106         /// Always true for Record types
107         /// </summary>
108         internal bool IsRootType
109         {
110             get
111             {
112                 return m_rootType == null;
113             }
114         }
115
116         /// <summary>
117         /// the types that derive from this type
118         /// </summary>
119         internal List<TypeInfo> ImmediateSubTypes
120         {
121             get
122             {
123                 return m_immediateSubTypes;
124             }
125         }
126
127         /// <summary>
128         /// the immediate parent type of this type.
129         /// </summary>
130         internal TypeInfo SuperType
131         {
132             get
133             {
134                 return m_superType;
135             }
136         }
137
138         /// <summary>
139         /// the top most type in the hierarchy.
140         /// </summary>
141         internal RootTypeInfo RootType
142         {
143             get
144             {
145                 return m_rootType ?? (RootTypeInfo)this;
146             }
147         }
148         /// <summary>
149         /// The metadata type
150         /// </summary>
151         internal md.TypeUsage Type
152         {
153             get
154             {
155                 return m_type;
156             }
157         }
158
159         /// <summary>
160         /// The typeid value for this type - only applies to nominal types
161         /// </summary>
162         internal object TypeId
163         {
164             get
165             {
166                 return m_typeId;
167             }
168             set
169             {
170                 m_typeId = value;
171             }
172         }
173
174         #endregion
175
176         #region "public" properties for root types
177
178         // These properties are actually stored on the RootType but we let
179         // let folks use the TypeInfo class as the proxy to get to them.
180         // Essentially, they are mostly sugar to simplify coding.
181         //
182         // For example:
183         //
184         // You could either write:
185         //
186         //      typeinfo.RootType.FlattenedType
187         //
188         // or you can write:
189         //
190         //      typeinfo.FlattenedType
191         //
192
193         /// <summary>
194         /// Flattened record version of the type
195         /// </summary>
196         internal virtual md.RowType FlattenedType
197         {
198             get
199             {
200                 return RootType.FlattenedType;
201             }
202         }
203
204         /// <summary>
205         /// TypeUsage that encloses the Flattened record version of the type
206         /// </summary>
207         internal virtual md.TypeUsage FlattenedTypeUsage
208         {
209             get
210             {
211                 return RootType.FlattenedTypeUsage;
212             }
213         }
214
215         /// <summary>
216         /// Get the property describing the entityset (if any)
217         /// </summary>
218         internal virtual md.EdmProperty EntitySetIdProperty
219         {
220             get
221             {
222                 return RootType.EntitySetIdProperty;
223             }
224         }
225
226         /// <summary>
227         /// Does this type have an entitySetId property
228         /// </summary>
229         internal bool HasEntitySetIdProperty
230         {
231             get
232             {
233                 return RootType.EntitySetIdProperty != null;
234             }
235         }
236
237         /// <summary>
238         /// Get the nullSentinel property (if any)
239         /// </summary>
240         internal virtual md.EdmProperty NullSentinelProperty
241         {
242             get
243             {
244                 return RootType.NullSentinelProperty;
245             }
246         }
247
248         /// <summary>
249         /// Does this type have a nullSentinel property?
250         /// </summary>
251         internal bool HasNullSentinelProperty
252         {
253             get
254             {
255                 return RootType.NullSentinelProperty != null;
256             }
257         }
258
259         /// <summary>
260         /// The typeid property in the flattened type - applies only to nominal types
261         /// this will be used as the type discriminator column.
262         /// </summary>
263         internal virtual md.EdmProperty TypeIdProperty
264         {
265             get
266             {
267                 return RootType.TypeIdProperty;
268             }
269         }
270
271         /// <summary>
272         /// Does this type need a typeid property? (Needed for complex types and entity types in general)
273         /// </summary>
274         internal bool HasTypeIdProperty
275         {
276             get
277             {
278                 return RootType.TypeIdProperty != null;
279             }
280         }
281
282         /// <summary>
283         /// All the properties of this type.
284         /// </summary>
285         internal virtual IEnumerable<PropertyRef> PropertyRefList
286         {
287             get
288             {
289                 return RootType.PropertyRefList;
290             }
291         }
292
293         /// <summary>
294         /// Get the new property for the supplied propertyRef
295         /// </summary>
296         /// <param name="propertyRef">property reference (on the old type)</param>
297         /// <returns></returns>
298         internal md.EdmProperty GetNewProperty(PropertyRef propertyRef)
299         {
300             md.EdmProperty property;
301             bool result = TryGetNewProperty(propertyRef, true, out property);
302             Debug.Assert(result, "Should have thrown if the property was not found");
303             return property;
304         }
305
306         /// <summary>
307         /// Try get the new property for the supplied propertyRef
308         /// </summary>
309         /// <param name="propertyRef">property reference (on the old type)</param>
310         /// <param name="throwIfMissing">throw if the property is not found</param>
311         /// <param name="newProperty">the corresponding property on the new type</param>
312         /// <returns></returns>
313         internal bool TryGetNewProperty(PropertyRef propertyRef, bool throwIfMissing, out md.EdmProperty newProperty)
314         {
315             return this.RootType.TryGetNewProperty(propertyRef, throwIfMissing, out newProperty);
316         }
317
318         /// <summary>
319         /// Get the list of "key" properties (in the flattened type)
320         /// </summary>
321         /// <returns>the key property equivalents in the flattened type</returns>
322         internal IEnumerable<PropertyRef> GetKeyPropertyRefs()
323         {
324             md.EntityTypeBase entityType = null;
325             md.RefType refType = null;
326             if (TypeHelpers.TryGetEdmType<md.RefType>(m_type, out refType))
327             {
328                 entityType = refType.ElementType;
329             }
330             else
331             {
332                 entityType = TypeHelpers.GetEdmType<md.EntityTypeBase>(m_type);
333             }
334
335             // Walk through the list of keys of the entity type, and find their analogs in the
336             // "flattened" type
337             foreach (md.EdmMember p in entityType.KeyMembers)
338             {
339                 // Eventually this could be RelationshipEndMember, but currently only properties are suppported as key members
340                 PlanCompiler.Assert(p is md.EdmProperty, "Non-EdmProperty key members are not supported");
341                 SimplePropertyRef spr = new SimplePropertyRef(p);
342                 yield return spr;
343             }
344         }
345
346         /// <summary>
347         /// Get the list of "identity" properties in the flattened type.
348         /// The identity properties include the entitysetid property, followed by the
349         /// key properties
350         /// </summary>
351         /// <returns>List of identity properties</returns>
352         internal IEnumerable<PropertyRef> GetIdentityPropertyRefs()
353         {
354             if (this.HasEntitySetIdProperty)
355             {
356                 yield return EntitySetIdPropertyRef.Instance;
357             }
358             foreach (PropertyRef p in this.GetKeyPropertyRefs())
359             {
360                 yield return p;
361             }
362         }
363
364         /// <summary>
365         /// Get the list of all properties in the flattened type
366         /// </summary>
367         /// <returns></returns>
368         internal IEnumerable<PropertyRef> GetAllPropertyRefs()
369         {
370             foreach (PropertyRef p in this.PropertyRefList)
371             {
372                 yield return p;
373             }
374         }
375
376         /// <summary>
377         /// Get the list of all properties in the flattened type
378         /// </summary>
379         /// <returns></returns>
380         internal IEnumerable<md.EdmProperty> GetAllProperties()
381         {
382             foreach (md.EdmProperty m in this.FlattenedType.Properties)
383             {
384                 yield return m;
385             }
386         }
387
388         /// <summary>
389         /// Gets all types in the hierarchy rooted at this.
390         /// </summary>
391         internal List<TypeInfo> GetTypeHierarchy()
392         {
393             List<TypeInfo> result = new List<TypeInfo>();
394             GetTypeHierarchy(result);
395             return result;
396         }
397
398         /// <summary>
399         /// Adds all types in the hierarchy to the given list.
400         /// </summary>
401         private void GetTypeHierarchy(List<TypeInfo> result)
402         {
403             result.Add(this);
404             foreach (TypeInfo subType in this.ImmediateSubTypes)
405             {
406                 subType.GetTypeHierarchy(result);
407             }
408         }
409         #endregion
410     }
411
412     /// <summary>
413     /// A subclass of the TypeInfo class above that only represents information
414     /// about "root" types
415     /// </summary>
416     internal class RootTypeInfo : TypeInfo
417     {
418
419         #region private state
420         private readonly List<PropertyRef> m_propertyRefList;
421         private readonly Dictionary<PropertyRef, md.EdmProperty> m_propertyMap;
422         private md.EdmProperty m_nullSentinelProperty;
423         private md.EdmProperty m_typeIdProperty;
424         private TypeIdKind m_typeIdKind;
425         private md.TypeUsage m_typeIdType;
426         private readonly ExplicitDiscriminatorMap m_discriminatorMap;
427         private md.EdmProperty m_entitySetIdProperty;
428         private md.RowType m_flattenedType;
429         private md.TypeUsage m_flattenedTypeUsage;
430         #endregion
431
432         #region Constructor
433
434         /// <summary>
435         /// Constructor for a root type
436         /// </summary>
437         /// <param name="type"></param>
438         internal RootTypeInfo(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap)
439             : base(type, null)
440         {
441             PlanCompiler.Assert(type.EdmType.BaseType == null, "only root types allowed here");
442
443             m_propertyMap = new Dictionary<PropertyRef, md.EdmProperty>();
444             m_propertyRefList = new List<PropertyRef>();
445             m_discriminatorMap = discriminatorMap;
446             m_typeIdKind = TypeIdKind.Generated;
447         }
448
449         #endregion
450
451         #region "public" surface area
452
453         /// <summary>
454         /// Kind of the typeid column (if any)
455         /// </summary>
456         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
457         internal TypeIdKind TypeIdKind
458         {
459             get { return m_typeIdKind; }
460             set { m_typeIdKind = value; }
461         }
462
463         /// <summary>
464         /// Datatype of the typeid column (if any)
465         /// </summary>
466         internal md.TypeUsage TypeIdType
467         {
468             get { return m_typeIdType; }
469             set { m_typeIdType = value; }
470         }
471         /// <summary>
472         /// Add a mapping from the propertyRef (of the old type) to the 
473         /// corresponding property in the new type.
474         /// 
475         /// NOTE: Only to be used by StructuredTypeInfo
476         /// </summary>
477         /// <param name="propertyRef"></param>
478         /// <param name="newProperty"></param>
479         internal void AddPropertyMapping(PropertyRef propertyRef, md.EdmProperty newProperty)
480         {
481             m_propertyMap[propertyRef] = newProperty;
482             if (propertyRef is TypeIdPropertyRef)
483             {
484                 m_typeIdProperty = newProperty;
485             }
486             else if (propertyRef is EntitySetIdPropertyRef)
487             {
488                 m_entitySetIdProperty = newProperty;
489             }
490             else if (propertyRef is NullSentinelPropertyRef)
491             {
492                 m_nullSentinelProperty = newProperty;
493             }
494         }
495
496         /// <summary>
497         /// Adds a new property reference to the list of desired properties
498         /// NOTE: Only to be used by StructuredTypeInfo
499         /// </summary>
500         /// <param name="propertyRef"></param>
501         internal void AddPropertyRef(PropertyRef propertyRef)
502         {
503             m_propertyRefList.Add(propertyRef);
504         }
505
506         /// <summary>
507         /// Flattened record version of the type
508         /// </summary>
509         internal new md.RowType FlattenedType
510         {
511             get
512             {
513                 return m_flattenedType;
514             }
515             set
516             {
517                 m_flattenedType = value;
518                 m_flattenedTypeUsage = md.TypeUsage.Create(value);
519             }
520         }
521
522         /// <summary>
523         /// TypeUsage that encloses the Flattened record version of the type
524         /// </summary>
525         internal new md.TypeUsage FlattenedTypeUsage
526         {
527             get
528             {
529                 return m_flattenedTypeUsage;
530             }
531         }
532
533         /// <summary>
534         /// Gets map information for types mapped using simple discriminator pattern.
535         /// </summary>
536         internal ExplicitDiscriminatorMap DiscriminatorMap
537         {
538             get
539             {
540                 return m_discriminatorMap;
541             }
542         }
543
544         /// <summary>
545         /// Get the property describing the entityset (if any)
546         /// </summary>
547         internal new md.EdmProperty EntitySetIdProperty
548         {
549             get
550             {
551                 return m_entitySetIdProperty;
552             }
553         }
554
555         internal new md.EdmProperty NullSentinelProperty
556         {
557             get
558             {
559                 return m_nullSentinelProperty;
560             }
561         }
562
563         /// <summary>
564         /// Get the list of property refs for this type
565         /// </summary>
566         internal new IEnumerable<PropertyRef> PropertyRefList
567         {
568             get
569             {
570                 return m_propertyRefList;
571             }
572         }
573
574         /// <summary>
575         /// Determines the offset for structured types in Flattened type. For instance, if the original type is of the form:
576         /// 
577         ///     { int X, ComplexType Y }
578         ///     
579         /// and the flattened type is of the form:
580         /// 
581         ///     { int X, Y_ComplexType_Prop1, Y_ComplexType_Prop2 }
582         ///     
583         /// GetNestedStructureOffset(Y) returns 1
584         /// </summary>
585         /// <param name="property">Complex property.</param>
586         /// <returns>Offset.</returns>
587         internal int GetNestedStructureOffset(PropertyRef property)
588         {
589             // m_propertyRefList contains every element of the flattened type
590             for (int i = 0; i < m_propertyRefList.Count; i++)
591             {
592                 NestedPropertyRef nestedPropertyRef = m_propertyRefList[i] as NestedPropertyRef;
593
594                 // match offset of the first element of the complex type property
595                 if (null != nestedPropertyRef && nestedPropertyRef.InnerProperty.Equals(property))
596                 {
597                     return i;
598                 }
599             }
600             PlanCompiler.Assert(false, "no complex structure " + property + " found in TypeInfo");
601             // return something so that the compiler doesn't complain
602             return default(int);
603         }
604
605         /// <summary>
606         /// Try get the new property for the supplied propertyRef
607         /// </summary>
608         /// <param name="propertyRef">property reference (on the old type)</param>
609         /// <param name="throwIfMissing">throw if the property is not found</param>
610         /// <param name="property">the corresponding property on the new type</param>
611         /// <returns></returns>
612         internal new bool TryGetNewProperty(PropertyRef propertyRef, bool throwIfMissing, out md.EdmProperty property)
613         {
614             bool result = m_propertyMap.TryGetValue(propertyRef, out property);
615             if (throwIfMissing && !result)
616             {
617                 {
618                     PlanCompiler.Assert(false, "Unable to find property " + propertyRef.ToString() + " in type " + this.Type.EdmType.Identity);
619                 }
620             }
621             return result;
622         }
623
624         /// <summary>
625         /// The typeid property in the flattened type - applies only to nominal types
626         /// this will be used as the type discriminator column.
627         /// </summary>
628         internal new md.EdmProperty TypeIdProperty
629         {
630             get
631             {
632                 return m_typeIdProperty;
633             }
634         }
635
636         #endregion
637     }
638 }