1 //---------------------------------------------------------------------
2 // <copyright file="TypeInfo.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
12 using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
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.
28 using System.Globalization;
30 using System.Data.Common;
31 using md = System.Data.Metadata.Edm;
32 using System.Data.Query.InternalTrees;
34 namespace System.Data.Query.PlanCompiler
38 /// The kind of type-id in use
40 internal enum TypeIdKind
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
51 internal class TypeInfo
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
62 #region Constructors and factory methods
65 /// Creates type information for a type
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)
73 if (superTypeInfo == null)
75 result = new RootTypeInfo(type, discriminatorMap);
79 result = new TypeInfo(type, superTypeInfo);
84 protected TypeInfo(md.TypeUsage type, TypeInfo superType)
87 m_immediateSubTypes = new List<TypeInfo>();
88 m_superType = superType;
89 if (superType != null)
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;
100 #region "public" properties for all types
103 /// Is this the root type?
104 /// True for entity, complex types and ref types, if this is the root of the
106 /// Always true for Record types
108 internal bool IsRootType
112 return m_rootType == null;
117 /// the types that derive from this type
119 internal List<TypeInfo> ImmediateSubTypes
123 return m_immediateSubTypes;
128 /// the immediate parent type of this type.
130 internal TypeInfo SuperType
139 /// the top most type in the hierarchy.
141 internal RootTypeInfo RootType
145 return m_rootType ?? (RootTypeInfo)this;
149 /// The metadata type
151 internal md.TypeUsage Type
160 /// The typeid value for this type - only applies to nominal types
162 internal object TypeId
176 #region "public" properties for root types
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.
184 // You could either write:
186 // typeinfo.RootType.FlattenedType
190 // typeinfo.FlattenedType
194 /// Flattened record version of the type
196 internal virtual md.RowType FlattenedType
200 return RootType.FlattenedType;
205 /// TypeUsage that encloses the Flattened record version of the type
207 internal virtual md.TypeUsage FlattenedTypeUsage
211 return RootType.FlattenedTypeUsage;
216 /// Get the property describing the entityset (if any)
218 internal virtual md.EdmProperty EntitySetIdProperty
222 return RootType.EntitySetIdProperty;
227 /// Does this type have an entitySetId property
229 internal bool HasEntitySetIdProperty
233 return RootType.EntitySetIdProperty != null;
238 /// Get the nullSentinel property (if any)
240 internal virtual md.EdmProperty NullSentinelProperty
244 return RootType.NullSentinelProperty;
249 /// Does this type have a nullSentinel property?
251 internal bool HasNullSentinelProperty
255 return RootType.NullSentinelProperty != null;
260 /// The typeid property in the flattened type - applies only to nominal types
261 /// this will be used as the type discriminator column.
263 internal virtual md.EdmProperty TypeIdProperty
267 return RootType.TypeIdProperty;
272 /// Does this type need a typeid property? (Needed for complex types and entity types in general)
274 internal bool HasTypeIdProperty
278 return RootType.TypeIdProperty != null;
283 /// All the properties of this type.
285 internal virtual IEnumerable<PropertyRef> PropertyRefList
289 return RootType.PropertyRefList;
294 /// Get the new property for the supplied propertyRef
296 /// <param name="propertyRef">property reference (on the old type)</param>
297 /// <returns></returns>
298 internal md.EdmProperty GetNewProperty(PropertyRef propertyRef)
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");
307 /// Try get the new property for the supplied propertyRef
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)
315 return this.RootType.TryGetNewProperty(propertyRef, throwIfMissing, out newProperty);
319 /// Get the list of "key" properties (in the flattened type)
321 /// <returns>the key property equivalents in the flattened type</returns>
322 internal IEnumerable<PropertyRef> GetKeyPropertyRefs()
324 md.EntityTypeBase entityType = null;
325 md.RefType refType = null;
326 if (TypeHelpers.TryGetEdmType<md.RefType>(m_type, out refType))
328 entityType = refType.ElementType;
332 entityType = TypeHelpers.GetEdmType<md.EntityTypeBase>(m_type);
335 // Walk through the list of keys of the entity type, and find their analogs in the
337 foreach (md.EdmMember p in entityType.KeyMembers)
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);
347 /// Get the list of "identity" properties in the flattened type.
348 /// The identity properties include the entitysetid property, followed by the
351 /// <returns>List of identity properties</returns>
352 internal IEnumerable<PropertyRef> GetIdentityPropertyRefs()
354 if (this.HasEntitySetIdProperty)
356 yield return EntitySetIdPropertyRef.Instance;
358 foreach (PropertyRef p in this.GetKeyPropertyRefs())
365 /// Get the list of all properties in the flattened type
367 /// <returns></returns>
368 internal IEnumerable<PropertyRef> GetAllPropertyRefs()
370 foreach (PropertyRef p in this.PropertyRefList)
377 /// Get the list of all properties in the flattened type
379 /// <returns></returns>
380 internal IEnumerable<md.EdmProperty> GetAllProperties()
382 foreach (md.EdmProperty m in this.FlattenedType.Properties)
389 /// Gets all types in the hierarchy rooted at this.
391 internal List<TypeInfo> GetTypeHierarchy()
393 List<TypeInfo> result = new List<TypeInfo>();
394 GetTypeHierarchy(result);
399 /// Adds all types in the hierarchy to the given list.
401 private void GetTypeHierarchy(List<TypeInfo> result)
404 foreach (TypeInfo subType in this.ImmediateSubTypes)
406 subType.GetTypeHierarchy(result);
413 /// A subclass of the TypeInfo class above that only represents information
414 /// about "root" types
416 internal class RootTypeInfo : TypeInfo
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;
435 /// Constructor for a root type
437 /// <param name="type"></param>
438 internal RootTypeInfo(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap)
441 PlanCompiler.Assert(type.EdmType.BaseType == null, "only root types allowed here");
443 m_propertyMap = new Dictionary<PropertyRef, md.EdmProperty>();
444 m_propertyRefList = new List<PropertyRef>();
445 m_discriminatorMap = discriminatorMap;
446 m_typeIdKind = TypeIdKind.Generated;
451 #region "public" surface area
454 /// Kind of the typeid column (if any)
456 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
457 internal TypeIdKind TypeIdKind
459 get { return m_typeIdKind; }
460 set { m_typeIdKind = value; }
464 /// Datatype of the typeid column (if any)
466 internal md.TypeUsage TypeIdType
468 get { return m_typeIdType; }
469 set { m_typeIdType = value; }
472 /// Add a mapping from the propertyRef (of the old type) to the
473 /// corresponding property in the new type.
475 /// NOTE: Only to be used by StructuredTypeInfo
477 /// <param name="propertyRef"></param>
478 /// <param name="newProperty"></param>
479 internal void AddPropertyMapping(PropertyRef propertyRef, md.EdmProperty newProperty)
481 m_propertyMap[propertyRef] = newProperty;
482 if (propertyRef is TypeIdPropertyRef)
484 m_typeIdProperty = newProperty;
486 else if (propertyRef is EntitySetIdPropertyRef)
488 m_entitySetIdProperty = newProperty;
490 else if (propertyRef is NullSentinelPropertyRef)
492 m_nullSentinelProperty = newProperty;
497 /// Adds a new property reference to the list of desired properties
498 /// NOTE: Only to be used by StructuredTypeInfo
500 /// <param name="propertyRef"></param>
501 internal void AddPropertyRef(PropertyRef propertyRef)
503 m_propertyRefList.Add(propertyRef);
507 /// Flattened record version of the type
509 internal new md.RowType FlattenedType
513 return m_flattenedType;
517 m_flattenedType = value;
518 m_flattenedTypeUsage = md.TypeUsage.Create(value);
523 /// TypeUsage that encloses the Flattened record version of the type
525 internal new md.TypeUsage FlattenedTypeUsage
529 return m_flattenedTypeUsage;
534 /// Gets map information for types mapped using simple discriminator pattern.
536 internal ExplicitDiscriminatorMap DiscriminatorMap
540 return m_discriminatorMap;
545 /// Get the property describing the entityset (if any)
547 internal new md.EdmProperty EntitySetIdProperty
551 return m_entitySetIdProperty;
555 internal new md.EdmProperty NullSentinelProperty
559 return m_nullSentinelProperty;
564 /// Get the list of property refs for this type
566 internal new IEnumerable<PropertyRef> PropertyRefList
570 return m_propertyRefList;
575 /// Determines the offset for structured types in Flattened type. For instance, if the original type is of the form:
577 /// { int X, ComplexType Y }
579 /// and the flattened type is of the form:
581 /// { int X, Y_ComplexType_Prop1, Y_ComplexType_Prop2 }
583 /// GetNestedStructureOffset(Y) returns 1
585 /// <param name="property">Complex property.</param>
586 /// <returns>Offset.</returns>
587 internal int GetNestedStructureOffset(PropertyRef property)
589 // m_propertyRefList contains every element of the flattened type
590 for (int i = 0; i < m_propertyRefList.Count; i++)
592 NestedPropertyRef nestedPropertyRef = m_propertyRefList[i] as NestedPropertyRef;
594 // match offset of the first element of the complex type property
595 if (null != nestedPropertyRef && nestedPropertyRef.InnerProperty.Equals(property))
600 PlanCompiler.Assert(false, "no complex structure " + property + " found in TypeInfo");
601 // return something so that the compiler doesn't complain
606 /// Try get the new property for the supplied propertyRef
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)
614 bool result = m_propertyMap.TryGetValue(propertyRef, out property);
615 if (throwIfMissing && !result)
618 PlanCompiler.Assert(false, "Unable to find property " + propertyRef.ToString() + " in type " + this.Type.EdmType.Identity);
625 /// The typeid property in the flattened type - applies only to nominal types
626 /// this will be used as the type discriminator column.
628 internal new md.EdmProperty TypeIdProperty
632 return m_typeIdProperty;