Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / PlanCompiler / PlanCompilerUtil.cs
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>
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>
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>
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>
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>
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 }