private Node VisitNestOp(Node n) { NestBaseOp op = n.Op as NestBaseOp; SingleStreamNestOp ssnOp = op as SingleStreamNestOp; Debug.Assert(op != null); // Visit the Node's children and map their Vars List <Node> newChildren = ProcessChildren(n); Var newDiscriminator = null; if (ssnOp != null) { newDiscriminator = GetMappedVar(ssnOp.Discriminator); } List <CollectionInfo> newCollectionInfoList = new List <CollectionInfo>(); foreach (CollectionInfo ci in op.CollectionInfo) { ColumnMap newColumnMap = Copy(ci.ColumnMap); Var newCollectionVar = m_destCmd.CreateComputedVar(ci.CollectionVar.Type); SetMappedVar(ci.CollectionVar, newCollectionVar); VarList newFlattendElementVars = Copy(ci.FlattenedElementVars); VarVec newKeys = Copy(ci.Keys); List <SortKey> newSortKeys = Copy(ci.SortKeys); CollectionInfo newCollectionInfo = Command.CreateCollectionInfo(newCollectionVar, newColumnMap, newFlattendElementVars, newKeys, newSortKeys, ci.DiscriminatorValue); newCollectionInfoList.Add(newCollectionInfo); } VarVec newOutputs = Copy(op.Outputs); NestBaseOp newOp = null; List <SortKey> newPrefixSortKeys = Copy(op.PrefixSortKeys); if (ssnOp != null) { VarVec newKeys = Copy(ssnOp.Keys); // Copy the SortOp's SortKeys List <SortKey> newPostfixSortKeys = Copy(ssnOp.PostfixSortKeys); newOp = m_destCmd.CreateSingleStreamNestOp(newKeys, newPrefixSortKeys, newPostfixSortKeys, newOutputs, newCollectionInfoList, newDiscriminator); } else { newOp = m_destCmd.CreateMultiStreamNestOp(newPrefixSortKeys, newOutputs, newCollectionInfoList); } return(m_destCmd.CreateNode(newOp, newChildren)); }
/// <summary> /// Convert a SingleStreamNestOp into a massive UnionAllOp /// </summary> /// <param name="nestOp"></param> /// <param name="nestNode"></param> /// <param name="drivingNodeVars"></param> /// <param name="discriminatorVarList"></param> /// <param name="discriminatorVar"></param> /// <param name="varMapList"></param> /// <returns></returns> 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; }
/// <summary> /// "Normalize" each input to the NestOp. /// We're now in the context of a MultiStreamNestOp, and we're trying to convert this /// into a SingleStreamNestOp. /// /// Normalization specifically refers to /// - augmenting each input with a discriminator value (that describes the collection) /// - removing the sort node at the root (and capturing this information as part of the sortkeys) /// </summary> /// <param name="nestOp">the nestOp</param> /// <param name="nestNode">the nestOp subtree</param> /// <param name="discriminatorVarList">Discriminator Vars for each Collection input</param> /// <param name="sortKeys">SortKeys (postfix) for each Collection input</param> /// /// private void NormalizeNestOpInputs( NestBaseOp nestOp, Node nestNode, out VarList discriminatorVarList, out List<List<SortKey>> sortKeys) { discriminatorVarList = Command.CreateVarList(); // We insert a dummy var and value at poistion 0 for the deriving node, which // we should never reference; discriminatorVarList.Add(null); sortKeys = new List<List<SortKey>>(); sortKeys.Add(nestOp.PrefixSortKeys); for (var i = 1; i < nestNode.Children.Count; i++) { var inputNode = nestNode.Children[i]; // Since we're called from ConvertToSingleStreamNest, it is possible that we have a // SingleStreamNest here, because the input to the MultiStreamNest we're converting // may have been a MultiStreamNest that was converted to a SingleStreamNest. var ssnOp = inputNode.Op as SingleStreamNestOp; // If this collection is a SingleStreamNest, we pull up the key information // in it, and pullup the input; if (null != ssnOp) { // Note that the sortKeys argument is 1:1 with the nestOp inputs, that is // each input may have exactly one entry in the list, so we have to combine // all of the sort key components (Prefix+Keys+Discriminator+PostFix) into // one list. var mySortKeys = BuildSortKeyList(ssnOp); sortKeys.Add(mySortKeys); inputNode = inputNode.Child0; } else { // If the current collection has a SortNode specified, then pull that // out, and add the information to the list of postfix SortColumns var sortOp = inputNode.Op as SortOp; if (null != sortOp) { inputNode = inputNode.Child0; // bypass the sort node // Add the sort keys to the list of postfix sort keys sortKeys.Add(sortOp.Keys); } else { // No postfix sort keys for this case sortKeys.Add(new List<SortKey>()); } } // #447304: Ensure that any SortKey Vars will be projected from the input in addition to showing up in the postfix sort keys // by adding them to the FlattenedElementVars for this NestOp input's CollectionInfo. var flattenedElementVars = nestOp.CollectionInfo[i - 1].FlattenedElementVars; foreach (var sortKey in sortKeys[i]) { if (!flattenedElementVars.Contains(sortKey.Var)) { flattenedElementVars.Add(sortKey.Var); } } // Add a discriminator column to the collection-side - this must // happen before the outer-apply is added on; we need to use the value of // the discriminator to distinguish between null and empty collections Var discriminatorVar; var augmentedInput = AugmentNodeWithInternalIntegerConstant(inputNode, i, out discriminatorVar); nestNode.Children[i] = augmentedInput; discriminatorVarList.Add(discriminatorVar); } }
/// <summary> /// MultiStreamNestOp/SingleStreamNestOp common processing. /// /// Pretty much just verifies that we didn't leave a NestOp behind. /// </summary> /// <param name="op"></param> /// <param name="n"></param> /// <returns></returns> /// protected override Node VisitNestOp(NestBaseOp op, Node n) { // First, visit my children VisitChildren(n); // If any of the children are a nestOp, then we have a // problem; it shouldn't have happened. foreach (var chi in n.Children) { if (IsNestOpNode(chi)) { throw new InvalidOperationException(Strings.ADP_InternalProviderError((int)EntityUtil.InternalErrorCode.NestOverNest)); } } return n; }
private NestBaseOp GetNestOpWithConsolidatedSortKeys(NestBaseOp inputNestOp, List<SortKey> sortKeys) { NestBaseOp result; // Include the sort keys as the prefix sort keys; // Note that we can't actually have a SSNest at this point in // the tree; they're only introduced once we've processed the // entire tree. if (inputNestOp.PrefixSortKeys.Count == 0) { foreach (var sk in sortKeys) { //SQLBUDT #507170 - We can't just add the sort keys, we need to copy them, // to avoid changes to one to affect the other inputNestOp.PrefixSortKeys.Add(Command.CreateSortKey(sk.Var, sk.AscendingSort, sk.Collation)); } result = inputNestOp; } else { var sortVars = Command.CreateVarVec(); // First add the sort keys from the SortBaseOp, then the NestOp keys var sortKeyList = ConsolidateSortKeys(sortKeys, inputNestOp.PrefixSortKeys); PlanCompiler.Assert(inputNestOp is MultiStreamNestOp, "Unexpected SingleStreamNestOp?"); // Finally, build a new NestOp with the keys... result = Command.CreateMultiStreamNestOp(sortKeyList, inputNestOp.Outputs, inputNestOp.CollectionInfo); } return result; }
protected override void VisitNestOp(NestBaseOp op, Node n) { Dictionary <string, object> attrs = new Dictionary <string, object>(); SingleStreamNestOp ssnOp = op as SingleStreamNestOp; if (null != ssnOp) { attrs.Add("Discriminator", (ssnOp.Discriminator == null) ? "<null>" : ssnOp.Discriminator.ToString()); } StringBuilder sb = new StringBuilder(); string separator; if (null != ssnOp) { sb.Length = 0; separator = string.Empty; foreach (Var v in ssnOp.Keys) { sb.Append(separator); sb.Append(v.Id); separator = ","; } if (0 != sb.Length) { attrs.Add("Keys", sb.ToString()); } } using (new AutoXml(this, op, attrs)) { using (new AutoXml(this, "outputs")) { foreach (Var v in op.Outputs) { DumpVar(v); } } foreach (CollectionInfo ci in op.CollectionInfo) { Dictionary <string, object> attrs2 = new Dictionary <string, object>(); attrs2.Add("CollectionVar", ci.CollectionVar); if (null != ci.DiscriminatorValue) { attrs2.Add("DiscriminatorValue", ci.DiscriminatorValue); } if (0 != ci.FlattenedElementVars.Count) { attrs2.Add("FlattenedElementVars", FormatVarList(sb, ci.FlattenedElementVars)); } if (0 != ci.Keys.Count) { attrs2.Add("Keys", ci.Keys); } if (0 != ci.SortKeys.Count) { attrs2.Add("SortKeys", FormatVarList(sb, ci.SortKeys)); } using (new AutoXml(this, "collection", attrs2)) { ci.ColumnMap.Accept(ColumnMapDumper.Instance, this); } } VisitChildren(n); } }
protected override void VisitNestOp(NestBaseOp op, Node n) { throw EntityUtil.NotSupported(); }
protected override void VisitNestOp(NestBaseOp op, Node n) { throw new NotSupportedException(); }
/// <summary> /// MultiStreamNestOp/SingleStreamNestOp common processing. /// /// Pretty much just verifies that we didn't leave a NestOp behind. /// </summary> /// <param name="op"></param> /// <param name="n"></param> /// <returns></returns> /// protected override Node VisitNestOp(NestBaseOp op, Node n) { // First, visit my children VisitChildren(n); // If any of the children are a nestOp, then we have a // problem; it shouldn't have happened. foreach (Node chi in n.Children) { if (IsNestOpNode(chi)) { throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.NestOverNest); } } return n; }
/// <summary> /// Common handling for all nestOps /// </summary> /// <param name="op">nest op</param> /// <param name="n"></param> protected virtual void VisitNestOp(NestBaseOp op, Node n) { VisitPhysicalOpDefault(op, n); }