Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / EntitySql / CqlQuery.cs
blob8c8fbe0d752b9fb58cb923f8388124c2bc263812
1 //---------------------------------------------------------------------
2 // <copyright file="CqlQuery.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.EntitySql
12 using System;
13 using System.Collections.Generic;
14 using System.Data.Common.CommandTrees;
15 using System.Data.Metadata.Edm;
16 using System.Diagnostics;
18 /// <summary>
19 /// Provides eSQL text Parsing and Compilation services.
20 /// </summary>
21 /// <remarks>
22 /// This class exposes services that perform syntactic and semantic analysis of eSQL commands.
23 /// The syntactic validation ensures the given command conforms to eSQL formal grammar. The semantic analysis will
24 /// perform (list not exhaustive): type resolution and validation, ensure semantic and scoping rules, etc.
25 /// The services exposed by this class are:
26 /// <list>
27 /// <item>Translation from eSQL text commands to valid <see cref="DbCommandTree"/>s</item>
28 /// <item>Translation from eSQL text commands to valid <see cref="DbExpression"/>s</item>
29 /// </list>
30 /// Queries can be formulated in O-Space, C-Space and S-Space and the services exposed by this class are agnostic of the especific typespace or
31 /// metadata instance passed as required parameter in the semantic analysis by the perspective parameter. It is assumed that the perspective and
32 /// metadata was properly initialized.
33 /// Provided that the command is syntacticaly correct and meaningful within the given typespace, the result will be a valid <see cref="DbCommandTree"/> or
34 /// <see cref="DbExpression"/> otherwise EntityException will be thrown indicating the reason(s) why the given command cannot be accepted.
35 /// It is also possible that MetadataException and MappingException be thrown if mapping or metadata related problems are encountered during compilation.
36 /// </remarks>
37 /// <list>
38 /// <item><seealso cref="ParserOptions"/></item>
39 /// <item><seealso cref="DbCommandTree"/></item>
40 /// <item><seealso cref="DbExpression"/></item>
41 /// </list>
42 internal static class CqlQuery
44 /// <summary>
45 /// Compiles an eSQL command producing a validated <see cref="DbCommandTree"/>.
46 /// </summary>
47 /// <param name="commandText">eSQL command text</param>
48 /// <param name="perspective">perspective</param>
49 /// <param name="parserOptions">parser options<seealso cref="ParserOptions"/></param>
50 /// <param name="parameters">ordinary parameters</param>
51 /// <param name="parseResult"></param>
52 /// <returns>A parse result with the command tree produced by parsing the given command.</returns>
53 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted</exception>
54 /// <exception cref="System.Data.MetadataException">Thrown when metadata related service requests fail</exception>
55 /// <exception cref="System.Data.MappingException">Thrown when mapping related service requests fail</exception>
56 /// <remarks>
57 /// This method is not thread safe.
58 /// </remarks>
59 /// <seealso cref="ParserOptions"/>
60 /// <seealso cref="DbCommandTree"/>
61 internal static ParseResult Compile(string commandText,
62 Perspective perspective,
63 ParserOptions parserOptions,
64 IEnumerable<DbParameterReferenceExpression> parameters)
66 ParseResult result = CompileCommon(commandText, perspective, parserOptions,
67 (astCommand, validatedParserOptions) =>
69 var parseResultInternal = AnalyzeCommandSemantics(astCommand, perspective, validatedParserOptions, parameters);
71 Debug.Assert(parseResultInternal != null, "parseResultInternal != null post-condition FAILED");
72 Debug.Assert(parseResultInternal.CommandTree != null, "parseResultInternal.CommandTree != null post-condition FAILED");
74 TypeHelpers.AssertEdmType(parseResultInternal.CommandTree);
76 return parseResultInternal;
77 });
79 return result;
82 /// <summary>
83 /// Compiles an eSQL query command producing a validated <see cref="DbLambda"/>.
84 /// </summary>
85 /// <param name="queryCommandText">eSQL query command text</param>
86 /// <param name="perspective">perspective</param>
87 /// <param name="parserOptions">parser options<seealso cref="ParserOptions"/></param>
88 /// <param name="parameters">ordinary command parameters</param>
89 /// <param name="variables">command free variables</param>
90 /// <returns>The query expression tree produced by parsing the given query command.</returns>
91 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query expression cannot be accepted</exception>
92 /// <exception cref="System.Data.MetadataException">Thrown when metadata related service requests fail</exception>
93 /// <exception cref="System.Data.MappingException">Thrown when mapping related service requests fail</exception>
94 /// <remarks>
95 /// This method is not thread safe.
96 /// </remarks>
97 /// <seealso cref="ParserOptions"/>
98 /// <seealso cref="DbExpression"/>
99 internal static DbLambda CompileQueryCommandLambda(string queryCommandText,
100 Perspective perspective,
101 ParserOptions parserOptions,
102 IEnumerable<DbParameterReferenceExpression> parameters,
103 IEnumerable<DbVariableReferenceExpression> variables)
105 return CompileCommon(queryCommandText, perspective, parserOptions, (astCommand, validatedParserOptions) =>
107 DbLambda lambda = AnalyzeQueryExpressionSemantics(astCommand,
108 perspective,
109 validatedParserOptions,
110 parameters,
111 variables);
114 TypeHelpers.AssertEdmType(lambda.Body.ResultType);
116 Debug.Assert(lambda != null, "lambda != null post-condition FAILED");
118 return lambda;
122 #region Private
123 /// <summary>
124 /// Parse eSQL command string into an AST
125 /// </summary>
126 /// <param name="commandText">eSQL command</param>
127 /// <param name="parserOptions">parser options<seealso cref="ParserOptions"/></param>
128 /// <returns>Ast</returns>
129 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted</exception>
130 /// <remarks>
131 /// This method is not thread safe.
132 /// </remarks>
133 /// <seealso cref="ParserOptions"/>
134 private static AST.Node Parse(string commandText, ParserOptions parserOptions)
136 AST.Node astExpr = null;
139 // commandText and parserOptions are validated inside of CqlParser
143 // Create Parser
145 CqlParser cqlParser = new CqlParser(parserOptions, true);
148 // Invoke parser
150 astExpr = cqlParser.Parse(commandText);
152 if (null == astExpr)
154 throw EntityUtil.EntitySqlError(commandText, System.Data.Entity.Strings.InvalidEmptyQuery, 0);
157 return astExpr;
160 private static TResult CompileCommon<TResult>(string commandText,
161 Perspective perspective,
162 ParserOptions parserOptions,
163 Func<AST.Node, ParserOptions, TResult> compilationFunction)
164 where TResult : class
166 TResult result = null;
169 // Validate arguments
171 EntityUtil.CheckArgumentNull(perspective, "commandText");
172 EntityUtil.CheckArgumentNull(perspective, "perspective");
175 // Validate parser options - if null, give default options
177 parserOptions = parserOptions ?? new ParserOptions();
180 // Invoke Parser
182 AST.Node astCommand = Parse(commandText, parserOptions);
185 // Perform Semantic Analysis/Conversion
187 result = compilationFunction(astCommand, parserOptions);
189 return result;
192 /// <summary>
193 /// Performs semantic conversion, validation on a command AST and creates a <see cref="DbCommandTree"/>
194 /// </summary>
195 /// <param name="astExpr">Abstract Syntax Tree of the command</param>
196 /// <param name="perspective">perspective</param>
197 /// <param name="parserOptions">parser options<seealso cref="ParserOptions"/></param>
198 /// <param name="parameters">ordinary command parameters</param>
199 /// <returns>a parse result with a valid command tree</returns>
200 /// <remarks>Parameters name/types must be bound before invoking this method</remarks>
201 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted.</exception>
202 /// <exception cref="System.Data.MetadataException">Thrown as inner exception of a EntityException when metadata related service requests fail.</exception>
203 /// <exception cref="System.Data.MappingException">Thrown as inner exception of a EntityException when mapping related service requests fail.</exception>
204 /// <remarks>
205 /// This method is not thread safe.
206 /// </remarks>
207 /// <seealso cref="ParserOptions"/>
208 /// <seealso cref="DbCommandTree"/>
209 private static ParseResult AnalyzeCommandSemantics(AST.Node astExpr,
210 Perspective perspective,
211 ParserOptions parserOptions,
212 IEnumerable<DbParameterReferenceExpression> parameters)
214 ParseResult result = AnalyzeSemanticsCommon(astExpr, perspective, parserOptions, parameters, null/*variables*/,
215 (analyzer, astExpression) =>
217 var parseResultInternal = analyzer.AnalyzeCommand(astExpression);
219 Debug.Assert(parseResultInternal != null, "parseResultInternal != null post-condition FAILED");
220 Debug.Assert(parseResultInternal.CommandTree != null, "parseResultInternal.CommandTree != null post-condition FAILED");
222 return parseResultInternal;
225 return result;
228 /// <summary>
229 /// Performs semantic conversion, validation on a query command AST and creates a <see cref="DbLambda"/>
230 /// </summary>
231 /// <param name="astQueryCommand">Abstract Syntax Tree of the query command</param>
232 /// <param name="perspective">perspective</param>
233 /// <param name="parserOptions">parser options<seealso cref="ParserOptions"/></param>
234 /// <param name="parameters">ordinary command parameters</param>
235 /// <param name="variables">command free variables</param>
236 /// <remarks>Parameters name/types must be bound before invoking this method</remarks>
237 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted.</exception>
238 /// <exception cref="System.Data.MetadataException">Thrown as inner exception of a EntityException when metadata related service requests fail.</exception>
239 /// <exception cref="System.Data.MappingException">Thrown as inner exception of a EntityException when mapping related service requests fail.</exception>
240 /// <remarks>
241 /// This method is not thread safe.
242 /// </remarks>
243 /// <seealso cref="ParserOptions"/>
244 /// <seealso cref="DbExpression"/>
245 private static DbLambda AnalyzeQueryExpressionSemantics(AST.Node astQueryCommand,
246 Perspective perspective,
247 ParserOptions parserOptions,
248 IEnumerable<DbParameterReferenceExpression> parameters,
249 IEnumerable<DbVariableReferenceExpression> variables)
251 return AnalyzeSemanticsCommon(
252 astQueryCommand,
253 perspective,
254 parserOptions,
255 parameters,
256 variables,
257 (analyzer, astExpr) =>
259 DbLambda lambda = analyzer.AnalyzeQueryCommand(astExpr);
260 Debug.Assert(null != lambda, "null != lambda post-condition FAILED");
261 return lambda;
265 private static TResult AnalyzeSemanticsCommon<TResult>(AST.Node astExpr,
266 Perspective perspective,
267 ParserOptions parserOptions,
268 IEnumerable<DbParameterReferenceExpression> parameters,
269 IEnumerable<DbVariableReferenceExpression> variables,
270 Func<SemanticAnalyzer, AST.Node, TResult> analysisFunction)
271 where TResult : class
273 TResult result = null;
278 // Validate arguments
280 EntityUtil.CheckArgumentNull(astExpr, "astExpr");
281 EntityUtil.CheckArgumentNull(perspective, "perspective");
284 // Invoke semantic analysis
286 SemanticAnalyzer analyzer = (new SemanticAnalyzer(SemanticResolver.Create(perspective, parserOptions, parameters, variables)));
287 result = analysisFunction(analyzer, astExpr);
290 // Wrap MetadataException as EntityException inner exception
292 catch (System.Data.MetadataException metadataException)
294 throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.GeneralExceptionAsQueryInnerException("Metadata"), metadataException);
297 // Wrap MappingException as EntityException inner exception
299 catch (System.Data.MappingException mappingException)
301 throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.GeneralExceptionAsQueryInnerException("Mapping"), mappingException);
304 return result;
306 #endregion