/// <summary> /// Convert a /// SingleRowOp(X) => X /// if X produces at most one row /// </summary> /// <param name="context">Rule Processing context</param> /// <param name="singleRowNode">Current subtree</param> /// <param name="newNode">transformed subtree</param> /// <returns>Transformation status</returns> private static bool ProcessSingleRowOpOverAnything(RuleProcessingContext context, Node singleRowNode, out Node newNode) { newNode = singleRowNode; var trc = (TransformationRulesContext)context; var childNodeInfo = context.Command.GetExtendedNodeInfo(singleRowNode.Child0); // If the input to this Op can produce at most one row, then we don't need the // singleRowOp - simply return the input if (childNodeInfo.MaxRows <= RowCount.One) { newNode = singleRowNode.Child0; return true; } // // if the current node is a FilterOp, then try and determine if the FilterOp // produces one row at most // if (singleRowNode.Child0.Op.OpType == OpType.Filter) { var predicate = new Predicate(context.Command, singleRowNode.Child0.Child1); if (predicate.SatisfiesKey(childNodeInfo.Keys.KeyVars, childNodeInfo.Definitions)) { childNodeInfo.MaxRows = RowCount.One; newNode = singleRowNode.Child0; return true; } } // we couldn't do anything return false; }
/// <summary> /// Split up a predicate into 2 parts - the pushdown and the non-pushdown predicate. /// /// If the filter node has no external references *and* the "columns" parameter is null, /// then the entire predicate can be pushed down /// /// We then compute the set of valid column references - if the "columns" parameter /// is non-null, this set is used. Otherwise, we get the definitions of the /// input relop node of the filterOp, and use that. /// /// We use this list of valid column references to identify which parts of the filter /// predicate can be pushed down - only those parts of the predicate that do not /// reference anything beyond these columns are considered for pushdown. The rest are /// stuffed into the nonPushdownPredicate output parameter /// /// </summary> /// <param name="command">Command object</param> /// <param name="filterNode">the FilterOp subtree</param> /// <param name="columns">(Optional) List of columns to consider for "pushdown"</param> /// <param name="nonPushdownPredicateNode">(output) Part of the predicate that cannot be pushed down</param> /// <returns>part of the predicate that can be pushed down</returns> private static Node GetPushdownPredicate(Command command, Node filterNode, VarVec columns, out Node nonPushdownPredicateNode) { var pushdownPredicateNode = filterNode.Child1; nonPushdownPredicateNode = null; var filterNodeInfo = command.GetExtendedNodeInfo(filterNode); if (columns == null && filterNodeInfo.ExternalReferences.IsEmpty) { return pushdownPredicateNode; } if (columns == null) { var inputNodeInfo = command.GetExtendedNodeInfo(filterNode.Child0); columns = inputNodeInfo.Definitions; } var predicate = new Predicate(command, pushdownPredicateNode); Predicate nonPushdownPredicate; predicate = predicate.GetSingleTablePredicates(columns, out nonPushdownPredicate); pushdownPredicateNode = predicate.BuildAndTree(); nonPushdownPredicateNode = nonPushdownPredicate.BuildAndTree(); return pushdownPredicateNode; }
private void GetSingleTablePredicates(List<VarVec> tableDefinitions, out List<Predicate> singleTablePredicates, out Predicate otherPredicates) { singleTablePredicates = new List<Predicate>(); foreach (VarVec vec in tableDefinitions) { singleTablePredicates.Add(new Predicate(m_command)); } otherPredicates = new Predicate(m_command); VarVec externalRefs = m_command.CreateVarVec(); foreach (Node part in m_parts) { NodeInfo nodeInfo = m_command.GetNodeInfo(part); bool singleTablePart = false; for (int i = 0; i < tableDefinitions.Count; i++) { VarVec tableColumns = tableDefinitions[i]; if (tableColumns != null) { externalRefs.InitFrom(nodeInfo.ExternalReferences); externalRefs.Minus(tableColumns); if (externalRefs.IsEmpty) { singleTablePart = true; singleTablePredicates[i].AddPart(part); break; } } } if (!singleTablePart) { otherPredicates.AddPart(part); } } }
internal Predicate GetJoinPredicates(VarVec leftTableDefinitions, VarVec rightTableDefinitions, out Predicate otherPredicates) { Predicate joinPredicate = new Predicate(m_command); otherPredicates = new Predicate(m_command); foreach (Node part in m_parts) { Var leftTableVar; Var rightTableVar; if (Predicate.IsEquiJoinPredicate(part, leftTableDefinitions, rightTableDefinitions, out leftTableVar, out rightTableVar)) { joinPredicate.AddPart(part); } else { otherPredicates.AddPart(part); } } return joinPredicate; }
/// <summary> /// Get the set of equi-join columns from this predicate /// </summary> /// <param name="leftTableDefinitions"></param> /// <param name="rightTableDefinitions"></param> /// <param name="leftTableEquiJoinColumns"></param> /// <param name="rightTableEquiJoinColumns"></param> /// <param name="otherPredicates"></param> internal void GetEquiJoinPredicates(VarVec leftTableDefinitions, VarVec rightTableDefinitions, out List<Var> leftTableEquiJoinColumns, out List<Var> rightTableEquiJoinColumns, out Predicate otherPredicates) { otherPredicates = new Predicate(m_command); leftTableEquiJoinColumns = new List<Var>(); rightTableEquiJoinColumns = new List<Var>(); foreach (Node part in m_parts) { Var leftTableVar; Var rightTableVar; if (IsEquiJoinPredicate(part, leftTableDefinitions, rightTableDefinitions, out leftTableVar, out rightTableVar)) { leftTableEquiJoinColumns.Add(leftTableVar); rightTableEquiJoinColumns.Add(rightTableVar); } else { otherPredicates.AddPart(part); } } }
/// <summary> /// Partition the current predicate into predicates that only apply /// to the specified table (single-table-predicates), and others /// </summary> /// <param name="tableDefinitions">current columns defined by the table</param> /// <param name="otherPredicates">non-single-table predicates</param> /// <returns>single-table-predicates</returns> internal Predicate GetSingleTablePredicates(VarVec tableDefinitions, out Predicate otherPredicates) { List<VarVec> tableDefinitionList = new List<VarVec>(); tableDefinitionList.Add(tableDefinitions); List<Predicate> singleTablePredicateList; GetSingleTablePredicates(tableDefinitionList, out singleTablePredicateList, out otherPredicates); return singleTablePredicateList[0]; }
/// <summary> /// Convert Filter(OuterApply(X,Y), p) into /// Filter(CrossApply(X,Y), p) /// if "p" is not null-preserving for Y (ie) "p" does not preserve null values from Y /// </summary> /// <param name="context">Rule processing context</param> /// <param name="filterNode">Filter node</param> /// <param name="newNode">modified subtree</param> /// <returns>transformation status</returns> private static bool ProcessFilterOverOuterApply(RuleProcessingContext context, Node filterNode, out Node newNode) { newNode = filterNode; var applyNode = filterNode.Child0; var applyOp = applyNode.Op; var applyRightInputNode = applyNode.Child1; var trc = (TransformationRulesContext)context; var command = trc.Command; // // Check to see if the current predicate preserves nulls for the right table. // If it doesn't then we can convert the outer apply into a cross-apply, // var rightTableNodeInfo = command.GetExtendedNodeInfo(applyRightInputNode); var predicate = new Predicate(command, filterNode.Child1); if (!predicate.PreservesNulls(rightTableNodeInfo.Definitions, true)) { var newApplyNode = command.CreateNode(command.CreateCrossApplyOp(), applyNode.Child0, applyRightInputNode); var newFilterNode = command.CreateNode(command.CreateFilterOp(), newApplyNode, filterNode.Child1); newNode = newFilterNode; return true; } return false; }
private static bool ProcessFilterOverJoin(RuleProcessingContext context, Node filterNode, out Node newNode) { newNode = filterNode; var trc = (TransformationRulesContext)context; // // Have we shut off filter pushdown for this node? Return // if (trc.IsFilterPushdownSuppressed(filterNode)) { return false; } var joinNode = filterNode.Child0; var joinOp = joinNode.Op; var leftInputNode = joinNode.Child0; var rightInputNode = joinNode.Child1; var command = trc.Command; var needsTransformation = false; // // If we're dealing with an outer-join, first check to see if the current // predicate preserves nulls for the right table. // If it doesn't then we can convert the outer join into an inner join, // and then continue with the rest of our processing here // var rightTableNodeInfo = command.GetExtendedNodeInfo(rightInputNode); var predicate = new Predicate(command, filterNode.Child1); if (joinOp.OpType == OpType.LeftOuterJoin) { if (!predicate.PreservesNulls(rightTableNodeInfo.Definitions, true)) { joinOp = command.CreateInnerJoinOp(); needsTransformation = true; } } var leftTableInfo = command.GetExtendedNodeInfo(leftInputNode); // // Check to see if the predicate contains any "single-table-filters". In those // cases, we could simply push that filter down to the child. // We can do this for inner joins and cross joins - for both inputs. // For left-outer joins, however, we can only do this for the left-side input // Further note that we only want to do the pushdown if it will help us - if // the join input is a ScanTable (or some other cases), then it doesn't help us. // Node leftSingleTablePredicateNode = null; if (leftInputNode.Op.OpType != OpType.ScanTable) { var leftSingleTablePredicates = predicate.GetSingleTablePredicates(leftTableInfo.Definitions, out predicate); leftSingleTablePredicateNode = leftSingleTablePredicates.BuildAndTree(); } Node rightSingleTablePredicateNode = null; if ((rightInputNode.Op.OpType != OpType.ScanTable) && (joinOp.OpType != OpType.LeftOuterJoin)) { var rightSingleTablePredicates = predicate.GetSingleTablePredicates(rightTableNodeInfo.Definitions, out predicate); rightSingleTablePredicateNode = rightSingleTablePredicates.BuildAndTree(); } // // Now check to see if the predicate contains some "join predicates". We can // add these to the existing join predicate (if any). // We can only do this for inner joins and cross joins - not for LOJs // Node newJoinPredicateNode = null; if (joinOp.OpType == OpType.CrossJoin || joinOp.OpType == OpType.InnerJoin) { var joinPredicate = predicate.GetJoinPredicates(leftTableInfo.Definitions, rightTableNodeInfo.Definitions, out predicate); newJoinPredicateNode = joinPredicate.BuildAndTree(); } // // Now for the dirty work. We've identified some predicates that could be pushed // into the left table, some predicates that could be pushed into the right table // and some that could become join predicates. // if (leftSingleTablePredicateNode != null) { leftInputNode = command.CreateNode(command.CreateFilterOp(), leftInputNode, leftSingleTablePredicateNode); needsTransformation = true; } if (rightSingleTablePredicateNode != null) { rightInputNode = command.CreateNode(command.CreateFilterOp(), rightInputNode, rightSingleTablePredicateNode); needsTransformation = true; } // Identify the new join predicate if (newJoinPredicateNode != null) { needsTransformation = true; if (joinOp.OpType == OpType.CrossJoin) { joinOp = command.CreateInnerJoinOp(); } else { PlanCompiler.Assert(joinOp.OpType == OpType.InnerJoin, "unexpected non-InnerJoin?"); newJoinPredicateNode = PlanCompilerUtil.CombinePredicates(joinNode.Child2, newJoinPredicateNode, command); } } else { newJoinPredicateNode = (joinOp.OpType == OpType.CrossJoin) ? null : joinNode.Child2; } // // If nothing has changed, then just return the current node. Otherwise, // we will loop forever // if (!needsTransformation) { return false; } Node newJoinNode; // // Finally build up a new join node // if (joinOp.OpType == OpType.CrossJoin) { newJoinNode = command.CreateNode(joinOp, leftInputNode, rightInputNode); } else { newJoinNode = command.CreateNode(joinOp, leftInputNode, rightInputNode, newJoinPredicateNode); } // // Build up a new filterNode above this join node. But only if we have a filter left // var newFilterPredicateNode = predicate.BuildAndTree(); if (newFilterPredicateNode == null) { newNode = newJoinNode; } else { newNode = command.CreateNode(command.CreateFilterOp(), newJoinNode, newFilterPredicateNode); } return true; }