//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- namespace System.Data.Common.EntitySql { using System; using System.Collections.Generic; using System.Data.Common.CommandTrees; using System.Data.Metadata.Edm; using System.Diagnostics; /// /// Provides eSQL text Parsing and Compilation services. /// /// /// This class exposes services that perform syntactic and semantic analysis of eSQL commands. /// The syntactic validation ensures the given command conforms to eSQL formal grammar. The semantic analysis will /// perform (list not exhaustive): type resolution and validation, ensure semantic and scoping rules, etc. /// The services exposed by this class are: /// /// Translation from eSQL text commands to valid s /// Translation from eSQL text commands to valid s /// /// 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 /// metadata instance passed as required parameter in the semantic analysis by the perspective parameter. It is assumed that the perspective and /// metadata was properly initialized. /// Provided that the command is syntacticaly correct and meaningful within the given typespace, the result will be a valid or /// otherwise EntityException will be thrown indicating the reason(s) why the given command cannot be accepted. /// It is also possible that MetadataException and MappingException be thrown if mapping or metadata related problems are encountered during compilation. /// /// /// /// /// /// internal static class CqlQuery { /// /// Compiles an eSQL command producing a validated . /// /// eSQL command text /// perspective /// parser options /// ordinary parameters /// /// A parse result with the command tree produced by parsing the given command. /// Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted /// Thrown when metadata related service requests fail /// Thrown when mapping related service requests fail /// /// This method is not thread safe. /// /// /// internal static ParseResult Compile(string commandText, Perspective perspective, ParserOptions parserOptions, IEnumerable parameters) { ParseResult result = CompileCommon(commandText, perspective, parserOptions, (astCommand, validatedParserOptions) => { var parseResultInternal = AnalyzeCommandSemantics(astCommand, perspective, validatedParserOptions, parameters); Debug.Assert(parseResultInternal != null, "parseResultInternal != null post-condition FAILED"); Debug.Assert(parseResultInternal.CommandTree != null, "parseResultInternal.CommandTree != null post-condition FAILED"); TypeHelpers.AssertEdmType(parseResultInternal.CommandTree); return parseResultInternal; }); return result; } /// /// Compiles an eSQL query command producing a validated . /// /// eSQL query command text /// perspective /// parser options /// ordinary command parameters /// command free variables /// The query expression tree produced by parsing the given query command. /// Thrown when Syntatic or Semantic rules are violated and the query expression cannot be accepted /// Thrown when metadata related service requests fail /// Thrown when mapping related service requests fail /// /// This method is not thread safe. /// /// /// internal static DbLambda CompileQueryCommandLambda(string queryCommandText, Perspective perspective, ParserOptions parserOptions, IEnumerable parameters, IEnumerable variables) { return CompileCommon(queryCommandText, perspective, parserOptions, (astCommand, validatedParserOptions) => { DbLambda lambda = AnalyzeQueryExpressionSemantics(astCommand, perspective, validatedParserOptions, parameters, variables); TypeHelpers.AssertEdmType(lambda.Body.ResultType); Debug.Assert(lambda != null, "lambda != null post-condition FAILED"); return lambda; }); } #region Private /// /// Parse eSQL command string into an AST /// /// eSQL command /// parser options /// Ast /// Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted /// /// This method is not thread safe. /// /// private static AST.Node Parse(string commandText, ParserOptions parserOptions) { AST.Node astExpr = null; // // commandText and parserOptions are validated inside of CqlParser // // // Create Parser // CqlParser cqlParser = new CqlParser(parserOptions, true); // // Invoke parser // astExpr = cqlParser.Parse(commandText); if (null == astExpr) { throw EntityUtil.EntitySqlError(commandText, System.Data.Entity.Strings.InvalidEmptyQuery, 0); } return astExpr; } private static TResult CompileCommon(string commandText, Perspective perspective, ParserOptions parserOptions, Func compilationFunction) where TResult : class { TResult result = null; // // Validate arguments // EntityUtil.CheckArgumentNull(perspective, "commandText"); EntityUtil.CheckArgumentNull(perspective, "perspective"); // // Validate parser options - if null, give default options // parserOptions = parserOptions ?? new ParserOptions(); // // Invoke Parser // AST.Node astCommand = Parse(commandText, parserOptions); // // Perform Semantic Analysis/Conversion // result = compilationFunction(astCommand, parserOptions); return result; } /// /// Performs semantic conversion, validation on a command AST and creates a /// /// Abstract Syntax Tree of the command /// perspective /// parser options /// ordinary command parameters /// a parse result with a valid command tree /// Parameters name/types must be bound before invoking this method /// Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted. /// Thrown as inner exception of a EntityException when metadata related service requests fail. /// Thrown as inner exception of a EntityException when mapping related service requests fail. /// /// This method is not thread safe. /// /// /// private static ParseResult AnalyzeCommandSemantics(AST.Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable parameters) { ParseResult result = AnalyzeSemanticsCommon(astExpr, perspective, parserOptions, parameters, null/*variables*/, (analyzer, astExpression) => { var parseResultInternal = analyzer.AnalyzeCommand(astExpression); Debug.Assert(parseResultInternal != null, "parseResultInternal != null post-condition FAILED"); Debug.Assert(parseResultInternal.CommandTree != null, "parseResultInternal.CommandTree != null post-condition FAILED"); return parseResultInternal; }); return result; } /// /// Performs semantic conversion, validation on a query command AST and creates a /// /// Abstract Syntax Tree of the query command /// perspective /// parser options /// ordinary command parameters /// command free variables /// Parameters name/types must be bound before invoking this method /// Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted. /// Thrown as inner exception of a EntityException when metadata related service requests fail. /// Thrown as inner exception of a EntityException when mapping related service requests fail. /// /// This method is not thread safe. /// /// /// private static DbLambda AnalyzeQueryExpressionSemantics(AST.Node astQueryCommand, Perspective perspective, ParserOptions parserOptions, IEnumerable parameters, IEnumerable variables) { return AnalyzeSemanticsCommon( astQueryCommand, perspective, parserOptions, parameters, variables, (analyzer, astExpr) => { DbLambda lambda = analyzer.AnalyzeQueryCommand(astExpr); Debug.Assert(null != lambda, "null != lambda post-condition FAILED"); return lambda; }); } private static TResult AnalyzeSemanticsCommon(AST.Node astExpr, Perspective perspective, ParserOptions parserOptions, IEnumerable parameters, IEnumerable variables, Func analysisFunction) where TResult : class { TResult result = null; try { // // Validate arguments // EntityUtil.CheckArgumentNull(astExpr, "astExpr"); EntityUtil.CheckArgumentNull(perspective, "perspective"); // // Invoke semantic analysis // SemanticAnalyzer analyzer = (new SemanticAnalyzer(SemanticResolver.Create(perspective, parserOptions, parameters, variables))); result = analysisFunction(analyzer, astExpr); } // // Wrap MetadataException as EntityException inner exception // catch (System.Data.MetadataException metadataException) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.GeneralExceptionAsQueryInnerException("Metadata"), metadataException); } // // Wrap MappingException as EntityException inner exception // catch (System.Data.MappingException mappingException) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.GeneralExceptionAsQueryInnerException("Mapping"), mappingException); } return result; } #endregion } }