private readonly RootTypeInfo m_rootType; // the top-most type in this types type hierarchy #endregion #region Constructors and factory methods /// <summary> /// Creates type information for a type /// </summary> /// <param name="type"></param> /// <param name="superTypeInfo"></param> /// <returns></returns> internal static TypeInfo Create(md.TypeUsage type, TypeInfo superTypeInfo, ExplicitDiscriminatorMap discriminatorMap) { TypeInfo result; if (superTypeInfo == null) { result = new RootTypeInfo(type, discriminatorMap); } else { result = new TypeInfo(type, superTypeInfo); } return result; }
protected TypeInfo(md.TypeUsage type, TypeInfo superType) { m_type = type; m_immediateSubTypes = new List<TypeInfo>(); m_superType = superType; if (superType != null) { // Add myself to my supertype's list of subtypes superType.m_immediateSubTypes.Add(this); // my supertype's root type is mine as well m_rootType = superType.RootType; } }
/// <summary> /// Create a node to represent the exact value of the typeid constant /// </summary> /// <param name="typeInfo">The current type</param> /// <returns>Node for the typeid constant</returns> private Node CreateTypeIdConstant(TypeInfo typeInfo) { object value = typeInfo.TypeId; md.TypeUsage typeIdType; if (typeInfo.RootType.DiscriminatorMap != null) { typeIdType = md.Helper.GetModelTypeUsage(typeInfo.RootType.DiscriminatorMap.DiscriminatorProperty); } else { typeIdType = DefaultTypeIdType; } InternalConstantOp op = m_command.CreateInternalConstantOp(typeIdType, value); return m_command.CreateNode(op); }
/// <summary> /// Assigns typeids to each subtype of the current type. /// Assertion: the current type has already had a typeid assigned to it. /// </summary> /// <param name="typeInfo">The current type</param> private void AssignTypeIdsToSubTypes(TypeInfo typeInfo) { // Now walk through all my subtypes, and assign their typeids int mySubTypeNum = 0; foreach (TypeInfo subtype in typeInfo.ImmediateSubTypes) { AssignTypeId(subtype, mySubTypeNum); mySubTypeNum++; } }
/// <summary> /// Builds up an accessor to the typeid property. If the type has no typeid /// property, then we simply create a constantOp with the corresponding /// typeid value for the type /// </summary> /// <param name="input">the input expression</param> /// <param name="typeInfo">the original type of the input expression</param> /// <returns></returns> private Node BuildTypeIdAccessor(Node input, TypeInfo typeInfo) { Node result; if (typeInfo.HasTypeIdProperty) { result = BuildAccessorWithNulls(input, typeInfo.TypeIdProperty); } else { result = CreateTypeIdConstant(typeInfo); } return result; }
/// <summary> /// Build up the list of columnmaps for the relproperties. /// Assumption: rel-properties follow after ALL the regular properties of the /// types in the type hierarchy. /// For now, we're simply going to ignore the rel-property columnmaps - we're /// just going to use this function to "drain" the corresponding vars /// </summary> /// <param name="typeInfo">typeinfo for the entity type</param> /// <param name="includeSupertypeRelProperties">should we get rel-properties from our supertype instances</param> private void BuildRelPropertyColumnMaps(TypeInfo typeInfo, bool includeSupertypeRelProperties) { // // Get the appropriate set of rel-properties // IEnumerable<RelProperty> relProperties = null; if (includeSupertypeRelProperties) { relProperties = m_typeInfo.RelPropertyHelper.GetRelProperties(typeInfo.Type.EdmType as md.EntityTypeBase); } else { relProperties = m_typeInfo.RelPropertyHelper.GetDeclaredOnlyRelProperties(typeInfo.Type.EdmType as md.EntityTypeBase); } // // Create a column-map for each rel-properties // foreach (RelProperty property in relProperties) { ColumnMap propertyColumnMap = CreateColumnMap(property.ToEnd.TypeUsage, property.ToString()); } // // Add all subtypes // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { BuildRelPropertyColumnMaps(subTypeInfo, false); } }
/// <summary> /// Create a column map for a record type. Simply iterates through the /// list of fields, and produces a column map for each field /// </summary> /// <param name="typeInfo">Type information for the record type</param> /// <param name="name">column name</param> /// <returns></returns> private RecordColumnMap CreateRecordColumnMap(TypeInfo typeInfo, string name) { PlanCompiler.Assert(typeInfo.Type.EdmType is md.RowType, "not RowType"); SimpleColumnMap nullSentinelColumnMap = null; if (typeInfo.HasNullSentinelProperty) { nullSentinelColumnMap = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(typeInfo.NullSentinelProperty), c_NullSentinelColumnName); } md.ReadOnlyMetadataCollection<md.EdmProperty> properties = TypeHelpers.GetProperties(typeInfo.Type); ColumnMap[] propertyColumnMapList = new ColumnMap[properties.Count]; for (int i = 0; i < propertyColumnMapList.Length; ++i) { md.EdmMember property = properties[i]; propertyColumnMapList[i] = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); } RecordColumnMap result = new RecordColumnMap(typeInfo.Type, name, propertyColumnMapList, nullSentinelColumnMap); return result; }
/// <summary> /// Get a list of properties and value (expressions) for each desired property of the /// input. The list of desired properties is based on the opKind parameter. /// The ignoreMissingProperties indicates if we should create a null constant, in case /// the input cannot produce the specified property /// </summary> /// <param name="typeInfo">typeinfo for the input</param> /// <param name="opKind">Current operation kind</param> /// <param name="input">The input expression tree</param> /// <param name="ignoreMissingProperties">Should we ignore missing properties</param> /// <param name="properties">Output: list of properties</param> /// <param name="values">Output: correspondng list of values</param> private void GetPropertyValues(TypeInfo typeInfo, OperationKind opKind, Node input, bool ignoreMissingProperties, out List<md.EdmProperty> properties, out List<Node> values) { values = new List<Node>(); properties = new List<md.EdmProperty>(); foreach (md.EdmProperty prop in GetProperties(typeInfo, opKind)) { KeyValuePair<md.EdmProperty, Node> kv = GetPropertyValue(input, prop, ignoreMissingProperties); if (kv.Value != null) { properties.Add(kv.Key); values.Add(kv.Value); } } }
/// <summary> /// Create a column map for a complextype column /// </summary> /// <param name="typeInfo">Type information for the type</param> /// <param name="name">column name</param> /// <param name="superTypeColumnMap">Supertype info if any</param> /// <param name="discriminatorMap">Dictionary of typeidvalue->column map</param> /// <param name="allMaps">List of all maps</param> /// <returns></returns> private ComplexTypeColumnMap CreateComplexTypeColumnMap(TypeInfo typeInfo, string name, ComplexTypeColumnMap superTypeColumnMap, Dictionary<object, TypedColumnMap> discriminatorMap, List<TypedColumnMap> allMaps) { List<ColumnMap> propertyColumnMapList = new List<ColumnMap>(); IEnumerable myProperties = null; SimpleColumnMap nullSentinelColumnMap = null; if (typeInfo.HasNullSentinelProperty) { nullSentinelColumnMap = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(typeInfo.NullSentinelProperty), c_NullSentinelColumnName); } // Copy over information from my supertype if it already exists if (superTypeColumnMap != null) { foreach (ColumnMap c in superTypeColumnMap.Properties) { propertyColumnMapList.Add(c); } myProperties = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); } else { // need to get all members otherwise myProperties = TypeHelpers.GetAllStructuralMembers(typeInfo.Type); } // Now add on all of my "specific" properties foreach (md.EdmMember property in myProperties) { ColumnMap propertyColumnMap = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); propertyColumnMapList.Add(propertyColumnMap); } // Create a map for myself ComplexTypeColumnMap columnMap = new ComplexTypeColumnMap(typeInfo.Type, name, propertyColumnMapList.ToArray(), nullSentinelColumnMap); // if a dictionary is supplied, add myself to the dictionary if (discriminatorMap != null) { discriminatorMap[typeInfo.TypeId] = columnMap; } if (allMaps != null) { allMaps.Add(columnMap); } // Finally walk through my subtypes - use the same column name foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { CreateComplexTypeColumnMap(subTypeInfo, name, columnMap, discriminatorMap, allMaps); } return columnMap; }
/// <summary> /// Create a node to represent a typeid constant for a prefix match. /// If the typeid value were "123X", then we would generate a constant /// like "123X%" /// </summary> /// <param name="typeInfo">the current type</param> /// <returns>Node for the typeid constant</returns> private Node CreateTypeIdConstantForPrefixMatch(TypeInfo typeInfo) { string value = typeInfo.TypeId + PrefixMatchCharacter; InternalConstantOp op = m_command.CreateInternalConstantOp(DefaultTypeIdType, value); return m_command.CreateNode(op); }
/// <summary> /// Get the list of "desired" propertyrefs for the specified type and operation /// </summary> /// <param name="typeInfo"></param> /// <param name="opKind"></param> /// <returns></returns> private IEnumerable<PropertyRef> GetPropertyRefs(TypeInfo typeInfo, OperationKind opKind) { PlanCompiler.Assert(opKind != OperationKind.All, "unexpected attempt to GetPropertyRefs(...,OperationKind.All)"); if (opKind == OperationKind.GetKeys) { return typeInfo.GetKeyPropertyRefs(); } else if (opKind == OperationKind.GetIdentity) { return typeInfo.GetIdentityPropertyRefs(); } else { return GetPropertyRefsForComparisonAndIsNull(typeInfo, opKind); } }
/// <summary> /// Helper for ExplodeType. /// Walks through each member introduced by the current type, and /// adds it onto the "flat" record type being constructed. /// We then walk through all subtypes of this type, and process those as /// well. /// Special handling for Refs: we only add the keys; there is no /// need to handle subtypes (since they won't be introducing anything /// different) /// </summary> /// <param name="typeInfo">type in the type hierarchy</param> private void ExplodeRootStructuredTypeHelper(TypeInfo typeInfo) { RootTypeInfo rootType = typeInfo.RootType; // Identify the members of this type. For Refs, use the key properties // of the target entity type. For all other types, simply use the type // members IEnumerable typeMembers = null; md.RefType refType; if (TypeHelpers.TryGetEdmType<md.RefType>(typeInfo.Type, out refType)) { // // If this is not the root type, then don't bother adding the keys. // the root type has already done this // if (!typeInfo.IsRootType) { return; } typeMembers = refType.ElementType.KeyMembers; } else { typeMembers = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); } // Walk through all the members of the type foreach (md.EdmMember p in typeMembers) { TypeInfo propertyType = ExplodeType(p.TypeUsage); // // If we can't find a TypeInfo for this property's type, then it must // be a scalar type or a collection type. In either case, we'll // build up a SimplePropertyRef // if (propertyType == null) { rootType.AddPropertyRef(new SimplePropertyRef(p)); } else { // // We're dealing with a structured type again. Create NestedPropertyRef // for each property of the nested type // foreach (PropertyRef nestedPropInfo in propertyType.PropertyRefList) { rootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p)); } } } // // Process all subtypes now // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { ExplodeRootStructuredTypeHelper(subTypeInfo); } }
/// <summary> /// Add the list of rel-properties for this type /// </summary> /// <param name="typeInfo">the type to process</param> private void AddRelProperties(TypeInfo typeInfo) { md.EntityTypeBase entityType = (md.EntityTypeBase)typeInfo.Type.EdmType; // // Walk through each rel-property defined for this specific type, // and add a corresponding property-ref // foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) { md.EdmType refType = p.ToEnd.TypeUsage.EdmType; TypeInfo refTypeInfo = GetTypeInfo(p.ToEnd.TypeUsage); // // We're dealing with a structured type again - flatten this out // as well // ExplodeType(refTypeInfo); foreach (PropertyRef nestedPropInfo in refTypeInfo.PropertyRefList) { typeInfo.RootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p)); } } // // Process all subtypes now // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { AddRelProperties(subTypeInfo); } }
/// <summary> /// Type Explosion - simply delegates to the root type /// </summary> /// <param name="typeInfo">type info</param> private void ExplodeType(TypeInfo typeInfo) { ExplodeRootStructuredType(typeInfo.RootType); }
/// <summary> /// The type needs an entitysetidproperty, if it is either an entity type /// or a reference type, AND we cannot determine that there is only entityset /// in the query that could be producing instances of this entity /// </summary> /// <param name="typeInfo"></param> /// <returns></returns> private bool NeedsEntitySetIdProperty(TypeInfo typeInfo) { md.EntityType entityType; md.RefType refType = typeInfo.Type.EdmType as md.RefType; if (refType != null) { entityType = refType.ElementType as md.EntityType; } else { entityType = typeInfo.Type.EdmType as md.EntityType; } bool result = ((entityType != null) && (GetEntitySet(entityType) == null)); return result; }
/// <summary> /// A type needs a null-sentinel property if it is an row type that was projected /// at the top level of the query; we capture that information in the preprocessor /// and pass it in here. /// </summary> /// <param name="typeInfo"></param> /// <returns></returns> private bool NeedsNullSentinelProperty(TypeInfo typeInfo) { return m_typesNeedingNullSentinel.Contains(typeInfo.Type.EdmType.Identity); }
/// <summary> /// A type needs a type-id property if it is an entity type or a complex tpe that /// has subtypes. /// Coming soon: relax the "need subtype" requirement (ie) any entity/complex type will /// have a typeid /// </summary> /// <param name="typeInfo"></param> /// <returns></returns> private bool NeedsTypeIdProperty(TypeInfo typeInfo) { return typeInfo.ImmediateSubTypes.Count > 0 && !md.TypeSemantics.IsReferenceType(typeInfo.Type); }
/// <summary> /// Assign a typeid to a non-root type. /// Assigns typeids to a non-root type based on a dewey encoding scheme. /// The typeid will be the typeId of the supertype suffixed by a /// local identifier for the type. /// </summary> /// <param name="typeInfo">the non-root type</param> /// <param name="subtypeNum">position in the subtype list</param> private void AssignTypeId(TypeInfo typeInfo, int subtypeNum) { typeInfo.TypeId = String.Format(CultureInfo.InvariantCulture, "{0}{1}X", typeInfo.SuperType.TypeId, subtypeNum); AssignTypeIdsToSubTypes(typeInfo); }
/// <summary> /// Identify the list of property refs for comparison and isnull semantics /// </summary> /// <param name="typeInfo"></param> /// <param name="opKind"></param> /// <returns></returns> private IEnumerable<PropertyRef> GetPropertyRefsForComparisonAndIsNull(TypeInfo typeInfo, OperationKind opKind) { PlanCompiler.Assert(opKind == OperationKind.IsNull || opKind == OperationKind.Equality, "Unexpected opKind: " + opKind + "; Can only handle IsNull and Equality"); md.TypeUsage currentType = typeInfo.Type; md.RowType recordType = null; if (TypeHelpers.TryGetEdmType<md.RowType>(currentType, out recordType)) { if (opKind == OperationKind.IsNull && typeInfo.HasNullSentinelProperty) { yield return NullSentinelPropertyRef.Instance; } else foreach (md.EdmProperty m in recordType.Properties) { if (!TypeUtils.IsStructuredType(md.Helper.GetModelTypeUsage(m))) { yield return new SimplePropertyRef(m); } else { TypeInfo nestedTypeInfo = m_typeInfo.GetTypeInfo(md.Helper.GetModelTypeUsage(m)); foreach (PropertyRef p in GetPropertyRefs(nestedTypeInfo, opKind)) { PropertyRef nestedPropertyRef = p.CreateNestedPropertyRef(m); yield return nestedPropertyRef; } } } yield break; } md.EntityType entityType = null; if (TypeHelpers.TryGetEdmType<md.EntityType>(currentType, out entityType)) { if (opKind == OperationKind.Equality || (opKind == OperationKind.IsNull && !typeInfo.HasTypeIdProperty)) { foreach (PropertyRef p in typeInfo.GetIdentityPropertyRefs()) { yield return p; } } else { yield return TypeIdPropertyRef.Instance; } yield break; } md.ComplexType complexType = null; if (TypeHelpers.TryGetEdmType<md.ComplexType>(currentType, out complexType)) { PlanCompiler.Assert(opKind == OperationKind.IsNull, "complex types not equality-comparable"); PlanCompiler.Assert(typeInfo.HasNullSentinelProperty, "complex type with no null sentinel property: can't handle isNull"); yield return NullSentinelPropertyRef.Instance; yield break; } md.RefType refType = null; if (TypeHelpers.TryGetEdmType<md.RefType>(currentType, out refType)) { foreach (PropertyRef p in typeInfo.GetAllPropertyRefs()) { yield return p; } yield break; } PlanCompiler.Assert(false, "Unknown type"); }
/// <summary> /// Flattens a CaseOp - Specifically, if the CaseOp returns a structuredtype, /// then the CaseOp is broken up so that we build up a "flat" record constructor /// for that structured type, with each argument to the record constructor being /// a (scalar) CaseOp. For example: /// /// Case when b1 then e1 else e2 end /// /// gets translated into: /// /// RecordOp(case when b1 then e1.a else e2.a end, /// case when b1 then e1.b else e2.b end, /// ...) /// /// The property extraction is optimized by producing only those properties /// that have actually been requested. /// </summary> /// <param name="op">the CaseOp</param> /// <param name="n">Node corresponding to the CaseOp</param> /// <param name="typeInfo">Information about the type</param> /// <param name="desiredProperties">Set of properties desired</param> /// <returns></returns> private Node FlattenCaseOp(CaseOp op, Node n, TypeInfo typeInfo, PropertyRefList desiredProperties) { // Build up a type constructor - with only as many fields filled in // as are desired. List<md.EdmProperty> fieldTypes = new List<md.EdmProperty>(); List<Node> fieldValues = new List<Node>(); foreach (PropertyRef pref in typeInfo.PropertyRefList) { // Is this property desired later? if (!desiredProperties.Contains(pref)) { continue; } md.EdmProperty property = typeInfo.GetNewProperty(pref); // Build up an accessor for this property across each when/then clause List<Node> caseChildren = new List<Node>(); for (int i = 0; i < n.Children.Count - 1; ) { Node whenNode = Copy(n.Children[i]); caseChildren.Add(whenNode); i++; Node propNode = BuildAccessorWithNulls(n.Children[i], property); caseChildren.Add(propNode); i++; } Node elseNode = BuildAccessorWithNulls(n.Children[n.Children.Count - 1], property); caseChildren.Add(elseNode); Node caseNode = m_command.CreateNode(m_command.CreateCaseOp(md.Helper.GetModelTypeUsage(property)), caseChildren); fieldTypes.Add(property); fieldValues.Add(caseNode); } NewRecordOp newRec = m_command.CreateNewRecordOp(typeInfo.FlattenedTypeUsage, fieldTypes); return m_command.CreateNode(newRec, fieldValues); }
/// <summary> /// Get a list of "desired" properties for each operationKind (specified by the opKind /// parameter). The OpKinds we support are /// /// * GetKeys /// Applies only to entity and ref types - gets the key properties (more specifically /// the flattened equivalents) /// * GetIdentity /// Applies only to entity and ref types - gets the entityset id property first, and then the /// the Key properties /// * All /// Gets all properties of the flattened type /// /// * Equality /// Scalar types - the entire instance /// Entity - the identity properties /// Ref - all properties (= identity properties) /// Complex/Collection - Not supported /// Record - recurse over each property /// /// * IsNull /// Scalar types - entire instance /// Entity - typeid property, if it exists; otherwise, the key properties /// ComplexType - typeid property /// Ref - all properties /// Collection - not supported /// Record - recurse over each property /// </summary> /// <param name="typeInfo">Type information for the current op</param> /// <param name="opKind">Current operation kind</param> /// <returns>List of desired properties</returns> private IEnumerable<md.EdmProperty> GetProperties(TypeInfo typeInfo, OperationKind opKind) { if (opKind == OperationKind.All) { foreach (md.EdmProperty p in typeInfo.GetAllProperties()) { yield return p; } } else { foreach (PropertyRef p in GetPropertyRefs(typeInfo, opKind)) { yield return typeInfo.GetNewProperty(p); } } }
// We have to adjust for when we're supposed to remove null sentinels; // columns (See SQLBUDT #553534 for an example). Note that we shouldn't // have to add null sentinels here, since reference types won't be expecting // them (the fact that the key is null is good enough...) private static void RemoveNullSentinel(TypeInfo inputTypeInfo, List<md.EdmProperty> inputFields, List<Node> inputFieldValues, List<md.EdmProperty> outputFields) { PlanCompiler.Assert(inputFields[0] == inputTypeInfo.NullSentinelProperty, "InputField0 must be the null sentinel property"); inputFields.RemoveAt(0); inputFieldValues.RemoveAt(0); }
/// <summary> /// Project properties of <paramref name="unnestOpTableTypeInfo"/> that represents the flattened type of the <paramref name="unnestNode"/>. /// The <paramref name="unnestNode"/> contains a TVF call. /// Return new node with ProjectOp and <paramref name="newVars"/> representing the projection outputs. /// </summary> private Node CreateTVFProjection(Node unnestNode, List<Var> unnestOpTableColumns, TypeInfo unnestOpTableTypeInfo, out List<Var> newVars) { md.RowType originalRowType = unnestOpTableTypeInfo.Type.EdmType as md.RowType; PlanCompiler.Assert(originalRowType != null, "Unexpected TVF return type (must be row): " + unnestOpTableTypeInfo.Type.ToString()); List<Var> convertToFlattenedTypeVars = new List<Var>(); List<Node> convertToFlattenedTypeVarDefs = new List<Node>(); PropertyRef[] propRefs = unnestOpTableTypeInfo.PropertyRefList.ToArray(); Dictionary<md.EdmProperty, PropertyRef> flattenedTypePropertyToPropertyRef = new Dictionary<md.EdmProperty, PropertyRef>(); foreach (var propRef in propRefs) { flattenedTypePropertyToPropertyRef.Add(unnestOpTableTypeInfo.GetNewProperty(propRef), propRef); } foreach (var flattenedTypeProperty in unnestOpTableTypeInfo.FlattenedType.Properties) { var propRef = flattenedTypePropertyToPropertyRef[flattenedTypeProperty]; Var var = null; SimplePropertyRef simplePropRef = propRef as SimplePropertyRef; if (simplePropRef != null) { // Find the corresponding column in the TVF output and build a var ref to it. int columnIndex = originalRowType.Members.IndexOf(simplePropRef.Property); PlanCompiler.Assert(columnIndex >= 0, "Can't find a column in the TVF result type"); convertToFlattenedTypeVarDefs.Add(m_command.CreateVarDefNode(m_command.CreateNode(m_command.CreateVarRefOp(unnestOpTableColumns[columnIndex])), out var)); } else { NullSentinelPropertyRef nullSentinelPropRef = propRef as NullSentinelPropertyRef; if (nullSentinelPropRef != null) { // Null sentinel does not exist in the TVF output, so build a new null sentinel expression. convertToFlattenedTypeVarDefs.Add(m_command.CreateVarDefNode(CreateNullSentinelConstant(), out var)); } } PlanCompiler.Assert(var != null, "TVFs returning a collection of rows with non-primitive properties are not supported"); convertToFlattenedTypeVars.Add(var); } // Make sure unnestTableColumnVar is mapped to the ProjectOp outputs. newVars = convertToFlattenedTypeVars; // Create Project(Unnest(Func())) return m_command.CreateNode(m_command.CreateProjectOp(m_command.CreateVarVec(convertToFlattenedTypeVars)), unnestNode, m_command.CreateNode(m_command.CreateVarDefListOp(), convertToFlattenedTypeVarDefs)); }
/// <summary> /// Create a typeid-comparison operator - more specifically, create an /// operator that compares a typeid value with the typeid property of an /// input structured type. /// The comparison may be "exact" - in which case we're looking for the exact /// type; otherwise, we're looking for any possible subtypes. /// The "exact" variant is used by the IsOfOp (only); the other variant is /// used by IsOfOp and TreatOp /// </summary> /// <param name="input">The input structured type expression</param> /// <param name="typeInfo">Augmented type information for the type</param> /// <param name="isExact">Exact comparison?</param> /// <returns>New comparison expression</returns> private Node CreateTypeComparisonOp(Node input, TypeInfo typeInfo, bool isExact) { Node typeIdProperty = BuildTypeIdAccessor(input, typeInfo); Node newNode = null; if (isExact) { newNode = CreateTypeEqualsOp(typeInfo, typeIdProperty); } else { if (typeInfo.RootType.DiscriminatorMap != null) { // where there are explicit discriminator values, LIKE '0X%' pattern does not work... newNode = CreateDisjunctiveTypeComparisonOp(typeInfo, typeIdProperty); } else { Node typeIdConstantNode = CreateTypeIdConstantForPrefixMatch(typeInfo); LikeOp likeOp = m_command.CreateLikeOp(); newNode = m_command.CreateNode(likeOp, typeIdProperty, typeIdConstantNode, CreateNullConstantNode(DefaultTypeIdType)); } } return newNode; }
/// <summary> /// Create a column map for an entitytype column. /// Currently, the key columns are not duplicated (ie) they point into the /// same locations as in the properties list. /// Note: we also don't handle keys that are properties of nested fields /// </summary> /// <param name="typeInfo">Type information for the type</param> /// <param name="name">column name</param> /// <param name="superTypeColumnMap">supertype information if any</param> /// <param name="discriminatorMap">Dictionary of typeid->column map information</param> /// <param name="allMaps">List of all column maps (including those without typeid)</param> /// <param name="handleRelProperties">should we handle rel-properties?</param> /// <returns></returns> private EntityColumnMap CreateEntityColumnMap(TypeInfo typeInfo, string name, EntityColumnMap superTypeColumnMap, Dictionary<object, TypedColumnMap> discriminatorMap, List<TypedColumnMap> allMaps, bool handleRelProperties) { EntityColumnMap columnMap = null; List<ColumnMap> propertyColumnMapList = new List<ColumnMap>(); // Copy over information from my supertype if it already exists if (superTypeColumnMap != null) { // get supertype properties foreach (ColumnMap c in superTypeColumnMap.Properties) { propertyColumnMapList.Add(c); } // Now add on all of my "specific" properties foreach (md.EdmMember property in TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type)) { ColumnMap propertyColumnMap = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); propertyColumnMapList.Add(propertyColumnMap); } // create the entity column map w/ information from my supertype columnMap = new EntityColumnMap(typeInfo.Type, name, propertyColumnMapList.ToArray(), superTypeColumnMap.EntityIdentity); } else { SimpleColumnMap entitySetIdColumnMap = null; if (typeInfo.HasEntitySetIdProperty) { entitySetIdColumnMap = CreateEntitySetIdColumnMap(typeInfo.EntitySetIdProperty); } // build up a list of key columns List<SimpleColumnMap> keyColumnMapList = new List<SimpleColumnMap>(); // Create a dictionary to look up the key properties Dictionary<md.EdmProperty, ColumnMap> keyPropertyMap = new Dictionary<md.EdmProperty, ColumnMap>(); foreach (md.EdmMember property in TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type)) { ColumnMap propertyColumnMap = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); propertyColumnMapList.Add(propertyColumnMap); // add property to keymap, if this property is part of the key if (md.TypeSemantics.IsPartOfKey(property)) { md.EdmProperty edmProperty = property as md.EdmProperty; PlanCompiler.Assert(edmProperty != null, "EntityType key member is not property?"); keyPropertyMap[edmProperty] = propertyColumnMap; } } // Build up the key list if required foreach (md.EdmMember keyProperty in TypeHelpers.GetEdmType<md.EntityType>(typeInfo.Type).KeyMembers) { md.EdmProperty edmKeyProperty = keyProperty as md.EdmProperty; PlanCompiler.Assert(edmKeyProperty != null, "EntityType key member is not property?"); SimpleColumnMap keyColumnMap = keyPropertyMap[edmKeyProperty] as SimpleColumnMap; PlanCompiler.Assert(keyColumnMap != null, "keyColumnMap is null"); keyColumnMapList.Add(keyColumnMap); } // // Create the entity identity. // EntityIdentity identity = CreateEntityIdentity((md.EntityType)typeInfo.Type.EdmType, entitySetIdColumnMap, keyColumnMapList.ToArray()); // finally create the entity column map columnMap = new EntityColumnMap(typeInfo.Type, name, propertyColumnMapList.ToArray(), identity); } // if a dictionary is supplied, add myself to the dictionary (abstract types need not be added) if (discriminatorMap != null) { // where DiscriminatedNewInstanceOp is used, there will not be an explicit type id for an abstract type // or types that do not appear in the QueryView // (the mapping will not include such information) if (null != typeInfo.TypeId) { discriminatorMap[typeInfo.TypeId] = columnMap; } } if (allMaps != null) { allMaps.Add(columnMap); } // Finally walk through my subtypes foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { CreateEntityColumnMap(subTypeInfo, name, columnMap, discriminatorMap, allMaps, false); } // // Build up the list of rel property column maps // if (handleRelProperties) { BuildRelPropertyColumnMaps(typeInfo, true); } return columnMap; }
/// <summary> /// Create a filter matching all types in the given hierarchy (typeIdProperty IN typeInfo.Hierarchy) e.g.: /// /// typeIdProperty = 'Base' OR typeIdProperty = 'Derived1' ... /// /// This is called only for types using DiscriminatorMap (explicit discriminator values) /// </summary> /// <param name="typeInfo"></param> /// <param name="typeIdProperty"></param> /// <returns>type hierarchy check</returns> private Node CreateDisjunctiveTypeComparisonOp(TypeInfo typeInfo, Node typeIdProperty) { PlanCompiler.Assert(typeInfo.RootType.DiscriminatorMap != null, "should be used only for DiscriminatorMap type checks"); // collect all non-abstract types in the given hierarchy IEnumerable<TypeInfo> types = typeInfo.GetTypeHierarchy().Where(t => !t.Type.EdmType.Abstract); // generate a disjunction Node current = null; foreach (TypeInfo type in types) { Node typeComparisonNode = CreateTypeEqualsOp(type, typeIdProperty); if (null == current) { current = typeComparisonNode; } else { current = m_command.CreateNode(m_command.CreateConditionalOp(OpType.Or), current, typeComparisonNode); } } if (null == current) { // only abstract types in this hierarchy... no values possible current = m_command.CreateNode(m_command.CreateFalseOp()); } return current; }
/// <summary> /// Creates a column map for a polymorphic type. This method first /// creates column maps for each type that is a subtype of the input type, /// and then creates a dictionary of typeid value -> column /// Finally, a PolymorphicColumnMap is created with these pieces of information /// </summary> /// <param name="typeInfo">Info about the type</param> /// <param name="name">column name</param> /// <returns></returns> private SimplePolymorphicColumnMap CreatePolymorphicColumnMap(TypeInfo typeInfo, string name) { // if the typeInfo has a DiscriminatorMap, use TrailingSpaceComparer to ensure that lookups // against discriminator values that SQL Server has right-padded (e.g. nchar and char) are properly // interpreted Dictionary<object, TypedColumnMap> discriminatorMap = new Dictionary<object, TypedColumnMap>( typeInfo.RootType.DiscriminatorMap == null ? null : TrailingSpaceComparer.Instance); // abstract types may not have discriminator values, but may nonetheless be interesting List<TypedColumnMap> allMaps = new List<TypedColumnMap>(); // SQLBUDT #433011 -- Polymorphic types must construct column maps // that map to the entire type hierarchy, so we // need to use the RootType, not the current type. TypeInfo rootTypeInfo = typeInfo.RootType; // Get the type discriminant column first SimpleColumnMap typeIdColumnMap = CreateTypeIdColumnMap(rootTypeInfo.TypeIdProperty); // Prepare a place for the constructors to put the columns on the base // type, as they identify them. TypedColumnMap rootTypeColumnMap = null; // process complex/entity types appropriately // use the same name for the column if (md.TypeSemantics.IsComplexType(typeInfo.Type)) { rootTypeColumnMap = CreateComplexTypeColumnMap(rootTypeInfo, name, null, discriminatorMap, allMaps); } else { rootTypeColumnMap = CreateEntityColumnMap(rootTypeInfo, name, null, discriminatorMap, allMaps, true); } // Naturally, nothing is simple; we need to walk the rootTypeColumnMap hierarchy // and find the column map for the type that we are supposed to have as the base // type of this hierarchy. TypedColumnMap baseTypeColumnMap = null; foreach (TypedColumnMap value in allMaps) { if (md.TypeSemantics.IsStructurallyEqual(value.Type, typeInfo.Type)) { baseTypeColumnMap = value; break; } } PlanCompiler.Assert(null != baseTypeColumnMap, "Didn't find requested type in polymorphic type hierarchy?"); // Create a polymorphic column map SimplePolymorphicColumnMap result = new SimplePolymorphicColumnMap(typeInfo.Type, name, baseTypeColumnMap.Properties, typeIdColumnMap, discriminatorMap); return result; }
/// <summary> /// Generates a node of the form typeIdProperty = typeInfo.TypeId /// </summary> /// <param name="typeInfo"></param> /// <param name="typeIdProperty"></param> /// <returns>type equality check</returns> private Node CreateTypeEqualsOp(TypeInfo typeInfo, Node typeIdProperty) { Node typeIdConstantNode = CreateTypeIdConstant(typeInfo); ComparisonOp eqCompOp = m_command.CreateComparisonOp(OpType.EQ); Node result = m_command.CreateNode(eqCompOp, typeIdProperty, typeIdConstantNode); return result; }
/// <summary> /// Create a column map for a ref type /// </summary> /// <param name="typeInfo">Type information for the ref type</param> /// <param name="name">Name of the column</param> /// <returns>Column map for the ref type</returns> private RefColumnMap CreateRefColumnMap(TypeInfo typeInfo, string name) { SimpleColumnMap entitySetIdColumnMap = null; if (typeInfo.HasEntitySetIdProperty) { entitySetIdColumnMap = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(typeInfo.EntitySetIdProperty), c_EntitySetIdColumnName); } // get the target entity type, md.EntityType entityType = (md.EntityType)(TypeHelpers.GetEdmType<md.RefType>(typeInfo.Type).ElementType); // Iterate through the list of "key" properties SimpleColumnMap[] keyColList = new SimpleColumnMap[entityType.KeyMembers.Count]; for (int i = 0; i < keyColList.Length; ++i) { md.EdmMember property = entityType.KeyMembers[i]; keyColList[i] = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); } // Create the entity identity EntityIdentity identity = CreateEntityIdentity(entityType, entitySetIdColumnMap, keyColList); RefColumnMap result = new RefColumnMap(typeInfo.Type, name, identity); return result; }
/// <summary> /// Assign a typeid to a root type /// </summary> /// <param name="typeInfo"></param> /// <param name="typeId"></param> private void AssignRootTypeId(TypeInfo typeInfo, string typeId) { typeInfo.TypeId = typeId; AssignTypeIdsToSubTypes(typeInfo); }