Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / PlanCompiler / ProviderCommandInfoUtils.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ProviderCommandInfoUtils.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.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
13
14 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
15 // to prevent from simple mistakes during development (e.g. method argument validation 
16 // in cases where it was you who created the variables or the variables had already been validated or 
17 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
18 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
19 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
20 // the shipped product. 
21 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
22 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
23 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
24 // or the tree was built/rewritten not the way we thought it was.
25 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
26 // PlanCompiler.Assert.
27
28 using System.Data.Common.CommandTrees;
29 using System.Data.Common;
30 using md = System.Data.Metadata.Edm;
31 using System.Data.Query.InternalTrees;
32 using System.Data.Query.PlanCompiler;
33
34 namespace System.Data.Query.PlanCompiler
35 {
36     /// <summary>
37     /// Helper class for creating a ProviderCommandInfo given an Iqt Node. 
38     /// </summary>
39     internal static class ProviderCommandInfoUtils
40     {
41         #region Public Methods
42
43         /// <summary>
44         /// Creates a ProviderCommandInfo for the given node. 
45         /// This method should be called when the keys, foreign keys and sort keys are known ahead of time.
46         /// Typically it is used when the original command is factored into multiple commands. 
47         /// </summary>
48         /// <param name="command">The owning command, used for creating VarVecs, etc</param>
49         /// <param name="node">The root of the sub-command for which a ProviderCommandInfo should be generated</param>
50         /// <param name="children">A list of ProviderCommandInfos that were created for the child sub-commands.</param>
51         /// <returns>The resulting ProviderCommandInfo</returns>
52         internal static ProviderCommandInfo Create(
53             Command command,
54             Node node, 
55             List<ProviderCommandInfo> children)
56         {
57             PhysicalProjectOp projectOp = node.Op as PhysicalProjectOp;
58             PlanCompiler.Assert(projectOp != null, "Expected root Op to be a physical Project");
59
60             // build up the CQT
61             DbCommandTree ctree = CTreeGenerator.Generate(command, node);
62             DbQueryCommandTree cqtree = ctree as DbQueryCommandTree;
63             PlanCompiler.Assert(cqtree != null, "null query command tree");
64
65             // Get the rowtype for the result cqt
66             md.CollectionType collType = TypeHelpers.GetEdmType<md.CollectionType>(cqtree.Query.ResultType);
67             PlanCompiler.Assert(md.TypeSemantics.IsRowType(collType.TypeUsage), "command rowtype is not a record");
68
69             // Build up a mapping from Vars to the corresponding output property/column
70             Dictionary<Var, md.EdmProperty> outputVarMap = BuildOutputVarMap(projectOp, collType.TypeUsage);
71
72             return new ProviderCommandInfo(ctree, children);
73         }
74
75         /// <summary>
76         /// Creates a ProviderCommandInfo for the given node. 
77         /// This method should be called when the keys and the sort keys are not known ahead of time.
78         /// Typically it is used when there is only one command, that is no query factoring is done.
79         /// This method also has the option of pulling up keys and sort information. 
80         /// </summary>
81         /// <param name="command">The owning command, used for creating VarVecs, etc</param>
82         /// <param name="node">The root of the sub-command for which a ProviderCommandInfo should be generated</param>
83         /// <returns>The resulting ProviderCommandInfo</returns>
84         internal static ProviderCommandInfo Create(
85             Command command,
86             Node node)
87         {   
88             return Create(
89                 command, 
90                 node, 
91                 new List<ProviderCommandInfo>() //children 
92                 );
93         }
94         #endregion
95
96         #region Private Methods
97         /// <summary>
98         /// Build up a mapping from Vars to the corresponding property of the output row type
99         /// </summary>
100         /// <param name="projectOp">the physical projectOp</param>
101         /// <param name="outputType">output type</param>
102         /// <returns>a map from Vars to the output type member</returns>
103         private static Dictionary<Var, md.EdmProperty> BuildOutputVarMap(PhysicalProjectOp projectOp, md.TypeUsage outputType)
104         {
105             Dictionary<Var, md.EdmProperty> outputVarMap = new Dictionary<Var, md.EdmProperty>();
106
107             PlanCompiler.Assert(md.TypeSemantics.IsRowType(outputType), "PhysicalProjectOp result type is not a RowType?");
108
109             IEnumerator<md.EdmProperty> propertyEnumerator = TypeHelpers.GetEdmType<md.RowType>(outputType).Properties.GetEnumerator();
110             IEnumerator<Var> varEnumerator = projectOp.Outputs.GetEnumerator();
111             while (true)
112             {
113                 bool foundProp = propertyEnumerator.MoveNext();
114                 bool foundVar = varEnumerator.MoveNext();
115                 if (foundProp != foundVar)
116                 {
117                     throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.ColumnCountMismatch, 1);
118                 }
119                 if (!foundProp)
120                 {
121                     break;
122                 }
123                 outputVarMap[varEnumerator.Current] = propertyEnumerator.Current;
124             }
125             return outputVarMap;
126         }
127
128         #endregion
129     }
130 }