private void Map(VarList varList) { var newList = Command.CreateVarList(MapVars(varList)); varList.Clear(); varList.AddRange(newList); }
private void Map(VarList varList) { VarList varList1 = Command.CreateVarList(this.MapVars((IEnumerable <Var>)varList)); varList.Clear(); varList.AddRange((IEnumerable <Var>)varList1); }
// <summary> // convert MultiStreamNestOp to SingleStreamNestOp // </summary> // <remarks> // A MultiStreamNestOp is typically of the form M(D, N1, N2, ..., Nk) // where D is the driver stream, and N1, N2 etc. represent the collections. // In general, this can be converted into a SingleStreamNestOp over: // (D+ outerApply N1) AugmentedUnionAll (D+ outerApply N2) ... // Where: // D+ is D with an extra discriminator column that helps to identify // the specific collection. // AugmentedUnionAll is simply a unionAll where each branch of the // unionAll is augmented with nulls for the corresponding columns // of other tables in the branch // The simple case where there is only a single nested collection is easier // to address, and can be represented by: // MultiStreamNest(D, N1) => SingleStreamNest(OuterApply(D, N1)) // The more complex case, where there is more than one nested column, requires // quite a bit more work: // MultiStreamNest(D, X, Y,...) => SingleStreamNest(UnionAll(Project{"1", D1...Dn, X1...Xn, nY1...nYn}(OuterApply(D, X)), Project{"2", D1...Dn, nX1...nXn, Y1...Yn}(OuterApply(D, Y)), ...)) // Where: // D is the driving collection // D1...Dn are the columns from the driving collection // X is the first nested collection // X1...Xn are the columns from the first nested collection // nX1...nXn are null values for all columns from the first nested collection // Y is the second nested collection // Y1...Yn are the columns from the second nested collection // nY1...nYn are null values for all columns from the second nested collection // </remarks> private Node ConvertToSingleStreamNest( Node nestNode, Dictionary<Var, ColumnMap> varRefReplacementMap, VarList flattenedOutputVarList, out SimpleColumnMap[] parentKeyColumnMaps) { #if DEBUG var input = Dump.ToXml(nestNode); #endif //DEBUG var nestOp = (MultiStreamNestOp)nestNode.Op; // We can't convert this node to a SingleStreamNest until all it's MultiStreamNest // inputs are converted, so do that first. for (var i = 1; i < nestNode.Children.Count; i++) { var chi = nestNode.Children[i]; if (chi.Op.OpType == OpType.MultiStreamNest) { var chiCi = nestOp.CollectionInfo[i - 1]; var childFlattenedOutputVars = Command.CreateVarList(); SimpleColumnMap[] childKeyColumnMaps; nestNode.Children[i] = ConvertToSingleStreamNest( chi, varRefReplacementMap, childFlattenedOutputVars, out childKeyColumnMaps); // Now this may seem odd here, and it may look like we should have done this // inside the recursive ConvertToSingleStreamNest call above, but that call // doesn't have access to the CollectionInfo for it's parent, which is what // we need to manipulate before we enter the loop below where we try and fold // THIS nestOp nodes into a singleStreamNestOp. var childColumnMap = ColumnMapTranslator.Translate(chiCi.ColumnMap, varRefReplacementMap); var childKeys = Command.CreateVarVec(((SingleStreamNestOp)nestNode.Children[i].Op).Keys); nestOp.CollectionInfo[i - 1] = Command.CreateCollectionInfo( chiCi.CollectionVar, childColumnMap, childFlattenedOutputVars, childKeys, chiCi.SortKeys, null /*discriminatorValue*/ ); } } // Make sure that the driving node has keys defined. Otherwise we're in // trouble; we must be able to infer keys from the driving node. var drivingNode = nestNode.Child0; var drivingNodeKeys = Command.PullupKeys(drivingNode); if (drivingNodeKeys.NoKeys) { // ALMINEEV: In this case we used to wrap drivingNode into a projection that would also project Edm.NewGuid() thus giving us a synthetic key. // This solution did not work however due to a bug in SQL Server that allowed pulling non-deterministic functions above joins and applies, thus // producing incorrect results. SQL Server bug was filed in "sqlbuvsts01\Sql Server" database as #725272. // The only known path how we can get a keyless drivingNode is if // - drivingNode is over a TVF call // - TVF is declared as Collection(Row) is SSDL (the only form of TVF definitions at the moment) // - TVF is not mapped to entities // Note that if TVF is mapped to entities via function import mapping, and the user query is actually the call of the // function import, we infer keys for the TVF from the c-space entity keys and their mappings. throw new NotSupportedException(Strings.ADP_KeysRequiredForNesting); } // Get a deterministic ordering of Vars from this node. // NOTE: we're using the drivingNode's definitions, which is a VarVec so it // won't match the order of the input's columns, but the key thing is // that we use the same order for all nested children, so it's OK. var drivingNodeInfo = Command.GetExtendedNodeInfo(drivingNode); var drivingNodeVarVec = drivingNodeInfo.Definitions; var drivingNodeVars = Command.CreateVarList(drivingNodeVarVec); // Normalize all collection inputs to the nestOp. Specifically, remove any // SortOps (adding the sort keys to the postfix sortkey list). Additionally, // add a discriminatorVar to each collection child VarList discriminatorVarList; List<List<SortKey>> postfixSortKeyList; NormalizeNestOpInputs(nestOp, nestNode, out discriminatorVarList, out postfixSortKeyList); // Now build up the union-all subquery List<Dictionary<Var, Var>> varMapList; Var outputDiscriminatorVar; var unionAllNode = BuildUnionAllSubqueryForNestOp( nestOp, nestNode, drivingNodeVars, discriminatorVarList, out outputDiscriminatorVar, out varMapList); var drivingNodeVarMap = varMapList[0]; // OK. We've finally created the UnionAll over each of the project/outerApply // combinations. We know that the output columns will be: // // Discriminator, DrivingColumns, Collection1Columns, Collection2Columns, ... // // Now, rebuild the columnMaps, since all of the columns in the original column // maps are now referencing newer variables. To do that, we'll walk the list of // outputs from the unionAll, and construct new VarRefColumnMaps for each one, // and adding it to a ColumnMapPatcher, which we'll use to actually fix everything // up. // // While we're at it, we'll build a new list of top-level output columns, which // should include only the Discriminator, the columns from the driving collection, // and and one column for each of the nested collections. // Start building the flattenedOutputVarList that the top level PhysicalProjectOp // is to output. flattenedOutputVarList.AddRange(RemapVars(drivingNodeVars, drivingNodeVarMap)); var flattenedOutputVarVec = Command.CreateVarVec(flattenedOutputVarList); var nestOpOutputs = Command.CreateVarVec(flattenedOutputVarVec); // Add any adjustments to the driving nodes vars to the column map patcher foreach (var kv in drivingNodeVarMap) { if (kv.Key != kv.Value) { varRefReplacementMap[kv.Key] = new VarRefColumnMap(kv.Value); } } RemapSortKeys(nestOp.PrefixSortKeys, drivingNodeVarMap); var newPostfixSortKeys = new List<SortKey>(); var newCollectionInfoList = new List<CollectionInfo>(); // Build the discriminator column map, and ensure it's in the outputs var discriminatorColumnMap = new VarRefColumnMap(outputDiscriminatorVar); nestOpOutputs.Set(outputDiscriminatorVar); if (!flattenedOutputVarVec.IsSet(outputDiscriminatorVar)) { flattenedOutputVarList.Add(outputDiscriminatorVar); flattenedOutputVarVec.Set(outputDiscriminatorVar); } // Build the key column maps, and ensure they're in the outputs as well. var parentKeys = RemapVarVec(drivingNodeKeys.KeyVars, drivingNodeVarMap); parentKeyColumnMaps = new SimpleColumnMap[parentKeys.Count]; var index = 0; foreach (var keyVar in parentKeys) { parentKeyColumnMaps[index] = new VarRefColumnMap(keyVar); index++; if (!flattenedOutputVarVec.IsSet(keyVar)) { flattenedOutputVarList.Add(keyVar); flattenedOutputVarVec.Set(keyVar); } } // Now that we've handled the driving node, deal with each of the // nested inputs, in sequence. for (var i = 1; i < nestNode.Children.Count; i++) { var ci = nestOp.CollectionInfo[i - 1]; var postfixSortKeys = postfixSortKeyList[i]; RemapSortKeys(postfixSortKeys, varMapList[i]); newPostfixSortKeys.AddRange(postfixSortKeys); var newColumnMap = ColumnMapTranslator.Translate(ci.ColumnMap, varMapList[i]); var newFlattenedElementVars = RemapVarList(ci.FlattenedElementVars, varMapList[i]); var newCollectionKeys = RemapVarVec(ci.Keys, varMapList[i]); RemapSortKeys(ci.SortKeys, varMapList[i]); var newCollectionInfo = Command.CreateCollectionInfo( ci.CollectionVar, newColumnMap, newFlattenedElementVars, newCollectionKeys, ci.SortKeys, i); newCollectionInfoList.Add(newCollectionInfo); // For a collection Var, we add the flattened elementVars for the // collection in place of the collection Var itself, and we create // a new column map to represent all the stuff we've done. foreach (var v in newFlattenedElementVars) { if (!flattenedOutputVarVec.IsSet(v)) { flattenedOutputVarList.Add(v); flattenedOutputVarVec.Set(v); } } nestOpOutputs.Set(ci.CollectionVar); var keyColumnMapIndex = 0; var keyColumnMaps = new SimpleColumnMap[newCollectionInfo.Keys.Count]; foreach (var keyVar in newCollectionInfo.Keys) { keyColumnMaps[keyColumnMapIndex] = new VarRefColumnMap(keyVar); keyColumnMapIndex++; } var collectionColumnMap = new DiscriminatedCollectionColumnMap( TypeUtils.CreateCollectionType(newCollectionInfo.ColumnMap.Type), newCollectionInfo.ColumnMap.Name, newCollectionInfo.ColumnMap, keyColumnMaps, parentKeyColumnMaps, discriminatorColumnMap, newCollectionInfo.DiscriminatorValue ); varRefReplacementMap[ci.CollectionVar] = collectionColumnMap; } // Finally, build up the SingleStreamNest Node var newSsnOp = Command.CreateSingleStreamNestOp( parentKeys, nestOp.PrefixSortKeys, newPostfixSortKeys, nestOpOutputs, newCollectionInfoList, outputDiscriminatorVar); var newNestNode = Command.CreateNode(newSsnOp, unionAllNode); #if DEBUG var size = input.Length; // GC.KeepAlive makes FxCop Grumpy. var output = Dump.ToXml(newNestNode); #endif //DEBUG return newNestNode; }
/// <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 }
private void Map(VarList varList) { VarList newList = Command.CreateVarList(MapVars(varList)); varList.Clear(); varList.AddRange(newList); }
public override void Visit(UnionAllOp op, System.Data.Entity.Core.Query.InternalTrees.Node n) { this.VisitChildren(n); Var var1 = (Var)this.m_command.CreateSetOpVar(this.m_command.IntegerType); VarList varList1 = Command.CreateVarList(); VarVec[] varVecArray = new VarVec[n.Children.Count]; for (int index = 0; index < n.Children.Count; ++index) { System.Data.Entity.Core.Query.InternalTrees.Node child = n.Children[index]; VarVec v = this.m_command.GetExtendedNodeInfo(child).Keys.KeyVars.Remap((Dictionary <Var, Var>)op.VarMap[index]); varVecArray[index] = this.m_command.CreateVarVec(v); varVecArray[index].Minus(op.Outputs); if (OpType.UnionAll == child.Op.OpType) { UnionAllOp op1 = (UnionAllOp)child.Op; varVecArray[index].Clear(op1.BranchDiscriminator); } varList1.AddRange((IEnumerable <Var>)varVecArray[index]); } VarList varList2 = Command.CreateVarList(); foreach (Var var2 in (List <Var>)varList1) { Var setOpVar = (Var)this.m_command.CreateSetOpVar(var2.Type); varList2.Add(setOpVar); } for (int index1 = 0; index1 < n.Children.Count; ++index1) { System.Data.Entity.Core.Query.InternalTrees.Node child = n.Children[index1]; ExtendedNodeInfo extendedNodeInfo1 = this.m_command.GetExtendedNodeInfo(child); VarVec varVec = this.m_command.CreateVarVec(); List <System.Data.Entity.Core.Query.InternalTrees.Node> args = new List <System.Data.Entity.Core.Query.InternalTrees.Node>(); Var computedVar1; if (OpType.UnionAll == child.Op.OpType && ((UnionAllOp)child.Op).BranchDiscriminator != null) { computedVar1 = ((UnionAllOp)child.Op).BranchDiscriminator; if (!op.VarMap[index1].ContainsValue(computedVar1)) { op.VarMap[index1].Add(var1, computedVar1); } else { System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Assert(0 == index1, "right branch has a discriminator var that the left branch doesn't have?"); var1 = op.VarMap[index1].GetReverseMap()[computedVar1]; } } else { args.Add(this.m_command.CreateVarDefNode(this.m_command.CreateNode((Op)this.m_command.CreateConstantOp(this.m_command.IntegerType, (object)this.m_command.NextBranchDiscriminatorValue)), out computedVar1)); varVec.Set(computedVar1); op.VarMap[index1].Add(var1, computedVar1); } for (int index2 = 0; index2 < varList1.Count; ++index2) { Var computedVar2 = varList1[index2]; if (!varVecArray[index1].IsSet(computedVar2)) { args.Add(this.m_command.CreateVarDefNode(this.m_command.CreateNode((Op)this.m_command.CreateNullOp(computedVar2.Type)), out computedVar2)); varVec.Set(computedVar2); } op.VarMap[index1].Add(varList2[index2], computedVar2); } if (varVec.IsEmpty) { extendedNodeInfo1.Keys.KeyVars.Set(computedVar1); } else { System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Assert(args.Count != 0, "no new nodes?"); foreach (Var v in op.VarMap[index1].Values) { varVec.Set(v); } n.Children[index1] = this.m_command.CreateNode((Op)this.m_command.CreateProjectOp(varVec), child, this.m_command.CreateNode((Op)this.m_command.CreateVarDefListOp(), args)); this.m_command.RecomputeNodeInfo(n.Children[index1]); ExtendedNodeInfo extendedNodeInfo2 = this.m_command.GetExtendedNodeInfo(n.Children[index1]); extendedNodeInfo2.Keys.KeyVars.InitFrom(extendedNodeInfo1.Keys.KeyVars); extendedNodeInfo2.Keys.KeyVars.Set(computedVar1); } } n.Op = (Op)this.m_command.CreateUnionAllOp(op.VarMap[0], op.VarMap[1], var1); this.m_command.RecomputeNodeInfo(n); }