// <summary> // Callback function to invoke *before* rules are applied. // Calls the VarRemapper to update any Vars in the entire subtree // If the given node has a RelOp it is pushed on the relOp ancestors stack. // </summary> internal override void PreProcessSubTree(Node subTree) { if (subTree.Op.IsRelOp) { m_relOpAncestors.Push(subTree); } if (m_remappedVars.IsEmpty) { return; } var nodeInfo = Command.GetNodeInfo(subTree); //We need to do remapping only if m_remappedVars overlaps with nodeInfo.ExternalReferences foreach (var v in nodeInfo.ExternalReferences) { if (m_remappedVars.IsSet(v)) { m_remapper.RemapSubtree(subTree); break; } } }
private void TryProcessCandidate( KeyValuePair<Node, Node> candidate, GroupAggregateVarInfo groupAggregateVarInfo) { IList<Node> functionAncestors; IList<Node> groupByAncestors; var 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 var 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; var functionOp = (FunctionOp)candidate.Key.Op; // // Remap the template from referencing the groupAggregate var to reference the input to // the group by into // var argumentNode = OpCopier.Copy(m_command, candidate.Value); var dictionary = new Dictionary<Var, Var>(1); dictionary.Add(groupAggregateVarInfo.GroupAggregateVar, inputVar); var remapper = new VarRemapper(m_command, dictionary); remapper.RemapSubtree(argumentNode); var newFunctionDefiningNode = m_command.CreateNode( m_command.CreateAggregateOp(functionOp.Function, false), argumentNode); Var newFunctionVar; var varDefNode = m_command.CreateVarDefNode(newFunctionDefiningNode, out newFunctionVar); // Add the new aggregate to the list of aggregates definingGroupNode.Child2.Children.Add(varDefNode); var groupByOp = (GroupByIntoOp)definingGroupNode.Op; groupByOp.Outputs.Set(newFunctionVar); //Propagate the new var throught the ancestors of the GroupByInto for (var i = 0; i < groupByAncestors.Count; i++) { var groupByAncestor = groupByAncestors[i]; if (groupByAncestor.Op.OpType == OpType.Project) { var ancestorProjectOp = (ProjectOp)groupByAncestor.Op; ancestorProjectOp.Outputs.Set(newFunctionVar); } } //Update the functionNode candidate.Key.Op = m_command.CreateVarRefOp(newFunctionVar); candidate.Key.Children.Clear(); }
// <summary> // Convert a SingleStreamNestOp into a massive UnionAllOp // </summary> private Node BuildUnionAllSubqueryForNestOp( NestBaseOp nestOp, Node nestNode, VarList drivingNodeVars, VarList discriminatorVarList, out Var discriminatorVar, out List<Dictionary<Var, Var>> varMapList) { var drivingNode = nestNode.Child0; // For each of the NESTED collections... Node unionAllNode = null; VarList unionAllOutputs = null; for (var i = 1; i < nestNode.Children.Count; i++) { // Ensure we only use the driving collection tree once, so other // transformations do not unintentionally change more than one path. // To prevent nodes in the tree from being used in multiple paths, // we copy the driving input on successive nodes. VarList newDrivingNodeVars; Node newDrivingNode; VarList newFlattenedElementVars; Op op; if (i > 1) { newDrivingNode = OpCopier.Copy(Command, drivingNode, drivingNodeVars, out newDrivingNodeVars); // // Bug 450245: If we copied the driver node, then references to driver node vars // from the collection subquery must be patched up // var varRemapper = new VarRemapper(Command); for (var j = 0; j < drivingNodeVars.Count; j++) { varRemapper.AddMapping(drivingNodeVars[j], newDrivingNodeVars[j]); } // Remap all references in the current subquery varRemapper.RemapSubtree(nestNode.Children[i]); // Bug 479183: Remap the flattened element vars newFlattenedElementVars = varRemapper.RemapVarList(nestOp.CollectionInfo[i - 1].FlattenedElementVars); // Create a cross apply for all but the first collection op = Command.CreateCrossApplyOp(); } else { newDrivingNode = drivingNode; newDrivingNodeVars = drivingNodeVars; newFlattenedElementVars = nestOp.CollectionInfo[i - 1].FlattenedElementVars; // Create an outer apply for the first collection, // that way we ensure at least one row for each row in the driver node. op = Command.CreateOuterApplyOp(); } // Create an outer apply with the driver node and the nested collection. var applyNode = Command.CreateNode(op, newDrivingNode, nestNode.Children[i]); // Now create a ProjectOp that augments the output from the OuterApplyOp // with nulls for each column from other collections // Build the VarDefList (the list of vars) for the Project, starting // with the collection discriminator var var varDefListChildren = new List<Node>(); var projectOutputs = Command.CreateVarList(); // Add the collection discriminator var to the output. projectOutputs.Add(discriminatorVarList[i]); // Add all columns from the driving node projectOutputs.AddRange(newDrivingNodeVars); // Add all the vars from all the nested collections; for (var j = 1; j < nestNode.Children.Count; j++) { var otherCollectionInfo = nestOp.CollectionInfo[j - 1]; // For the current nested collection, we just pick the var that's // coming from there and don't need have a new var defined, but for // the rest we construct null values. if (i == j) { projectOutputs.AddRange(newFlattenedElementVars); } else { foreach (var v in otherCollectionInfo.FlattenedElementVars) { var nullOp = Command.CreateNullOp(v.Type); var nullOpNode = Command.CreateNode(nullOp); Var nullOpVar; var nullOpVarDefNode = Command.CreateVarDefNode(nullOpNode, out nullOpVar); varDefListChildren.Add(nullOpVarDefNode); projectOutputs.Add(nullOpVar); } } } var varDefListNode = Command.CreateNode(Command.CreateVarDefListOp(), varDefListChildren); // Now, build up the projectOp var projectOutputsVarSet = Command.CreateVarVec(projectOutputs); var projectOp = Command.CreateProjectOp(projectOutputsVarSet); var projectNode = Command.CreateNode(projectOp, applyNode, varDefListNode); // finally, build the union all if (unionAllNode == null) { unionAllNode = projectNode; unionAllOutputs = projectOutputs; } else { var unionAllMap = new VarMap(); var projectMap = new VarMap(); for (var idx = 0; idx < unionAllOutputs.Count; idx++) { Var outputVar = Command.CreateSetOpVar(unionAllOutputs[idx].Type); unionAllMap.Add(outputVar, unionAllOutputs[idx]); projectMap.Add(outputVar, projectOutputs[idx]); } var unionAllOp = Command.CreateUnionAllOp(unionAllMap, projectMap); unionAllNode = Command.CreateNode(unionAllOp, unionAllNode, projectNode); // Get the output vars from the union-op. This must be in the same order // as the original list of Vars unionAllOutputs = GetUnionOutputs(unionAllOp, unionAllOutputs); } } // We're done building the node, but now we have to build a mapping from // the before-Vars to the after-Vars varMapList = new List<Dictionary<Var, Var>>(); IEnumerator<Var> outputVarsEnumerator = unionAllOutputs.GetEnumerator(); if (!outputVarsEnumerator.MoveNext()) { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.ColumnCountMismatch, 4, null); // more columns from children than are on the unionAll? } // The discriminator var is always first discriminatorVar = outputVarsEnumerator.Current; // Build a map for each input for (var i = 0; i < nestNode.Children.Count; i++) { var varMap = new Dictionary<Var, Var>(); var varList = (i == 0) ? drivingNodeVars : nestOp.CollectionInfo[i - 1].FlattenedElementVars; foreach (var v in varList) { if (!outputVarsEnumerator.MoveNext()) { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.ColumnCountMismatch, 5, null); // more columns from children than are on the unionAll? } varMap[v] = outputVarsEnumerator.Current; } varMapList.Add(varMap); } if (outputVarsEnumerator.MoveNext()) { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.ColumnCountMismatch, 6, null); // at this point, we better be done with both lists... } return unionAllNode; }
private void TryProcessCandidate( KeyValuePair <Node, Node> candidate, GroupAggregateVarInfo groupAggregateVarInfo) { IList <Node> functionAncestors; IList <Node> groupByAncestors; var 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 var 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; var functionOp = (FunctionOp)candidate.Key.Op; // // Remap the template from referencing the groupAggregate var to reference the input to // the group by into // var argumentNode = OpCopier.Copy(m_command, candidate.Value); var dictionary = new Dictionary <Var, Var>(1); dictionary.Add(groupAggregateVarInfo.GroupAggregateVar, inputVar); var remapper = new VarRemapper(m_command, dictionary); remapper.RemapSubtree(argumentNode); var newFunctionDefiningNode = m_command.CreateNode( m_command.CreateAggregateOp(functionOp.Function, false), argumentNode); Var newFunctionVar; var varDefNode = m_command.CreateVarDefNode(newFunctionDefiningNode, out newFunctionVar); // Add the new aggregate to the list of aggregates definingGroupNode.Child2.Children.Add(varDefNode); var groupByOp = (GroupByIntoOp)definingGroupNode.Op; groupByOp.Outputs.Set(newFunctionVar); //Propagate the new var throught the ancestors of the GroupByInto for (var i = 0; i < groupByAncestors.Count; i++) { var groupByAncestor = groupByAncestors[i]; if (groupByAncestor.Op.OpType == OpType.Project) { var ancestorProjectOp = (ProjectOp)groupByAncestor.Op; ancestorProjectOp.Outputs.Set(newFunctionVar); } } //Update the functionNode candidate.Key.Op = m_command.CreateVarRefOp(newFunctionVar); candidate.Key.Children.Clear(); }