1 //---------------------------------------------------------------------
2 // <copyright file="CqlQuery.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System
.Data
.Common
.EntitySql
13 using System
.Collections
.Generic
;
14 using System
.Data
.Common
.CommandTrees
;
15 using System
.Data
.Metadata
.Edm
;
16 using System
.Diagnostics
;
19 /// Provides eSQL text Parsing and Compilation services.
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:
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>
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.
38 /// <item><seealso cref="ParserOptions"/></item>
39 /// <item><seealso cref="DbCommandTree"/></item>
40 /// <item><seealso cref="DbExpression"/></item>
42 internal static class CqlQuery
45 /// Compiles an eSQL command producing a validated <see cref="DbCommandTree"/>.
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>
57 /// This method is not thread safe.
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
;
83 /// Compiles an eSQL query command producing a validated <see cref="DbLambda"/>.
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>
95 /// This method is not thread safe.
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
,
109 validatedParserOptions
,
114 TypeHelpers
.AssertEdmType(lambda
.Body
.ResultType
);
116 Debug
.Assert(lambda
!= null, "lambda != null post-condition FAILED");
124 /// Parse eSQL command string into an AST
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>
131 /// This method is not thread safe.
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
145 CqlParser cqlParser
= new CqlParser(parserOptions
, true);
150 astExpr
= cqlParser
.Parse(commandText
);
154 throw EntityUtil
.EntitySqlError(commandText
, System
.Data
.Entity
.Strings
.InvalidEmptyQuery
, 0);
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();
182 AST
.Node astCommand
= Parse(commandText
, parserOptions
);
185 // Perform Semantic Analysis/Conversion
187 result
= compilationFunction(astCommand
, parserOptions
);
193 /// Performs semantic conversion, validation on a command AST and creates a <see cref="DbCommandTree"/>
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>
205 /// This method is not thread safe.
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
;
229 /// Performs semantic conversion, validation on a query command AST and creates a <see cref="DbLambda"/>
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>
241 /// This method is not thread safe.
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(
257 (analyzer
, astExpr
) =>
259 DbLambda lambda
= analyzer
.AnalyzeQueryCommand(astExpr
);
260 Debug
.Assert(null != lambda
, "null != lambda post-condition FAILED");
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
);