/// <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); } } }
/// <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> /// 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); }