Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / PlanCompiler / Normalizer.cs
1 //---------------------------------------------------------------------
2 // <copyright file="Normalizer.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.Collections.Generic;
12 using System.Data.Common;
13 using System.Data.Metadata.Edm;
14 using System.Data.Query.InternalTrees;
15 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
16
17 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
18 // to prevent from simple mistakes during development (e.g. method argument validation 
19 // in cases where it was you who created the variables or the variables had already been validated or 
20 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
21 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
22 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
23 // the shipped product. 
24 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
25 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
26 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
27 // or the tree was built/rewritten not the way we thought it was.
28 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
29 // PlanCompiler.Assert.
30
31 //
32 // The normalizer performs transformations of the tree to bring it to a 'normalized' format
33 // In particular it does the following: 
34 //  (a) Transforms collection aggregate functions into a GroupBy. 
35 //  (b) Translates Exists(X) into Exists(select 1 from X)
36 //
37 namespace System.Data.Query.PlanCompiler
38 {
39     /// <summary>
40     /// The normalizer performs transformations of the tree to bring it to a 'normalized' format
41     /// </summary>
42     internal class Normalizer : SubqueryTrackingVisitor
43     {
44         #region constructors
45         private Normalizer(PlanCompiler planCompilerState)
46             :base(planCompilerState)
47         {
48         }
49         #endregion
50
51         #region public methods
52         /// <summary>
53         /// The driver routine.
54         /// </summary>
55         /// <param name="planCompilerState">plan compiler state</param>
56         internal static void Process(PlanCompiler planCompilerState)
57         {
58             Normalizer normalizer = new Normalizer(planCompilerState);
59             normalizer.Process();
60         }
61
62         #endregion
63
64         #region private methods
65
66         #region driver
67         private void Process()
68         {
69             m_command.Root = VisitNode(m_command.Root);
70         }
71         #endregion
72
73         #region visitor methods
74
75         #region ScalarOps
76
77         /// <summary>
78         /// Translate Exists(X) into Exists(select 1 from X)
79         /// </summary>
80         /// <param name="op"></param>
81         /// <param name="n"></param>
82         /// <returns></returns>
83         public override Node Visit(ExistsOp op, Node n)
84         {
85             VisitChildren(n);
86
87             // Build up a dummy project node over the input
88             n.Child0 = BuildDummyProjectForExists(n.Child0);
89
90             return n;
91         }
92
93         /// <summary>
94         /// Build Project(select 1 from child).
95         /// </summary>
96         /// <param name="child"></param>
97         /// <returns></returns>
98         private Node BuildDummyProjectForExists(Node child)
99         {
100             Var newVar;
101             Node projectNode = m_command.BuildProject(
102                 child,
103                 m_command.CreateNode(m_command.CreateInternalConstantOp(m_command.IntegerType, 1)),
104                 out newVar);
105             return projectNode;
106         }
107
108         /// <summary>
109         /// Build up an unnest above a scalar op node
110         ///    X => unnest(X)
111         /// </summary>
112         /// <param name="collectionNode">the scalarop collection node</param>
113         /// <returns>the unnest node</returns>
114         private Node BuildUnnest(Node collectionNode)
115         {
116             PlanCompiler.Assert(collectionNode.Op.IsScalarOp, "non-scalar usage of Unnest?");
117             PlanCompiler.Assert(TypeSemantics.IsCollectionType(collectionNode.Op.Type), "non-collection usage for Unnest?");
118
119             Var newVar;
120             Node varDefNode = m_command.CreateVarDefNode(collectionNode, out newVar);
121             UnnestOp unnestOp = m_command.CreateUnnestOp(newVar);
122             Node unnestNode = m_command.CreateNode(unnestOp, varDefNode);
123
124             return unnestNode;
125         }
126
127         /// <summary>
128         /// Converts the reference to a TVF as following: Collect(PhysicalProject(Unnest(Func)))
129         /// </summary>
130         /// <param name="op">current function op</param>
131         /// <param name="n">current function subtree</param>
132         /// <returns>the new expression that corresponds to the TVF</returns>
133         private Node VisitCollectionFunction(FunctionOp op, Node n)
134         {
135             PlanCompiler.Assert(TypeSemantics.IsCollectionType(op.Type), "non-TVF function?");
136
137             Node unnestNode = BuildUnnest(n);
138             UnnestOp unnestOp = unnestNode.Op as UnnestOp;
139             PhysicalProjectOp projectOp = m_command.CreatePhysicalProjectOp(unnestOp.Table.Columns[0]);
140             Node projectNode = m_command.CreateNode(projectOp, unnestNode);
141             CollectOp collectOp = m_command.CreateCollectOp(n.Op.Type);
142             Node collectNode = m_command.CreateNode(collectOp, projectNode);
143
144             return collectNode;
145         }
146
147         /// <summary>
148         /// Converts a collection aggregate function count(X), where X is a collection into
149         /// two parts. Part A is a groupby subquery that looks like
150         ///    GroupBy(Unnest(X), empty, count(y))
151         /// where "empty" describes the fact that the groupby has no keys, and y is an
152         /// element var of the Unnest
153         /// 
154         /// Part 2 is a VarRef that refers to the aggregate var for count(y) described above.
155         /// 
156         /// Logically, we would replace the entire functionOp by element(GroupBy...). However,
157         /// since we also want to translate element() into single-row-subqueries, we do this
158         /// here as well.
159         /// 
160         /// The function itself is replaced by the VarRef, and the GroupBy is added to the list
161         /// of scalar subqueries for the current relOp node on the stack
162         /// 
163         /// </summary>
164         /// <param name="op">the functionOp for the collection agg</param>
165         /// <param name="n">current subtree</param>
166         /// <returns>the VarRef node that should replace the function</returns>
167         private Node VisitCollectionAggregateFunction(FunctionOp op, Node n)
168         {
169             TypeUsage softCastType = null;
170             Node argNode = n.Child0;
171             if (OpType.SoftCast == argNode.Op.OpType)
172             {
173                 softCastType = TypeHelpers.GetEdmType<CollectionType>(argNode.Op.Type).TypeUsage;
174                 argNode = argNode.Child0;
175
176                 while (OpType.SoftCast == argNode.Op.OpType)
177                 {
178                     argNode = argNode.Child0;
179                 }
180             }
181
182             Node unnestNode = BuildUnnest(argNode);
183             UnnestOp unnestOp = unnestNode.Op as UnnestOp;
184             Var unnestOutputVar = unnestOp.Table.Columns[0];
185
186             AggregateOp aggregateOp = m_command.CreateAggregateOp(op.Function, false);
187             VarRefOp unnestVarRefOp = m_command.CreateVarRefOp(unnestOutputVar);
188             Node unnestVarRefNode = m_command.CreateNode(unnestVarRefOp);
189             if (softCastType != null)
190             {
191                 unnestVarRefNode = m_command.CreateNode(m_command.CreateSoftCastOp(softCastType), unnestVarRefNode);
192             }
193             Node aggExprNode = m_command.CreateNode(aggregateOp, unnestVarRefNode);
194
195             VarVec keyVars = m_command.CreateVarVec(); // empty keys
196             Node keyVarDefListNode = m_command.CreateNode(m_command.CreateVarDefListOp());
197
198             VarVec gbyOutputVars = m_command.CreateVarVec();
199             Var aggVar;
200             Node aggVarDefListNode = m_command.CreateVarDefListNode(aggExprNode, out aggVar);
201             gbyOutputVars.Set(aggVar);
202             GroupByOp gbyOp = m_command.CreateGroupByOp(keyVars, gbyOutputVars);
203             Node gbySubqueryNode = m_command.CreateNode(gbyOp, unnestNode, keyVarDefListNode, aggVarDefListNode);
204
205             // "Move" this subquery to my parent relop
206             Node ret = AddSubqueryToParentRelOp(aggVar, gbySubqueryNode);
207
208             return ret;
209         }
210
211         /// <summary>
212         /// Pre-processing for a function. Does the default scalar op processing.
213         /// If the function returns a collection (TVF), the method converts this expression into
214         ///    Collect(PhysicalProject(Unnest(Func))).
215         /// If the function is a collection aggregate, converts it into the corresponding group aggregate.   
216         /// </summary>
217         /// <param name="op"></param>
218         /// <param name="n"></param>
219         /// <returns></returns>
220         public override Node Visit(FunctionOp op, Node n)
221         {
222             VisitScalarOpDefault(op, n);
223             Node newNode = null;
224
225             // Is this a TVF?
226             if (TypeSemantics.IsCollectionType(op.Type))
227             {
228                 newNode = VisitCollectionFunction(op, n);
229             }
230             // Is this a collection-aggregate function?
231             else if (PlanCompilerUtil.IsCollectionAggregateFunction(op, n))
232             {
233                 newNode = VisitCollectionAggregateFunction(op, n);
234             }
235             else
236             {
237                 newNode = n;
238             }
239
240             PlanCompiler.Assert(newNode != null, "failure to construct a functionOp?");
241             return newNode;
242         }
243
244         #endregion
245
246         #region RelOps
247         /// <summary>
248         /// Processing for all JoinOps
249         /// </summary>
250         /// <param name="op">JoinOp</param>
251         /// <param name="n">Current subtree</param>
252         /// <returns></returns>
253         protected override Node VisitJoinOp(JoinBaseOp op, Node n)
254         {
255             if (base.ProcessJoinOp(op, n))
256             {
257                 // update the join condition
258                 // #479372: Build up a dummy project node over the input, as we always wrap the child of exists
259                 n.Child2.Child0 =  BuildDummyProjectForExists(n.Child2.Child0);
260             }
261             return n;
262         }
263
264         #endregion
265
266         #endregion
267
268         #endregion
269     }
270 }