/// <summary> /// Gets the list of key properties for an entity /// </summary> /// <param name="entityType"></param> /// <returns></returns> private static PropertyRefList GetKeyProperties(md.EntityType entityType) { PropertyRefList desiredProperties = new PropertyRefList(); foreach (md.EdmMember p in entityType.KeyMembers) { md.EdmProperty edmP = p as md.EdmProperty; PlanCompiler.Assert(edmP != null, "EntityType had non-EdmProperty key member?"); SimplePropertyRef pRef = new SimplePropertyRef(edmP); desiredProperties.Add(pRef); } return(desiredProperties); }
/// <summary> /// Creates a var info for var variables of primitive or enum type. /// </summary> /// <param name="v">Current variable of primitive or enum type.</param> /// <param name="newVar">The new variable replacing <paramref name="v"/>.</param> /// <returns><see cref="PrimitiveTypeVarInfo"/> for <paramref name="v"/>.</returns> internal VarInfo CreatePrimitiveTypeVarInfo(Var v, Var newVar) { System.Diagnostics.Debug.Assert(v != null, "v != null"); System.Diagnostics.Debug.Assert(newVar != null, "newVar != null"); PlanCompiler.Assert(md.TypeSemantics.IsScalarType(v.Type), "The current variable should be of primitive or enum type."); PlanCompiler.Assert(md.TypeSemantics.IsScalarType(newVar.Type), "The new variable should be of primitive or enum type."); VarInfo varInfo = new PrimitiveTypeVarInfo(newVar); m_map.Add(v, varInfo); return(varInfo); }
/// <summary> /// ScanViewOp /// /// ask for all properties from the view definition /// that have currently been requested from the view itself /// </summary> /// <param name="op">current ScanViewOp</param> /// <param name="n">current node</param> public override void Visit(ScanViewOp op, Node n) { PlanCompiler.Assert(op.Table.Columns.Count == 1, "ScanViewOp with multiple columns?"); Var columnVar = op.Table.Columns[0]; PropertyRefList columnProps = GetPropertyRefList(columnVar); Var inputVar = NominalTypeEliminator.GetSingletonVar(n.Child0); PlanCompiler.Assert(inputVar != null, "cannot determine single Var from ScanViewOp's input"); AddPropertyRefs(inputVar, columnProps.Clone()); VisitChildren(n); }
/// <summary> /// Find the TypeInfo entry for a type. For non-structured types, we always /// return null. For structured types, we return the entry in the typeInfoMap. /// If we don't find one, and the typeInfoMap has already been populated, then we /// assert /// </summary> /// <param name="type">the type to look up</param> /// <returns>the typeinfo for the type (null if we couldn't find one)</returns> internal TypeInfo GetTypeInfo(md.TypeUsage type) { if (!TypeUtils.IsStructuredType(type)) { return(null); } TypeInfo typeInfo = null; if (!m_typeInfoMap.TryGetValue(type, out typeInfo)) { PlanCompiler.Assert(!TypeUtils.IsStructuredType(type) || !m_typeInfoMapPopulated, "cannot find typeInfo for type " + type); } return(typeInfo); }
/// <summary> /// GetEntityRefOp handling /// /// Ask for the "identity" properties from the input entity, and push that /// down to my child /// </summary> /// <param name="op"></param> /// <param name="n"></param> public override void Visit(GetEntityRefOp op, Node n) { ScalarOp childOp = n.Child0.Op as ScalarOp; PlanCompiler.Assert(childOp != null, "input to GetEntityRefOp is not a ScalarOp?"); // md.EntityType entityType = TypeHelpers.GetEdmType <md.EntityType>(childOp.Type); PropertyRefList desiredProperties = GetIdentityProperties(entityType); AddPropertyRefs(n.Child0, desiredProperties); VisitNode(n.Child0); }
/// <summary> /// Determines the offset for structured types in Flattened type. For instance, if the original type is of the form: /// /// { int X, ComplexType Y } /// /// and the flattened type is of the form: /// /// { int X, Y_ComplexType_Prop1, Y_ComplexType_Prop2 } /// /// GetNestedStructureOffset(Y) returns 1 /// </summary> /// <param name="property">Complex property.</param> /// <returns>Offset.</returns> internal int GetNestedStructureOffset(PropertyRef property) { // m_propertyRefList contains every element of the flattened type for (int i = 0; i < m_propertyRefList.Count; i++) { NestedPropertyRef nestedPropertyRef = m_propertyRefList[i] as NestedPropertyRef; // match offset of the first element of the complex type property if (null != nestedPropertyRef && nestedPropertyRef.InnerProperty.Equals(property)) { return(i); } } PlanCompiler.Assert(false, "no complex structure " + property + " found in TypeInfo"); // return something so that the compiler doesn't complain return(default(int)); }
/// <summary> /// Fills the StructuredTypeInfo instance from the itree provided. /// </summary> /// <param name="itree"></param> /// <param name="referencedTypes">referenced structured types</param> /// <param name="referencedEntitySets">referenced entitysets</param> /// <param name="freeFloatingEntityConstructorTypes">free-floating entityConstructor types</param> /// <param name="discriminatorMaps">discriminator information for entity sets mapped using TPH pattern</param> /// <param name="relPropertyHelper">helper for rel properties</param> private void Process(Command itree, HashSet <md.TypeUsage> referencedTypes, HashSet <md.EntitySet> referencedEntitySets, HashSet <md.EntityType> freeFloatingEntityConstructorTypes, Dictionary <md.EntitySetBase, DiscriminatorMapInfo> discriminatorMaps, RelPropertyHelper relPropertyHelper) { PlanCompiler.Assert(null != itree, "null itree?"); m_stringType = itree.StringType; m_intType = itree.IntegerType; m_relPropertyHelper = relPropertyHelper; ProcessEntitySets(referencedEntitySets, freeFloatingEntityConstructorTypes); ProcessDiscriminatorMaps(discriminatorMaps); ProcessTypes(referencedTypes); }
/// <summary> /// Build out an EntityIdentity structure - for use by EntityColumnMap and RefColumnMap /// </summary> /// <param name="entityType">the entity type in question</param> /// <param name="entitySetIdColumnMap">column map for the entitysetid column</param> /// <param name="keyColumnMaps">column maps for the keys</param> /// <returns></returns> private EntityIdentity CreateEntityIdentity(md.EntityType entityType, SimpleColumnMap entitySetIdColumnMap, SimpleColumnMap[] keyColumnMaps) { // // If we have an entitysetid (and therefore, a column map for the entitysetid), // then use a discriminated entity identity; otherwise, we use a simpleentityidentity // instead // if (entitySetIdColumnMap != null) { return(new DiscriminatedEntityIdentity(entitySetIdColumnMap, m_typeInfo.EntitySetIdToEntitySetMap, keyColumnMaps)); } else { md.EntitySet entitySet = m_typeInfo.GetEntitySet(entityType); PlanCompiler.Assert(entitySet != null, "Expected non-null entityset when no entitysetid is required. Entity type = " + entityType); return(new SimpleEntityIdentity(entitySet, keyColumnMaps)); } }
/// <summary> /// If the op is a collection aggregate function it checks whether its arguement can be translated over /// a single group aggregate var. If so, it is tracked as a candidate to be pushed into that /// group by into node. /// </summary> /// <param name="op"></param> /// <param name="n"></param> public override void Visit(FunctionOp op, Node n) { VisitDefault(n); if (!PlanCompilerUtil.IsCollectionAggregateFunction(op, n)) { return; } PlanCompiler.Assert(n.Children.Count == 1, "Aggregate Function must have one argument"); Node argumentNode = n.Child0; GroupAggregateVarInfo referencedGroupAggregateVarInfo; Node templateNode; bool isUnnested; if (GroupAggregateVarComputationTranslator.TryTranslateOverGroupAggregateVar(n.Child0, false, _command, _groupAggregateVarInfoManager, out referencedGroupAggregateVarInfo, out templateNode, out isUnnested) && (isUnnested || AggregatePushdownUtil.IsVarRefOverGivenVar(templateNode, referencedGroupAggregateVarInfo.GroupAggregateVar))) { referencedGroupAggregateVarInfo.CandidateAggregateNodes.Add(new KeyValuePair <Node, Node>(n, templateNode)); } }
/// <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> /// Default processing for RelOps. /// - First, we mark the current node as its own ancestor (so that any /// subqueries that we detect internally will be added to this node's list) /// - then, visit each child /// - finally, accumulate all nested subqueries. /// - if the current RelOp has only one input, then add the nested subqueries via /// Outer apply nodes to this input. /// /// The interesting RelOps are /// Project, Filter, GroupBy, Sort, /// Should we break this out into separate functions instead? /// </summary> /// <param name="op">Current RelOp</param> /// <param name="n">Node to process</param> /// <returns>Current subtree</returns> protected override Node VisitRelOpDefault(RelOp op, Node n) { VisitChildren(n); // visit all my children first // Then identify all the subqueries that have shown up as part of my node // Create Apply Nodes for each of these. List <Node> nestedSubqueries; if (m_nodeSubqueries.TryGetValue(n, out nestedSubqueries) && nestedSubqueries.Count > 0) { // Validate - this must only apply to the following nodes PlanCompiler.Assert( n.Op.OpType == OpType.Project || n.Op.OpType == OpType.Filter || n.Op.OpType == OpType.GroupBy || n.Op.OpType == OpType.GroupByInto, "VisitRelOpDefault: Unexpected op?" + n.Op.OpType); Node newInputNode = AugmentWithSubqueries(n.Child0, nestedSubqueries, true); // Now make this the new input child n.Child0 = newInputNode; } return(n); }
/// <summary> /// Build up an equivalence map of primary keys and foreign keys (ie) for each /// foreign key column, identify the corresponding primary key property /// </summary> private void BuildKeyMap() { if (m_keyMap != null) { return; } m_keyMap = new Dictionary <string, string>(); IEnumerator <md.EdmProperty> parentProps = m_constraint.FromProperties.GetEnumerator(); IEnumerator <md.EdmProperty> childProps = m_constraint.ToProperties.GetEnumerator(); while (true) { bool parentOver = !parentProps.MoveNext(); bool childOver = !childProps.MoveNext(); PlanCompiler.Assert(parentOver == childOver, "key count mismatch"); if (parentOver) { break; } m_keyMap[childProps.Current.Name] = parentProps.Current.Name; } }
/// <summary> /// Get the list of "key" properties (in the flattened type) /// </summary> /// <returns>the key property equivalents in the flattened type</returns> internal IEnumerable <PropertyRef> GetKeyPropertyRefs() { md.EntityTypeBase entityType = null; md.RefType refType = null; if (TypeHelpers.TryGetEdmType <md.RefType>(m_type, out refType)) { entityType = refType.ElementType; } else { entityType = TypeHelpers.GetEdmType <md.EntityTypeBase>(m_type); } // Walk through the list of keys of the entity type, and find their analogs in the // "flattened" type foreach (md.EdmMember p in entityType.KeyMembers) { // Eventually this could be RelationshipEndMember, but currently only properties are suppported as key members PlanCompiler.Assert(p is md.EdmProperty, "Non-EdmProperty key members are not supported"); SimplePropertyRef spr = new SimplePropertyRef(p); yield return(spr); } }
/// <summary> /// Pre-processing for a function. Does the default scalar op processing. /// If the function returns a collection (TVF), the method converts this expression into /// Collect(PhysicalProject(Unnest(Func))). /// If the function is a collection aggregate, converts it into the corresponding group aggregate. /// </summary> /// <param name="op"></param> /// <param name="n"></param> /// <returns></returns> public override Node Visit(FunctionOp op, Node n) { VisitScalarOpDefault(op, n); Node newNode = null; // Is this a TVF? if (TypeSemantics.IsCollectionType(op.Type)) { newNode = VisitCollectionFunction(op, n); } // Is this a collection-aggregate function? else if (PlanCompilerUtil.IsCollectionAggregateFunction(op, n)) { newNode = VisitCollectionAggregateFunction(op, n); } else { newNode = n; } PlanCompiler.Assert(newNode != null, "failure to construct a functionOp?"); return(newNode); }
/// <summary> /// Creates a ProviderCommandInfo for the given node. /// This method should be called when the keys, foreign keys and sort keys are known ahead of time. /// Typically it is used when the original command is factored into multiple commands. /// </summary> /// <param name="command">The owning command, used for creating VarVecs, etc</param> /// <param name="node">The root of the sub-command for which a ProviderCommandInfo should be generated</param> /// <param name="children">A list of ProviderCommandInfos that were created for the child sub-commands.</param> /// <returns>The resulting ProviderCommandInfo</returns> internal static ProviderCommandInfo Create( Command command, Node node, List <ProviderCommandInfo> children) { PhysicalProjectOp projectOp = node.Op as PhysicalProjectOp; PlanCompiler.Assert(projectOp != null, "Expected root Op to be a physical Project"); // build up the CQT DbCommandTree ctree = CTreeGenerator.Generate(command, node); DbQueryCommandTree cqtree = ctree as DbQueryCommandTree; PlanCompiler.Assert(cqtree != null, "null query command tree"); // Get the rowtype for the result cqt md.CollectionType collType = TypeHelpers.GetEdmType <md.CollectionType>(cqtree.Query.ResultType); PlanCompiler.Assert(md.TypeSemantics.IsRowType(collType.TypeUsage), "command rowtype is not a record"); // Build up a mapping from Vars to the corresponding output property/column Dictionary <Var, md.EdmProperty> outputVarMap = BuildOutputVarMap(projectOp, collType.TypeUsage); return(new ProviderCommandInfo(ctree, children)); }
/// <summary> /// ScanTableOp handler /// </summary> /// <param name="op"></param> /// <param name="n"></param> public override void Visit(ScanTableOp op, Node n) { PlanCompiler.Assert(!n.HasChild0, "scanTableOp with an input?"); }
/// <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 predicate from a node tree /// </summary> /// <param name="command">current iqt command</param> /// <param name="andTree">the node tree</param> internal Predicate(Command command, Node andTree) : this(command) { PlanCompiler.Assert(andTree != null, "null node passed to Predicate() constructor"); InitFromAndTree(andTree); }
/// <summary> /// Comments from Murali: /// /// There are several cases to consider here. /// /// Case 0: /// Let’s assume that K1 is the set of keys ({k1, k2, ..., kn}) for the /// first input, and K2 ({l1, l2, …}) is the set of keys for the second /// input. /// /// The best case is when both K1 and K2 have the same cardinality (hopefully /// greater than 0), and the keys are in the same locations (ie) the corresponding /// positions in the select-list. Even in this case, its not enough to take /// the keys, and treat them as the keys of the union-all. What we’ll need to /// do is to add a “branch” discriminator constant for each branch of the /// union-all, and use this as the prefix for the keys. /// /// For example, if I had: /// /// Select c1, c2, c3... from ... /// Union all /// Select d1, d2, d3... from ... /// /// And for the sake of argument, lets say that {c2} and {d2} are the keys of /// each of the branches. What you’ll need to do is to translate this into /// /// Select 0 as bd, c1, c2, c3... from ... /// Union all /// Select 1 as bd, d1, d2, d3... from ... /// /// And then treat {bd, c2/d2} as the key of the union-all /// /// Case 1: (actually, a subcase of Case 0): /// Now, if the keys don’t align, then we can simply take the union of the /// corresponding positions, and make them all the keys (we would still need /// the branch discriminator) /// /// Case 2: /// Finally, if you need to “pull” up keys from either of the branches, it is /// possible that the branches get out of whack. We will then need to push up /// the keys (with nulls if the other branch doesn’t have the corresponding key) /// into the union-all. (We still need the branch discriminator). /// /// Now, unfortunately, whenever we've got polymorphic entity types, we'll end up /// in case 2 way more often than we really want to, because when we're pulling up /// keys, we don't want to reason about a caseop (which is how polymorphic types /// wrap their key value). /// /// To simplify all of this, we: /// /// (1) Pulling up the keys for both branches of the UnionAll, and computing which /// keys are in the outputs and which are missing from the outputs. /// /// (2) Accumulate all the missing keys. /// /// (3) Slap a projectOp around each branch, adding a branch discriminator /// var and all the missing keys. When keys are missing from a different /// branch, we'll construct null ops for them on the other branches. If /// a branch already has a branch descriminator, we'll re-use it instead /// of constructing a new one. (Of course, if there aren't any keys to /// add and it's already including the branch discriminator we won't /// need the projectOp) /// /// </summary> /// <param name="op">the UnionAllOp</param> /// <param name="n">current subtree</param> public override void Visit(UnionAllOp op, Node n) { #if DEBUG string input = Dump.ToXml(m_command, n); #endif //DEBUG // Ensure we have keys pulled up on each branch of the union all. VisitChildren(n); // Create the setOp var we'll use to output the branch discriminator value; if // any of the branches are already surfacing a branchDiscriminator var to the // output of this operation then we won't need to use this but we construct it // early to simplify logic. Var outputBranchDiscriminatorVar = m_command.CreateSetOpVar(m_command.IntegerType); // Now ensure that we're outputting the key vars from this op as well. VarList allKeyVarsMissingFromOutput = Command.CreateVarList(); VarVec[] keyVarsMissingFromOutput = new VarVec[n.Children.Count]; for (int i = 0; i < n.Children.Count; i++) { Node branchNode = n.Children[i]; ExtendedNodeInfo branchNodeInfo = m_command.GetExtendedNodeInfo(branchNode); // Identify keys that aren't in the output list of this operation. We // determine these by remapping the keys that are found through the node's // VarMap, which gives us the keys in the same "varspace" as the outputs // of the UnionAll, then we subtract out the outputs of this UnionAll op, // leaving things that are not in the output vars. Of course, if they're // not in the output vars, then we didn't really remap. VarVec existingKeyVars = branchNodeInfo.Keys.KeyVars.Remap(op.VarMap[i]); keyVarsMissingFromOutput[i] = m_command.CreateVarVec(existingKeyVars); keyVarsMissingFromOutput[i].Minus(op.Outputs); // Special Case: if the branch is a UnionAll, it will already have it's // branch discriminator var added in the keys; we don't want to add that // a second time... if (OpType.UnionAll == branchNode.Op.OpType) { UnionAllOp branchUnionAllOp = (UnionAllOp)branchNode.Op; keyVarsMissingFromOutput[i].Clear(branchUnionAllOp.BranchDiscriminator); } allKeyVarsMissingFromOutput.AddRange(keyVarsMissingFromOutput[i]); } // Construct the setOp vars we're going to map to output. VarList allKeyVarsToAddToOutput = Command.CreateVarList(); foreach (Var v in allKeyVarsMissingFromOutput) { Var newKeyVar = m_command.CreateSetOpVar(v.Type); allKeyVarsToAddToOutput.Add(newKeyVar); } // Now that we've identified all the keys we need to add, ensure that each branch // has both the branch discrimination var and the all the keys in them, even when // the keys are just going to null (which we construct, as needed) for (int i = 0; i < n.Children.Count; i++) { Node branchNode = n.Children[i]; ExtendedNodeInfo branchNodeInfo = m_command.GetExtendedNodeInfo(branchNode); VarVec branchOutputVars = m_command.CreateVarVec(); List <Node> varDefNodes = new List <Node>(); // If the branch is a UnionAllOp that has a branch discriminator var then we can // use it, otherwise we'll construct a new integer constant with the next value // of the branch discriminator value from the command object. Var branchDiscriminatorVar; if (OpType.UnionAll == branchNode.Op.OpType && null != ((UnionAllOp)branchNode.Op).BranchDiscriminator) { branchDiscriminatorVar = ((UnionAllOp)branchNode.Op).BranchDiscriminator; // If the branch has a discriminator var, but we haven't added it to the // varmap yet, then we do so now. if (!op.VarMap[i].ContainsValue(branchDiscriminatorVar)) { op.VarMap[i].Add(outputBranchDiscriminatorVar, branchDiscriminatorVar); // We don't need to add this to the branch outputs, because it's already there, // otherwise we wouln't have gotten here, yes? } else { // In this case, we're already outputting the branch discriminator var -- we'll // just use it for both sides. We should never have a case where only one of the // two branches are outputting the branch discriminator var, because it can only // be constructed in this method, and we wouldn't need it for any other purpose. PlanCompiler.Assert(0 == i, "right branch has a discriminator var that the left branch doesn't have?"); VarMap reverseVarMap = op.VarMap[i].GetReverseMap(); outputBranchDiscriminatorVar = reverseVarMap[branchDiscriminatorVar]; } } else { // Not a unionAll -- we have to add a BranchDiscriminator var. varDefNodes.Add( m_command.CreateVarDefNode( m_command.CreateNode( m_command.CreateConstantOp(m_command.IntegerType, m_command.NextBranchDiscriminatorValue)), out branchDiscriminatorVar)); branchOutputVars.Set(branchDiscriminatorVar); op.VarMap[i].Add(outputBranchDiscriminatorVar, branchDiscriminatorVar); } // Append all the missing keys to the branch outputs. If the missing key // is not from this branch then create a null. for (int j = 0; j < allKeyVarsMissingFromOutput.Count; j++) { Var keyVar = allKeyVarsMissingFromOutput[j]; if (!keyVarsMissingFromOutput[i].IsSet(keyVar)) { varDefNodes.Add( m_command.CreateVarDefNode( m_command.CreateNode( m_command.CreateNullOp(keyVar.Type)), out keyVar)); branchOutputVars.Set(keyVar); } // In all cases, we're adding a key to the output so we need to update the // varmap. op.VarMap[i].Add(allKeyVarsToAddToOutput[j], keyVar); } // If we got this far and didn't add anything to the branch, then we're done. // Otherwise we'll have to construct the new projectOp around the input branch // to add the stuff we've added. if (branchOutputVars.IsEmpty) { // Actually, we're not quite done -- we need to update the key vars for the // branch to include the branch discriminator var we branchNodeInfo.Keys.KeyVars.Set(branchDiscriminatorVar); } else { PlanCompiler.Assert(varDefNodes.Count != 0, "no new nodes?"); // Start by ensuring all the existing outputs from the branch are in the list. foreach (Var v in op.VarMap[i].Values) { branchOutputVars.Set(v); } // Now construct a project op to project out everything we've added, and // replace the branchNode with it in the flattened ladder. n.Children[i] = m_command.CreateNode(m_command.CreateProjectOp(branchOutputVars), branchNode, m_command.CreateNode(m_command.CreateVarDefListOp(), varDefNodes)); // Finally, ensure that we update the Key info for the projectOp to include // the original branch's keys, along with the branch discriminator var. m_command.RecomputeNodeInfo(n.Children[i]); ExtendedNodeInfo projectNodeInfo = m_command.GetExtendedNodeInfo(n.Children[i]); projectNodeInfo.Keys.KeyVars.InitFrom(branchNodeInfo.Keys.KeyVars); projectNodeInfo.Keys.KeyVars.Set(branchDiscriminatorVar); } } // All done with the branches, now it's time to update the UnionAll op to indicate // that we've got a branch discriminator var. n.Op = m_command.CreateUnionAllOp(op.VarMap[0], op.VarMap[1], outputBranchDiscriminatorVar); // Finally, the thing we've all been waiting for -- computing the keys. We cheat here and let // nodeInfo do it so we don't have to duplicate the logic... m_command.RecomputeNodeInfo(n); #if DEBUG input = input.Trim(); string output = Dump.ToXml(m_command, n); #endif //DEBUG }
/// <summary> /// Create the flattened record type for the type. /// Walk through the list of property refs, and creates a new field /// (which we name as "F1", "F2" etc.) with the required property type. /// /// We then produce a mapping from the original property (propertyRef really) /// to the new property for use in later modules. /// /// Finally, we identify the TypeId and EntitySetId property if they exist /// </summary> /// <param name="type"></param> private void CreateFlattenedRecordType(RootTypeInfo type) { // // If this type corresponds to an entity type, and that entity type // has no subtypes, and that that entity type has no complex properties // then simply use the name from that property // bool usePropertyNamesFromUnderlyingType; if (md.TypeSemantics.IsEntityType(type.Type) && type.ImmediateSubTypes.Count == 0) { usePropertyNamesFromUnderlyingType = true; } else { usePropertyNamesFromUnderlyingType = false; } // Build the record type List <KeyValuePair <string, md.TypeUsage> > fieldList = new List <KeyValuePair <string, md.TypeUsage> >(); HashSet <string> fieldNames = new HashSet <string>(); int nextFieldId = 0; foreach (PropertyRef p in type.PropertyRefList) { string fieldName = null; if (usePropertyNamesFromUnderlyingType) { SimplePropertyRef simpleP = p as SimplePropertyRef; if (simpleP != null) { fieldName = simpleP.Property.Name; } } if (fieldName == null) { fieldName = "F" + nextFieldId.ToString(CultureInfo.InvariantCulture); nextFieldId++; } // Deal with collisions while (fieldNames.Contains(fieldName)) { fieldName = "F" + nextFieldId.ToString(CultureInfo.InvariantCulture); nextFieldId++; } md.TypeUsage propertyType = GetPropertyType(type, p); fieldList.Add(new KeyValuePair <string, md.TypeUsage>(fieldName, propertyType)); fieldNames.Add(fieldName); } type.FlattenedType = TypeHelpers.CreateRowType(fieldList); // Now build up the property map IEnumerator <PropertyRef> origProps = type.PropertyRefList.GetEnumerator(); foreach (md.EdmProperty p in type.FlattenedType.Properties) { if (!origProps.MoveNext()) { PlanCompiler.Assert(false, "property refs count and flattened type member count mismatch?"); } type.AddPropertyMapping(origProps.Current, p); } }
/// <summary> /// Add a new entry to the map. If an entry already exists, then this function /// simply returns the existing entry. Otherwise a new entry is created. If /// the type has a supertype, then we ensure that the supertype also exists in /// the map, and we add our info to the supertype's list of subtypes /// </summary> /// <param name="type">New type to add</param> /// <param name="discriminatorMap">type discriminator map</param> /// <returns>The TypeInfo for this type</returns> private TypeInfo CreateTypeInfoForStructuredType(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap) { TypeInfo typeInfo; PlanCompiler.Assert(TypeUtils.IsStructuredType(type), "expected structured type. Found " + type); // Return existing entry, if one is available typeInfo = GetTypeInfo(type); if (typeInfo != null) { return(typeInfo); } // Ensure that my supertype has been added to the map. TypeInfo superTypeInfo = null; md.RefType refType; if (type.EdmType.BaseType != null) { superTypeInfo = CreateTypeInfoForStructuredType(md.TypeUsage.Create(type.EdmType.BaseType), discriminatorMap); } // // Handle Ref types also in a similar fashion // else if (TypeHelpers.TryGetEdmType <md.RefType>(type, out refType)) { md.EntityType entityType = refType.ElementType as md.EntityType; if (entityType != null && entityType.BaseType != null) { md.TypeUsage baseRefType = TypeHelpers.CreateReferenceTypeUsage(entityType.BaseType as md.EntityType); superTypeInfo = CreateTypeInfoForStructuredType(baseRefType, discriminatorMap); } } // // Add the types of my properties to the TypeInfo map // foreach (md.EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(type)) { CreateTypeInfoForType(m.TypeUsage); } // // Get the types of the rel properties also // { md.EntityTypeBase entityType; if (TypeHelpers.TryGetEdmType <md.EntityTypeBase>(type, out entityType)) { foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) { CreateTypeInfoForType(p.ToEnd.TypeUsage); } } } // Now add myself to the map typeInfo = TypeInfo.Create(type, superTypeInfo, discriminatorMap); m_typeInfoMap.Add(type, typeInfo); return(typeInfo); }
/// <summary> /// If the Subtree rooted at the collect is of the following structure: /// /// PhysicalProject(outputVar) /// | /// Project(s) /// | /// Unnest /// /// where the unnest is over the group aggregate var and the output var /// is either a reference to the group aggregate var or to a constant, it returns the /// translation of the ouput var. /// </summary> /// <param name="n"></param> /// <returns></returns> private Node VisitCollect(Node n) { //Make sure the only children are projects over unnest Node currentNode = n.Child0; Dictionary <Var, Node> constantDefinitions = new Dictionary <Var, Node>(); while (currentNode.Child0.Op.OpType == OpType.Project) { currentNode = currentNode.Child0; //Visit the VarDefListOp child if (VisitDefault(currentNode.Child1) == null) { return(null); } foreach (Node definitionNode in currentNode.Child1.Children) { if (IsConstant(definitionNode.Child0)) { constantDefinitions.Add(((VarDefOp)definitionNode.Op).Var, definitionNode.Child0); } } } if (currentNode.Child0.Op.OpType != OpType.Unnest) { return(null); } // Handle the unnest UnnestOp unnestOp = (UnnestOp)currentNode.Child0.Op; GroupAggregateVarRefInfo groupAggregateVarRefInfo; if (_groupAggregateVarInfoManager.TryGetReferencedGroupAggregateVarInfo(unnestOp.Var, out groupAggregateVarRefInfo)) { if (_targetGroupAggregateVarInfo == null) { _targetGroupAggregateVarInfo = groupAggregateVarRefInfo.GroupAggregateVarInfo; } else if (_targetGroupAggregateVarInfo != groupAggregateVarRefInfo.GroupAggregateVarInfo) { return(null); } if (!_isUnnested) { return(null); } } else { return(null); } PhysicalProjectOp physicalProjectOp = (PhysicalProjectOp)n.Child0.Op; PlanCompiler.Assert(physicalProjectOp.Outputs.Count == 1, "PhysicalProject should only have one output at this stage"); Var outputVar = physicalProjectOp.Outputs[0]; Node computationTemplate = TranslateOverGroupAggregateVar(outputVar, null); if (computationTemplate != null) { _isUnnested = true; return(computationTemplate); } Node constantDefinitionNode; if (constantDefinitions.TryGetValue(outputVar, out constantDefinitionNode)) { _isUnnested = true; return(constantDefinitionNode); } return(null); }
/// <summary> /// Try to push the given function aggregate candidate to the corresponding group into node. /// The candidate can be pushed if all ancestors of the group into node up to the least common /// ancestor between the group into node and the function aggregate have one of the following node op types: /// Project /// Filter /// ConstraintSortOp /// </summary> /// <param name="command"></param> /// <param name="candidate"></param> /// <param name="groupAggregateVarInfo"></param> /// <param name="m_childToParent"></param> private void TryProcessCandidate( KeyValuePair <Node, Node> candidate, GroupAggregateVarInfo groupAggregateVarInfo) { IList <Node> functionAncestors; IList <Node> groupByAncestors; Node definingGroupNode = groupAggregateVarInfo.DefiningGroupNode; FindPathsToLeastCommonAncestor(candidate.Key, definingGroupNode, out functionAncestors, out groupByAncestors); //Check whether all ancestors of the GroupByInto node are of type that we support propagating through if (!AreAllNodesSupportedForPropagation(groupByAncestors)) { return; } //Add the function to the group by node GroupByIntoOp definingGroupOp = (GroupByIntoOp)definingGroupNode.Op; PlanCompiler.Assert(definingGroupOp.Inputs.Count == 1, "There should be one input var to GroupByInto at this stage"); Var inputVar = definingGroupOp.Inputs.First; FunctionOp functionOp = (FunctionOp)candidate.Key.Op; // // Remap the template from referencing the groupAggregate var to reference the input to // the group by into // Node argumentNode = OpCopier.Copy(m_command, candidate.Value); Dictionary <Var, Var> dictionary = new Dictionary <Var, Var>(1); dictionary.Add(groupAggregateVarInfo.GroupAggregateVar, inputVar); VarRemapper remapper = new VarRemapper(m_command, dictionary); remapper.RemapSubtree(argumentNode); Node newFunctionDefiningNode = m_command.CreateNode( m_command.CreateAggregateOp(functionOp.Function, false), argumentNode); Var newFunctionVar; Node varDefNode = m_command.CreateVarDefNode(newFunctionDefiningNode, out newFunctionVar); // Add the new aggregate to the list of aggregates definingGroupNode.Child2.Children.Add(varDefNode); GroupByIntoOp groupByOp = (GroupByIntoOp)definingGroupNode.Op; groupByOp.Outputs.Set(newFunctionVar); //Propagate the new var throught the ancestors of the GroupByInto for (int i = 0; i < groupByAncestors.Count; i++) { Node groupByAncestor = groupByAncestors[i]; if (groupByAncestor.Op.OpType == OpType.Project) { ProjectOp ancestorProjectOp = (ProjectOp)groupByAncestor.Op; ancestorProjectOp.Outputs.Set(newFunctionVar); } } //Update the functionNode candidate.Key.Op = m_command.CreateVarRefOp(newFunctionVar); candidate.Key.Children.Clear(); }
/// <summary> /// Basic constructor. /// Represents the access of property "propertyRef" within property "property" /// </summary> /// <param name="innerProperty">the inner property</param> /// <param name="outerProperty">the outer property</param> internal NestedPropertyRef(PropertyRef innerProperty, PropertyRef outerProperty) { PlanCompiler.Assert(!(innerProperty is NestedPropertyRef), "innerProperty cannot be a NestedPropertyRef"); m_inner = innerProperty; m_outer = outerProperty; }