1 //--------------------------------------------------------------------- 2 // <copyright file="PlanCompilerUtil.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System; 11 using System.Collections.Generic; 12 using System.Data.Common.Utils; 13 using System.Data.Metadata.Edm; 14 using System.Data.Query.InternalTrees; 15 16 namespace System.Data.Query.PlanCompiler 17 { 18 /// <summary> 19 /// Utility class for the methods shared among the classes comprising the plan compiler 20 /// </summary> 21 internal static class PlanCompilerUtil 22 { 23 /// <summary> 24 /// Utility method that determines whether a given CaseOp subtree can be optimized. 25 /// Called by both PreProcessor and NominalTypeEliminator. 26 /// 27 /// If the case statement is of the shape: 28 /// case when X then NULL else Y, or 29 /// case when X then Y else NULL, 30 /// where Y is of row type, and the types of the input CaseOp, the NULL and Y are the same, 31 /// return true 32 /// </summary> 33 /// <param name="op"></param> 34 /// <param name="n"></param> 35 /// <returns></returns> IsRowTypeCaseOpWithNullability(CaseOp op, Node n, out bool thenClauseIsNull)36 internal static bool IsRowTypeCaseOpWithNullability(CaseOp op, Node n, out bool thenClauseIsNull) 37 { 38 thenClauseIsNull = false; //any default value will do 39 40 if (!TypeSemantics.IsRowType(op.Type)) 41 { 42 return false; 43 } 44 if (n.Children.Count != 3) 45 { 46 return false; 47 } 48 49 //All three types must be equal 50 if (!n.Child1.Op.Type.EdmEquals(op.Type) || !n.Child2.Op.Type.EdmEquals(op.Type)) 51 { 52 return false; 53 } 54 55 //At least one of Child1 and Child2 needs to be a null 56 if (n.Child1.Op.OpType == OpType.Null) 57 { 58 thenClauseIsNull = true; 59 return true; 60 } 61 if (n.Child2.Op.OpType == OpType.Null) 62 { 63 // thenClauseIsNull stays false 64 return true; 65 } 66 67 return false; 68 } 69 70 /// <summary> 71 /// Is this function a collection aggregate function. It is, if 72 /// - it has exactly one child 73 /// - that child is a collection type 74 /// - and the function has been marked with the aggregate attribute 75 /// </summary> 76 /// <param name="op">the function op</param> 77 /// <param name="n">the current subtree</param> 78 /// <returns>true, if this was a collection aggregate function</returns> IsCollectionAggregateFunction(FunctionOp op, Node n)79 internal static bool IsCollectionAggregateFunction(FunctionOp op, Node n) 80 { 81 return ((n.Children.Count == 1) && 82 TypeSemantics.IsCollectionType(n.Child0.Op.Type) && 83 TypeSemantics.IsAggregateFunction(op.Function)); 84 } 85 86 /// <summary> 87 /// Is the given op one of the ConstantBaseOp-s 88 /// </summary> 89 /// <param name="opType"></param> 90 /// <returns></returns> IsConstantBaseOp(OpType opType)91 internal static bool IsConstantBaseOp(OpType opType) 92 { 93 return opType == OpType.Constant || 94 opType == OpType.InternalConstant || 95 opType == OpType.Null || 96 opType == OpType.NullSentinel; 97 } 98 99 /// <summary> 100 /// Combine two predicates by trying to avoid the predicate parts of the 101 /// second one that are already present in the first one. 102 /// 103 /// In particular, given two nodes, predicate1 and predicate2, 104 /// it creates a combined predicate logically equivalent to 105 /// predicate1 AND predicate2, 106 /// but it does not include any AND parts of predicate2 that are present 107 /// in predicate1. 108 /// </summary> 109 /// <param name="predicate1"></param> 110 /// <param name="predicate2"></param> 111 /// <param name="command"></param> 112 /// <returns></returns> CombinePredicates(Node predicate1, Node predicate2, Command command)113 internal static Node CombinePredicates(Node predicate1, Node predicate2, Command command) 114 { 115 IEnumerable<Node> andParts1 = BreakIntoAndParts(predicate1); 116 IEnumerable<Node> andParts2 = BreakIntoAndParts(predicate2); 117 118 Node result = predicate1; 119 120 foreach (Node predicatePart2 in andParts2) 121 { 122 bool foundMatch = false; 123 foreach (Node predicatePart1 in andParts1) 124 { 125 if (predicatePart1.IsEquivalent(predicatePart2)) 126 { 127 foundMatch = true; 128 break; 129 } 130 } 131 if (!foundMatch) 132 { 133 result = command.CreateNode(command.CreateConditionalOp(OpType.And), result, predicatePart2); 134 } 135 } 136 return result; 137 } 138 139 /// <summary> 140 /// Create a list of AND parts for a given predicate. 141 /// For example, if the predicate is of the shape: 142 /// ((p1 and p2) and (p3 and p4)) the list is p1, p2, p3, p4 143 /// The predicates p1,p2, p3, p4 may be roots of subtrees that 144 /// have nodes with AND ops, but 145 /// would not be broken unless they are the AND nodes themselves. 146 /// </summary> 147 /// <param name="predicate"></param> 148 /// <param name="andParts"></param> BreakIntoAndParts(Node predicate)149 private static IEnumerable<Node> BreakIntoAndParts(Node predicate) 150 { 151 return Helpers.GetLeafNodes<Node>(predicate, 152 node => (node.Op.OpType != OpType.And), 153 node => (new[] {node.Child0, node.Child1})); 154 } 155 } 156 } 157