Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Common / EntitySql / SemanticAnalyzer.cs / 1 / SemanticAnalyzer.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backup [....]
//---------------------------------------------------------------------
namespace System.Data.Common.EntitySql
{
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.Common.CommandTrees;
using System.Data.Metadata.Edm;
using System.Data.Entity;
///
/// Implements Semantic Analysis and Conversion
/// Provides the translation service between an abstract syntax tree to a canonical command tree
/// For complete documentation of the language syntax and semantics, refer to http://sqlweb/default.asp?specDirId=764
/// The class was designed to be type system agnostic by delegating to a given SemanticResolver instance all type related services as well as to TypeHelper class, however
/// we rely on the assumption that metadata was pre-loaded and is relevant to the query.
///
internal sealed class SemanticAnalyzer
{
private SemanticResolver _sr;
///
/// Initializes semantic analyzer
///
/// initialized SemanticResolver instance for a given typespace/type system
internal SemanticAnalyzer( SemanticResolver sr )
{
EntityUtil.CheckArgumentNull(sr, "sr");
_sr = sr;
}
///
/// Entry point to semantic analysis. Converts astTree into DbExpression.
///
/// ast tree
/// command tree to build the expression
///
/// 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
///
/// DbExpression
internal DbExpression Analyze(Expr astExpr, DbCommandTree commandTree)
{
CommandExpr command = Initialize(astExpr, commandTree);
DbExpression converted = ConvertRootExpression(command.QueryExpr, _sr);
return converted;
}
///
/// Entry point to semantic analysis. Converts astTree into command tree.
///
/// ast tree
///
/// 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
///
/// DbCommandTree
internal DbCommandTree Analyze( Expr astExpr )
{
CommandExpr astCommandExpr = Initialize(astExpr, null);
//
// Convert query Expression
//
DbCommandTree commandTree = ConvertCommand(astCommandExpr, _sr);
Debug.Assert(null != commandTree,"null != commandTree");
return commandTree;
}
///
/// Common initialization required whether the semantic analysis process
/// is producing a command tree or only an expression. Validates that the
/// parse phase did in fact produce a command AST.
///
/// The root of the abstract syntax tree produced by the parse phase
///
/// An optional command tree to use, only provided when constructing an expression.
/// When constructing a command tree, an instance of the appropriate command tree
/// class will be created and returned by semantic analysis.
///
/// The that is the root of the AST specified by
private CommandExpr Initialize(Expr astExpr, DbCommandTree tree)
{
CommandExpr astCommandExpr = astExpr as CommandExpr;
if (null == astCommandExpr)
{
throw EntityUtil.Argument(System.Data.Entity.Strings.UnknownAstCommandExpression);
}
//
// Sets DbCommandTree Factory
//
if (tree == null)
{
_sr.SetCommandTreeFactory(astCommandExpr);
}
else
{
_sr.SetCommandTreeFactory(astCommandExpr, tree);
}
//
// Declare Canonical Namespace
//
_sr.DeclareCanonicalNamespace();
//
// Declare Namespaces
//
_sr.DeclareNamespaces(astCommandExpr.NamespaceDeclList);
return astCommandExpr;
}
#region command converter delegates
delegate DbCommandTree CommandConverter( Expr astExpr, SemanticResolver sr );
static CommandConverter[] commandConverters =
{
/* ExprKind.Generic */ ConvertGeneralExpression,
/* ExprKind.Query */ ConvertGeneralExpression,
};
#endregion
///
/// Dispatches/Converts top command expressions.
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbCommandTree ConvertCommand( CommandExpr astCommandExpr, SemanticResolver sr )
{
EntityUtil.CheckArgumentNull(astCommandExpr, "astCommandExpr");
Expr queryExpr = astCommandExpr.QueryExpr;
return commandConverters[(int)queryExpr.ExprKind](queryExpr, sr);
}
///
/// Converts {Query|General} Expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbCommandTree ConvertGeneralExpression( Expr astExpr, SemanticResolver sr )
{
DbExpression converted = ConvertRootExpression(astExpr, sr);
DbQueryCommandTree qryCmdTree = (DbQueryCommandTree)sr.CmdTree;
qryCmdTree.Query = converted;
Debug.Assert(null != qryCmdTree,"null != qryCmdTree");
return qryCmdTree;
}
///
/// Converts the expression that is the 'root' of a query AST to a
/// normalized and validated . This entry
/// point to the semantic analysis phase is used when producing a
/// query command tree (from a Query or General AST expression) or
/// producing only a .
///
/// The root AST node for this query
/// The instance to use
///
/// An instance of , adjusted to handle 'inline' projections
/// and validated to produce a result type appropriate for the root of a query command tree.
///
private static DbExpression ConvertRootExpression(Expr astExpr, SemanticResolver sr)
{
DbExpression converted = Convert(astExpr, sr);
//
// ensure converted expression is not untyped-null
//
if (TypeSemantics.IsNullType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(astExpr.ErrCtx, System.Data.Entity.Strings.ResultingExpressionTypeCannotBeNull);
}
//
// Handles the "inline" projection case
//
if (converted is DbScanExpression)
{
DbExpressionBinding source = sr.CmdTree.CreateExpressionBinding(converted, sr.GenerateInternalName("extent"));
converted = sr.CmdTree.CreateProjectExpression(source, source.Variable);
}
//
// ensure return type is valid for query. for V1, association types are the only
// type that cannot be at 'top' level result. Note that this is only applicable in
// general queries and association types are valid in view gen mode queries
//
if (sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.NormalMode)
{
SemanticResolver.ValidateQueryResultType(converted.ResultType, astExpr.ErrCtx);
}
return converted;
}
///
/// Dispatches/Converts general expressions
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression Convert( Expr astExpr, SemanticResolver sr )
{
if (null == astExpr)
{
return null;
}
AstExprConverter converter = _astExprConverters[astExpr.GetType()];
if (null == converter)
{
throw EntityUtil.Argument(System.Data.Entity.Strings.UnknownAstExpressionType);
}
DbExpression converted = converter(astExpr, sr);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts Literal Expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertLiteral( Expr expr, SemanticResolver sr )
{
Literal literal = (Literal)expr;
if (literal.IsNullLiteral)
{
//
// if it is literal null, return untyped null
// untyped nulls will later have their type inferred depending on the
// especific expression in which it participates
//
return new UntypedNullExpression(sr.CmdTree);
}
else
{
return sr.CmdTree.CreateConstantExpression(literal.Value,
sr.TypeResolver.GetLiteralTypeUsage(literal));
}
}
///
/// Converts Simple Identifier
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertIdentifier( Expr expr, SemanticResolver sr )
{
Identifier idExpr = (Identifier)expr;
DbExpression converted = sr.ResolveIdentifier(new string[] { idExpr.Name }, expr.ErrCtx);
if (null == converted)
{
CqlErrorHelper.ReportIdentifierError(expr, sr);
}
return converted;
}
///
/// Converts DotExpression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertDotExpr( Expr expr, SemanticResolver sr )
{
DotExpr dotExpr = (DotExpr)expr;
DbExpression converted = null;
if (dotExpr.IsDottedIdentifier)
{
converted = sr.ResolveIdentifier(dotExpr.Names, dotExpr.ErrCtx);
if (null == converted)
{
CqlErrorHelper.ReportIdentifierError(expr, sr);
}
}
else
{
converted = ConvertDotExpressionProcess(dotExpr, sr);
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts dot expression. dotted id case is handled by resolveId
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
///
///
///
///
static private DbExpression ConvertDotExpressionProcess( DotExpr dotExpr, SemanticResolver sr )
{
Debug.Assert(!dotExpr.IsDottedIdentifier,"!dotExpr.IsDottedIdentifier");
return sr.ResolveIdentifierChain(dotExpr.Names, 0, Convert(dotExpr.LeftMostExpression, sr), dotExpr.ErrCtx);
}
///
/// Converts methods, functions and type constructors
/// methods (instance or static) can be in the form: (expr).chain.of.names(args) or chain.of.names(args)
/// Functions are chain.of.names(args)
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertMethodExpr( Expr expr, SemanticResolver sr )
{
MethodExpr methodExpr = (MethodExpr)expr;
DbExpression converted = null;
DotExpr dotExpr = methodExpr.MethodPrefixExpr;
//
// resolve base expression if one exists
//
DbExpression baseExpr = (null != dotExpr.LeftMostExpression) ? Convert(dotExpr.LeftMostExpression, sr) : null;
//
// If base expression is still unresolved, check if leftmost name element is in in scope
//
int prefixIndex = 0;
KeyValuePair varInfo;
if (null == baseExpr && dotExpr.IsDottedIdentifier && dotExpr.Names.Length > 0)
{
ScopeEntry scopeEntry;
if (sr.TryScopeLookup(dotExpr.Names[0], out scopeEntry))
{
//
// make sure is a valid type for method call
//
if (TypeResolver.IsValidTypeForMethodCall(scopeEntry.Expression.ResultType))
{
baseExpr = scopeEntry.Expression;
prefixIndex = 1;
}
else
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.DefiningTypeDoesNotSupportMethodCalls);
}
}
else if (sr.Variables.TryGetValue(dotExpr.Names[0], out varInfo))
{
baseExpr = sr.CmdTree.CreateVariableReferenceExpression(varInfo.Key, varInfo.Value);
prefixIndex = 1;
}
}
//
// check if base expression if is untyped null
//
if (baseExpr is UntypedNullExpression)
{
throw EntityUtil.EntitySqlError(dotExpr.LeftMostExpression.ErrCtx, System.Data.Entity.Strings.ExpressionCannotBeNull);
}
//
// dispatches methodExpr to static or instance kind of method conversion
//
if (null == baseExpr)
{
//
// if base expression is still unresolved, it should be a function(aggregates included), static method or type constructor
//
converted = ConvertStaticMethodOrFunction(methodExpr, sr);
}
else
{
//
// otherwise, should be an instance method invocation
//
converted = ConvertMethodInstance(baseExpr, methodExpr, prefixIndex, sr);
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts a method expr into a Static Method, Type Constructor or EdmFunction call (including aggregates)
///
///
///
///
private static DbExpression ConvertStaticMethodOrFunction( MethodExpr methodExpr, SemanticResolver sr )
{
DbExpression converted = null;
//
// Find out if given name path is a Static Method, Type Constructor or EdmFunction call (including aggregates)
//
TypeUsage constructorType;
TypeUsage staticMethodType;
IList functionType;
//
// Resolve methodExpr
//
sr.ResolveNameAsStaticMethodOrFunction(methodExpr, out constructorType, out staticMethodType, out functionType);
//
// at this point, only one of the three must be not null
// that is ensured by ResolveNameAsStaticMethodOrFunction()
//
Debug.Assert(constructorType != null || staticMethodType != null || functionType != null,"constructorType != null || staticMethodType != null || functionType != null");
//
// if it is a constructorType, create an instance of it
//
if (null != constructorType)
{
List relshipExprList = null;
//
// convert relationships if present
//
if (methodExpr.HasRelationships)
{
if (ParserOptions.CompilationMode.NormalMode == sr.ParserOptions.ParserCompilationMode)
{
throw EntityUtil.EntitySqlError(methodExpr.Relationships.ErrCtx, System.Data.Entity.Strings.InvalidModeForWithRelationshipClause);
}
HashSet targetEnds = new HashSet();
relshipExprList = new List(methodExpr.Relationships.Count);
for (int i = 0; i < methodExpr.Relationships.Count; i++)
{
RelshipNavigationExpr relshipExpr = methodExpr.Relationships[i];
DbRelatedEntityRef relshipTarget = ConvertRelatedEntityRef(relshipExpr, sr);
string targetEndId = String.Join(":", new String[] { relshipTarget.TargetEnd.DeclaringType.Identity, relshipTarget.TargetEnd.Identity });
if (targetEnds.Contains(targetEndId))
{
throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx,
System.Data.Entity.Strings.RelationshipTargetMustBeUnique(relshipTarget.TargetEntityReference.ResultType.EdmType.Identity));
}
targetEnds.Add(targetEndId);
relshipExprList.Add(relshipTarget);
}
}
converted = sr.CreateInstanceOfType(constructorType,
ConvertFunctionArguments(methodExpr.Args, sr),
relshipExprList,
methodExpr);
}
//
// if it is staticMethodType, create method call expression
//
else if (null != staticMethodType)
{
converted = SemanticResolver.CreateStaticMethod(staticMethodType, ConvertFunctionArguments(methodExpr.Args, sr), methodExpr);
}
//
// if it is functionType
//
else if (null != functionType && 0 < functionType.Count)
{
//
// decide if it is an ordinary function or group aggreagate
//
if (TypeSemantics.IsAggregateFunction(functionType[0]) && sr.IsInAnyGroupScope())
{
//
// if it is an aggreagate function inside a group scope, dispatch to ConvertGroupAggregate()
//
converted = ConvertAggregateFunctionInGroupScope(methodExpr, functionType, sr);
}
else
{
//
// else, is just an ordinary function call (including collection aggregates)
//
converted = sr.CreateFunction(functionType, ConvertFunctionArguments(methodExpr.Args, sr), methodExpr);
}
}
else
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.CannotResolveNameToFunction(methodExpr.MethodPrefixExpr.FullName));
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts Group Aggregates
///
///
///
///
///
///
/// This method convert group aggregates in two phases:
/// Phase 1 - it will resolve the actual inner (argument) expression and then anotate the ast node and add the resolved aggregate
/// to the scope
/// Phase 2 - if ast node was annotated, just extract the precomputed expression from the scope.
///
private static DbExpression ConvertAggregateFunctionInGroupScope( MethodExpr methodExpr, IList functionTypes, SemanticResolver sr )
{
DbExpression converted = null;
//
// first, try if aggregate was already pre resolved
//
if (TryConvertAsResolvedGroupAggregate(methodExpr, sr, out converted))
{
return converted;
}
//
// then, try to resolve as Ordinary function (collection aggregate)
//
if (TryConvertAsOrdinaryFunctionInGroup(methodExpr, functionTypes, sr, out converted))
{
return converted;
}
//
// finally, try to convert as group aggregate
//
if (TryConvertAsGroupAggregateFunction(methodExpr, functionTypes, sr, out converted))
{
return converted;
}
//
// if we reach this point, means the resolution failed
//
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.FailedToResolveAggregateFunction(methodExpr.MethodPrefixExpr.FullName));
}
///
/// try to convert as pre resolved aggregate function
///
///
///
///
///
private static bool TryConvertAsResolvedGroupAggregate( MethodExpr methodExpr, SemanticResolver sr, out DbExpression converted )
{
converted = null;
//
// if ast node was annotated in a previous pass, means it was already resolved and should be in scope
//
if (methodExpr.WasResolved)
{
sr.CurrentScopeRegionFlags.DecrementGroupAggregateNestingCount();
ScopeEntry scopeEntry;
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
sr.SetScopeView(SemanticResolver.ScopeViewKind.All);
int scopeIndex;
if (!sr.TryScopeLookup(methodExpr.InternalAggregateName, out scopeEntry, out scopeIndex))
{
Debug.Assert(methodExpr.DummyExpression != null, "resolved aggregate dummy expression must not be null");
converted = methodExpr.DummyExpression;
return true;
}
else
{
//
// Sets correlation flag
//
sr.SetScopeRegionCorrelationFlag(scopeIndex);
}
sr.SetScopeView(saveScopeView);
converted = scopeEntry.Expression;
return true;
}
return false;
}
///
/// Try convert method expr in a group scope as an ordinary function (collection aggregate)
///
///
///
///
///
///
private static bool TryConvertAsOrdinaryFunctionInGroup( MethodExpr methodExpr ,
IList functionTypes ,
SemanticResolver sr ,
out DbExpression converted )
{
converted = null;
//
// save scope view
//
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
//
// convert aggregate arguments
//
List args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// collect argument types
//
List argTypes = new List(args.Count);
for (int i = 0 ; i < args.Count ; i++)
{
argTypes.Add(args[i].ResultType);
}
//
// try to see if there is a overload match
//
bool isAmbiguous = false;
EdmFunction functionType = TypeResolver.ResolveFunctionOverloads( functionTypes,
argTypes,
false /* isGroupAggregateFunction */,
out isAmbiguous);
//
// if there is more then one overload that matches given arguments, throw
//
if (isAmbiguous)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionArguments);
}
//
// if not null, means a match was found as an ordinary function
//
if (null != functionType)
{
//
// make sure all referenced vars are from group scope only
//
if (!sr.CurrentScopeRegionFlags.IsImplicitGroup)
{
sr.SetScopeView(SemanticResolver.ScopeViewKind.CurrentScope);
}
//
// convert aggregate arguments
//
args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// restore previous scope view
//
sr.SetScopeView(saveScopeView);
//
// return function
//
converted = sr.CmdTree.CreateFunctionExpression(functionType, args);
}
return (null != functionType);
}
///
///
///
///
///
///
///
///
private static bool TryConvertAsGroupAggregateFunction( MethodExpr methodExpr,
IList functionTypeList,
SemanticResolver sr,
out DbExpression converted )
{
converted = null;
//
// save scope view
//
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
//
// Aggregates in groups can refer to all scopes
//
sr.SetScopeView(SemanticResolver.ScopeViewKind.All);
//
// flag that it is inside a group aggregate
//
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = true;
//
// reset nested references flag
//
sr.CurrentScopeRegionFlags.WasNestedGroupAggregateReferredByInnerExpressions = false;
//
// pushes candidate aggregate ast node
//
sr.PushAggregateAstNode(methodExpr);
sr.CurrentScopeRegionFlags.DecrementGroupAggregateNestingCount();
//
// convert aggregate arguments
//
List args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// clear inside group aggregate flag
//
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = false;
//
// collect argument types
//
List argTypes = new List(args.Count);
for (int i = 0 ; i < args.Count ; i++)
{
argTypes.Add(args[i].ResultType);
}
//
// try to find an overload match as group aggregate
//
bool isAmbiguous = false;
EdmFunction functionType = TypeResolver.ResolveFunctionOverloads(functionTypeList,
argTypes,
true /* isGroupAggregateFunction */,
out isAmbiguous);
//
// if there is more then one overload that matches given arguments, throw
//
if (isAmbiguous)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionArguments);
}
//
// if it still null, then there is no overload as an ordinary function or group aggregate function
//
if (null == functionType)
{
CqlErrorHelper.ReportFunctionOverloadError(methodExpr, functionTypeList[0], argTypes);
}
//
// ensure that group aggregate was not referenced by inner sub-expression
//
if (sr.CurrentScopeRegionFlags.WasNestedGroupAggregateReferredByInnerExpressions)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.NestedAggregatesCannotBeUsedInAggregateFunctions);
}
//
// ensure it is not a nested group aggregate call
//
if (sr.CurrentScopeRegionFlags.GroupAggregateNestingCount < -1)
{
throw EntityUtil.EntitySqlError(methodExpr.MethodIdentifier.ErrCtx, System.Data.Entity.Strings.InvalidNestedGroupAggregateCall);
}
//
// aggregate functions in the current release can have only one argument and must of of collectio type
//
Debug.Assert((1 == functionType.Parameters.Count), "(1 == functionType.Parameters.Count)"); // we only support monadic aggregate functions
Debug.Assert(TypeSemantics.IsCollectionType(functionType.Parameters[0].TypeUsage), "functionType.Parameters[0].Type is CollectionType");
TypeUsage argumentType = TypeHelpers.GetElementTypeUsage(functionType.Parameters[0].TypeUsage);
if (TypeSemantics.IsNullType(args[0].ResultType))
{
args[0] = sr.CmdTree.CreateNullExpression(argumentType);
}
//
// create function aggregate expression
//
DbFunctionAggregate functionAggregate;
// create distinct expression if espeficied
if (methodExpr.DistinctKind == DistinctKind.Distinct)
{
functionAggregate = sr.CmdTree.CreateDistinctFunctionAggregate(functionType, args[0]);
}
else
{
functionAggregate = sr.CmdTree.CreateFunctionAggregate(functionType, args[0]);
}
//
// generate name for the aggregate 'property'. this name will the internal name of the pre-computed expression in the scope and
// annotated ast node in the ast tree
//
string internalAggregateName = sr.GenerateInternalName("groupAgg" + functionType.Name);
//
// add aggreate to aggreate list
//
AggregateAstNodeInfo aggrAstInfo = sr.PopAggregateAstNode();
aggrAstInfo.AssertMethodExprEquivalent(methodExpr);
sr.AddGroupAggregateInfoToScopeRegion(methodExpr, internalAggregateName, functionAggregate, aggrAstInfo.ScopeIndex);
//
// return 'dummy' expression with same type as aggregate function
//
converted = sr.CmdTree.CreateNullExpression(functionType.ReturnParameter.TypeUsage);
//
// anotate method expression node as aggregate
//
methodExpr.SetAggregateInfo(internalAggregateName, converted);
//
// restore visibility to group scope only
//
sr.SetScopeView(saveScopeView);
//
// increment nesting count
//
sr.CurrentScopeRegionFlags.IncrementGroupAggregateNestingCount();
return true;
}
///
/// Converted a instance method.
///
///
///
///
///
///
private static DbExpression ConvertMethodInstance( DbExpression baseExpr, MethodExpr methodExpr, int prefixIndex, SemanticResolver sr )
{
DbExpression converted = null;
Debug.Assert(null != baseExpr,"null != baseExpr");
DotExpr dotExpr = methodExpr.MethodPrefixExpr;
//
// ensure methods are not called on Scalar type instances
//
if (TypeSemantics.IsPrimitiveType(baseExpr.ResultType))
{
throw EntityUtil.EntitySqlError(dotExpr.LeftMostExpression.ErrCtx, System.Data.Entity.Strings.MethodNotAllowedOnScalars);
}
//
// build the instance property references chain up to the method name
//
DbExpression innerExpression = baseExpr;
for (int i = prefixIndex ; i < dotExpr.Length - 1 ; i++)
{
innerExpression = sr.ResolveIdentifierElement(innerExpression.ResultType, innerExpression, dotExpr.Names[i], dotExpr.ErrCtx);
//
// if this point is reached, means that an element in the path name is not a valid property
// such as name[ i + 1 ] is not a valid property/member of defining type resolved previosly for name[ i ]
//
if (null == innerExpression)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidMethodPathElement(dotExpr.Names[i], innerExpression.ResultType.EdmType.FullName));
}
}
//
// convert method arguments
//
List args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// Resolve/Validate overloads and create method expression
//
converted = SemanticResolver.CreateInstanceMethod(innerExpression, args, methodExpr);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts EdmFunction Arguments representes a list of astExpr nodes
///
///
///
///
private static List ConvertFunctionArguments( ExprList astExprList, SemanticResolver sr )
{
List convertedArgs = new List();
if (null != astExprList)
{
for (int i = 0 ; i < astExprList.Count ; i++)
{
convertedArgs.Add(Convert(astExprList[i], sr));
}
}
return convertedArgs;
}
///
/// Convert Paramerters
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertParameter( Expr expr, SemanticResolver sr )
{
Parameter parameter = (Parameter)expr;
TypeUsage paramType = null;
KeyValuePair varInfo;
if (sr.Variables != null && sr.Variables.TryGetValue(parameter.Name, out varInfo))
{
return sr.CmdTree.CreateVariableReferenceExpression(varInfo.Key, varInfo.Value);
}
if (null == sr.Parameters || !sr.Parameters.TryGetValue(parameter.Name, out paramType))
{
throw EntityUtil.EntitySqlError(parameter.ErrCtx, System.Data.Entity.Strings.ParameterWasNotDefined(parameter.Name));
}
sr.CmdTree.AddParameter(parameter.Name, TypeHelpers.GetReadOnlyType(paramType));
return sr.CmdTree.CreateParameterReferenceExpression(parameter.Name);
}
///
/// Validate a relationship-traversal - used for both Navigate expressions
/// and for entity construction with related entity refs.
///
/// For "related entity refs", "isTargetEnd" is true - for Navigate expressions,
/// this parameter is "false".
///
///
/// the relationshipExpr AST
///
/// resolver context
/// the source/target expression
/// the relationship type
/// from end of the relationship
/// to-end of the relationship
private static void ValidateRelationshipTraversal(RelshipNavigationExpr relshipExpr,
bool isTargetEnd,
SemanticResolver sr,
out DbExpression refExpr,
out RelationshipType relationshipType,
out RelationshipEndMember refEnd,
out RelationshipEndMember otherEnd)
{
relationshipType = null;
refEnd = null;
otherEnd = null;
refExpr = null;
//
// resolve relationship type
//
relationshipType = sr.ResolveNameAsType(relshipExpr.RelationTypeNames, relshipExpr.RelationTypeNameIdentifier).EdmType as RelationshipType;
if (null == relationshipType)
{
throw EntityUtil.EntitySqlError(relshipExpr.RelationTypeNameIdentifier.ErrCtx, System.Data.Entity.Strings.InvalidRelationshipTypeName(relshipExpr.RelationTypeFullName));
}
//
// convert relationship 'instance' expression
//
refExpr = Convert(relshipExpr.RelationshipSource, sr);
//
// if is entity, create entity ref out if it
//
if (!isTargetEnd && TypeSemantics.IsEntityType(refExpr.ResultType))
{
refExpr = sr.CmdTree.CreateEntityRefExpression(refExpr);
}
//
// make sure is ref type
//
if (!TypeSemantics.IsReferenceType(refExpr.ResultType))
{
throw EntityUtil.EntitySqlError(relshipExpr.RelationshipSource.ErrCtx, System.Data.Entity.Strings.InvalidRelationshipSourceType);
}
//
// ensure entity 'participates' in the given relationship type
//
if (!TypeSemantics.IsTypeValidForRelationship(TypeHelpers.GetElementTypeUsage(refExpr.ResultType), relationshipType))
{
throw EntityUtil.EntitySqlError(relshipExpr.RelationTypeNameIdentifier.ErrCtx, System.Data.Entity.Strings.RelationshipTypeIsNotCompatibleWithEntity(
TypeHelpers.GetFullName(TypeHelpers.GetElementTypeUsage(refExpr.ResultType)),
TypeHelpers.GetFullName(relationshipType)));
}
//
// ensure relationship ends are valid
// metadata ensures that there will never happen to have two equal end names
//
TypeUsage fromEndType = null;
int fromEndMatchCount = 0;
TypeUsage elementType = TypeHelpers.GetElementTypeUsage(refExpr.ResultType);
for (int i = 0; i < relationshipType.Members.Count; i++)
{
//
// check 'to' end
//
if (relationshipType.Members[i].Name.Equals(relshipExpr.ToEndIdentifierName, StringComparison.OrdinalIgnoreCase))
{
otherEnd = (RelationshipEndMember)relationshipType.Members[i];
continue;
}
//
// check 'from' end
//
if (
(null != relshipExpr.FromEndIdentifier && relationshipType.Members[i].Name.Equals(relshipExpr.FromEndIdentifierName, StringComparison.OrdinalIgnoreCase)) ||
(null == relshipExpr.FromEndIdentifier && TypeSemantics.IsEquivalentOrPromotableTo(elementType, TypeHelpers.GetElementTypeUsage(relationshipType.Members[i].TypeUsage)))
)
{
fromEndMatchCount++;
if (fromEndMatchCount > 1)
{
ErrorContext errCtx = (null == relshipExpr.FromEndIdentifier) ? relshipExpr.ErrCtx : relshipExpr.FromEndIdentifier.ErrCtx;
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.RelationshipFromEndIsAmbiguos);
}
refEnd = (RelationshipEndMember)relationshipType.Members[i];
fromEndType = relationshipType.Members[i].TypeUsage;
}
}
//
// ensure relationship 'To' end contains given property
//
if (null == otherEnd)
{
if (null != relshipExpr.ToEndIdentifier)
{
throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, System.Data.Entity.Strings.InvalidRelationshipMember(relshipExpr.ToEndIdentifierName, relshipExpr.RelationTypeFullName));
}
if (2 != relationshipType.Members.Count)
{
throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, System.Data.Entity.Strings.InvalidImplicitRelationshipToEnd(relshipExpr.RelationTypeFullName));
}
Debug.Assert(null != refEnd, "null!=fromEnd");
otherEnd = (RelationshipEndMember)(refEnd.Name.Equals(relationshipType.Members[0].Name, StringComparison.OrdinalIgnoreCase) ? relationshipType.Members[1] : relationshipType.Members[0]);
}
//
// ensure relationship 'From' end contains given entity
//
if (null == refEnd || null == fromEndType)
{
ErrorContext errCtx = (null == relshipExpr.FromEndIdentifier) ? relshipExpr.ErrCtx : relshipExpr.FromEndIdentifier.ErrCtx;
if (null == relshipExpr.FromEndIdentifier)
{
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.InvalidImplicitRelationshipFromEnd(relshipExpr.RelationTypeFullName));
}
else
{
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.InvalidRelationshipMember(relshipExpr.FromEndIdentifierName, relshipExpr.RelationTypeFullName));
}
}
//
// check if source is promotable to from end type
//
if (!TypeSemantics.IsValidPolymorphicCast(TypeHelpers.GetElementTypeUsage(refExpr.ResultType),
TypeHelpers.GetElementTypeUsage(refEnd.TypeUsage)))
{
ErrorContext errCtx = (null == relshipExpr.FromEndIdentifier) ? relshipExpr.ErrCtx : relshipExpr.FromEndIdentifier.ErrCtx;
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.SourceTypeMustBePromotoableToFromEndRelationType(TypeHelpers.GetElementTypeUsage(refExpr.ResultType).EdmType.FullName, TypeHelpers.GetElementTypeUsage(fromEndType).EdmType.FullName));
}
return;
}
///
/// Build out a RelatedEntityRef
///
/// the ast expression
/// the Semantic Resolver context
/// a DbRelatedEntityRef instance
private static DbRelatedEntityRef ConvertRelatedEntityRef(RelshipNavigationExpr relshipExpr, SemanticResolver sr)
{
//
// Validate the relationship traversal
//
DbExpression targetRef;
RelationshipEndMember targetRefEnd;
RelationshipEndMember otherEnd;
RelationshipType relationshipType;
ValidateRelationshipTraversal(relshipExpr,
true /* targetEnd */ ,
sr,
out targetRef,
out relationshipType,
out targetRefEnd,
out otherEnd);
//
// ensure is *..{0|1}
//
if (RelationshipMultiplicity.One != targetRefEnd.RelationshipMultiplicity &&
RelationshipMultiplicity.ZeroOrOne != targetRefEnd.RelationshipMultiplicity)
{
throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx,
System.Data.Entity.Strings.InvalidWithRelationshipTargetEndMultiplicity(targetRefEnd.Identity,
targetRefEnd.RelationshipMultiplicity.ToString()));
}
DbRelatedEntityRef relatedEntityRef = sr.CmdTree.CreateRelatedEntityRef(otherEnd, targetRefEnd, targetRef);
return relatedEntityRef;
}
///
/// converts Relationship Navigation operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertRelshipNavigationExpr( Expr astExpr, SemanticResolver sr )
{
RelshipNavigationExpr relshipExpr = (RelshipNavigationExpr)astExpr;
//
// Validate the relationship traversal
//
DbExpression relationshipSource;
RelationshipEndMember fromEnd;
RelationshipEndMember toEnd;
RelationshipType relationshipType;
ValidateRelationshipTraversal(relshipExpr,
false /* !targetEnd */,
sr,
out relationshipSource,
out relationshipType,
out fromEnd,
out toEnd);
//
// create cqt expression
//
DbExpression converted = sr.CmdTree.CreateRelationshipNavigationExpression(fromEnd, toEnd, relationshipSource);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts REF operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertRefExpr( Expr astExpr, SemanticResolver sr )
{
RefExpr refExpr = (RefExpr)astExpr;
DbExpression converted = Convert(refExpr.RefArgExpr, sr);
//
// check if is entity type
//
if (!TypeSemantics.IsEntityType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(refExpr.RefArgExpr.ErrCtx, System.Data.Entity.Strings.RefArgIsNotOfEntityType(converted.ResultType.EdmType.FullName));
}
//
// create ref expression
//
converted = sr.CmdTree.CreateEntityRefExpression(converted);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts DEREF operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertDeRefExpr( Expr astExpr, SemanticResolver sr )
{
DerefExpr deRefExpr = (DerefExpr)astExpr;
DbExpression converted = null;
converted = Convert(deRefExpr.RefExpr, sr);
//
// check if return type is RefType
//
if (!TypeSemantics.IsReferenceType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(deRefExpr.RefExpr.ErrCtx, System.Data.Entity.Strings.DeRefArgIsNotOfRefType(converted.ResultType.EdmType.FullName));
}
//
// create DeRef expression
//
converted = sr.CmdTree.CreateDerefExpression(converted);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts CREATEREF operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertCreateRefExpr( Expr astExpr, SemanticResolver sr )
{
CreateRefExpr createRefExpr = (CreateRefExpr)astExpr;
DbExpression converted = null;
//
// Convert the entity set, also, ensure that we get back an extent expression
//
DbScanExpression entitySetExpr = Convert(createRefExpr.EntitySet, sr) as DbScanExpression;
if (entitySetExpr == null)
{
throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, System.Data.Entity.Strings.ExprIsNotValidEntitySetForCreateRef);
}
//
// Ensure that the extent is an entity set
//
EntitySet entitySet = entitySetExpr.Target as EntitySet;
if (entitySet == null)
{
throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, System.Data.Entity.Strings.ExprIsNotValidEntitySetForCreateRef);
}
DbExpression keyRowExpression = Convert(createRefExpr.Keys, sr);
SemanticResolver.EnsureIsNotUntypedNull(keyRowExpression, createRefExpr.Keys.ErrCtx);
RowType inputKeyRowType = keyRowExpression.ResultType.EdmType as RowType;
if (null == inputKeyRowType)
{
throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx,System.Data.Entity.Strings.InvalidCreateRefKeyType);
}
RowType entityKeyRowType = TypeHelpers.CreateKeyRowType(entitySet.ElementType, sr.CmdTree.MetadataWorkspace);
if (entityKeyRowType.Members.Count != inputKeyRowType.Members.Count)
{
throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, System.Data.Entity.Strings.ImcompatibleCreateRefKeyType);
}
if (!TypeSemantics.IsEquivalentOrPromotableTo(keyRowExpression.ResultType, TypeUsage.Create(entityKeyRowType)))
{
throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, System.Data.Entity.Strings.ImcompatibleCreateRefKeyElementType);
}
//
// if CREATEREF specifies a type, resolve and validate the type
//
if (null != createRefExpr.TypeIdentifier)
{
TypeUsage targetTypeUsage = ConvertTypeIdentifier(createRefExpr.TypeIdentifier, sr);
//
// ensure type is entity
//
if (!TypeSemantics.IsEntityType(targetTypeUsage))
{
throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx,
System.Data.Entity.Strings.CreateRefTypeIdentifierMustSpecifyAnEntityType(
targetTypeUsage.EdmType.Identity,
targetTypeUsage.EdmType.BuiltInTypeKind.ToString()));
}
if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, targetTypeUsage.EdmType))
{
throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx,
System.Data.Entity.Strings.CreateRefTypeIdentifierMustBeASubOrSuperType(
entitySet.ElementType.Identity,
targetTypeUsage.EdmType.FullName));
}
converted = sr.CmdTree.CreateRefExpression(entitySet,
keyRowExpression,
(EntityType)targetTypeUsage.EdmType);
}
else
{
//
// finally creates the expression
//
converted = sr.CmdTree.CreateRefExpression(entitySet, keyRowExpression);
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts KEY operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertKeyExpr( Expr astExpr, SemanticResolver sr )
{
KeyExpr keyExpr = (KeyExpr)astExpr;
DbExpression converted = Convert(keyExpr.RefExpr, sr);
SemanticResolver.EnsureIsNotUntypedNull(converted, keyExpr.RefExpr.ErrCtx);
if (TypeSemantics.IsEntityType(converted.ResultType))
{
converted = sr.CmdTree.CreateEntityRefExpression(converted);
}
else if (!TypeSemantics.IsReferenceType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(keyExpr.RefExpr.ErrCtx,System.Data.Entity.Strings.InvalidKeyArgument(TypeHelpers.GetFullName(converted.ResultType)));
}
converted = sr.CmdTree.CreateRefKeyExpression(converted);
Debug.Assert(null != converted, "null != converted");
return converted;
}
///
/// Dispatches/Converts BuiltIn Expressions
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertBuiltIn( Expr astExpr, SemanticResolver sr )
{
if (null == astExpr)
{
return null;
}
BuiltInExpr bltInExpr = (BuiltInExpr)astExpr;
BuiltInExprConverter builtInConverter = _builtInExprConverter[bltInExpr.Kind];
if (null == builtInConverter)
{
throw EntityUtil.Argument(System.Data.Entity.Strings.UnknownBuiltInAstExpressionType);
}
DbExpression converted = builtInConverter(bltInExpr, sr);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts Arithmetic Expressions Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertArithmeticArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (!(TypeSemantics.IsNumericType(leftExpr.ResultType) || SemanticResolver.IsNullExpression(leftExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionMustBeNumericType);
}
DbExpression rightExpr = null;
if (null != astBuiltInExpr.Arg2)
{
rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (!(TypeSemantics.IsNumericType(rightExpr.ResultType) || SemanticResolver.IsNullExpression(rightExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.ExpressionMustBeNumericType);
}
if (null == TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
}
}
return sr.EnsureTypedNulls(leftExpr, rightExpr, astBuiltInExpr.ErrCtx, () => Strings.InvalidNullArithmetic);
}
///
/// Converts Plus Args - specific case since string type is an allowed type for '+'
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertPlusOperands( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (!(TypeSemantics.IsNumericType(leftExpr.ResultType) ||
TypeSemantics.IsPrimitiveType(leftExpr.ResultType,PrimitiveTypeKind.String) ||
SemanticResolver.IsNullExpression(leftExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.PlusLeftExpressionInvalidType);
}
DbExpression rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (!(TypeSemantics.IsNumericType(rightExpr.ResultType) ||
TypeSemantics.IsPrimitiveType(rightExpr.ResultType, PrimitiveTypeKind.String) ||
SemanticResolver.IsNullExpression(rightExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.PlusRightExpressionInvalidType);
}
if (null == TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
}
return sr.EnsureTypedNulls(leftExpr, rightExpr, astBuiltInExpr.ErrCtx, () => Strings.InvalidNullArithmetic);
}
///
/// Converts Logical Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertLogicalArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (leftExpr is UntypedNullExpression)
{
leftExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType);
}
DbExpression rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (rightExpr is UntypedNullExpression)
{
rightExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType);
}
//
// ensure left expression type is boolean
//
if (!TypeResolver.IsBooleanType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
//
// ensure right expression type is boolean
//
if (null != rightExpr && !TypeResolver.IsBooleanType(rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
return new Pair(leftExpr, rightExpr);
}
///
/// Converts Equal Comparison Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertEqualCompArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
//
// convert left and right types and infer null types
//
Pair compArgs = sr.EnsureTypedNulls( Convert(astBuiltInExpr.Arg1, sr),
Convert(astBuiltInExpr.Arg2, sr),
astBuiltInExpr.ErrCtx,
() => Strings.InvalidNullComparison);
//
// ensure both operand types are equal-comparable
//
if (!TypeSemantics.IsEqualComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName));
}
return compArgs;
}
///
/// Converts Order Comparison Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertOrderCompArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
Pair compArgs = sr.EnsureTypedNulls(
Convert(astBuiltInExpr.Arg1, sr),
Convert(astBuiltInExpr.Arg2, sr),
astBuiltInExpr.ErrCtx,
() => Strings.InvalidNullComparison);
//
// ensure both operand types are order-comparable
//
if (!TypeSemantics.IsOrderComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName));
}
return compArgs;
}
///
/// Converts Set Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertSetArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
//
// convert left expression
//
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
//
// convert right expression if binary set op kind
//
DbExpression rightExpr = null;
if (null != astBuiltInExpr.Arg2)
{
//
// binary set op
//
//
// make sure left expression type is of sequence type (ICollection or Extent)
//
if (!TypeSemantics.IsCollectionType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.LeftSetExpressionArgsMustBeCollection);
}
//
// convert right expression
//
rightExpr = Convert(astBuiltInExpr.Arg2, sr);
//
// make sure right expression type is of sequence type (ICollection or Extent)
//
if (!TypeSemantics.IsCollectionType(rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.RightSetExpressionArgsMustBeCollection);
}
TypeUsage commonType;
TypeUsage leftElemType = TypeHelpers.GetElementTypeUsage(leftExpr.ResultType);
TypeUsage rightElemType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType);
if (!TypeSemantics.TryGetCommonType(leftElemType, rightElemType, out commonType))
{
CqlErrorHelper.ReportIncompatibleCommonType(astBuiltInExpr.ErrCtx, leftElemType, rightElemType);
}
if (astBuiltInExpr.Kind != BuiltInKind.UnionAll)
{
//
// ensure left argument is set op comparable
//
if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.PlaceholderSetArgTypeIsNotEqualComparable(
astBuiltInExpr.Kind.ToString().ToUpperInvariant(),
System.Data.Entity.Strings.LocalizedLeft,
TypeHelpers.GetElementTypeUsage(leftExpr.ResultType).EdmType.FullName));
}
//
// ensure right argument is set op comparable
//
if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(rightExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.PlaceholderSetArgTypeIsNotEqualComparable(
astBuiltInExpr.Kind.ToString().ToUpperInvariant(),
System.Data.Entity.Strings.LocalizedRight,
TypeHelpers.GetElementTypeUsage(rightExpr.ResultType).EdmType.FullName));
}
}
else
{
if (Helper.IsAssociationType(leftElemType.EdmType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidAssociationTypeForUnion(leftElemType.Identity));
}
if (Helper.IsAssociationType(rightElemType.EdmType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.InvalidAssociationTypeForUnion(rightElemType.Identity));
}
}
}
else
{
//
// unary set op
//
//
// make sure expression type is of sequence type (ICollection or Extent)
//
if (!TypeSemantics.IsCollectionType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidUnarySetOpArgument(astBuiltInExpr.Name));
}
//
// make sure that if is distinct unary operator, arg element type must be equal-comparable
//
if (astBuiltInExpr.Kind == BuiltInKind.Distinct && !TypeHelpers.IsValidDistinctOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeEqualComparable);
}
}
return new Pair(leftExpr, rightExpr);
}
///
/// Converts Set 'IN' expression args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertInExprArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (TypeSemantics.IsCollectionType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustNotBeCollection);
}
DbExpression rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (!TypeSemantics.IsCollectionType(rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.RightSetExpressionArgsMustBeCollection);
}
//
// if left expression type is null, infer its type from the collection element type
//
if (SemanticResolver.IsNullExpression(leftExpr))
{
TypeUsage elementType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType);
SemanticResolver.EnsureValidTypeForNullExpression(elementType, astBuiltInExpr.Arg1.ErrCtx);
leftExpr = sr.CmdTree.CreateNullExpression(elementType);
}
else
{
//
// ensure that if left and right are typed expressions then their types must be comparable for IN op
//
TypeUsage commonElemType = TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, TypeHelpers.GetElementTypeUsage(rightExpr.ResultType));
if (null == commonElemType || !TypeHelpers.IsValidInOpType(commonElemType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.InvalidInExprArgs(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
}
}
return new Pair(leftExpr, rightExpr);
}
///
/// Converts Type Expression Args
///
///
/// SemanticResolver instance relative to a specific typespace/system
///
private static Pair ConvertTypeExprArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
return new Pair(Convert(astBuiltInExpr.Arg1, sr),
ConvertTypeIdentifier(astBuiltInExpr.Arg2, sr));
}
///
/// Converts TypeIdentifier
/// TypeIdentifier can have the 'shape' of a simple identifier (product), a dotted identifier (namespace.bar) or a methodExpr ( edm.decimal(10,4) )
///
///
///
///
private static TypeUsage ConvertTypeIdentifier(Expr typeIdentifierExpr, SemanticResolver sr)
{
MethodExpr methodExpr = null;
string[] typeNames;
//
// if it is dot id get full id name
//
DotExpr dotExpr = typeIdentifierExpr as DotExpr;
if (null != dotExpr && dotExpr.IsDottedIdentifier)
{
typeNames = dotExpr.Names;
}
else
{
//
// method expression, type with parameters
//
methodExpr = typeIdentifierExpr as MethodExpr;
if (null != methodExpr)
{
typeNames = methodExpr.MethodPrefixExpr.Names;
Debug.Assert(methodExpr.Args.Count == 1 || methodExpr.Args.Count == 2, "decimal type must have one or two arguments");
Debug.Assert(methodExpr.Args[0] is Literal, "type expression must have literal arg");
Debug.Assert((methodExpr.Args.Count == 2) ? methodExpr.Args[1] is Literal : true, "type expression must have literal arg");
}
else
{
//
// if it is single id, get it is name
//
Identifier id = typeIdentifierExpr as Identifier;
if (null != id)
{
typeNames = new string[] { id.Name };
}
else
{
throw EntityUtil.EntitySqlError(typeIdentifierExpr.ErrCtx, System.Data.Entity.Strings.InvalidTypeNameExpression);
}
}
}
//
// Resolve type
//
TypeUsage typeUsage = sr.ResolveNameAsType(typeNames, typeIdentifierExpr);
return typeUsage;
}
///
/// Converts Row type constructor expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertRowConstructor( Expr expr, SemanticResolver sr )
{
RowConstructorExpr rowExpr = (RowConstructorExpr)expr;
Dictionary rowColumns = new Dictionary(sr.ScopeStringComparer);
List fieldExprs = new List(rowExpr.AliasExprList.Count);
for (int i = 0 ; i < rowExpr.AliasExprList.Count ; i++)
{
AliasExpr aliasExpr = rowExpr.AliasExprList[i];
DbExpression colExpr = Convert(aliasExpr.Expr, sr);
string aliasName = sr.InferAliasName(aliasExpr, colExpr);
if (rowColumns.ContainsKey(aliasName))
{
if (aliasExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InRowConstructor);
}
else
{
aliasName = sr.GenerateInternalName("autoRowCol");
}
}
if (SemanticResolver.IsNullExpression(colExpr))
{
throw EntityUtil.EntitySqlError(aliasExpr.Expr.ErrCtx, System.Data.Entity.Strings.RowCtorElementCannotBeNull);
}
rowColumns.Add(aliasName, colExpr.ResultType);
fieldExprs.Add(colExpr);
}
return sr.CmdTree.CreateNewInstanceExpression(TypeHelpers.CreateRowTypeUsage(rowColumns, true /* readOnly */), fieldExprs);
}
///
/// Converts Multiset type constructor expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertMultisetConstructor( Expr expr, SemanticResolver sr )
{
MultisetConstructorExpr msetCtor = (MultisetConstructorExpr)expr;
if (null == msetCtor.ExprList)
{
throw EntityUtil.EntitySqlError(expr.ErrCtx, System.Data.Entity.Strings.CannotCreateEmptyMultiset);
}
PairOfLists mSetExprs = ProcessExprList(msetCtor.ExprList, sr);
TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(mSetExprs.Left);
//
// ensure all elems have a common type
//
if (null == commonType)
{
throw EntityUtil.EntitySqlError(expr.ErrCtx, System.Data.Entity.Strings.MultisetElemsAreNotTypeCompatible);
}
//
// ensure common type is not an untyped null
//
if (TypeSemantics.IsNullType(commonType))
{
throw EntityUtil.EntitySqlError(expr.ErrCtx, System.Data.Entity.Strings.CannotCreateMultisetofNulls);
}
commonType = TypeHelpers.GetReadOnlyType(commonType);
//
// fixup untyped nulls
//
for (int i = 0 ; i < mSetExprs.Count ; i++)
{
if (SemanticResolver.IsNullExpression(mSetExprs.Right[i]))
{
SemanticResolver.EnsureValidTypeForNullExpression(commonType, msetCtor.ExprList[i].ErrCtx);
mSetExprs.Right[i] = sr.CmdTree.CreateNullExpression(commonType);
}
}
return sr.CmdTree.CreateNewInstanceExpression(TypeHelpers.CreateCollectionTypeUsage(commonType, true /* readOnly */), mSetExprs.Right);
}
///
/// converts case-when-then expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertCaseExpr( Expr expr, SemanticResolver sr )
{
CaseExpr caseExpr = (CaseExpr)expr;
List whenExprList = new List(caseExpr.WhenThenExprList.Count);
PairOfLists thenExprList = new PairOfLists();
//
// Resolve then expressions
//
for (int i = 0 ; i < caseExpr.WhenThenExprList.Count ; i++)
{
WhenThenExpr whenThenExpr = caseExpr.WhenThenExprList[i];
DbExpression whenExpression = Convert(whenThenExpr.WhenExpr, sr);
if (!TypeResolver.IsBooleanType(whenExpression.ResultType))
{
throw EntityUtil.EntitySqlError(whenThenExpr.WhenExpr.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
whenExprList.Add(whenExpression);
DbExpression thenExpression = Convert(whenThenExpr.ThenExpr, sr);
thenExprList.Add(thenExpression, thenExpression.ResultType);
}
TypeUsage resultType = TypeHelpers.GetCommonTypeUsage(thenExprList.Right);
if (null == resultType)
{
throw EntityUtil.EntitySqlError(caseExpr.WhenThenExprList.Expressions[0].ThenExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseThenTypes);
}
if ((null == caseExpr.ElseExpr) && TypeSemantics.IsNullType(resultType))
{
throw EntityUtil.EntitySqlError(caseExpr.WhenThenExprList.Expressions[0].ThenExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseThenNullType);
}
//
// Converts else if present
//
DbExpression elseExpr = null;
if (null != caseExpr.ElseExpr)
{
elseExpr = Convert(caseExpr.ElseExpr, sr);
resultType = TypeHelpers.GetCommonTypeUsage(resultType, elseExpr.ResultType);
if (null == resultType)
{
throw EntityUtil.EntitySqlError(caseExpr.ElseExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseElseType);
}
if (TypeSemantics.IsNullType(resultType))
{
throw EntityUtil.EntitySqlError(caseExpr.ElseExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseWhenThenNullType);
}
if (SemanticResolver.IsNullExpression(elseExpr))
{
SemanticResolver.EnsureValidTypeForNullExpression(resultType, caseExpr.ElseExpr.ErrCtx);
elseExpr = sr.CmdTree.CreateNullExpression(resultType);
}
}
else
{
if (TypeSemantics.IsCollectionType(resultType))
{
elseExpr = sr.CmdTree.CreateNewEmptyCollectionExpression(resultType);
}
else
{
SemanticResolver.EnsureValidTypeForNullExpression(resultType, caseExpr.ErrCtx);
elseExpr = sr.CmdTree.CreateNullExpression(resultType);
}
}
//
// fixup untyped nulls
//
for (int i = 0 ; i < thenExprList.Count ; i++)
{
if (SemanticResolver.IsNullExpression(thenExprList.Left[i]))
{
SemanticResolver.EnsureValidTypeForNullExpression(resultType, caseExpr.WhenThenExprList[i].ThenExpr.ErrCtx);
thenExprList[i] = new Pair(sr.CmdTree.CreateNullExpression(resultType), resultType);
}
}
return sr.CmdTree.CreateCaseExpression(whenExprList, thenExprList.Left, elseExpr);
}
///
/// Converts Query Expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertQuery( Expr expr, SemanticResolver sr )
{
QueryExpr queryExpr = (QueryExpr)expr;
DbExpression converted = null;
bool isRestrictedViewGenerationMode = (ParserOptions.CompilationMode.RestrictedViewGenerationMode == sr.ParserOptions.ParserCompilationMode);
//
// Validate & Compensate Query
//
ValidateAndCompensateQuery(queryExpr);
//
// Create Source Scope Region
//
using (sr.EnterScopeRegion())
{
//
// Process From Clause
//
DbExpressionBinding sourceExpr = ProcessFromClause(queryExpr.FromClause, sr);
//
// Process Where Clause
//
sourceExpr = ProcessWhereClause(sourceExpr, queryExpr.WhereClause, sr);
Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.GroupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode");
Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.HavingClause : true, "HAVING clause must be null in RestrictedViewGenerationMode");
Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode");
if ( !isRestrictedViewGenerationMode )
{
//
// Process GroupBy Clause
//
sourceExpr = ProcessGroupByClause(sourceExpr, queryExpr, sr);
//
// Process Having Clause
//
sourceExpr = ProcessHavingClause(sourceExpr, queryExpr.HavingClause, sr);
//
// Process OrderBy Clause
//
sourceExpr = ProcessOrderByClause(sourceExpr, queryExpr, sr);
}
//
// Process Projection Clause
//
converted = ProcessSelectClause(sourceExpr, queryExpr, sr);
} // end query scope region
return converted;
}
///
/// Validates and Compensates query expression
///
///
private static void ValidateAndCompensateQuery( QueryExpr queryExpr )
{
if (null != queryExpr.HavingClause && null == queryExpr.GroupByClause)
{
throw EntityUtil.EntitySqlError(queryExpr.ErrCtx, System.Data.Entity.Strings.HavingRequiresGroupClause);
}
if (queryExpr.SelectClause.HasTopClause)
{
if ((null != queryExpr.OrderByClause) && queryExpr.OrderByClause.HasLimitSubClause)
{
throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, System.Data.Entity.Strings.TopAndLimitCannotCoexist);
}
if ((null != queryExpr.OrderByClause) && queryExpr.OrderByClause.HasSkipSubClause)
{
throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, System.Data.Entity.Strings.TopAndSkipCannotCoexist);
}
}
}
///
/// Process Select Clause
///
///
///
/// SemanticResolver instance relative to a especific typespace/system
///
private static DbExpression ProcessSelectClause( DbExpressionBinding source, QueryExpr queryExpr, SemanticResolver sr )
{
DbExpression projectExpression = null;
SelectClause selectClause = queryExpr.SelectClause;
HashSet projectionAliases = new HashSet(sr.ScopeStringComparer);
List> projFields = new List>(selectClause.Items.Count);
//
// if source is sort/skip expression, then skip projection conversion since it was already
// performed
//
if (queryExpr.OrderByClause != null && selectClause.DistinctKind == DistinctKind.Distinct)
{
projectExpression = source.Expression;
}
else
{
//
// Converts projection list
//
#region Process projection list
for (int i = 0; i < selectClause.Items.Count; i++)
{
AliasExpr selectExprItem = selectClause.Items[i];
DbExpression converted = Convert(selectExprItem.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, selectExprItem.Expr.ErrCtx);
//
// infer projection expression alias
//
string aliasName = sr.InferAliasName(selectExprItem, converted);
//
// ensure the alias was not used already
//
if (projectionAliases.Contains(aliasName))
{
if (selectExprItem.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, selectExprItem.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InSelectProjectionList);
}
else
{
aliasName = sr.GenerateInternalName("autoProject");
}
}
projectionAliases.Add(aliasName);
projFields.Add(new KeyValuePair(aliasName, converted));
}
#endregion
//
// VALUE projection
//
#region handles VALUE modifier
if (selectClause.SelectKind == SelectKind.SelectValue)
{
if (projFields.Count > 1)
{
throw EntityUtil.EntitySqlError(selectClause.ErrCtx, System.Data.Entity.Strings.InvalidSelectItem);
}
projectExpression = sr.CmdTree.CreateProjectExpression(source, projFields[0].Value);
}
else
{
projectExpression = sr.CmdTree.CreateProjectExpression(source, sr.CmdTree.CreateNewRowExpression(projFields));
}
#endregion
//
// handle DISTINCT modifier
//
#region DISTINCT
if (selectClause.DistinctKind == DistinctKind.Distinct)
{
//
// ensure element type is equal-comparable
//
SemanticResolver.ValidateDistinctProjection(selectClause, projectExpression.ResultType);
//
// create distinct expression
//
projectExpression = sr.CmdTree.CreateDistinctExpression(projectExpression);
}
#endregion
}
//
// TOP sub-clause
// NOTE: WITH TIES is not supported in M3.2
//
#region TOP/LIMIT sub-clause
if (selectClause.HasTopClause || ((null != queryExpr.OrderByClause) && queryExpr.OrderByClause.HasLimitSubClause))
{
ErrorContext errCtx = (selectClause.HasTopClause) ? selectClause.TopExpr.ErrCtx : queryExpr.OrderByClause.LimitSubClause.ErrCtx;
//
// convert top argument
//
DbExpression limitExpr = Convert((selectClause.HasTopClause) ? selectClause.TopExpr : queryExpr.OrderByClause.LimitSubClause, sr);
//
// ensure is not NULL expr
//
SemanticResolver.EnsureIsNotUntypedNull(limitExpr, errCtx);
Debug.Assert(limitExpr is DbConstantExpression || limitExpr is DbParameterReferenceExpression, "TOP/LIMIT inner expression must be a parameter or numeric literal in this release");
//
// ensure proper pre-conditions hold for TOP expression
//
sr.EnsureValidLimitExpression(
errCtx,
limitExpr,
(selectClause.HasTopClause) ? "TOP" : "LIMIT");
// create expression - WITH TIES is not supported in M3.2
projectExpression = sr.CmdTree.CreateLimitExpression(projectExpression, limitExpr);
}
#endregion
Debug.Assert(null != projectExpression,"null != projectExpr");
return projectExpression;
}
///
/// Process From Clause
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessFromClause( FromClause fromClause, SemanticResolver sr )
{
DbExpressionBinding fromBinding = null;
DbExpressionBinding fromBindingAux = null;
//
// Process Each From Clause Item
//
for (int i = 0 ; i < fromClause.FromClauseItems.Count ; i++ )
{
//
// Set Scope Source Var Kind
//
sr.SetCurrentScopeVarKind(fromClause.FromClauseItems[i].FromClauseItemKind);
//
// Convert From Clause
//
fromBindingAux = ProcessFromClauseItem(fromClause.FromClauseItems[i], sr);
//
// Reset All Vars to SourceVar Kind
//
sr.ResetCurrentScopeVarKind();
if (null == fromBinding)
{
fromBinding = fromBindingAux;
}
else
{
fromBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateCrossApplyExpression(fromBinding, fromBindingAux),
sr.GenerateInternalName("lcapply"));
sr.FixupNamedSourceVarBindings(fromBinding.Variable);
}
}
Debug.Assert(null != fromBinding,"null != fromBinding");
return fromBinding;
}
///
/// Process Generic From Clause Item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessFromClauseItem( FromClauseItem fromClauseItem, SemanticResolver sr )
{
DbExpressionBinding fromItemBinding = null;
AliasExpr aliasExpr = fromClauseItem.FromExpr as AliasExpr;
if (null != aliasExpr)
{
fromItemBinding = ProcessAliasedFromClauseItem(aliasExpr, sr);
}
else
{
JoinClauseItem joinClauseItem = fromClauseItem.FromExpr as JoinClauseItem;
if (null != joinClauseItem)
{
fromItemBinding = ProcessJoinClauseItem(joinClauseItem, sr);
}
else
{
fromItemBinding = ProcessApplyClauseItem((ApplyClauseItem)fromClauseItem.FromExpr, sr);
}
}
Debug.Assert(null != fromItemBinding,"null != fromItemBinding");
return fromItemBinding;
}
///
/// Process 'Simple' From Clause Item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessAliasedFromClauseItem( AliasExpr aliasedExpr, SemanticResolver sr )
{
DbExpressionBinding aliasedBinding = null;
//
// converts from item expression
//
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// validate it is of sequence type (Extent || ICollection)
//
if (!TypeSemantics.IsCollectionType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, System.Data.Entity.Strings.ExpressionMustBeCollection);
}
//
// infer source var alias name
//
string aliasName = sr.InferAliasName(aliasedExpr, converted);
//
// validate the name was not used yet.
//
if (sr.IsInCurrentScope(aliasName))
{
if (aliasedExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasedExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InFromClause);
}
else
{
aliasName = sr.GenerateInternalName("autoFrom");
}
}
//
// create cqt expression
//
aliasedBinding = sr.CmdTree.CreateExpressionBinding(converted, aliasName);
//
// add source var to scope
//
sr.AddSourceBinding(aliasedBinding);
Debug.Assert(null != aliasedBinding,"null != aliasedBinding");
return aliasedBinding;
}
///
/// process join clause item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessJoinClauseItem( JoinClauseItem joinClause, SemanticResolver sr )
{
DbExpressionBinding joinBinding = null;
//
// make sure inner join have on predicate AND cross join has no ON predicate
//
if (null == joinClause.OnExpr)
{
if (JoinKind.Inner == joinClause.JoinKind)
{
throw EntityUtil.EntitySqlError(joinClause.ErrCtx, System.Data.Entity.Strings.InnerJoinMustHaveOnPredicate);
}
}
else
{
if (JoinKind.Cross == joinClause.JoinKind)
{
throw EntityUtil.EntitySqlError(joinClause.OnExpr.ErrCtx, System.Data.Entity.Strings.InvalidPredicateForCrossJoin);
}
}
//
// Resolve Left Expression
//
sr.CurrentScopeRegionFlags.PathTagger.VisitLeftNode();
DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(joinClause.LeftExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// Resolve Right Expression
//
sr.CurrentScopeRegionFlags.IsInsideJoinOnPredicate = false;
sr.CurrentScopeRegionFlags.PathTagger.VisitRightNode();
DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(joinClause.RightExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// convert right outer to left outer
//
if (joinClause.JoinKind == JoinKind.RightOuter)
{
joinClause.JoinKind = JoinKind.LeftOuter;
DbExpressionBinding tmpExpr = leftBindingExpr;
leftBindingExpr = rightBindingExpr;
rightBindingExpr = tmpExpr;
}
//
// Resolve JoinType
//
DbExpressionKind joinKind = SemanticResolver.MapJoinKind(joinClause.JoinKind);
//
// Resolve ON
//
sr.CurrentScopeRegionFlags.IsInsideJoinOnPredicate = true;
DbExpression onExpr = null;
if (null == joinClause.OnExpr)
{
if (DbExpressionKind.CrossJoin != joinKind)
{
onExpr = sr.CmdTree.CreateTrueExpression();
}
}
else
{
onExpr = Convert(joinClause.OnExpr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(onExpr, joinClause.OnExpr.ErrCtx);
}
sr.CurrentScopeRegionFlags.IsInsideJoinOnPredicate = false;
//
// Create New Join
//
joinBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateJoinExpressionByKind(joinKind, onExpr, leftBindingExpr, rightBindingExpr),
sr.GenerateInternalName("join"));
//
// Fixup Join Source Vars in Scope
//
sr.FixupNamedSourceVarBindings(joinBinding.Variable);
Debug.Assert(null != joinBinding,"null != joinBinding");
return joinBinding;
}
///
/// Process apply clause item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessApplyClauseItem( ApplyClauseItem applyClause, SemanticResolver sr )
{
DbExpressionBinding applyBinding = null;
//
// Resolve Left Expression
//
sr.CurrentScopeRegionFlags.PathTagger.VisitLeftNode();
DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(applyClause.LeftExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// Resolve Right Expression
//
sr.CurrentScopeRegionFlags.PathTagger.VisitRightNode();
DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(applyClause.RightExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// Create Apply
//
applyBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateApplyExpressionByKind(SemanticResolver.MapApplyKind(applyClause.ApplyKind),
leftBindingExpr,
rightBindingExpr),
sr.GenerateInternalName("apply"));
//
// Fixup Apply Source Vars in Scope
//
sr.FixupNamedSourceVarBindings(applyBinding.Variable);
Debug.Assert(null != applyBinding,"null != applyBinding");
return applyBinding;
}
///
/// Process Where Expression
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessWhereClause( DbExpressionBinding source, Expr whereClause, SemanticResolver sr )
{
if (null == whereClause)
{
return source;
}
DbExpressionBinding whereBinding = null;
//
// Convert Where Condition
//
DbExpression filterConditionExpr = Convert(whereClause, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(filterConditionExpr, whereClause.ErrCtx);
//
// ensure the predicate type is boolean
//
if (!TypeResolver.IsBooleanType(filterConditionExpr.ResultType))
{
throw EntityUtil.EntitySqlError(whereClause.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
//
// Create New Filter Binding
//
whereBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateFilterExpression(source, filterConditionExpr),
sr.GenerateInternalName("where"));
//
// Fixup Bindings
//
sr.FixupSourceVarBindings(whereBinding.Variable);
Debug.Assert(null != whereBinding,"null != whereBinding");
return whereBinding;
}
///
/// Process Group By Clause
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessGroupByClause( DbExpressionBinding source, QueryExpr queryExpr, SemanticResolver sr )
{
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
GroupByClause groupByClause = queryExpr.GroupByClause;
Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == groupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode");
//
// if group expression is null, create a dummy and speculate that there are group aggregates in the remaining query expression
// if no group aggregate if found after partial evaluation of Having, OrderBy and Select in the 1st pass, rollback what we did
// and return source expression.
//
#region Define Implicit Group if needed
if (null == queryExpr.GroupByClause)
{
if (!queryExpr.HasMethodCall)
{
return source;
}
//
// if group expression is null create a dummy and speculate that there are group aggregates in the remaining query expression
// if no group aggregate if found after partial evaluation of Having, OrderBy and Select in the 1st pass, rollback what we did
// and return source expression.
//
sr.CurrentScopeRegionFlags.IsImplicitGroup = true;
}
else
{
sr.CurrentScopeRegionFlags.IsImplicitGroup = false;
}
#endregion
DbExpressionBinding groupBinding = null;
//
// Create Group Binding
//
DbGroupExpressionBinding groupExprBinding = sr.CmdTree.CreateGroupExpressionBinding(
source.Expression,
sr.GenerateInternalName("geb"),
sr.GenerateInternalName("group"));
//
// Update source scope vars
//
sr.FixupGroupSourceVarBindings(groupExprBinding.Variable, groupExprBinding.GroupVariable);
//
// convert group elements
//
#region Convert Group Key/Expressions
int groupKeysCount = (null != groupByClause ) ? groupByClause.GroupItems.Count : 0;
List> groupKeys = new List>(groupKeysCount);
HashSet groupKeyNames = new HashSet(sr.ScopeStringComparer);
List groupKeysForAggregates = new List(8);
if (!sr.CurrentScopeRegionFlags.IsImplicitGroup)
{
Debug.Assert(null != groupByClause, "groupByClause must not be null at this point");
for (int i = 0 ; i < groupKeysCount ; i++)
{
AliasExpr aliasedExpr = groupByClause.GroupItems[i];
sr.ResetScopeRegionCorrelationFlag();
//
// convert key expression (relative to DbGroupExpressionBinding.Var)
//
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// ensure group key expression is correlated
//
if (!sr.CurrentScopeRegionFlags.WasResolutionCorrelated)
{
throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, System.Data.Entity.Strings.KeyMustBeCorrelated("GROUP BY"));
}
//
// convert key expression (relative to DbGroupExpressionBinding.GroupVar)
// this is only needed because during the search for groupaggregates, group aggregates may
// refer to group keys.
//
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = true;
DbExpression groupKeyAggExpr = Convert(aliasedExpr.Expr, sr);
groupKeysForAggregates.Add(groupKeyAggExpr);
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = false;
//
// ensure keys are valid
//
if (!TypeHelpers.IsValidGroupKeyType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, System.Data.Entity.Strings.GroupingKeysMustBeEqualComparable);
}
//
// infer alias name
//
string groupKeyAlias = sr.InferAliasName(aliasedExpr, converted);
//
// check if alias was already used
//
if (groupKeyNames.Contains(groupKeyAlias))
{
if (aliasedExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(groupKeyAlias, aliasedExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InGroupClause);
}
else
{
groupKeyAlias = sr.GenerateInternalName("autoGroup");
}
}
//
// add key name to alias dictionary
//
groupKeyNames.Add(groupKeyAlias);
//
// add key to keys collection
//
groupKeys.Add(new KeyValuePair(groupKeyAlias, converted));
//
// group keys should visible by their 'original' key expression name. All forms should be allowed:
// SELECT k FROM ... as p GROUP BY p.Price as k (explicit key alias) - handled above by InferAliasName()
// SELECT Price FROM ... as p GROUP BY p.Price (implicit alias - leading name) - handled above by InferAliasName()
// SELECT p.Price FROM ... as p GROUP BY p.Price (original key expression) - case handled in the code bellow
//
if (!aliasedExpr.HasAlias)
{
DotExpr dotExpr = aliasedExpr.Expr as DotExpr;
if (null != dotExpr && dotExpr.IsDottedIdentifier)
{
groupKeyAlias = dotExpr.FullName;
if (groupKeyNames.Contains(groupKeyAlias))
{
CqlErrorHelper.ReportAliasAlreadyUsedError(groupKeyAlias, dotExpr.ErrCtx, System.Data.Entity.Strings.InGroupClause);
}
groupKeyNames.Add(groupKeyAlias);
groupKeys.Add(new KeyValuePair(groupKeyAlias, converted));
groupKeysForAggregates.Add(groupKeyAggExpr);
}
}
}
}
#endregion
//
// save scope status
//
SavePoint savePoint = sr.CreateSavePoint();
//
// Push Group scope
//
sr.EnterScope();
//
// Add converted group variables/expressions to group scope
//
#region Add Converted Group Variables to Scope
//
// add 'dummy' keys to scope.
// this is needed since during the aggreagate search phase, keys may be referenced.
//
for (int i = 0 ; i < groupKeys.Count ; i++)
{
sr.AddDummyGroupKeyToScope(groupKeys[i].Key, groupKeys[i].Value, groupKeysForAggregates[i]);
}
//
// flags that we are inside a group scope
//
sr.CurrentScopeRegionFlags.IsInGroupScope = true;
#endregion
//
// Convert/Search Aggregates
// since aggregates can be defined in Having, OrderBy and/or Select clauses must be resolved as part of the group expression.
// The resolution of these clauses result in potential collection of resolved group aggregates and the actual resulting
// expression is ignored. These clauses will be then resolved as usual on a second pass.
//
#region Search for group Aggregates
//
// search for aggregates in HAVING clause
//
if (null != queryExpr.HavingClause && queryExpr.HavingClause.HasMethodCall)
{
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
DbExpression converted = Convert(queryExpr.HavingClause.HavingPredicate, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, queryExpr.HavingClause.ErrCtx);
}
//
// search for aggregates in SELECT clause
//
Dictionary sortExpr = null;
if ( null != queryExpr.OrderByClause || queryExpr.SelectClause.HasMethodCall )
{
sortExpr = new Dictionary(queryExpr.SelectClause.Items.Count, sr.ScopeStringComparer);
for ( int i = 0 ; i < queryExpr.SelectClause.Items.Count ; i++ )
{
AliasExpr aliasedExpr = queryExpr.SelectClause.Items[i];
//
// Reset Group aggregate nesting count
//
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
//
// convert projection item expression
//
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// create Null Expression with actual type
//
converted = sr.CmdTree.CreateNullExpression(converted.ResultType);
//
// infer alias
//
string aliasName = sr.InferAliasName(aliasedExpr, converted);
if ( sortExpr.ContainsKey(aliasName) )
{
if ( aliasedExpr.HasAlias )
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName,
aliasedExpr.AliasIdentifier.ErrCtx,
System.Data.Entity.Strings.InSelectProjectionList);
}
else
{
aliasName = sr.GenerateInternalName("autoProject");
}
}
sortExpr.Add(aliasName, converted);
}
}
//
// search for aggregates in ORDER BY clause
//
if (null != queryExpr.OrderByClause && queryExpr.OrderByClause.HasMethodCall)
{
//
// push projection key scope
//
sr.EnterScope();
//
// Add projection items to scope (it may be used in ORDER BY)
//
foreach ( KeyValuePair kvp in sortExpr )
{
sr.AddToScope(kvp.Key, new ProjectionScopeEntry(kvp.Key, kvp.Value));
}
//
// search for aggregates in ORDER BY clause
//
for (int i = 0 ; i < queryExpr.OrderByClause.OrderByClauseItem.Count ; i++)
{
OrderByClauseItem orderItem = queryExpr.OrderByClause.OrderByClauseItem[i];
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
sr.ResetScopeRegionCorrelationFlag();
DbExpression converted = Convert(orderItem.OrderExpr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, orderItem.OrderExpr.ErrCtx);
//
// ensure key expression is correlated
//
if (!sr.CurrentScopeRegionFlags.WasResolutionCorrelated)
{
throw EntityUtil.EntitySqlError(orderItem.ErrCtx, System.Data.Entity.Strings.KeyMustBeCorrelated("ORDER BY"));
}
}
//
// pop projection scope
//
sr.LeaveScope();
}
#endregion
//
// if we introduced a fake group but did not 'found' any group aggregate
// on the first pass, then there is no need for creating an implicit group.
// rollback to the status before entering ProcessGroupByClause().
// if we did find group aggregates, make sure all non-group aggregate function
// expressions refer to group scope variables only
//
#region Implicit Group Rollback
if (sr.CurrentScopeRegionFlags.IsImplicitGroup)
{
if (0 == sr.CurrentScopeRegionFlags.GroupAggregatesInfo.Count)
{
//
// rolls back scope status
//
sr.RollbackToSavepoint(savePoint);
//
// undo any group source fixups, re-applying the source var
//
sr.UndoFixupGroupSourceVarBindings(source.Variable);
//
// reset is inside group scope flag
//
sr.CurrentScopeRegionFlags.IsInGroupScope = false;
////
//// reset implict group flag
////
sr.CurrentScopeRegionFlags.IsImplicitGroup = false;
//
// restore scope view kind
//
sr.SetScopeView(saveScopeView);
//
// return the original source var binding
//
return source;
}
//
// now that we know that there are group aggregates in other expression, reset implict group flag to false
// since it is now considered a legitimate group
//
sr.CurrentScopeRegionFlags.IsImplicitGroup = false;
}
#endregion
//
// extract list of aggregates and names
//
List> aggregates = new List>(sr.CurrentScopeRegionFlags.GroupAggregatesInfo.Count);
foreach(KeyValuePair kvp in sr.CurrentScopeRegionFlags.GroupAggregatesInfo)
{
aggregates.Add(new KeyValuePair(kvp.Value.AggregateName, kvp.Value.AggregateExpression));
kvp.Key.ResetDummyExpression();
}
//
// Create Group Expression
//
groupBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateGroupByExpression(
groupExprBinding,
groupKeys,
aggregates),
sr.GenerateInternalName("group"));
//
// replace dummy keys with real keys
//
for (int i = 0 ; i < groupKeys.Count ; i++)
{
sr.ReplaceGroupVarInScope(groupKeys[i].Key, groupBinding.Variable);
}
//
// add aggregates to scope
//
for (int i = 0 ; i < aggregates.Count ; i++ )
{
sr.CurrentScopeRegionFlags.AddGroupAggregateToScopeFlags(aggregates[i].Key);
sr.AddGroupAggregateToScope(aggregates[i].Key, groupBinding.Variable);
}
//
// restrict group scope visibility
//
sr.SetScopeView(SemanticResolver.ScopeViewKind.GroupScope);
//
// fixup all source vars
//
sr.FixupNamedSourceVarBindings(groupBinding.Variable);
//
// Mark source vars as group input vars
//
sr.MarkGroupInputVars();
Debug.Assert(null != groupBinding,"null != groupBinding");
return groupBinding;
}
///
/// Process Having Clause
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessHavingClause( DbExpressionBinding source, HavingClause havingClause, SemanticResolver sr )
{
Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == havingClause : true, "HAVING clause must be null in RestrictedViewGenerationMode");
if (null == havingClause)
{
return source;
}
DbExpressionBinding havingBinding = null;
//
// Convert Having Expression
//
DbExpression filterConditionExpr = Convert(havingClause.HavingPredicate, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(filterConditionExpr, havingClause.ErrCtx);
//
// ensure having predicate of boolean type
//
if (!TypeResolver.IsBooleanType(filterConditionExpr.ResultType))
{
throw EntityUtil.EntitySqlError(havingClause.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
//
// Create New Filter Binding
//
havingBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateFilterExpression(source, filterConditionExpr),
sr.GenerateInternalName("having"));
//
// Fixup Bindings
//
sr.FixupSourceVarBindings(havingBinding.Variable);
Debug.Assert(null != havingBinding,"null != havingBinding");
return havingBinding;
}
///
/// Process Order By Clause
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessOrderByClause( DbExpressionBinding source, QueryExpr queryExpr, SemanticResolver sr )
{
Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode");
if (null == queryExpr.OrderByClause)
{
return source;
}
DbExpressionBinding sortBinding = null;
OrderByClause orderByClause = queryExpr.OrderByClause;
SelectClause selectClause = queryExpr.SelectClause;
//
// Create a savepoint
//
SavePoint savePoint = sr.CreateSavePoint();
//
// perform partial conversion of SELECT statements
//
Dictionary projectionExpressions = new Dictionary(selectClause.Items.Count, sr.ScopeStringComparer);
for (int i = 0 ; i < selectClause.Items.Count ; i++)
{
AliasExpr aliasedExpr = selectClause.Items[i];
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// infer projection alias
//
string aliasName = sr.InferAliasName(aliasedExpr, converted);
if (projectionExpressions.ContainsKey(aliasName))
{
if (aliasedExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasedExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InSelectProjectionList);
}
else
{
aliasName = sr.GenerateInternalName("autoProject");
}
}
projectionExpressions.Add(aliasName, converted);
}
//
// convert paging sub-clauses they if exists before adding projection list to scope
// NOTE: TOP, LIMIT and SKIP have nearly the same constraints in M3.2. in the future this is likely to change by allowing
// these sub-clauses to have generic expressions.
//
#region Handles SKIP sub-clause
DbExpression skipExpr = null;
if (orderByClause.HasSkipSubClause)
{
skipExpr = Convert(orderByClause.SkipSubClause, sr);
//
// ensure is not NULL expr
//
SemanticResolver.EnsureIsNotUntypedNull(skipExpr, orderByClause.SkipSubClause.ErrCtx);
DbConstantExpression constantExpr = skipExpr as DbConstantExpression;
Debug.Assert(constantExpr!=null || skipExpr is DbParameterReferenceExpression, "SKIP inner expression must be a parameter or numeric literal in this release");
//
// ensure SKIP expression have the right type
//
if (!TypeSemantics.IsPromotableTo(skipExpr.ResultType, sr.TypeResolver.Int64Type))
{
throw EntityUtil.EntitySqlError(orderByClause.SkipSubClause.ErrCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeCompatibleWithEdm64("SKIP", skipExpr.ResultType.EdmType.FullName));
}
//
// if it is a literal, make sure it has the correct value
//
if (null != constantExpr && System.Convert.ToInt64(constantExpr.Value, CultureInfo.InvariantCulture) < 0)
{
throw EntityUtil.EntitySqlError(orderByClause.SkipSubClause.ErrCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeGreaterThanOrEqualToZero("SKIP"));
}
}
#endregion
//
// Push scope for projection items
//
sr.EnterScope();
//
// Add projection items to scope
//
foreach (KeyValuePair kvp in projectionExpressions)
{
//
// if the reference expression is a group aggregate, then there is no need to add to scope
//
if (!sr.CurrentScopeRegionFlags.ContainsGroupAggregate(kvp.Key))
{
sr.AddToScope(kvp.Key, new ProjectionScopeEntry(kvp.Key, kvp.Value));
}
}
//
// save scope view
//
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
//
// If DISTICT was especified set visibility to current scope only
// if DISTINCT modifier is present, push the projection and distinct expression down bellow
// sort/skip expressions
//
if (selectClause.DistinctKind == DistinctKind.Distinct)
{
sr.SetScopeView(SemanticResolver.ScopeViewKind.CurrentScope);
List> projectionExpressionList = new List>(projectionExpressions);
//
// Create projection
//
DbExpression projectExpression;
if (selectClause.SelectKind == SelectKind.SelectRow)
{
projectExpression = sr.CmdTree.CreateNewRowExpression(projectionExpressionList);
}
else
{
Debug.Assert(selectClause.Items.Count == 1, "SELECT VALUE must have only one argument");
projectExpression = projectionExpressionList[0].Value;
}
projectExpression = sr.CmdTree.CreateProjectExpression(source, projectExpression);
//
// Ensure Projection is valid for DISTINCT modifier
//
SemanticResolver.ValidateDistinctProjection(selectClause, projectExpression.ResultType);
//
// create new source binding
//
source = sr.CmdTree.CreateExpressionBinding(sr.CmdTree.CreateDistinctExpression(projectExpression),
sr.GenerateInternalName("distinct"));
//
// replace Projection scope with new expression bindings
//
for (int i = 0; i < projectionExpressionList.Count; i++)
{
if (!sr.CurrentScopeRegionFlags.ContainsGroupAggregate(projectionExpressionList[i].Key))
{
// remove old scope var
sr.RemoveFromScope(projectionExpressionList[i].Key);
// create and add new source var to scope
SourceScopeEntry sce = new SourceScopeEntry(ScopeEntryKind.SourceVar, projectionExpressionList[i].Key, source.Variable);
if (selectClause.SelectKind == SelectKind.SelectRow)
{
sce.AddBindingPrefix(projectionExpressionList[i].Key);
}
sr.AddToScope(projectionExpressionList[i].Key, sce);
}
}
}
//
// if is not DISTINCT, but is a group scope, then should be the group scope and the
// projection list
//
else if (sr.CurrentScopeRegionFlags.IsInGroupScope)
{
sr.SetScopeView(SemanticResolver.ScopeViewKind.CurrentAndPreviousScope);
}
//
// convert sort keys
//
List sortKeys = new List(orderByClause.OrderByClauseItem.Expressions.Count);
for (int i = 0 ; i < orderByClause.OrderByClauseItem.Expressions.Count ; i++)
{
OrderByClauseItem orderClauseItem = orderByClause.OrderByClauseItem.Expressions[i];
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
sr.ResetScopeRegionCorrelationFlag();
//
// convert order key expression
//
DbExpression keyExpr = Convert(orderClauseItem.OrderExpr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(keyExpr, orderClauseItem.OrderExpr.ErrCtx);
//
// ensure key expression is correlated. if group by is present, then the check was already performed
//
if (!sr.CurrentScopeRegionFlags.WasResolutionCorrelated)
{
throw EntityUtil.EntitySqlError(orderClauseItem.ErrCtx, System.Data.Entity.Strings.KeyMustBeCorrelated("ORDER BY"));
}
//
// ensure key is order comparable
//
if (!TypeHelpers.IsValidSortOpKeyType(keyExpr.ResultType))
{
throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, System.Data.Entity.Strings.OrderByKeyIsNotOrderComparable);
}
//
// define order
//
bool ascSort = (orderClauseItem.OrderKind == OrderKind.None) || (orderClauseItem.OrderKind == OrderKind.Asc);
//
// define collation
//
string collation = null;
if (orderClauseItem.IsCollated)
{
if (!TypeResolver.IsKeyValidForCollation(keyExpr.ResultType))
{
throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, System.Data.Entity.Strings.InvalidKeyTypeForCollation(keyExpr.ResultType.EdmType.FullName));
}
collation = orderClauseItem.CollateIdentifier.Name;
}
//
// if orderby has no collation defined, check if a default was given through ParserOptions
//
else if (sr.ParserOptions.DefaultOrderByCollation.Length > 0 && TypeResolver.IsKeyValidForCollation(keyExpr.ResultType))
{
collation = sr.ParserOptions.DefaultOrderByCollation;
}
//
// add keys to key collection
//
if (string.IsNullOrEmpty(collation))
{
sortKeys.Add(sr.CmdTree.CreateSortClause(keyExpr, ascSort));
}
else
{
sortKeys.Add(sr.CmdTree.CreateSortClause(keyExpr, ascSort, collation));
}
}
DbExpression sortSourceExpr = null;
if (orderByClause.HasSkipSubClause)
{
sortSourceExpr = sr.CmdTree.CreateSkipExpression(source, sortKeys, skipExpr);
}
else
{
sortSourceExpr = sr.CmdTree.CreateSortExpression(source, sortKeys);
}
//
// Create Sort Binding
//
sortBinding = sr.CmdTree.CreateExpressionBinding(
sortSourceExpr,
sr.GenerateInternalName("sort"));
//
// Fixup Bindings
//
sr.FixupSourceVarBindings(sortBinding.Variable);
//
// pops projection keys from scope
//
sr.RollbackToSavepoint(savePoint);
//
// restore scope view
//
sr.SetScopeView(saveScopeView);
Debug.Assert(null != sortBinding,"null != sortBinding");
return sortBinding;
}
///
/// Converts a list of ast expression nodes
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static PairOfLists ProcessExprList( ExprList astExprList, SemanticResolver sr )
{
List types = new List(astExprList.Count);
List convertedExprs = new List(astExprList.Count);
for (int i = 0 ; i < astExprList.Count ; i++)
{
DbExpression e = Convert(astExprList[i], sr);
types.Add(e.ResultType);
convertedExprs.Add(e);
}
return new PairOfLists(types, convertedExprs);
}
///
/// [....]: Temporary workaround for 2/3 milestone.
/// Convert "x in multiset(y1, y2, ..., yn)" into
/// x = y1 or x = y2 or x = y3 ...
///
/// semantic resolver
/// left-expression (the probe)
/// right expression (the collection)
/// Or chain of equality comparisons
private static DbExpression ConvertSimpleInExpression( SemanticResolver sr, DbExpression left, DbExpression right )
{
// Only handle cases when the right-side is a new instance expression
Debug.Assert(right.ExpressionKind == DbExpressionKind.NewInstance,"right.ExpressionKind == DbExpressionKind.NewInstance");
DbNewInstanceExpression rightColl = (DbNewInstanceExpression)right;
if (rightColl.Arguments.Count == 0)
{
return sr.CmdTree.CreateConstantExpression(false);
}
DbExpression orExpr = null;
foreach (DbExpression e in rightColl.Arguments)
{
DbExpression leftClone = (DbExpression)left.Clone();
DbExpression eqExpr = sr.CmdTree.CreateEqualsExpression(leftClone, e);
if (orExpr == null)
{
orExpr = eqExpr;
}
else
{
orExpr = sr.CmdTree.CreateOrExpression(orExpr, eqExpr);
}
}
return orExpr;
}
#region Conversion Delegate Mappings
private delegate DbExpression AstExprConverter( Expr astExpr, SemanticResolver sr );
private static readonly Dictionary _astExprConverters = CreateAstExprConverters();
private delegate DbExpression BuiltInExprConverter( BuiltInExpr astBltInExpr, SemanticResolver sr );
private static readonly Dictionary _builtInExprConverter = CreateBuiltInExprConverter();
#region Define converter delegates
private static Dictionary CreateAstExprConverters()
{
const int NumberOfElements = 15; // number of elements initialized by the dictionary
Dictionary astExprConverters = new Dictionary(NumberOfElements);
astExprConverters.Add(typeof(Literal), new AstExprConverter(ConvertLiteral));
astExprConverters.Add(typeof(Parameter), new AstExprConverter(ConvertParameter));
astExprConverters.Add(typeof(Identifier), new AstExprConverter(ConvertIdentifier));
astExprConverters.Add(typeof(DotExpr), new AstExprConverter(ConvertDotExpr));
astExprConverters.Add(typeof(BuiltInExpr), new AstExprConverter(ConvertBuiltIn));
astExprConverters.Add(typeof(QueryExpr), new AstExprConverter(ConvertQuery));
astExprConverters.Add(typeof(RowConstructorExpr), new AstExprConverter(ConvertRowConstructor));
astExprConverters.Add(typeof(MultisetConstructorExpr), new AstExprConverter(ConvertMultisetConstructor));
astExprConverters.Add(typeof(CaseExpr), new AstExprConverter(ConvertCaseExpr));
astExprConverters.Add(typeof(RelshipNavigationExpr), new AstExprConverter(ConvertRelshipNavigationExpr));
astExprConverters.Add(typeof(RefExpr), new AstExprConverter(ConvertRefExpr));
astExprConverters.Add(typeof(DerefExpr), new AstExprConverter(ConvertDeRefExpr));
astExprConverters.Add(typeof(MethodExpr), new AstExprConverter(ConvertMethodExpr));
astExprConverters.Add(typeof(CreateRefExpr), new AstExprConverter(ConvertCreateRefExpr));
astExprConverters.Add(typeof(KeyExpr), new AstExprConverter(ConvertKeyExpr));
Debug.Assert(NumberOfElements == astExprConverters.Count, "The number of elements and initial capacity don't match");
return astExprConverters;
}
private static Dictionary CreateBuiltInExprConverter()
{
Dictionary builtInExprConverter = new Dictionary(sizeof(BuiltInKind));
////////////////////////////
// Arithmetic Expressions
////////////////////////////
//
// e1 + e2
//
#region e1 + e2
builtInExprConverter.Add(BuiltInKind.Plus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertPlusOperands(bltInExpr, sr);
if (TypeSemantics.IsNumericType(args.Left.ResultType))
{
return sr.CmdTree.CreatePlusExpression(args.Left, args.Right);
}
else
{
//
// fold '+' operator into concat canonical function
//
IList functions;
if (!sr.TypeResolver.TryGetFunctionFromMetadata("Concat","Edm", true /* ignoreCase */, out functions))
{
throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, System.Data.Entity.Strings.ConcatBuiltinNotSupported);
}
List argTypes = new List(2);
argTypes.Add(args.Left.ResultType);
argTypes.Add(args.Right.ResultType);
bool isAmbiguous = false;
EdmFunction concatFunction = TypeResolver.ResolveFunctionOverloads(functions, argTypes, false /* isGroupAggregate */, out isAmbiguous);
if (null == concatFunction || isAmbiguous)
{
throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, System.Data.Entity.Strings.ConcatBuiltinNotSupported);
}
return sr.CmdTree.CreateFunctionExpression(concatFunction,
new DbExpression[] { args.Left, args.Right });
}
});
#endregion
//
// e1 - e2
//
#region e1 - e2
builtInExprConverter.Add(BuiltInKind.Minus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateMinusExpression(args.Left, args.Right);
});
#endregion
//
// e1 * e2
//
#region e1 * e2
builtInExprConverter.Add(BuiltInKind.Multiply, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateMultiplyExpression(args.Left, args.Right);
});
#endregion
//
// e1 / e2
//
#region e1 / e2
builtInExprConverter.Add(BuiltInKind.Divide, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateDivideExpression(args.Left, args.Right);
});
#endregion
//
// e1 % e2
//
#region e1 % e2
builtInExprConverter.Add(BuiltInKind.Modulus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateModuloExpression(args.Left, args.Right);
});
#endregion
//
// - e
//
#region - e
builtInExprConverter.Add(BuiltInKind.UnaryMinus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
DbExpression unaryExpr = sr.CmdTree.CreateUnaryMinusExpression(ConvertArithmeticArgs(bltInExpr, sr).Left);
if (TypeSemantics.IsUnsignedNumericType(unaryExpr.ResultType))
{
TypeUsage closestPromotableType = null;
if (TypeHelpers.TryGetClosestPromotableType(unaryExpr.ResultType, out closestPromotableType))
{
unaryExpr = sr.CmdTree.CreateCastExpression(unaryExpr, closestPromotableType);
}
else
{
throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.InvalidUnsignedTypeForUnaryMinusOperation(unaryExpr.ResultType.EdmType.FullName));
}
}
return unaryExpr;
});
#endregion
//
// + e
//
#region + e
builtInExprConverter.Add(BuiltInKind.UnaryPlus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return ConvertArithmeticArgs(bltInExpr, sr).Left;
});
#endregion
////////////////////////////
// Logical Expressions
////////////////////////////
//
// e1 AND e2
// e1 && e2
//
#region e1 AND e2
builtInExprConverter.Add(BuiltInKind.And, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr);
return sr.CmdTree.CreateAndExpression(args.Left, args.Right);
});
#endregion
//
// e1 OR e2
// e1 || e2
//
#region e1 OR e2
builtInExprConverter.Add(BuiltInKind.Or, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr);
return sr.CmdTree.CreateOrExpression(args.Left, args.Right);
});
#endregion
//
// NOT e
// ! e
//
#region NOT e
builtInExprConverter.Add(BuiltInKind.Not, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return sr.CmdTree.CreateNotExpression(ConvertLogicalArgs(bltInExpr, sr).Left);
});
#endregion
////////////////////////////
// Comparison Expressions
////////////////////////////
//
// e1 == e2 | e1 = e2
//
#region e1 == e2
builtInExprConverter.Add(BuiltInKind.Equal, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertEqualCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateEqualsExpression(args.Left, args.Right);
});
#endregion
//
// e1 != e2 | e1 <> e2
//
#region e1 != e2
builtInExprConverter.Add(BuiltInKind.NotEqual, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertEqualCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateEqualsExpression(args.Left, args.Right));
});
#endregion
//
// e1 >= e2
//
#region e1 >= e2
builtInExprConverter.Add(BuiltInKind.GreaterEqual, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateGreaterThanOrEqualsExpression(args.Left, args.Right);
});
#endregion
//
// e1 > e2
//
#region e1 > e2
builtInExprConverter.Add(BuiltInKind.GreaterThan, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateGreaterThanExpression(args.Left, args.Right);
});
#endregion
//
// e1 <= e2
//
#region e1 <= e2
builtInExprConverter.Add(BuiltInKind.LessEqual, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateLessThanOrEqualsExpression(args.Left, args.Right);
});
#endregion
//
// e1 < e2
//
#region e1 < e2
builtInExprConverter.Add(BuiltInKind.LessThan, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateLessThanExpression(args.Left, args.Right);
});
#endregion
////////////////////////////
// SET EXPRESSIONS
////////////////////////////
//
// e1 UNION e2
//
#region e1 UNION e2
builtInExprConverter.Add(BuiltInKind.Union, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateDistinctExpression(sr.CmdTree.CreateUnionAllExpression(args.Left, args.Right));
});
#endregion
//
// e1 UNION ALL e2
//
#region e1 UNION ALL e2
builtInExprConverter.Add(BuiltInKind.UnionAll, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateUnionAllExpression(args.Left, args.Right);
});
#endregion
//
// e1 INTERSECT e2
//
#region e1 INTERSECT e2
builtInExprConverter.Add(BuiltInKind.Intersect, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateIntersectExpression(args.Left, args.Right);
});
#endregion
//
// e1 OVERLAPS e2
//
#region e1 OVERLAPS e1
builtInExprConverter.Add(BuiltInKind.Overlaps, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateIsEmptyExpression(
sr.CmdTree.CreateIntersectExpression(args.Left, args.Right)));
});
#endregion
//
// ANYELEMENT( e )
//
#region ANYELEMENT( e )
builtInExprConverter.Add(BuiltInKind.AnyElement, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return sr.CmdTree.CreateElementExpression(ConvertSetArgs(bltInExpr, sr).Left);
});
#endregion
//
// ELEMENT( e )
//
#region ELEMENT( e ) - NOT SUPPORTED IN ORCAS TIMEFRAME
builtInExprConverter.Add(BuiltInKind.Element, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ElementOperatorIsNotSupported);
});
#endregion
//
// e1 EXCEPT e2
//
#region e1 EXCEPT e2
builtInExprConverter.Add(BuiltInKind.Except, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateExceptExpression(args.Left, args.Right);
});
#endregion
//
// EXISTS( e )
//
#region EXISTS( e )
builtInExprConverter.Add(BuiltInKind.Exists, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateIsEmptyExpression(ConvertSetArgs(bltInExpr, sr).Left));
});
#endregion
//
// FLATTEN( e )
//
#region FLATTEN( e )
builtInExprConverter.Add(BuiltInKind.Flatten, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
DbExpression elemExpr = Convert(bltInExpr.Arg1, sr);
if (!TypeSemantics.IsCollectionType(elemExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidFlattenArgument);
}
if (!TypeSemantics.IsCollectionType(TypeHelpers.GetElementTypeUsage(elemExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidFlattenArgument);
}
DbExpressionBinding leftExpr = sr.CmdTree.CreateExpressionBinding(elemExpr, sr.GenerateInternalName("l_flatten"));
DbExpressionBinding rightExpr = sr.CmdTree.CreateExpressionBinding(leftExpr.Variable, sr.GenerateInternalName("r_flatten"));
DbExpressionBinding applyBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateCrossApplyExpression(leftExpr, rightExpr),
sr.GenerateInternalName("flatten"));
return sr.CmdTree.CreateProjectExpression(applyBinding, sr.CmdTree.CreatePropertyExpression(rightExpr.VariableName, applyBinding.Variable));
});
#endregion
//
// e1 IN e2
//
#region e1 IN e2
builtInExprConverter.Add(BuiltInKind.In, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertInExprArgs(bltInExpr, sr);
// [....]: Temporary workaround for 2/3 milestone.
// Convert "x in multiset(y1, y2, ..., yn)" into
// x = y1 or x = y2 or x = y3 ...
//
if (args.Right.ExpressionKind == DbExpressionKind.NewInstance)
{
return ConvertSimpleInExpression(sr, args.Left, args.Right);
}
else
{
DbExpressionBinding rSet = sr.CmdTree.CreateExpressionBinding(args.Right, sr.GenerateInternalName("in-filter"));
DbExpression leftIn = args.Left;
DbExpression rightSet = rSet.Variable;
DbExpression exists = sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateIsEmptyExpression(
sr.CmdTree.CreateFilterExpression(
rSet,
sr.CmdTree.CreateEqualsExpression(leftIn, rightSet))));
List whenExpr = new List(1);
whenExpr.Add(sr.CmdTree.CreateIsNullExpression(leftIn));
List thenExpr = new List(1);
thenExpr.Add(sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType));
DbExpression left = sr.CmdTree.CreateCaseExpression(
whenExpr,
thenExpr,
sr.CmdTree.CreateFalseExpression());
DbExpression converted = sr.CmdTree.CreateOrExpression(left, exists);
return converted;
}
});
#endregion
//
// e1 NOT IN e1
//
#region e1 NOT IN e1
builtInExprConverter.Add(BuiltInKind.NotIn, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertInExprArgs(bltInExpr, sr);
if (args.Right.ExpressionKind == DbExpressionKind.NewInstance)
{
return sr.CmdTree.CreateNotExpression(
ConvertSimpleInExpression(sr, args.Left, args.Right));
}
else
{
DbExpressionBinding rSet = sr.CmdTree.CreateExpressionBinding(args.Right, sr.GenerateInternalName("in-filter"));
DbExpression leftIn = args.Left;
DbExpression rightSet = rSet.Variable;
DbExpression exists = sr.CmdTree.CreateIsEmptyExpression(
sr.CmdTree.CreateFilterExpression(
rSet,
sr.CmdTree.CreateEqualsExpression(leftIn, rightSet)));
List whenExpr = new List(1);
whenExpr.Add(sr.CmdTree.CreateIsNullExpression(leftIn));
List thenExpr = new List(1);
thenExpr.Add(sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType));
DbExpression left = sr.CmdTree.CreateCaseExpression(
whenExpr,
thenExpr,
sr.CmdTree.CreateTrueExpression());
DbExpression converted = sr.CmdTree.CreateAndExpression(left, exists);
return converted;
}
});
#endregion
//
// SET( e ) - DISTINCT( e ) before
//
#region SET( e )
builtInExprConverter.Add(BuiltInKind.Distinct, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateDistinctExpression(args.Left);
});
#endregion
////////////////////////////
// Nullabity Expressions
////////////////////////////
//
// e IS NULL
//
#region e IS NULL
builtInExprConverter.Add(BuiltInKind.IsNull, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
DbExpression isNullExpr = Convert(bltInExpr.Arg1, sr);
//
// ensure expression type is valid for this operation
//
if (!TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.IsNullInvalidType);
}
return SemanticResolver.IsNullExpression(isNullExpr) ? sr.CmdTree.CreateTrueExpression() : (DbExpression)sr.CmdTree.CreateIsNullExpression(isNullExpr);
});
#endregion
//
// e IS NOT NULL
//
#region e IS NOT NULL
builtInExprConverter.Add(BuiltInKind.IsNotNull, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
DbExpression isNullExpr = Convert(bltInExpr.Arg1, sr);
//
// ensure expression type is valid for this operation
//
if (!TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.IsNullInvalidType);
}
isNullExpr = SemanticResolver.IsNullExpression(isNullExpr) ? sr.CmdTree.CreateTrueExpression() : (DbExpression)sr.CmdTree.CreateIsNullExpression(isNullExpr);
return sr.CmdTree.CreateNotExpression(isNullExpr);
});
#endregion
////////////////////////////
// Type Expressions
////////////////////////////
//
// e IS OF ( [ONLY] T )
//
#region e IS OF ( [ONLY] T )
builtInExprConverter.Add(BuiltInKind.IsOf, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
bool isOnly = (bool)((Literal)bltInExpr.ArgList[2]).Value;
bool isNot = (bool)((Literal)bltInExpr.ArgList[3]).Value;
bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
if (TypeSemantics.IsNullType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionCannotBeNull);
}
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeEntityType(System.Data.Entity.Strings.CtxIsOf,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeNominalType(System.Data.Entity.Strings.CtxIsOf,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.TypeMustBeEntityType(System.Data.Entity.Strings.CtxIsOf,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.TypeMustBeNominalType(System.Data.Entity.Strings.CtxIsOf,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
if (!TypeSemantics.IsPolymorphicType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeSemantics.IsPolymorphicType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeResolver.IsSubOrSuperType(args.Left.ResultType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx,
System.Data.Entity.Strings.NotASuperOrSubType(args.Left.ResultType.EdmType.FullName,
args.Right.EdmType.FullName));
}
args.Right = TypeHelpers.GetReadOnlyType(args.Right);
DbExpression retExpr = null;
if (isOnly)
{
retExpr = sr.CmdTree.CreateIsOfOnlyExpression(args.Left, args.Right);
}
else
{
retExpr = sr.CmdTree.CreateIsOfExpression(args.Left, args.Right);
}
if (isNot)
{
retExpr = sr.CmdTree.CreateNotExpression(retExpr);
}
return retExpr;
});
#endregion
//
// TREAT( e as T )
//
#region TREAT( e as T )
builtInExprConverter.Add(BuiltInKind.Treat, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeEntityType(System.Data.Entity.Strings.CtxTreat,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeNominalType(System.Data.Entity.Strings.CtxTreat,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
if (TypeSemantics.IsNullType(args.Left.ResultType))
{
args.Left = sr.CmdTree.CreateNullExpression(args.Right);
}
else if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeEntityType(System.Data.Entity.Strings.CtxTreat,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeNominalType(System.Data.Entity.Strings.CtxTreat,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
if (!TypeSemantics.IsPolymorphicType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeSemantics.IsPolymorphicType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeResolver.IsSubOrSuperType(args.Left.ResultType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.NotASuperOrSubType(args.Left.ResultType.EdmType.FullName,
args.Right.EdmType.FullName));
}
return sr.CmdTree.CreateTreatExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
});
#endregion
//
// CAST( e AS T )
//
#region CAST( e AS T )
builtInExprConverter.Add(BuiltInKind.Cast, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
//
// ensure CAST target type is Scalar
//
if (!TypeSemantics.IsPrimitiveType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.InvalidCastType);
}
if (SemanticResolver.IsNullExpression(args.Left))
{
return sr.CmdTree.CreateCastExpression(
sr.CmdTree.CreateNullExpression(args.Right),
args.Right);
}
if (args.Left.ResultType.BuiltInTypeKind != BuiltInTypeKind.EnumType)
{
if (!TypeSemantics.IsPrimitiveType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidCastExpressionType);
}
if (!TypeSemantics.IsCastAllowed(args.Left.ResultType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.InvalidCast(
args.Left.ResultType.EdmType,
args.Right.EdmType.FullName));
}
}
return sr.CmdTree.CreateCastExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
});
#endregion
//
// OFTYPE( [ONLY] e, T )
//
#region OFTYPE( [ONLY] e, T )
builtInExprConverter.Add(BuiltInKind.OfType, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
bool isOnly = (bool)((Literal)bltInExpr.ArgList[2]).Value;
bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
if (!TypeSemantics.IsCollectionType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionMustBeCollection);
}
TypeUsage elementType = TypeHelpers.GetElementTypeUsage(args.Left.ResultType);
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(elementType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.OfTypeExpressionElementTypeMustBeEntityType(elementType.EdmType.BuiltInTypeKind.ToString(),
elementType));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(elementType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.OfTypeExpressionElementTypeMustBeNominalType(elementType.EdmType.BuiltInTypeKind.ToString(),
elementType));
}
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeEntityType(System.Data.Entity.Strings.CtxOfType,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeNominalType(System.Data.Entity.Strings.CtxOfType,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
if (isOnly && args.Right.EdmType.Abstract)
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.OfTypeOnlyTypeArgumentCannotBeAbstract(args.Right.EdmType.FullName));
}
if (!TypeResolver.IsSubOrSuperType(elementType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.NotASuperOrSubType(elementType.EdmType.FullName,
args.Right.EdmType.FullName));
}
DbExpression ofTypeExpression = null;
if (isOnly)
{
ofTypeExpression = sr.CmdTree.CreateOfTypeOnlyExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
}
else
{
ofTypeExpression = sr.CmdTree.CreateOfTypeExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
}
return ofTypeExpression;
});
#endregion
//
// e LIKE pattern [ESCAPE escape]
//
#region e LIKE pattern [ESCAPE escape]
builtInExprConverter.Add(BuiltInKind.Like, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
DbExpression likeExpr = null;
DbExpression matchExpr = Convert(bltInExpr.Arg1, sr);
if (TypeSemantics.IsNullType(matchExpr.ResultType))
{
matchExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.StringType);
}
else if (!TypeResolver.IsStringType(matchExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.LikeArgMustBeStringType);
}
DbExpression patternExpr = Convert(bltInExpr.Arg2, sr);
if (patternExpr is UntypedNullExpression)
{
patternExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.StringType);
}
else if (!TypeResolver.IsStringType(patternExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.LikeArgMustBeStringType);
}
if (3 == bltInExpr.ArgCount)
{
DbExpression escapeExpr = Convert(bltInExpr.ArgList[2], sr);
if (escapeExpr is UntypedNullExpression)
{
escapeExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.StringType);
}
else if (!TypeResolver.IsStringType(escapeExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[2].ErrCtx, System.Data.Entity.Strings.LikeArgMustBeStringType);
}
likeExpr = sr.CmdTree.CreateLikeExpression(matchExpr, patternExpr, escapeExpr);
}
else
{
likeExpr = sr.CmdTree.CreateLikeExpression(matchExpr, patternExpr);
}
return likeExpr;
});
#endregion
//
// e BETWEEN e1 AND e2
//
#region e BETWEEN e1 AND e2
builtInExprConverter.Add(BuiltInKind.Between, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Debug.Assert(bltInExpr.ArgCount == 3);
//
// convert lower and upper limits
//
Pair limitsExpr = sr.EnsureTypedNulls(
Convert(bltInExpr.ArgList[1], sr),
Convert(bltInExpr.ArgList[2], sr),
bltInExpr.ArgList[0].ErrCtx,
() => Strings.BetweenLimitsCannotBeUntypedNulls);
//
// Get and check common type for limits
//
TypeUsage rangeCommonType = TypeHelpers.GetCommonTypeUsage(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType);
if (null == rangeCommonType)
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[0].ErrCtx, System.Data.Entity.Strings.BetweenLimitsTypesAreNotCompatible(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName));
}
//
// check if limit types are order-comp
//
if (!TypeSemantics.IsOrderComparableTo(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[0].ErrCtx, System.Data.Entity.Strings.BetweenLimitsTypesAreNotOrderComparable(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName));
}
//
// convert value expression
//
DbExpression valueExpr = Convert(bltInExpr.ArgList[0], sr);
//
// Fixup value type if untyped
//
if (TypeSemantics.IsNullType(valueExpr.ResultType))
{
valueExpr = sr.CmdTree.CreateNullExpression(rangeCommonType);
}
//
// check if valueExpr is order-comparable to limits
//
if (!TypeSemantics.IsOrderComparableTo(valueExpr.ResultType, rangeCommonType))
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[0].ErrCtx, System.Data.Entity.Strings.BetweenValueIsNotOrderComparable(valueExpr.ResultType.EdmType.FullName, rangeCommonType.EdmType.FullName));
}
return sr.CmdTree.CreateAndExpression(
sr.CmdTree.CreateGreaterThanOrEqualsExpression(valueExpr, limitsExpr.Left),
sr.CmdTree.CreateLessThanOrEqualsExpression(valueExpr, limitsExpr.Right));
});
#endregion
//
// e NOT BETWEEN e1 AND e2
//
#region e NOT BETWEEN e1 AND e2
builtInExprConverter.Add(BuiltInKind.NotBetween, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
bltInExpr.Kind = BuiltInKind.Between;
DbExpression converted = sr.CmdTree.CreateNotExpression(ConvertBuiltIn(bltInExpr, sr));
bltInExpr.Kind = BuiltInKind.NotBetween;
return converted;
});
#endregion
return builtInExprConverter;
}
#endregion
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backup [....]
//---------------------------------------------------------------------
namespace System.Data.Common.EntitySql
{
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.Common.CommandTrees;
using System.Data.Metadata.Edm;
using System.Data.Entity;
///
/// Implements Semantic Analysis and Conversion
/// Provides the translation service between an abstract syntax tree to a canonical command tree
/// For complete documentation of the language syntax and semantics, refer to http://sqlweb/default.asp?specDirId=764
/// The class was designed to be type system agnostic by delegating to a given SemanticResolver instance all type related services as well as to TypeHelper class, however
/// we rely on the assumption that metadata was pre-loaded and is relevant to the query.
///
internal sealed class SemanticAnalyzer
{
private SemanticResolver _sr;
///
/// Initializes semantic analyzer
///
/// initialized SemanticResolver instance for a given typespace/type system
internal SemanticAnalyzer( SemanticResolver sr )
{
EntityUtil.CheckArgumentNull(sr, "sr");
_sr = sr;
}
///
/// Entry point to semantic analysis. Converts astTree into DbExpression.
///
/// ast tree
/// command tree to build the expression
///
/// 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
///
/// DbExpression
internal DbExpression Analyze(Expr astExpr, DbCommandTree commandTree)
{
CommandExpr command = Initialize(astExpr, commandTree);
DbExpression converted = ConvertRootExpression(command.QueryExpr, _sr);
return converted;
}
///
/// Entry point to semantic analysis. Converts astTree into command tree.
///
/// ast tree
///
/// 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
///
/// DbCommandTree
internal DbCommandTree Analyze( Expr astExpr )
{
CommandExpr astCommandExpr = Initialize(astExpr, null);
//
// Convert query Expression
//
DbCommandTree commandTree = ConvertCommand(astCommandExpr, _sr);
Debug.Assert(null != commandTree,"null != commandTree");
return commandTree;
}
///
/// Common initialization required whether the semantic analysis process
/// is producing a command tree or only an expression. Validates that the
/// parse phase did in fact produce a command AST.
///
/// The root of the abstract syntax tree produced by the parse phase
///
/// An optional command tree to use, only provided when constructing an expression.
/// When constructing a command tree, an instance of the appropriate command tree
/// class will be created and returned by semantic analysis.
///
/// The that is the root of the AST specified by
private CommandExpr Initialize(Expr astExpr, DbCommandTree tree)
{
CommandExpr astCommandExpr = astExpr as CommandExpr;
if (null == astCommandExpr)
{
throw EntityUtil.Argument(System.Data.Entity.Strings.UnknownAstCommandExpression);
}
//
// Sets DbCommandTree Factory
//
if (tree == null)
{
_sr.SetCommandTreeFactory(astCommandExpr);
}
else
{
_sr.SetCommandTreeFactory(astCommandExpr, tree);
}
//
// Declare Canonical Namespace
//
_sr.DeclareCanonicalNamespace();
//
// Declare Namespaces
//
_sr.DeclareNamespaces(astCommandExpr.NamespaceDeclList);
return astCommandExpr;
}
#region command converter delegates
delegate DbCommandTree CommandConverter( Expr astExpr, SemanticResolver sr );
static CommandConverter[] commandConverters =
{
/* ExprKind.Generic */ ConvertGeneralExpression,
/* ExprKind.Query */ ConvertGeneralExpression,
};
#endregion
///
/// Dispatches/Converts top command expressions.
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbCommandTree ConvertCommand( CommandExpr astCommandExpr, SemanticResolver sr )
{
EntityUtil.CheckArgumentNull(astCommandExpr, "astCommandExpr");
Expr queryExpr = astCommandExpr.QueryExpr;
return commandConverters[(int)queryExpr.ExprKind](queryExpr, sr);
}
///
/// Converts {Query|General} Expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbCommandTree ConvertGeneralExpression( Expr astExpr, SemanticResolver sr )
{
DbExpression converted = ConvertRootExpression(astExpr, sr);
DbQueryCommandTree qryCmdTree = (DbQueryCommandTree)sr.CmdTree;
qryCmdTree.Query = converted;
Debug.Assert(null != qryCmdTree,"null != qryCmdTree");
return qryCmdTree;
}
///
/// Converts the expression that is the 'root' of a query AST to a
/// normalized and validated . This entry
/// point to the semantic analysis phase is used when producing a
/// query command tree (from a Query or General AST expression) or
/// producing only a .
///
/// The root AST node for this query
/// The instance to use
///
/// An instance of , adjusted to handle 'inline' projections
/// and validated to produce a result type appropriate for the root of a query command tree.
///
private static DbExpression ConvertRootExpression(Expr astExpr, SemanticResolver sr)
{
DbExpression converted = Convert(astExpr, sr);
//
// ensure converted expression is not untyped-null
//
if (TypeSemantics.IsNullType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(astExpr.ErrCtx, System.Data.Entity.Strings.ResultingExpressionTypeCannotBeNull);
}
//
// Handles the "inline" projection case
//
if (converted is DbScanExpression)
{
DbExpressionBinding source = sr.CmdTree.CreateExpressionBinding(converted, sr.GenerateInternalName("extent"));
converted = sr.CmdTree.CreateProjectExpression(source, source.Variable);
}
//
// ensure return type is valid for query. for V1, association types are the only
// type that cannot be at 'top' level result. Note that this is only applicable in
// general queries and association types are valid in view gen mode queries
//
if (sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.NormalMode)
{
SemanticResolver.ValidateQueryResultType(converted.ResultType, astExpr.ErrCtx);
}
return converted;
}
///
/// Dispatches/Converts general expressions
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression Convert( Expr astExpr, SemanticResolver sr )
{
if (null == astExpr)
{
return null;
}
AstExprConverter converter = _astExprConverters[astExpr.GetType()];
if (null == converter)
{
throw EntityUtil.Argument(System.Data.Entity.Strings.UnknownAstExpressionType);
}
DbExpression converted = converter(astExpr, sr);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts Literal Expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertLiteral( Expr expr, SemanticResolver sr )
{
Literal literal = (Literal)expr;
if (literal.IsNullLiteral)
{
//
// if it is literal null, return untyped null
// untyped nulls will later have their type inferred depending on the
// especific expression in which it participates
//
return new UntypedNullExpression(sr.CmdTree);
}
else
{
return sr.CmdTree.CreateConstantExpression(literal.Value,
sr.TypeResolver.GetLiteralTypeUsage(literal));
}
}
///
/// Converts Simple Identifier
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertIdentifier( Expr expr, SemanticResolver sr )
{
Identifier idExpr = (Identifier)expr;
DbExpression converted = sr.ResolveIdentifier(new string[] { idExpr.Name }, expr.ErrCtx);
if (null == converted)
{
CqlErrorHelper.ReportIdentifierError(expr, sr);
}
return converted;
}
///
/// Converts DotExpression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertDotExpr( Expr expr, SemanticResolver sr )
{
DotExpr dotExpr = (DotExpr)expr;
DbExpression converted = null;
if (dotExpr.IsDottedIdentifier)
{
converted = sr.ResolveIdentifier(dotExpr.Names, dotExpr.ErrCtx);
if (null == converted)
{
CqlErrorHelper.ReportIdentifierError(expr, sr);
}
}
else
{
converted = ConvertDotExpressionProcess(dotExpr, sr);
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts dot expression. dotted id case is handled by resolveId
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
///
///
///
///
static private DbExpression ConvertDotExpressionProcess( DotExpr dotExpr, SemanticResolver sr )
{
Debug.Assert(!dotExpr.IsDottedIdentifier,"!dotExpr.IsDottedIdentifier");
return sr.ResolveIdentifierChain(dotExpr.Names, 0, Convert(dotExpr.LeftMostExpression, sr), dotExpr.ErrCtx);
}
///
/// Converts methods, functions and type constructors
/// methods (instance or static) can be in the form: (expr).chain.of.names(args) or chain.of.names(args)
/// Functions are chain.of.names(args)
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertMethodExpr( Expr expr, SemanticResolver sr )
{
MethodExpr methodExpr = (MethodExpr)expr;
DbExpression converted = null;
DotExpr dotExpr = methodExpr.MethodPrefixExpr;
//
// resolve base expression if one exists
//
DbExpression baseExpr = (null != dotExpr.LeftMostExpression) ? Convert(dotExpr.LeftMostExpression, sr) : null;
//
// If base expression is still unresolved, check if leftmost name element is in in scope
//
int prefixIndex = 0;
KeyValuePair varInfo;
if (null == baseExpr && dotExpr.IsDottedIdentifier && dotExpr.Names.Length > 0)
{
ScopeEntry scopeEntry;
if (sr.TryScopeLookup(dotExpr.Names[0], out scopeEntry))
{
//
// make sure is a valid type for method call
//
if (TypeResolver.IsValidTypeForMethodCall(scopeEntry.Expression.ResultType))
{
baseExpr = scopeEntry.Expression;
prefixIndex = 1;
}
else
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.DefiningTypeDoesNotSupportMethodCalls);
}
}
else if (sr.Variables.TryGetValue(dotExpr.Names[0], out varInfo))
{
baseExpr = sr.CmdTree.CreateVariableReferenceExpression(varInfo.Key, varInfo.Value);
prefixIndex = 1;
}
}
//
// check if base expression if is untyped null
//
if (baseExpr is UntypedNullExpression)
{
throw EntityUtil.EntitySqlError(dotExpr.LeftMostExpression.ErrCtx, System.Data.Entity.Strings.ExpressionCannotBeNull);
}
//
// dispatches methodExpr to static or instance kind of method conversion
//
if (null == baseExpr)
{
//
// if base expression is still unresolved, it should be a function(aggregates included), static method or type constructor
//
converted = ConvertStaticMethodOrFunction(methodExpr, sr);
}
else
{
//
// otherwise, should be an instance method invocation
//
converted = ConvertMethodInstance(baseExpr, methodExpr, prefixIndex, sr);
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts a method expr into a Static Method, Type Constructor or EdmFunction call (including aggregates)
///
///
///
///
private static DbExpression ConvertStaticMethodOrFunction( MethodExpr methodExpr, SemanticResolver sr )
{
DbExpression converted = null;
//
// Find out if given name path is a Static Method, Type Constructor or EdmFunction call (including aggregates)
//
TypeUsage constructorType;
TypeUsage staticMethodType;
IList functionType;
//
// Resolve methodExpr
//
sr.ResolveNameAsStaticMethodOrFunction(methodExpr, out constructorType, out staticMethodType, out functionType);
//
// at this point, only one of the three must be not null
// that is ensured by ResolveNameAsStaticMethodOrFunction()
//
Debug.Assert(constructorType != null || staticMethodType != null || functionType != null,"constructorType != null || staticMethodType != null || functionType != null");
//
// if it is a constructorType, create an instance of it
//
if (null != constructorType)
{
List relshipExprList = null;
//
// convert relationships if present
//
if (methodExpr.HasRelationships)
{
if (ParserOptions.CompilationMode.NormalMode == sr.ParserOptions.ParserCompilationMode)
{
throw EntityUtil.EntitySqlError(methodExpr.Relationships.ErrCtx, System.Data.Entity.Strings.InvalidModeForWithRelationshipClause);
}
HashSet targetEnds = new HashSet();
relshipExprList = new List(methodExpr.Relationships.Count);
for (int i = 0; i < methodExpr.Relationships.Count; i++)
{
RelshipNavigationExpr relshipExpr = methodExpr.Relationships[i];
DbRelatedEntityRef relshipTarget = ConvertRelatedEntityRef(relshipExpr, sr);
string targetEndId = String.Join(":", new String[] { relshipTarget.TargetEnd.DeclaringType.Identity, relshipTarget.TargetEnd.Identity });
if (targetEnds.Contains(targetEndId))
{
throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx,
System.Data.Entity.Strings.RelationshipTargetMustBeUnique(relshipTarget.TargetEntityReference.ResultType.EdmType.Identity));
}
targetEnds.Add(targetEndId);
relshipExprList.Add(relshipTarget);
}
}
converted = sr.CreateInstanceOfType(constructorType,
ConvertFunctionArguments(methodExpr.Args, sr),
relshipExprList,
methodExpr);
}
//
// if it is staticMethodType, create method call expression
//
else if (null != staticMethodType)
{
converted = SemanticResolver.CreateStaticMethod(staticMethodType, ConvertFunctionArguments(methodExpr.Args, sr), methodExpr);
}
//
// if it is functionType
//
else if (null != functionType && 0 < functionType.Count)
{
//
// decide if it is an ordinary function or group aggreagate
//
if (TypeSemantics.IsAggregateFunction(functionType[0]) && sr.IsInAnyGroupScope())
{
//
// if it is an aggreagate function inside a group scope, dispatch to ConvertGroupAggregate()
//
converted = ConvertAggregateFunctionInGroupScope(methodExpr, functionType, sr);
}
else
{
//
// else, is just an ordinary function call (including collection aggregates)
//
converted = sr.CreateFunction(functionType, ConvertFunctionArguments(methodExpr.Args, sr), methodExpr);
}
}
else
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.CannotResolveNameToFunction(methodExpr.MethodPrefixExpr.FullName));
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts Group Aggregates
///
///
///
///
///
///
/// This method convert group aggregates in two phases:
/// Phase 1 - it will resolve the actual inner (argument) expression and then anotate the ast node and add the resolved aggregate
/// to the scope
/// Phase 2 - if ast node was annotated, just extract the precomputed expression from the scope.
///
private static DbExpression ConvertAggregateFunctionInGroupScope( MethodExpr methodExpr, IList functionTypes, SemanticResolver sr )
{
DbExpression converted = null;
//
// first, try if aggregate was already pre resolved
//
if (TryConvertAsResolvedGroupAggregate(methodExpr, sr, out converted))
{
return converted;
}
//
// then, try to resolve as Ordinary function (collection aggregate)
//
if (TryConvertAsOrdinaryFunctionInGroup(methodExpr, functionTypes, sr, out converted))
{
return converted;
}
//
// finally, try to convert as group aggregate
//
if (TryConvertAsGroupAggregateFunction(methodExpr, functionTypes, sr, out converted))
{
return converted;
}
//
// if we reach this point, means the resolution failed
//
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.FailedToResolveAggregateFunction(methodExpr.MethodPrefixExpr.FullName));
}
///
/// try to convert as pre resolved aggregate function
///
///
///
///
///
private static bool TryConvertAsResolvedGroupAggregate( MethodExpr methodExpr, SemanticResolver sr, out DbExpression converted )
{
converted = null;
//
// if ast node was annotated in a previous pass, means it was already resolved and should be in scope
//
if (methodExpr.WasResolved)
{
sr.CurrentScopeRegionFlags.DecrementGroupAggregateNestingCount();
ScopeEntry scopeEntry;
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
sr.SetScopeView(SemanticResolver.ScopeViewKind.All);
int scopeIndex;
if (!sr.TryScopeLookup(methodExpr.InternalAggregateName, out scopeEntry, out scopeIndex))
{
Debug.Assert(methodExpr.DummyExpression != null, "resolved aggregate dummy expression must not be null");
converted = methodExpr.DummyExpression;
return true;
}
else
{
//
// Sets correlation flag
//
sr.SetScopeRegionCorrelationFlag(scopeIndex);
}
sr.SetScopeView(saveScopeView);
converted = scopeEntry.Expression;
return true;
}
return false;
}
///
/// Try convert method expr in a group scope as an ordinary function (collection aggregate)
///
///
///
///
///
///
private static bool TryConvertAsOrdinaryFunctionInGroup( MethodExpr methodExpr ,
IList functionTypes ,
SemanticResolver sr ,
out DbExpression converted )
{
converted = null;
//
// save scope view
//
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
//
// convert aggregate arguments
//
List args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// collect argument types
//
List argTypes = new List(args.Count);
for (int i = 0 ; i < args.Count ; i++)
{
argTypes.Add(args[i].ResultType);
}
//
// try to see if there is a overload match
//
bool isAmbiguous = false;
EdmFunction functionType = TypeResolver.ResolveFunctionOverloads( functionTypes,
argTypes,
false /* isGroupAggregateFunction */,
out isAmbiguous);
//
// if there is more then one overload that matches given arguments, throw
//
if (isAmbiguous)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionArguments);
}
//
// if not null, means a match was found as an ordinary function
//
if (null != functionType)
{
//
// make sure all referenced vars are from group scope only
//
if (!sr.CurrentScopeRegionFlags.IsImplicitGroup)
{
sr.SetScopeView(SemanticResolver.ScopeViewKind.CurrentScope);
}
//
// convert aggregate arguments
//
args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// restore previous scope view
//
sr.SetScopeView(saveScopeView);
//
// return function
//
converted = sr.CmdTree.CreateFunctionExpression(functionType, args);
}
return (null != functionType);
}
///
///
///
///
///
///
///
///
private static bool TryConvertAsGroupAggregateFunction( MethodExpr methodExpr,
IList functionTypeList,
SemanticResolver sr,
out DbExpression converted )
{
converted = null;
//
// save scope view
//
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
//
// Aggregates in groups can refer to all scopes
//
sr.SetScopeView(SemanticResolver.ScopeViewKind.All);
//
// flag that it is inside a group aggregate
//
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = true;
//
// reset nested references flag
//
sr.CurrentScopeRegionFlags.WasNestedGroupAggregateReferredByInnerExpressions = false;
//
// pushes candidate aggregate ast node
//
sr.PushAggregateAstNode(methodExpr);
sr.CurrentScopeRegionFlags.DecrementGroupAggregateNestingCount();
//
// convert aggregate arguments
//
List args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// clear inside group aggregate flag
//
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = false;
//
// collect argument types
//
List argTypes = new List(args.Count);
for (int i = 0 ; i < args.Count ; i++)
{
argTypes.Add(args[i].ResultType);
}
//
// try to find an overload match as group aggregate
//
bool isAmbiguous = false;
EdmFunction functionType = TypeResolver.ResolveFunctionOverloads(functionTypeList,
argTypes,
true /* isGroupAggregateFunction */,
out isAmbiguous);
//
// if there is more then one overload that matches given arguments, throw
//
if (isAmbiguous)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionArguments);
}
//
// if it still null, then there is no overload as an ordinary function or group aggregate function
//
if (null == functionType)
{
CqlErrorHelper.ReportFunctionOverloadError(methodExpr, functionTypeList[0], argTypes);
}
//
// ensure that group aggregate was not referenced by inner sub-expression
//
if (sr.CurrentScopeRegionFlags.WasNestedGroupAggregateReferredByInnerExpressions)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.NestedAggregatesCannotBeUsedInAggregateFunctions);
}
//
// ensure it is not a nested group aggregate call
//
if (sr.CurrentScopeRegionFlags.GroupAggregateNestingCount < -1)
{
throw EntityUtil.EntitySqlError(methodExpr.MethodIdentifier.ErrCtx, System.Data.Entity.Strings.InvalidNestedGroupAggregateCall);
}
//
// aggregate functions in the current release can have only one argument and must of of collectio type
//
Debug.Assert((1 == functionType.Parameters.Count), "(1 == functionType.Parameters.Count)"); // we only support monadic aggregate functions
Debug.Assert(TypeSemantics.IsCollectionType(functionType.Parameters[0].TypeUsage), "functionType.Parameters[0].Type is CollectionType");
TypeUsage argumentType = TypeHelpers.GetElementTypeUsage(functionType.Parameters[0].TypeUsage);
if (TypeSemantics.IsNullType(args[0].ResultType))
{
args[0] = sr.CmdTree.CreateNullExpression(argumentType);
}
//
// create function aggregate expression
//
DbFunctionAggregate functionAggregate;
// create distinct expression if espeficied
if (methodExpr.DistinctKind == DistinctKind.Distinct)
{
functionAggregate = sr.CmdTree.CreateDistinctFunctionAggregate(functionType, args[0]);
}
else
{
functionAggregate = sr.CmdTree.CreateFunctionAggregate(functionType, args[0]);
}
//
// generate name for the aggregate 'property'. this name will the internal name of the pre-computed expression in the scope and
// annotated ast node in the ast tree
//
string internalAggregateName = sr.GenerateInternalName("groupAgg" + functionType.Name);
//
// add aggreate to aggreate list
//
AggregateAstNodeInfo aggrAstInfo = sr.PopAggregateAstNode();
aggrAstInfo.AssertMethodExprEquivalent(methodExpr);
sr.AddGroupAggregateInfoToScopeRegion(methodExpr, internalAggregateName, functionAggregate, aggrAstInfo.ScopeIndex);
//
// return 'dummy' expression with same type as aggregate function
//
converted = sr.CmdTree.CreateNullExpression(functionType.ReturnParameter.TypeUsage);
//
// anotate method expression node as aggregate
//
methodExpr.SetAggregateInfo(internalAggregateName, converted);
//
// restore visibility to group scope only
//
sr.SetScopeView(saveScopeView);
//
// increment nesting count
//
sr.CurrentScopeRegionFlags.IncrementGroupAggregateNestingCount();
return true;
}
///
/// Converted a instance method.
///
///
///
///
///
///
private static DbExpression ConvertMethodInstance( DbExpression baseExpr, MethodExpr methodExpr, int prefixIndex, SemanticResolver sr )
{
DbExpression converted = null;
Debug.Assert(null != baseExpr,"null != baseExpr");
DotExpr dotExpr = methodExpr.MethodPrefixExpr;
//
// ensure methods are not called on Scalar type instances
//
if (TypeSemantics.IsPrimitiveType(baseExpr.ResultType))
{
throw EntityUtil.EntitySqlError(dotExpr.LeftMostExpression.ErrCtx, System.Data.Entity.Strings.MethodNotAllowedOnScalars);
}
//
// build the instance property references chain up to the method name
//
DbExpression innerExpression = baseExpr;
for (int i = prefixIndex ; i < dotExpr.Length - 1 ; i++)
{
innerExpression = sr.ResolveIdentifierElement(innerExpression.ResultType, innerExpression, dotExpr.Names[i], dotExpr.ErrCtx);
//
// if this point is reached, means that an element in the path name is not a valid property
// such as name[ i + 1 ] is not a valid property/member of defining type resolved previosly for name[ i ]
//
if (null == innerExpression)
{
throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidMethodPathElement(dotExpr.Names[i], innerExpression.ResultType.EdmType.FullName));
}
}
//
// convert method arguments
//
List args = ConvertFunctionArguments(methodExpr.Args, sr);
//
// Resolve/Validate overloads and create method expression
//
converted = SemanticResolver.CreateInstanceMethod(innerExpression, args, methodExpr);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts EdmFunction Arguments representes a list of astExpr nodes
///
///
///
///
private static List ConvertFunctionArguments( ExprList astExprList, SemanticResolver sr )
{
List convertedArgs = new List();
if (null != astExprList)
{
for (int i = 0 ; i < astExprList.Count ; i++)
{
convertedArgs.Add(Convert(astExprList[i], sr));
}
}
return convertedArgs;
}
///
/// Convert Paramerters
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertParameter( Expr expr, SemanticResolver sr )
{
Parameter parameter = (Parameter)expr;
TypeUsage paramType = null;
KeyValuePair varInfo;
if (sr.Variables != null && sr.Variables.TryGetValue(parameter.Name, out varInfo))
{
return sr.CmdTree.CreateVariableReferenceExpression(varInfo.Key, varInfo.Value);
}
if (null == sr.Parameters || !sr.Parameters.TryGetValue(parameter.Name, out paramType))
{
throw EntityUtil.EntitySqlError(parameter.ErrCtx, System.Data.Entity.Strings.ParameterWasNotDefined(parameter.Name));
}
sr.CmdTree.AddParameter(parameter.Name, TypeHelpers.GetReadOnlyType(paramType));
return sr.CmdTree.CreateParameterReferenceExpression(parameter.Name);
}
///
/// Validate a relationship-traversal - used for both Navigate expressions
/// and for entity construction with related entity refs.
///
/// For "related entity refs", "isTargetEnd" is true - for Navigate expressions,
/// this parameter is "false".
///
///
/// the relationshipExpr AST
///
/// resolver context
/// the source/target expression
/// the relationship type
/// from end of the relationship
/// to-end of the relationship
private static void ValidateRelationshipTraversal(RelshipNavigationExpr relshipExpr,
bool isTargetEnd,
SemanticResolver sr,
out DbExpression refExpr,
out RelationshipType relationshipType,
out RelationshipEndMember refEnd,
out RelationshipEndMember otherEnd)
{
relationshipType = null;
refEnd = null;
otherEnd = null;
refExpr = null;
//
// resolve relationship type
//
relationshipType = sr.ResolveNameAsType(relshipExpr.RelationTypeNames, relshipExpr.RelationTypeNameIdentifier).EdmType as RelationshipType;
if (null == relationshipType)
{
throw EntityUtil.EntitySqlError(relshipExpr.RelationTypeNameIdentifier.ErrCtx, System.Data.Entity.Strings.InvalidRelationshipTypeName(relshipExpr.RelationTypeFullName));
}
//
// convert relationship 'instance' expression
//
refExpr = Convert(relshipExpr.RelationshipSource, sr);
//
// if is entity, create entity ref out if it
//
if (!isTargetEnd && TypeSemantics.IsEntityType(refExpr.ResultType))
{
refExpr = sr.CmdTree.CreateEntityRefExpression(refExpr);
}
//
// make sure is ref type
//
if (!TypeSemantics.IsReferenceType(refExpr.ResultType))
{
throw EntityUtil.EntitySqlError(relshipExpr.RelationshipSource.ErrCtx, System.Data.Entity.Strings.InvalidRelationshipSourceType);
}
//
// ensure entity 'participates' in the given relationship type
//
if (!TypeSemantics.IsTypeValidForRelationship(TypeHelpers.GetElementTypeUsage(refExpr.ResultType), relationshipType))
{
throw EntityUtil.EntitySqlError(relshipExpr.RelationTypeNameIdentifier.ErrCtx, System.Data.Entity.Strings.RelationshipTypeIsNotCompatibleWithEntity(
TypeHelpers.GetFullName(TypeHelpers.GetElementTypeUsage(refExpr.ResultType)),
TypeHelpers.GetFullName(relationshipType)));
}
//
// ensure relationship ends are valid
// metadata ensures that there will never happen to have two equal end names
//
TypeUsage fromEndType = null;
int fromEndMatchCount = 0;
TypeUsage elementType = TypeHelpers.GetElementTypeUsage(refExpr.ResultType);
for (int i = 0; i < relationshipType.Members.Count; i++)
{
//
// check 'to' end
//
if (relationshipType.Members[i].Name.Equals(relshipExpr.ToEndIdentifierName, StringComparison.OrdinalIgnoreCase))
{
otherEnd = (RelationshipEndMember)relationshipType.Members[i];
continue;
}
//
// check 'from' end
//
if (
(null != relshipExpr.FromEndIdentifier && relationshipType.Members[i].Name.Equals(relshipExpr.FromEndIdentifierName, StringComparison.OrdinalIgnoreCase)) ||
(null == relshipExpr.FromEndIdentifier && TypeSemantics.IsEquivalentOrPromotableTo(elementType, TypeHelpers.GetElementTypeUsage(relationshipType.Members[i].TypeUsage)))
)
{
fromEndMatchCount++;
if (fromEndMatchCount > 1)
{
ErrorContext errCtx = (null == relshipExpr.FromEndIdentifier) ? relshipExpr.ErrCtx : relshipExpr.FromEndIdentifier.ErrCtx;
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.RelationshipFromEndIsAmbiguos);
}
refEnd = (RelationshipEndMember)relationshipType.Members[i];
fromEndType = relationshipType.Members[i].TypeUsage;
}
}
//
// ensure relationship 'To' end contains given property
//
if (null == otherEnd)
{
if (null != relshipExpr.ToEndIdentifier)
{
throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, System.Data.Entity.Strings.InvalidRelationshipMember(relshipExpr.ToEndIdentifierName, relshipExpr.RelationTypeFullName));
}
if (2 != relationshipType.Members.Count)
{
throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, System.Data.Entity.Strings.InvalidImplicitRelationshipToEnd(relshipExpr.RelationTypeFullName));
}
Debug.Assert(null != refEnd, "null!=fromEnd");
otherEnd = (RelationshipEndMember)(refEnd.Name.Equals(relationshipType.Members[0].Name, StringComparison.OrdinalIgnoreCase) ? relationshipType.Members[1] : relationshipType.Members[0]);
}
//
// ensure relationship 'From' end contains given entity
//
if (null == refEnd || null == fromEndType)
{
ErrorContext errCtx = (null == relshipExpr.FromEndIdentifier) ? relshipExpr.ErrCtx : relshipExpr.FromEndIdentifier.ErrCtx;
if (null == relshipExpr.FromEndIdentifier)
{
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.InvalidImplicitRelationshipFromEnd(relshipExpr.RelationTypeFullName));
}
else
{
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.InvalidRelationshipMember(relshipExpr.FromEndIdentifierName, relshipExpr.RelationTypeFullName));
}
}
//
// check if source is promotable to from end type
//
if (!TypeSemantics.IsValidPolymorphicCast(TypeHelpers.GetElementTypeUsage(refExpr.ResultType),
TypeHelpers.GetElementTypeUsage(refEnd.TypeUsage)))
{
ErrorContext errCtx = (null == relshipExpr.FromEndIdentifier) ? relshipExpr.ErrCtx : relshipExpr.FromEndIdentifier.ErrCtx;
throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.SourceTypeMustBePromotoableToFromEndRelationType(TypeHelpers.GetElementTypeUsage(refExpr.ResultType).EdmType.FullName, TypeHelpers.GetElementTypeUsage(fromEndType).EdmType.FullName));
}
return;
}
///
/// Build out a RelatedEntityRef
///
/// the ast expression
/// the Semantic Resolver context
/// a DbRelatedEntityRef instance
private static DbRelatedEntityRef ConvertRelatedEntityRef(RelshipNavigationExpr relshipExpr, SemanticResolver sr)
{
//
// Validate the relationship traversal
//
DbExpression targetRef;
RelationshipEndMember targetRefEnd;
RelationshipEndMember otherEnd;
RelationshipType relationshipType;
ValidateRelationshipTraversal(relshipExpr,
true /* targetEnd */ ,
sr,
out targetRef,
out relationshipType,
out targetRefEnd,
out otherEnd);
//
// ensure is *..{0|1}
//
if (RelationshipMultiplicity.One != targetRefEnd.RelationshipMultiplicity &&
RelationshipMultiplicity.ZeroOrOne != targetRefEnd.RelationshipMultiplicity)
{
throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx,
System.Data.Entity.Strings.InvalidWithRelationshipTargetEndMultiplicity(targetRefEnd.Identity,
targetRefEnd.RelationshipMultiplicity.ToString()));
}
DbRelatedEntityRef relatedEntityRef = sr.CmdTree.CreateRelatedEntityRef(otherEnd, targetRefEnd, targetRef);
return relatedEntityRef;
}
///
/// converts Relationship Navigation operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertRelshipNavigationExpr( Expr astExpr, SemanticResolver sr )
{
RelshipNavigationExpr relshipExpr = (RelshipNavigationExpr)astExpr;
//
// Validate the relationship traversal
//
DbExpression relationshipSource;
RelationshipEndMember fromEnd;
RelationshipEndMember toEnd;
RelationshipType relationshipType;
ValidateRelationshipTraversal(relshipExpr,
false /* !targetEnd */,
sr,
out relationshipSource,
out relationshipType,
out fromEnd,
out toEnd);
//
// create cqt expression
//
DbExpression converted = sr.CmdTree.CreateRelationshipNavigationExpression(fromEnd, toEnd, relationshipSource);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts REF operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertRefExpr( Expr astExpr, SemanticResolver sr )
{
RefExpr refExpr = (RefExpr)astExpr;
DbExpression converted = Convert(refExpr.RefArgExpr, sr);
//
// check if is entity type
//
if (!TypeSemantics.IsEntityType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(refExpr.RefArgExpr.ErrCtx, System.Data.Entity.Strings.RefArgIsNotOfEntityType(converted.ResultType.EdmType.FullName));
}
//
// create ref expression
//
converted = sr.CmdTree.CreateEntityRefExpression(converted);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts DEREF operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertDeRefExpr( Expr astExpr, SemanticResolver sr )
{
DerefExpr deRefExpr = (DerefExpr)astExpr;
DbExpression converted = null;
converted = Convert(deRefExpr.RefExpr, sr);
//
// check if return type is RefType
//
if (!TypeSemantics.IsReferenceType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(deRefExpr.RefExpr.ErrCtx, System.Data.Entity.Strings.DeRefArgIsNotOfRefType(converted.ResultType.EdmType.FullName));
}
//
// create DeRef expression
//
converted = sr.CmdTree.CreateDerefExpression(converted);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts CREATEREF operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertCreateRefExpr( Expr astExpr, SemanticResolver sr )
{
CreateRefExpr createRefExpr = (CreateRefExpr)astExpr;
DbExpression converted = null;
//
// Convert the entity set, also, ensure that we get back an extent expression
//
DbScanExpression entitySetExpr = Convert(createRefExpr.EntitySet, sr) as DbScanExpression;
if (entitySetExpr == null)
{
throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, System.Data.Entity.Strings.ExprIsNotValidEntitySetForCreateRef);
}
//
// Ensure that the extent is an entity set
//
EntitySet entitySet = entitySetExpr.Target as EntitySet;
if (entitySet == null)
{
throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, System.Data.Entity.Strings.ExprIsNotValidEntitySetForCreateRef);
}
DbExpression keyRowExpression = Convert(createRefExpr.Keys, sr);
SemanticResolver.EnsureIsNotUntypedNull(keyRowExpression, createRefExpr.Keys.ErrCtx);
RowType inputKeyRowType = keyRowExpression.ResultType.EdmType as RowType;
if (null == inputKeyRowType)
{
throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx,System.Data.Entity.Strings.InvalidCreateRefKeyType);
}
RowType entityKeyRowType = TypeHelpers.CreateKeyRowType(entitySet.ElementType, sr.CmdTree.MetadataWorkspace);
if (entityKeyRowType.Members.Count != inputKeyRowType.Members.Count)
{
throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, System.Data.Entity.Strings.ImcompatibleCreateRefKeyType);
}
if (!TypeSemantics.IsEquivalentOrPromotableTo(keyRowExpression.ResultType, TypeUsage.Create(entityKeyRowType)))
{
throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, System.Data.Entity.Strings.ImcompatibleCreateRefKeyElementType);
}
//
// if CREATEREF specifies a type, resolve and validate the type
//
if (null != createRefExpr.TypeIdentifier)
{
TypeUsage targetTypeUsage = ConvertTypeIdentifier(createRefExpr.TypeIdentifier, sr);
//
// ensure type is entity
//
if (!TypeSemantics.IsEntityType(targetTypeUsage))
{
throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx,
System.Data.Entity.Strings.CreateRefTypeIdentifierMustSpecifyAnEntityType(
targetTypeUsage.EdmType.Identity,
targetTypeUsage.EdmType.BuiltInTypeKind.ToString()));
}
if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, targetTypeUsage.EdmType))
{
throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx,
System.Data.Entity.Strings.CreateRefTypeIdentifierMustBeASubOrSuperType(
entitySet.ElementType.Identity,
targetTypeUsage.EdmType.FullName));
}
converted = sr.CmdTree.CreateRefExpression(entitySet,
keyRowExpression,
(EntityType)targetTypeUsage.EdmType);
}
else
{
//
// finally creates the expression
//
converted = sr.CmdTree.CreateRefExpression(entitySet, keyRowExpression);
}
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// converts KEY operator
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertKeyExpr( Expr astExpr, SemanticResolver sr )
{
KeyExpr keyExpr = (KeyExpr)astExpr;
DbExpression converted = Convert(keyExpr.RefExpr, sr);
SemanticResolver.EnsureIsNotUntypedNull(converted, keyExpr.RefExpr.ErrCtx);
if (TypeSemantics.IsEntityType(converted.ResultType))
{
converted = sr.CmdTree.CreateEntityRefExpression(converted);
}
else if (!TypeSemantics.IsReferenceType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(keyExpr.RefExpr.ErrCtx,System.Data.Entity.Strings.InvalidKeyArgument(TypeHelpers.GetFullName(converted.ResultType)));
}
converted = sr.CmdTree.CreateRefKeyExpression(converted);
Debug.Assert(null != converted, "null != converted");
return converted;
}
///
/// Dispatches/Converts BuiltIn Expressions
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertBuiltIn( Expr astExpr, SemanticResolver sr )
{
if (null == astExpr)
{
return null;
}
BuiltInExpr bltInExpr = (BuiltInExpr)astExpr;
BuiltInExprConverter builtInConverter = _builtInExprConverter[bltInExpr.Kind];
if (null == builtInConverter)
{
throw EntityUtil.Argument(System.Data.Entity.Strings.UnknownBuiltInAstExpressionType);
}
DbExpression converted = builtInConverter(bltInExpr, sr);
Debug.Assert(null != converted,"null != converted");
return converted;
}
///
/// Converts Arithmetic Expressions Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertArithmeticArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (!(TypeSemantics.IsNumericType(leftExpr.ResultType) || SemanticResolver.IsNullExpression(leftExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionMustBeNumericType);
}
DbExpression rightExpr = null;
if (null != astBuiltInExpr.Arg2)
{
rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (!(TypeSemantics.IsNumericType(rightExpr.ResultType) || SemanticResolver.IsNullExpression(rightExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.ExpressionMustBeNumericType);
}
if (null == TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
}
}
return sr.EnsureTypedNulls(leftExpr, rightExpr, astBuiltInExpr.ErrCtx, () => Strings.InvalidNullArithmetic);
}
///
/// Converts Plus Args - specific case since string type is an allowed type for '+'
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertPlusOperands( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (!(TypeSemantics.IsNumericType(leftExpr.ResultType) ||
TypeSemantics.IsPrimitiveType(leftExpr.ResultType,PrimitiveTypeKind.String) ||
SemanticResolver.IsNullExpression(leftExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.PlusLeftExpressionInvalidType);
}
DbExpression rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (!(TypeSemantics.IsNumericType(rightExpr.ResultType) ||
TypeSemantics.IsPrimitiveType(rightExpr.ResultType, PrimitiveTypeKind.String) ||
SemanticResolver.IsNullExpression(rightExpr)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.PlusRightExpressionInvalidType);
}
if (null == TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
}
return sr.EnsureTypedNulls(leftExpr, rightExpr, astBuiltInExpr.ErrCtx, () => Strings.InvalidNullArithmetic);
}
///
/// Converts Logical Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertLogicalArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (leftExpr is UntypedNullExpression)
{
leftExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType);
}
DbExpression rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (rightExpr is UntypedNullExpression)
{
rightExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType);
}
//
// ensure left expression type is boolean
//
if (!TypeResolver.IsBooleanType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
//
// ensure right expression type is boolean
//
if (null != rightExpr && !TypeResolver.IsBooleanType(rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
return new Pair(leftExpr, rightExpr);
}
///
/// Converts Equal Comparison Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertEqualCompArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
//
// convert left and right types and infer null types
//
Pair compArgs = sr.EnsureTypedNulls( Convert(astBuiltInExpr.Arg1, sr),
Convert(astBuiltInExpr.Arg2, sr),
astBuiltInExpr.ErrCtx,
() => Strings.InvalidNullComparison);
//
// ensure both operand types are equal-comparable
//
if (!TypeSemantics.IsEqualComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName));
}
return compArgs;
}
///
/// Converts Order Comparison Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertOrderCompArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
Pair compArgs = sr.EnsureTypedNulls(
Convert(astBuiltInExpr.Arg1, sr),
Convert(astBuiltInExpr.Arg2, sr),
astBuiltInExpr.ErrCtx,
() => Strings.InvalidNullComparison);
//
// ensure both operand types are order-comparable
//
if (!TypeSemantics.IsOrderComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.ArgumentTypesAreIncompatible(compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName));
}
return compArgs;
}
///
/// Converts Set Expression Args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertSetArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
//
// convert left expression
//
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
//
// convert right expression if binary set op kind
//
DbExpression rightExpr = null;
if (null != astBuiltInExpr.Arg2)
{
//
// binary set op
//
//
// make sure left expression type is of sequence type (ICollection or Extent)
//
if (!TypeSemantics.IsCollectionType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.LeftSetExpressionArgsMustBeCollection);
}
//
// convert right expression
//
rightExpr = Convert(astBuiltInExpr.Arg2, sr);
//
// make sure right expression type is of sequence type (ICollection or Extent)
//
if (!TypeSemantics.IsCollectionType(rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.RightSetExpressionArgsMustBeCollection);
}
TypeUsage commonType;
TypeUsage leftElemType = TypeHelpers.GetElementTypeUsage(leftExpr.ResultType);
TypeUsage rightElemType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType);
if (!TypeSemantics.TryGetCommonType(leftElemType, rightElemType, out commonType))
{
CqlErrorHelper.ReportIncompatibleCommonType(astBuiltInExpr.ErrCtx, leftElemType, rightElemType);
}
if (astBuiltInExpr.Kind != BuiltInKind.UnionAll)
{
//
// ensure left argument is set op comparable
//
if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.PlaceholderSetArgTypeIsNotEqualComparable(
astBuiltInExpr.Kind.ToString().ToUpperInvariant(),
System.Data.Entity.Strings.LocalizedLeft,
TypeHelpers.GetElementTypeUsage(leftExpr.ResultType).EdmType.FullName));
}
//
// ensure right argument is set op comparable
//
if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(rightExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.PlaceholderSetArgTypeIsNotEqualComparable(
astBuiltInExpr.Kind.ToString().ToUpperInvariant(),
System.Data.Entity.Strings.LocalizedRight,
TypeHelpers.GetElementTypeUsage(rightExpr.ResultType).EdmType.FullName));
}
}
else
{
if (Helper.IsAssociationType(leftElemType.EdmType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidAssociationTypeForUnion(leftElemType.Identity));
}
if (Helper.IsAssociationType(rightElemType.EdmType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.InvalidAssociationTypeForUnion(rightElemType.Identity));
}
}
}
else
{
//
// unary set op
//
//
// make sure expression type is of sequence type (ICollection or Extent)
//
if (!TypeSemantics.IsCollectionType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidUnarySetOpArgument(astBuiltInExpr.Name));
}
//
// make sure that if is distinct unary operator, arg element type must be equal-comparable
//
if (astBuiltInExpr.Kind == BuiltInKind.Distinct && !TypeHelpers.IsValidDistinctOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeEqualComparable);
}
}
return new Pair(leftExpr, rightExpr);
}
///
/// Converts Set 'IN' expression args
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static Pair ConvertInExprArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
DbExpression leftExpr = Convert(astBuiltInExpr.Arg1, sr);
if (TypeSemantics.IsCollectionType(leftExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustNotBeCollection);
}
DbExpression rightExpr = Convert(astBuiltInExpr.Arg2, sr);
if (!TypeSemantics.IsCollectionType(rightExpr.ResultType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.RightSetExpressionArgsMustBeCollection);
}
//
// if left expression type is null, infer its type from the collection element type
//
if (SemanticResolver.IsNullExpression(leftExpr))
{
TypeUsage elementType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType);
SemanticResolver.EnsureValidTypeForNullExpression(elementType, astBuiltInExpr.Arg1.ErrCtx);
leftExpr = sr.CmdTree.CreateNullExpression(elementType);
}
else
{
//
// ensure that if left and right are typed expressions then their types must be comparable for IN op
//
TypeUsage commonElemType = TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, TypeHelpers.GetElementTypeUsage(rightExpr.ResultType));
if (null == commonElemType || !TypeHelpers.IsValidInOpType(commonElemType))
{
throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, System.Data.Entity.Strings.InvalidInExprArgs(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
}
}
return new Pair(leftExpr, rightExpr);
}
///
/// Converts Type Expression Args
///
///
/// SemanticResolver instance relative to a specific typespace/system
///
private static Pair ConvertTypeExprArgs( BuiltInExpr astBuiltInExpr, SemanticResolver sr )
{
return new Pair(Convert(astBuiltInExpr.Arg1, sr),
ConvertTypeIdentifier(astBuiltInExpr.Arg2, sr));
}
///
/// Converts TypeIdentifier
/// TypeIdentifier can have the 'shape' of a simple identifier (product), a dotted identifier (namespace.bar) or a methodExpr ( edm.decimal(10,4) )
///
///
///
///
private static TypeUsage ConvertTypeIdentifier(Expr typeIdentifierExpr, SemanticResolver sr)
{
MethodExpr methodExpr = null;
string[] typeNames;
//
// if it is dot id get full id name
//
DotExpr dotExpr = typeIdentifierExpr as DotExpr;
if (null != dotExpr && dotExpr.IsDottedIdentifier)
{
typeNames = dotExpr.Names;
}
else
{
//
// method expression, type with parameters
//
methodExpr = typeIdentifierExpr as MethodExpr;
if (null != methodExpr)
{
typeNames = methodExpr.MethodPrefixExpr.Names;
Debug.Assert(methodExpr.Args.Count == 1 || methodExpr.Args.Count == 2, "decimal type must have one or two arguments");
Debug.Assert(methodExpr.Args[0] is Literal, "type expression must have literal arg");
Debug.Assert((methodExpr.Args.Count == 2) ? methodExpr.Args[1] is Literal : true, "type expression must have literal arg");
}
else
{
//
// if it is single id, get it is name
//
Identifier id = typeIdentifierExpr as Identifier;
if (null != id)
{
typeNames = new string[] { id.Name };
}
else
{
throw EntityUtil.EntitySqlError(typeIdentifierExpr.ErrCtx, System.Data.Entity.Strings.InvalidTypeNameExpression);
}
}
}
//
// Resolve type
//
TypeUsage typeUsage = sr.ResolveNameAsType(typeNames, typeIdentifierExpr);
return typeUsage;
}
///
/// Converts Row type constructor expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertRowConstructor( Expr expr, SemanticResolver sr )
{
RowConstructorExpr rowExpr = (RowConstructorExpr)expr;
Dictionary rowColumns = new Dictionary(sr.ScopeStringComparer);
List fieldExprs = new List(rowExpr.AliasExprList.Count);
for (int i = 0 ; i < rowExpr.AliasExprList.Count ; i++)
{
AliasExpr aliasExpr = rowExpr.AliasExprList[i];
DbExpression colExpr = Convert(aliasExpr.Expr, sr);
string aliasName = sr.InferAliasName(aliasExpr, colExpr);
if (rowColumns.ContainsKey(aliasName))
{
if (aliasExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InRowConstructor);
}
else
{
aliasName = sr.GenerateInternalName("autoRowCol");
}
}
if (SemanticResolver.IsNullExpression(colExpr))
{
throw EntityUtil.EntitySqlError(aliasExpr.Expr.ErrCtx, System.Data.Entity.Strings.RowCtorElementCannotBeNull);
}
rowColumns.Add(aliasName, colExpr.ResultType);
fieldExprs.Add(colExpr);
}
return sr.CmdTree.CreateNewInstanceExpression(TypeHelpers.CreateRowTypeUsage(rowColumns, true /* readOnly */), fieldExprs);
}
///
/// Converts Multiset type constructor expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertMultisetConstructor( Expr expr, SemanticResolver sr )
{
MultisetConstructorExpr msetCtor = (MultisetConstructorExpr)expr;
if (null == msetCtor.ExprList)
{
throw EntityUtil.EntitySqlError(expr.ErrCtx, System.Data.Entity.Strings.CannotCreateEmptyMultiset);
}
PairOfLists mSetExprs = ProcessExprList(msetCtor.ExprList, sr);
TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(mSetExprs.Left);
//
// ensure all elems have a common type
//
if (null == commonType)
{
throw EntityUtil.EntitySqlError(expr.ErrCtx, System.Data.Entity.Strings.MultisetElemsAreNotTypeCompatible);
}
//
// ensure common type is not an untyped null
//
if (TypeSemantics.IsNullType(commonType))
{
throw EntityUtil.EntitySqlError(expr.ErrCtx, System.Data.Entity.Strings.CannotCreateMultisetofNulls);
}
commonType = TypeHelpers.GetReadOnlyType(commonType);
//
// fixup untyped nulls
//
for (int i = 0 ; i < mSetExprs.Count ; i++)
{
if (SemanticResolver.IsNullExpression(mSetExprs.Right[i]))
{
SemanticResolver.EnsureValidTypeForNullExpression(commonType, msetCtor.ExprList[i].ErrCtx);
mSetExprs.Right[i] = sr.CmdTree.CreateNullExpression(commonType);
}
}
return sr.CmdTree.CreateNewInstanceExpression(TypeHelpers.CreateCollectionTypeUsage(commonType, true /* readOnly */), mSetExprs.Right);
}
///
/// converts case-when-then expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertCaseExpr( Expr expr, SemanticResolver sr )
{
CaseExpr caseExpr = (CaseExpr)expr;
List whenExprList = new List(caseExpr.WhenThenExprList.Count);
PairOfLists thenExprList = new PairOfLists();
//
// Resolve then expressions
//
for (int i = 0 ; i < caseExpr.WhenThenExprList.Count ; i++)
{
WhenThenExpr whenThenExpr = caseExpr.WhenThenExprList[i];
DbExpression whenExpression = Convert(whenThenExpr.WhenExpr, sr);
if (!TypeResolver.IsBooleanType(whenExpression.ResultType))
{
throw EntityUtil.EntitySqlError(whenThenExpr.WhenExpr.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
whenExprList.Add(whenExpression);
DbExpression thenExpression = Convert(whenThenExpr.ThenExpr, sr);
thenExprList.Add(thenExpression, thenExpression.ResultType);
}
TypeUsage resultType = TypeHelpers.GetCommonTypeUsage(thenExprList.Right);
if (null == resultType)
{
throw EntityUtil.EntitySqlError(caseExpr.WhenThenExprList.Expressions[0].ThenExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseThenTypes);
}
if ((null == caseExpr.ElseExpr) && TypeSemantics.IsNullType(resultType))
{
throw EntityUtil.EntitySqlError(caseExpr.WhenThenExprList.Expressions[0].ThenExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseThenNullType);
}
//
// Converts else if present
//
DbExpression elseExpr = null;
if (null != caseExpr.ElseExpr)
{
elseExpr = Convert(caseExpr.ElseExpr, sr);
resultType = TypeHelpers.GetCommonTypeUsage(resultType, elseExpr.ResultType);
if (null == resultType)
{
throw EntityUtil.EntitySqlError(caseExpr.ElseExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseElseType);
}
if (TypeSemantics.IsNullType(resultType))
{
throw EntityUtil.EntitySqlError(caseExpr.ElseExpr.ErrCtx, System.Data.Entity.Strings.InvalidCaseWhenThenNullType);
}
if (SemanticResolver.IsNullExpression(elseExpr))
{
SemanticResolver.EnsureValidTypeForNullExpression(resultType, caseExpr.ElseExpr.ErrCtx);
elseExpr = sr.CmdTree.CreateNullExpression(resultType);
}
}
else
{
if (TypeSemantics.IsCollectionType(resultType))
{
elseExpr = sr.CmdTree.CreateNewEmptyCollectionExpression(resultType);
}
else
{
SemanticResolver.EnsureValidTypeForNullExpression(resultType, caseExpr.ErrCtx);
elseExpr = sr.CmdTree.CreateNullExpression(resultType);
}
}
//
// fixup untyped nulls
//
for (int i = 0 ; i < thenExprList.Count ; i++)
{
if (SemanticResolver.IsNullExpression(thenExprList.Left[i]))
{
SemanticResolver.EnsureValidTypeForNullExpression(resultType, caseExpr.WhenThenExprList[i].ThenExpr.ErrCtx);
thenExprList[i] = new Pair(sr.CmdTree.CreateNullExpression(resultType), resultType);
}
}
return sr.CmdTree.CreateCaseExpression(whenExprList, thenExprList.Left, elseExpr);
}
///
/// Converts Query Expression
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpression ConvertQuery( Expr expr, SemanticResolver sr )
{
QueryExpr queryExpr = (QueryExpr)expr;
DbExpression converted = null;
bool isRestrictedViewGenerationMode = (ParserOptions.CompilationMode.RestrictedViewGenerationMode == sr.ParserOptions.ParserCompilationMode);
//
// Validate & Compensate Query
//
ValidateAndCompensateQuery(queryExpr);
//
// Create Source Scope Region
//
using (sr.EnterScopeRegion())
{
//
// Process From Clause
//
DbExpressionBinding sourceExpr = ProcessFromClause(queryExpr.FromClause, sr);
//
// Process Where Clause
//
sourceExpr = ProcessWhereClause(sourceExpr, queryExpr.WhereClause, sr);
Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.GroupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode");
Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.HavingClause : true, "HAVING clause must be null in RestrictedViewGenerationMode");
Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode");
if ( !isRestrictedViewGenerationMode )
{
//
// Process GroupBy Clause
//
sourceExpr = ProcessGroupByClause(sourceExpr, queryExpr, sr);
//
// Process Having Clause
//
sourceExpr = ProcessHavingClause(sourceExpr, queryExpr.HavingClause, sr);
//
// Process OrderBy Clause
//
sourceExpr = ProcessOrderByClause(sourceExpr, queryExpr, sr);
}
//
// Process Projection Clause
//
converted = ProcessSelectClause(sourceExpr, queryExpr, sr);
} // end query scope region
return converted;
}
///
/// Validates and Compensates query expression
///
///
private static void ValidateAndCompensateQuery( QueryExpr queryExpr )
{
if (null != queryExpr.HavingClause && null == queryExpr.GroupByClause)
{
throw EntityUtil.EntitySqlError(queryExpr.ErrCtx, System.Data.Entity.Strings.HavingRequiresGroupClause);
}
if (queryExpr.SelectClause.HasTopClause)
{
if ((null != queryExpr.OrderByClause) && queryExpr.OrderByClause.HasLimitSubClause)
{
throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, System.Data.Entity.Strings.TopAndLimitCannotCoexist);
}
if ((null != queryExpr.OrderByClause) && queryExpr.OrderByClause.HasSkipSubClause)
{
throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, System.Data.Entity.Strings.TopAndSkipCannotCoexist);
}
}
}
///
/// Process Select Clause
///
///
///
/// SemanticResolver instance relative to a especific typespace/system
///
private static DbExpression ProcessSelectClause( DbExpressionBinding source, QueryExpr queryExpr, SemanticResolver sr )
{
DbExpression projectExpression = null;
SelectClause selectClause = queryExpr.SelectClause;
HashSet projectionAliases = new HashSet(sr.ScopeStringComparer);
List> projFields = new List>(selectClause.Items.Count);
//
// if source is sort/skip expression, then skip projection conversion since it was already
// performed
//
if (queryExpr.OrderByClause != null && selectClause.DistinctKind == DistinctKind.Distinct)
{
projectExpression = source.Expression;
}
else
{
//
// Converts projection list
//
#region Process projection list
for (int i = 0; i < selectClause.Items.Count; i++)
{
AliasExpr selectExprItem = selectClause.Items[i];
DbExpression converted = Convert(selectExprItem.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, selectExprItem.Expr.ErrCtx);
//
// infer projection expression alias
//
string aliasName = sr.InferAliasName(selectExprItem, converted);
//
// ensure the alias was not used already
//
if (projectionAliases.Contains(aliasName))
{
if (selectExprItem.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, selectExprItem.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InSelectProjectionList);
}
else
{
aliasName = sr.GenerateInternalName("autoProject");
}
}
projectionAliases.Add(aliasName);
projFields.Add(new KeyValuePair(aliasName, converted));
}
#endregion
//
// VALUE projection
//
#region handles VALUE modifier
if (selectClause.SelectKind == SelectKind.SelectValue)
{
if (projFields.Count > 1)
{
throw EntityUtil.EntitySqlError(selectClause.ErrCtx, System.Data.Entity.Strings.InvalidSelectItem);
}
projectExpression = sr.CmdTree.CreateProjectExpression(source, projFields[0].Value);
}
else
{
projectExpression = sr.CmdTree.CreateProjectExpression(source, sr.CmdTree.CreateNewRowExpression(projFields));
}
#endregion
//
// handle DISTINCT modifier
//
#region DISTINCT
if (selectClause.DistinctKind == DistinctKind.Distinct)
{
//
// ensure element type is equal-comparable
//
SemanticResolver.ValidateDistinctProjection(selectClause, projectExpression.ResultType);
//
// create distinct expression
//
projectExpression = sr.CmdTree.CreateDistinctExpression(projectExpression);
}
#endregion
}
//
// TOP sub-clause
// NOTE: WITH TIES is not supported in M3.2
//
#region TOP/LIMIT sub-clause
if (selectClause.HasTopClause || ((null != queryExpr.OrderByClause) && queryExpr.OrderByClause.HasLimitSubClause))
{
ErrorContext errCtx = (selectClause.HasTopClause) ? selectClause.TopExpr.ErrCtx : queryExpr.OrderByClause.LimitSubClause.ErrCtx;
//
// convert top argument
//
DbExpression limitExpr = Convert((selectClause.HasTopClause) ? selectClause.TopExpr : queryExpr.OrderByClause.LimitSubClause, sr);
//
// ensure is not NULL expr
//
SemanticResolver.EnsureIsNotUntypedNull(limitExpr, errCtx);
Debug.Assert(limitExpr is DbConstantExpression || limitExpr is DbParameterReferenceExpression, "TOP/LIMIT inner expression must be a parameter or numeric literal in this release");
//
// ensure proper pre-conditions hold for TOP expression
//
sr.EnsureValidLimitExpression(
errCtx,
limitExpr,
(selectClause.HasTopClause) ? "TOP" : "LIMIT");
// create expression - WITH TIES is not supported in M3.2
projectExpression = sr.CmdTree.CreateLimitExpression(projectExpression, limitExpr);
}
#endregion
Debug.Assert(null != projectExpression,"null != projectExpr");
return projectExpression;
}
///
/// Process From Clause
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessFromClause( FromClause fromClause, SemanticResolver sr )
{
DbExpressionBinding fromBinding = null;
DbExpressionBinding fromBindingAux = null;
//
// Process Each From Clause Item
//
for (int i = 0 ; i < fromClause.FromClauseItems.Count ; i++ )
{
//
// Set Scope Source Var Kind
//
sr.SetCurrentScopeVarKind(fromClause.FromClauseItems[i].FromClauseItemKind);
//
// Convert From Clause
//
fromBindingAux = ProcessFromClauseItem(fromClause.FromClauseItems[i], sr);
//
// Reset All Vars to SourceVar Kind
//
sr.ResetCurrentScopeVarKind();
if (null == fromBinding)
{
fromBinding = fromBindingAux;
}
else
{
fromBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateCrossApplyExpression(fromBinding, fromBindingAux),
sr.GenerateInternalName("lcapply"));
sr.FixupNamedSourceVarBindings(fromBinding.Variable);
}
}
Debug.Assert(null != fromBinding,"null != fromBinding");
return fromBinding;
}
///
/// Process Generic From Clause Item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessFromClauseItem( FromClauseItem fromClauseItem, SemanticResolver sr )
{
DbExpressionBinding fromItemBinding = null;
AliasExpr aliasExpr = fromClauseItem.FromExpr as AliasExpr;
if (null != aliasExpr)
{
fromItemBinding = ProcessAliasedFromClauseItem(aliasExpr, sr);
}
else
{
JoinClauseItem joinClauseItem = fromClauseItem.FromExpr as JoinClauseItem;
if (null != joinClauseItem)
{
fromItemBinding = ProcessJoinClauseItem(joinClauseItem, sr);
}
else
{
fromItemBinding = ProcessApplyClauseItem((ApplyClauseItem)fromClauseItem.FromExpr, sr);
}
}
Debug.Assert(null != fromItemBinding,"null != fromItemBinding");
return fromItemBinding;
}
///
/// Process 'Simple' From Clause Item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessAliasedFromClauseItem( AliasExpr aliasedExpr, SemanticResolver sr )
{
DbExpressionBinding aliasedBinding = null;
//
// converts from item expression
//
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// validate it is of sequence type (Extent || ICollection)
//
if (!TypeSemantics.IsCollectionType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, System.Data.Entity.Strings.ExpressionMustBeCollection);
}
//
// infer source var alias name
//
string aliasName = sr.InferAliasName(aliasedExpr, converted);
//
// validate the name was not used yet.
//
if (sr.IsInCurrentScope(aliasName))
{
if (aliasedExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasedExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InFromClause);
}
else
{
aliasName = sr.GenerateInternalName("autoFrom");
}
}
//
// create cqt expression
//
aliasedBinding = sr.CmdTree.CreateExpressionBinding(converted, aliasName);
//
// add source var to scope
//
sr.AddSourceBinding(aliasedBinding);
Debug.Assert(null != aliasedBinding,"null != aliasedBinding");
return aliasedBinding;
}
///
/// process join clause item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessJoinClauseItem( JoinClauseItem joinClause, SemanticResolver sr )
{
DbExpressionBinding joinBinding = null;
//
// make sure inner join have on predicate AND cross join has no ON predicate
//
if (null == joinClause.OnExpr)
{
if (JoinKind.Inner == joinClause.JoinKind)
{
throw EntityUtil.EntitySqlError(joinClause.ErrCtx, System.Data.Entity.Strings.InnerJoinMustHaveOnPredicate);
}
}
else
{
if (JoinKind.Cross == joinClause.JoinKind)
{
throw EntityUtil.EntitySqlError(joinClause.OnExpr.ErrCtx, System.Data.Entity.Strings.InvalidPredicateForCrossJoin);
}
}
//
// Resolve Left Expression
//
sr.CurrentScopeRegionFlags.PathTagger.VisitLeftNode();
DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(joinClause.LeftExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// Resolve Right Expression
//
sr.CurrentScopeRegionFlags.IsInsideJoinOnPredicate = false;
sr.CurrentScopeRegionFlags.PathTagger.VisitRightNode();
DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(joinClause.RightExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// convert right outer to left outer
//
if (joinClause.JoinKind == JoinKind.RightOuter)
{
joinClause.JoinKind = JoinKind.LeftOuter;
DbExpressionBinding tmpExpr = leftBindingExpr;
leftBindingExpr = rightBindingExpr;
rightBindingExpr = tmpExpr;
}
//
// Resolve JoinType
//
DbExpressionKind joinKind = SemanticResolver.MapJoinKind(joinClause.JoinKind);
//
// Resolve ON
//
sr.CurrentScopeRegionFlags.IsInsideJoinOnPredicate = true;
DbExpression onExpr = null;
if (null == joinClause.OnExpr)
{
if (DbExpressionKind.CrossJoin != joinKind)
{
onExpr = sr.CmdTree.CreateTrueExpression();
}
}
else
{
onExpr = Convert(joinClause.OnExpr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(onExpr, joinClause.OnExpr.ErrCtx);
}
sr.CurrentScopeRegionFlags.IsInsideJoinOnPredicate = false;
//
// Create New Join
//
joinBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateJoinExpressionByKind(joinKind, onExpr, leftBindingExpr, rightBindingExpr),
sr.GenerateInternalName("join"));
//
// Fixup Join Source Vars in Scope
//
sr.FixupNamedSourceVarBindings(joinBinding.Variable);
Debug.Assert(null != joinBinding,"null != joinBinding");
return joinBinding;
}
///
/// Process apply clause item
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessApplyClauseItem( ApplyClauseItem applyClause, SemanticResolver sr )
{
DbExpressionBinding applyBinding = null;
//
// Resolve Left Expression
//
sr.CurrentScopeRegionFlags.PathTagger.VisitLeftNode();
DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(applyClause.LeftExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// Resolve Right Expression
//
sr.CurrentScopeRegionFlags.PathTagger.VisitRightNode();
DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(applyClause.RightExpr, sr);
sr.CurrentScopeRegionFlags.PathTagger.LeaveNode();
//
// Create Apply
//
applyBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateApplyExpressionByKind(SemanticResolver.MapApplyKind(applyClause.ApplyKind),
leftBindingExpr,
rightBindingExpr),
sr.GenerateInternalName("apply"));
//
// Fixup Apply Source Vars in Scope
//
sr.FixupNamedSourceVarBindings(applyBinding.Variable);
Debug.Assert(null != applyBinding,"null != applyBinding");
return applyBinding;
}
///
/// Process Where Expression
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessWhereClause( DbExpressionBinding source, Expr whereClause, SemanticResolver sr )
{
if (null == whereClause)
{
return source;
}
DbExpressionBinding whereBinding = null;
//
// Convert Where Condition
//
DbExpression filterConditionExpr = Convert(whereClause, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(filterConditionExpr, whereClause.ErrCtx);
//
// ensure the predicate type is boolean
//
if (!TypeResolver.IsBooleanType(filterConditionExpr.ResultType))
{
throw EntityUtil.EntitySqlError(whereClause.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
//
// Create New Filter Binding
//
whereBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateFilterExpression(source, filterConditionExpr),
sr.GenerateInternalName("where"));
//
// Fixup Bindings
//
sr.FixupSourceVarBindings(whereBinding.Variable);
Debug.Assert(null != whereBinding,"null != whereBinding");
return whereBinding;
}
///
/// Process Group By Clause
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessGroupByClause( DbExpressionBinding source, QueryExpr queryExpr, SemanticResolver sr )
{
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
GroupByClause groupByClause = queryExpr.GroupByClause;
Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == groupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode");
//
// if group expression is null, create a dummy and speculate that there are group aggregates in the remaining query expression
// if no group aggregate if found after partial evaluation of Having, OrderBy and Select in the 1st pass, rollback what we did
// and return source expression.
//
#region Define Implicit Group if needed
if (null == queryExpr.GroupByClause)
{
if (!queryExpr.HasMethodCall)
{
return source;
}
//
// if group expression is null create a dummy and speculate that there are group aggregates in the remaining query expression
// if no group aggregate if found after partial evaluation of Having, OrderBy and Select in the 1st pass, rollback what we did
// and return source expression.
//
sr.CurrentScopeRegionFlags.IsImplicitGroup = true;
}
else
{
sr.CurrentScopeRegionFlags.IsImplicitGroup = false;
}
#endregion
DbExpressionBinding groupBinding = null;
//
// Create Group Binding
//
DbGroupExpressionBinding groupExprBinding = sr.CmdTree.CreateGroupExpressionBinding(
source.Expression,
sr.GenerateInternalName("geb"),
sr.GenerateInternalName("group"));
//
// Update source scope vars
//
sr.FixupGroupSourceVarBindings(groupExprBinding.Variable, groupExprBinding.GroupVariable);
//
// convert group elements
//
#region Convert Group Key/Expressions
int groupKeysCount = (null != groupByClause ) ? groupByClause.GroupItems.Count : 0;
List> groupKeys = new List>(groupKeysCount);
HashSet groupKeyNames = new HashSet(sr.ScopeStringComparer);
List groupKeysForAggregates = new List(8);
if (!sr.CurrentScopeRegionFlags.IsImplicitGroup)
{
Debug.Assert(null != groupByClause, "groupByClause must not be null at this point");
for (int i = 0 ; i < groupKeysCount ; i++)
{
AliasExpr aliasedExpr = groupByClause.GroupItems[i];
sr.ResetScopeRegionCorrelationFlag();
//
// convert key expression (relative to DbGroupExpressionBinding.Var)
//
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// ensure group key expression is correlated
//
if (!sr.CurrentScopeRegionFlags.WasResolutionCorrelated)
{
throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, System.Data.Entity.Strings.KeyMustBeCorrelated("GROUP BY"));
}
//
// convert key expression (relative to DbGroupExpressionBinding.GroupVar)
// this is only needed because during the search for groupaggregates, group aggregates may
// refer to group keys.
//
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = true;
DbExpression groupKeyAggExpr = Convert(aliasedExpr.Expr, sr);
groupKeysForAggregates.Add(groupKeyAggExpr);
sr.CurrentScopeRegionFlags.IsInsideGroupAggregate = false;
//
// ensure keys are valid
//
if (!TypeHelpers.IsValidGroupKeyType(converted.ResultType))
{
throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, System.Data.Entity.Strings.GroupingKeysMustBeEqualComparable);
}
//
// infer alias name
//
string groupKeyAlias = sr.InferAliasName(aliasedExpr, converted);
//
// check if alias was already used
//
if (groupKeyNames.Contains(groupKeyAlias))
{
if (aliasedExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(groupKeyAlias, aliasedExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InGroupClause);
}
else
{
groupKeyAlias = sr.GenerateInternalName("autoGroup");
}
}
//
// add key name to alias dictionary
//
groupKeyNames.Add(groupKeyAlias);
//
// add key to keys collection
//
groupKeys.Add(new KeyValuePair(groupKeyAlias, converted));
//
// group keys should visible by their 'original' key expression name. All forms should be allowed:
// SELECT k FROM ... as p GROUP BY p.Price as k (explicit key alias) - handled above by InferAliasName()
// SELECT Price FROM ... as p GROUP BY p.Price (implicit alias - leading name) - handled above by InferAliasName()
// SELECT p.Price FROM ... as p GROUP BY p.Price (original key expression) - case handled in the code bellow
//
if (!aliasedExpr.HasAlias)
{
DotExpr dotExpr = aliasedExpr.Expr as DotExpr;
if (null != dotExpr && dotExpr.IsDottedIdentifier)
{
groupKeyAlias = dotExpr.FullName;
if (groupKeyNames.Contains(groupKeyAlias))
{
CqlErrorHelper.ReportAliasAlreadyUsedError(groupKeyAlias, dotExpr.ErrCtx, System.Data.Entity.Strings.InGroupClause);
}
groupKeyNames.Add(groupKeyAlias);
groupKeys.Add(new KeyValuePair(groupKeyAlias, converted));
groupKeysForAggregates.Add(groupKeyAggExpr);
}
}
}
}
#endregion
//
// save scope status
//
SavePoint savePoint = sr.CreateSavePoint();
//
// Push Group scope
//
sr.EnterScope();
//
// Add converted group variables/expressions to group scope
//
#region Add Converted Group Variables to Scope
//
// add 'dummy' keys to scope.
// this is needed since during the aggreagate search phase, keys may be referenced.
//
for (int i = 0 ; i < groupKeys.Count ; i++)
{
sr.AddDummyGroupKeyToScope(groupKeys[i].Key, groupKeys[i].Value, groupKeysForAggregates[i]);
}
//
// flags that we are inside a group scope
//
sr.CurrentScopeRegionFlags.IsInGroupScope = true;
#endregion
//
// Convert/Search Aggregates
// since aggregates can be defined in Having, OrderBy and/or Select clauses must be resolved as part of the group expression.
// The resolution of these clauses result in potential collection of resolved group aggregates and the actual resulting
// expression is ignored. These clauses will be then resolved as usual on a second pass.
//
#region Search for group Aggregates
//
// search for aggregates in HAVING clause
//
if (null != queryExpr.HavingClause && queryExpr.HavingClause.HasMethodCall)
{
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
DbExpression converted = Convert(queryExpr.HavingClause.HavingPredicate, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, queryExpr.HavingClause.ErrCtx);
}
//
// search for aggregates in SELECT clause
//
Dictionary sortExpr = null;
if ( null != queryExpr.OrderByClause || queryExpr.SelectClause.HasMethodCall )
{
sortExpr = new Dictionary(queryExpr.SelectClause.Items.Count, sr.ScopeStringComparer);
for ( int i = 0 ; i < queryExpr.SelectClause.Items.Count ; i++ )
{
AliasExpr aliasedExpr = queryExpr.SelectClause.Items[i];
//
// Reset Group aggregate nesting count
//
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
//
// convert projection item expression
//
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// create Null Expression with actual type
//
converted = sr.CmdTree.CreateNullExpression(converted.ResultType);
//
// infer alias
//
string aliasName = sr.InferAliasName(aliasedExpr, converted);
if ( sortExpr.ContainsKey(aliasName) )
{
if ( aliasedExpr.HasAlias )
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName,
aliasedExpr.AliasIdentifier.ErrCtx,
System.Data.Entity.Strings.InSelectProjectionList);
}
else
{
aliasName = sr.GenerateInternalName("autoProject");
}
}
sortExpr.Add(aliasName, converted);
}
}
//
// search for aggregates in ORDER BY clause
//
if (null != queryExpr.OrderByClause && queryExpr.OrderByClause.HasMethodCall)
{
//
// push projection key scope
//
sr.EnterScope();
//
// Add projection items to scope (it may be used in ORDER BY)
//
foreach ( KeyValuePair kvp in sortExpr )
{
sr.AddToScope(kvp.Key, new ProjectionScopeEntry(kvp.Key, kvp.Value));
}
//
// search for aggregates in ORDER BY clause
//
for (int i = 0 ; i < queryExpr.OrderByClause.OrderByClauseItem.Count ; i++)
{
OrderByClauseItem orderItem = queryExpr.OrderByClause.OrderByClauseItem[i];
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
sr.ResetScopeRegionCorrelationFlag();
DbExpression converted = Convert(orderItem.OrderExpr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, orderItem.OrderExpr.ErrCtx);
//
// ensure key expression is correlated
//
if (!sr.CurrentScopeRegionFlags.WasResolutionCorrelated)
{
throw EntityUtil.EntitySqlError(orderItem.ErrCtx, System.Data.Entity.Strings.KeyMustBeCorrelated("ORDER BY"));
}
}
//
// pop projection scope
//
sr.LeaveScope();
}
#endregion
//
// if we introduced a fake group but did not 'found' any group aggregate
// on the first pass, then there is no need for creating an implicit group.
// rollback to the status before entering ProcessGroupByClause().
// if we did find group aggregates, make sure all non-group aggregate function
// expressions refer to group scope variables only
//
#region Implicit Group Rollback
if (sr.CurrentScopeRegionFlags.IsImplicitGroup)
{
if (0 == sr.CurrentScopeRegionFlags.GroupAggregatesInfo.Count)
{
//
// rolls back scope status
//
sr.RollbackToSavepoint(savePoint);
//
// undo any group source fixups, re-applying the source var
//
sr.UndoFixupGroupSourceVarBindings(source.Variable);
//
// reset is inside group scope flag
//
sr.CurrentScopeRegionFlags.IsInGroupScope = false;
////
//// reset implict group flag
////
sr.CurrentScopeRegionFlags.IsImplicitGroup = false;
//
// restore scope view kind
//
sr.SetScopeView(saveScopeView);
//
// return the original source var binding
//
return source;
}
//
// now that we know that there are group aggregates in other expression, reset implict group flag to false
// since it is now considered a legitimate group
//
sr.CurrentScopeRegionFlags.IsImplicitGroup = false;
}
#endregion
//
// extract list of aggregates and names
//
List> aggregates = new List>(sr.CurrentScopeRegionFlags.GroupAggregatesInfo.Count);
foreach(KeyValuePair kvp in sr.CurrentScopeRegionFlags.GroupAggregatesInfo)
{
aggregates.Add(new KeyValuePair(kvp.Value.AggregateName, kvp.Value.AggregateExpression));
kvp.Key.ResetDummyExpression();
}
//
// Create Group Expression
//
groupBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateGroupByExpression(
groupExprBinding,
groupKeys,
aggregates),
sr.GenerateInternalName("group"));
//
// replace dummy keys with real keys
//
for (int i = 0 ; i < groupKeys.Count ; i++)
{
sr.ReplaceGroupVarInScope(groupKeys[i].Key, groupBinding.Variable);
}
//
// add aggregates to scope
//
for (int i = 0 ; i < aggregates.Count ; i++ )
{
sr.CurrentScopeRegionFlags.AddGroupAggregateToScopeFlags(aggregates[i].Key);
sr.AddGroupAggregateToScope(aggregates[i].Key, groupBinding.Variable);
}
//
// restrict group scope visibility
//
sr.SetScopeView(SemanticResolver.ScopeViewKind.GroupScope);
//
// fixup all source vars
//
sr.FixupNamedSourceVarBindings(groupBinding.Variable);
//
// Mark source vars as group input vars
//
sr.MarkGroupInputVars();
Debug.Assert(null != groupBinding,"null != groupBinding");
return groupBinding;
}
///
/// Process Having Clause
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessHavingClause( DbExpressionBinding source, HavingClause havingClause, SemanticResolver sr )
{
Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == havingClause : true, "HAVING clause must be null in RestrictedViewGenerationMode");
if (null == havingClause)
{
return source;
}
DbExpressionBinding havingBinding = null;
//
// Convert Having Expression
//
DbExpression filterConditionExpr = Convert(havingClause.HavingPredicate, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(filterConditionExpr, havingClause.ErrCtx);
//
// ensure having predicate of boolean type
//
if (!TypeResolver.IsBooleanType(filterConditionExpr.ResultType))
{
throw EntityUtil.EntitySqlError(havingClause.ErrCtx, System.Data.Entity.Strings.ExpressionTypeMustBeBoolean);
}
//
// Create New Filter Binding
//
havingBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateFilterExpression(source, filterConditionExpr),
sr.GenerateInternalName("having"));
//
// Fixup Bindings
//
sr.FixupSourceVarBindings(havingBinding.Variable);
Debug.Assert(null != havingBinding,"null != havingBinding");
return havingBinding;
}
///
/// Process Order By Clause
///
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static DbExpressionBinding ProcessOrderByClause( DbExpressionBinding source, QueryExpr queryExpr, SemanticResolver sr )
{
Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode");
if (null == queryExpr.OrderByClause)
{
return source;
}
DbExpressionBinding sortBinding = null;
OrderByClause orderByClause = queryExpr.OrderByClause;
SelectClause selectClause = queryExpr.SelectClause;
//
// Create a savepoint
//
SavePoint savePoint = sr.CreateSavePoint();
//
// perform partial conversion of SELECT statements
//
Dictionary projectionExpressions = new Dictionary(selectClause.Items.Count, sr.ScopeStringComparer);
for (int i = 0 ; i < selectClause.Items.Count ; i++)
{
AliasExpr aliasedExpr = selectClause.Items[i];
DbExpression converted = Convert(aliasedExpr.Expr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(converted, aliasedExpr.Expr.ErrCtx);
//
// infer projection alias
//
string aliasName = sr.InferAliasName(aliasedExpr, converted);
if (projectionExpressions.ContainsKey(aliasName))
{
if (aliasedExpr.HasAlias)
{
CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasedExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.InSelectProjectionList);
}
else
{
aliasName = sr.GenerateInternalName("autoProject");
}
}
projectionExpressions.Add(aliasName, converted);
}
//
// convert paging sub-clauses they if exists before adding projection list to scope
// NOTE: TOP, LIMIT and SKIP have nearly the same constraints in M3.2. in the future this is likely to change by allowing
// these sub-clauses to have generic expressions.
//
#region Handles SKIP sub-clause
DbExpression skipExpr = null;
if (orderByClause.HasSkipSubClause)
{
skipExpr = Convert(orderByClause.SkipSubClause, sr);
//
// ensure is not NULL expr
//
SemanticResolver.EnsureIsNotUntypedNull(skipExpr, orderByClause.SkipSubClause.ErrCtx);
DbConstantExpression constantExpr = skipExpr as DbConstantExpression;
Debug.Assert(constantExpr!=null || skipExpr is DbParameterReferenceExpression, "SKIP inner expression must be a parameter or numeric literal in this release");
//
// ensure SKIP expression have the right type
//
if (!TypeSemantics.IsPromotableTo(skipExpr.ResultType, sr.TypeResolver.Int64Type))
{
throw EntityUtil.EntitySqlError(orderByClause.SkipSubClause.ErrCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeCompatibleWithEdm64("SKIP", skipExpr.ResultType.EdmType.FullName));
}
//
// if it is a literal, make sure it has the correct value
//
if (null != constantExpr && System.Convert.ToInt64(constantExpr.Value, CultureInfo.InvariantCulture) < 0)
{
throw EntityUtil.EntitySqlError(orderByClause.SkipSubClause.ErrCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeGreaterThanOrEqualToZero("SKIP"));
}
}
#endregion
//
// Push scope for projection items
//
sr.EnterScope();
//
// Add projection items to scope
//
foreach (KeyValuePair kvp in projectionExpressions)
{
//
// if the reference expression is a group aggregate, then there is no need to add to scope
//
if (!sr.CurrentScopeRegionFlags.ContainsGroupAggregate(kvp.Key))
{
sr.AddToScope(kvp.Key, new ProjectionScopeEntry(kvp.Key, kvp.Value));
}
}
//
// save scope view
//
SemanticResolver.ScopeViewKind saveScopeView = sr.GetScopeView();
//
// If DISTICT was especified set visibility to current scope only
// if DISTINCT modifier is present, push the projection and distinct expression down bellow
// sort/skip expressions
//
if (selectClause.DistinctKind == DistinctKind.Distinct)
{
sr.SetScopeView(SemanticResolver.ScopeViewKind.CurrentScope);
List> projectionExpressionList = new List>(projectionExpressions);
//
// Create projection
//
DbExpression projectExpression;
if (selectClause.SelectKind == SelectKind.SelectRow)
{
projectExpression = sr.CmdTree.CreateNewRowExpression(projectionExpressionList);
}
else
{
Debug.Assert(selectClause.Items.Count == 1, "SELECT VALUE must have only one argument");
projectExpression = projectionExpressionList[0].Value;
}
projectExpression = sr.CmdTree.CreateProjectExpression(source, projectExpression);
//
// Ensure Projection is valid for DISTINCT modifier
//
SemanticResolver.ValidateDistinctProjection(selectClause, projectExpression.ResultType);
//
// create new source binding
//
source = sr.CmdTree.CreateExpressionBinding(sr.CmdTree.CreateDistinctExpression(projectExpression),
sr.GenerateInternalName("distinct"));
//
// replace Projection scope with new expression bindings
//
for (int i = 0; i < projectionExpressionList.Count; i++)
{
if (!sr.CurrentScopeRegionFlags.ContainsGroupAggregate(projectionExpressionList[i].Key))
{
// remove old scope var
sr.RemoveFromScope(projectionExpressionList[i].Key);
// create and add new source var to scope
SourceScopeEntry sce = new SourceScopeEntry(ScopeEntryKind.SourceVar, projectionExpressionList[i].Key, source.Variable);
if (selectClause.SelectKind == SelectKind.SelectRow)
{
sce.AddBindingPrefix(projectionExpressionList[i].Key);
}
sr.AddToScope(projectionExpressionList[i].Key, sce);
}
}
}
//
// if is not DISTINCT, but is a group scope, then should be the group scope and the
// projection list
//
else if (sr.CurrentScopeRegionFlags.IsInGroupScope)
{
sr.SetScopeView(SemanticResolver.ScopeViewKind.CurrentAndPreviousScope);
}
//
// convert sort keys
//
List sortKeys = new List(orderByClause.OrderByClauseItem.Expressions.Count);
for (int i = 0 ; i < orderByClause.OrderByClauseItem.Expressions.Count ; i++)
{
OrderByClauseItem orderClauseItem = orderByClause.OrderByClauseItem.Expressions[i];
sr.CurrentScopeRegionFlags.ResetGroupAggregateNestingCount();
sr.ResetScopeRegionCorrelationFlag();
//
// convert order key expression
//
DbExpression keyExpr = Convert(orderClauseItem.OrderExpr, sr);
//
// ensure expression is typed
//
SemanticResolver.EnsureIsNotUntypedNull(keyExpr, orderClauseItem.OrderExpr.ErrCtx);
//
// ensure key expression is correlated. if group by is present, then the check was already performed
//
if (!sr.CurrentScopeRegionFlags.WasResolutionCorrelated)
{
throw EntityUtil.EntitySqlError(orderClauseItem.ErrCtx, System.Data.Entity.Strings.KeyMustBeCorrelated("ORDER BY"));
}
//
// ensure key is order comparable
//
if (!TypeHelpers.IsValidSortOpKeyType(keyExpr.ResultType))
{
throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, System.Data.Entity.Strings.OrderByKeyIsNotOrderComparable);
}
//
// define order
//
bool ascSort = (orderClauseItem.OrderKind == OrderKind.None) || (orderClauseItem.OrderKind == OrderKind.Asc);
//
// define collation
//
string collation = null;
if (orderClauseItem.IsCollated)
{
if (!TypeResolver.IsKeyValidForCollation(keyExpr.ResultType))
{
throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, System.Data.Entity.Strings.InvalidKeyTypeForCollation(keyExpr.ResultType.EdmType.FullName));
}
collation = orderClauseItem.CollateIdentifier.Name;
}
//
// if orderby has no collation defined, check if a default was given through ParserOptions
//
else if (sr.ParserOptions.DefaultOrderByCollation.Length > 0 && TypeResolver.IsKeyValidForCollation(keyExpr.ResultType))
{
collation = sr.ParserOptions.DefaultOrderByCollation;
}
//
// add keys to key collection
//
if (string.IsNullOrEmpty(collation))
{
sortKeys.Add(sr.CmdTree.CreateSortClause(keyExpr, ascSort));
}
else
{
sortKeys.Add(sr.CmdTree.CreateSortClause(keyExpr, ascSort, collation));
}
}
DbExpression sortSourceExpr = null;
if (orderByClause.HasSkipSubClause)
{
sortSourceExpr = sr.CmdTree.CreateSkipExpression(source, sortKeys, skipExpr);
}
else
{
sortSourceExpr = sr.CmdTree.CreateSortExpression(source, sortKeys);
}
//
// Create Sort Binding
//
sortBinding = sr.CmdTree.CreateExpressionBinding(
sortSourceExpr,
sr.GenerateInternalName("sort"));
//
// Fixup Bindings
//
sr.FixupSourceVarBindings(sortBinding.Variable);
//
// pops projection keys from scope
//
sr.RollbackToSavepoint(savePoint);
//
// restore scope view
//
sr.SetScopeView(saveScopeView);
Debug.Assert(null != sortBinding,"null != sortBinding");
return sortBinding;
}
///
/// Converts a list of ast expression nodes
///
///
/// SemanticResolver instance relative to a especif typespace/system
///
private static PairOfLists ProcessExprList( ExprList astExprList, SemanticResolver sr )
{
List types = new List(astExprList.Count);
List convertedExprs = new List(astExprList.Count);
for (int i = 0 ; i < astExprList.Count ; i++)
{
DbExpression e = Convert(astExprList[i], sr);
types.Add(e.ResultType);
convertedExprs.Add(e);
}
return new PairOfLists(types, convertedExprs);
}
///
/// [....]: Temporary workaround for 2/3 milestone.
/// Convert "x in multiset(y1, y2, ..., yn)" into
/// x = y1 or x = y2 or x = y3 ...
///
/// semantic resolver
/// left-expression (the probe)
/// right expression (the collection)
/// Or chain of equality comparisons
private static DbExpression ConvertSimpleInExpression( SemanticResolver sr, DbExpression left, DbExpression right )
{
// Only handle cases when the right-side is a new instance expression
Debug.Assert(right.ExpressionKind == DbExpressionKind.NewInstance,"right.ExpressionKind == DbExpressionKind.NewInstance");
DbNewInstanceExpression rightColl = (DbNewInstanceExpression)right;
if (rightColl.Arguments.Count == 0)
{
return sr.CmdTree.CreateConstantExpression(false);
}
DbExpression orExpr = null;
foreach (DbExpression e in rightColl.Arguments)
{
DbExpression leftClone = (DbExpression)left.Clone();
DbExpression eqExpr = sr.CmdTree.CreateEqualsExpression(leftClone, e);
if (orExpr == null)
{
orExpr = eqExpr;
}
else
{
orExpr = sr.CmdTree.CreateOrExpression(orExpr, eqExpr);
}
}
return orExpr;
}
#region Conversion Delegate Mappings
private delegate DbExpression AstExprConverter( Expr astExpr, SemanticResolver sr );
private static readonly Dictionary _astExprConverters = CreateAstExprConverters();
private delegate DbExpression BuiltInExprConverter( BuiltInExpr astBltInExpr, SemanticResolver sr );
private static readonly Dictionary _builtInExprConverter = CreateBuiltInExprConverter();
#region Define converter delegates
private static Dictionary CreateAstExprConverters()
{
const int NumberOfElements = 15; // number of elements initialized by the dictionary
Dictionary astExprConverters = new Dictionary(NumberOfElements);
astExprConverters.Add(typeof(Literal), new AstExprConverter(ConvertLiteral));
astExprConverters.Add(typeof(Parameter), new AstExprConverter(ConvertParameter));
astExprConverters.Add(typeof(Identifier), new AstExprConverter(ConvertIdentifier));
astExprConverters.Add(typeof(DotExpr), new AstExprConverter(ConvertDotExpr));
astExprConverters.Add(typeof(BuiltInExpr), new AstExprConverter(ConvertBuiltIn));
astExprConverters.Add(typeof(QueryExpr), new AstExprConverter(ConvertQuery));
astExprConverters.Add(typeof(RowConstructorExpr), new AstExprConverter(ConvertRowConstructor));
astExprConverters.Add(typeof(MultisetConstructorExpr), new AstExprConverter(ConvertMultisetConstructor));
astExprConverters.Add(typeof(CaseExpr), new AstExprConverter(ConvertCaseExpr));
astExprConverters.Add(typeof(RelshipNavigationExpr), new AstExprConverter(ConvertRelshipNavigationExpr));
astExprConverters.Add(typeof(RefExpr), new AstExprConverter(ConvertRefExpr));
astExprConverters.Add(typeof(DerefExpr), new AstExprConverter(ConvertDeRefExpr));
astExprConverters.Add(typeof(MethodExpr), new AstExprConverter(ConvertMethodExpr));
astExprConverters.Add(typeof(CreateRefExpr), new AstExprConverter(ConvertCreateRefExpr));
astExprConverters.Add(typeof(KeyExpr), new AstExprConverter(ConvertKeyExpr));
Debug.Assert(NumberOfElements == astExprConverters.Count, "The number of elements and initial capacity don't match");
return astExprConverters;
}
private static Dictionary CreateBuiltInExprConverter()
{
Dictionary builtInExprConverter = new Dictionary(sizeof(BuiltInKind));
////////////////////////////
// Arithmetic Expressions
////////////////////////////
//
// e1 + e2
//
#region e1 + e2
builtInExprConverter.Add(BuiltInKind.Plus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertPlusOperands(bltInExpr, sr);
if (TypeSemantics.IsNumericType(args.Left.ResultType))
{
return sr.CmdTree.CreatePlusExpression(args.Left, args.Right);
}
else
{
//
// fold '+' operator into concat canonical function
//
IList functions;
if (!sr.TypeResolver.TryGetFunctionFromMetadata("Concat","Edm", true /* ignoreCase */, out functions))
{
throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, System.Data.Entity.Strings.ConcatBuiltinNotSupported);
}
List argTypes = new List(2);
argTypes.Add(args.Left.ResultType);
argTypes.Add(args.Right.ResultType);
bool isAmbiguous = false;
EdmFunction concatFunction = TypeResolver.ResolveFunctionOverloads(functions, argTypes, false /* isGroupAggregate */, out isAmbiguous);
if (null == concatFunction || isAmbiguous)
{
throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, System.Data.Entity.Strings.ConcatBuiltinNotSupported);
}
return sr.CmdTree.CreateFunctionExpression(concatFunction,
new DbExpression[] { args.Left, args.Right });
}
});
#endregion
//
// e1 - e2
//
#region e1 - e2
builtInExprConverter.Add(BuiltInKind.Minus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateMinusExpression(args.Left, args.Right);
});
#endregion
//
// e1 * e2
//
#region e1 * e2
builtInExprConverter.Add(BuiltInKind.Multiply, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateMultiplyExpression(args.Left, args.Right);
});
#endregion
//
// e1 / e2
//
#region e1 / e2
builtInExprConverter.Add(BuiltInKind.Divide, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateDivideExpression(args.Left, args.Right);
});
#endregion
//
// e1 % e2
//
#region e1 % e2
builtInExprConverter.Add(BuiltInKind.Modulus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertArithmeticArgs(bltInExpr, sr);
return sr.CmdTree.CreateModuloExpression(args.Left, args.Right);
});
#endregion
//
// - e
//
#region - e
builtInExprConverter.Add(BuiltInKind.UnaryMinus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
DbExpression unaryExpr = sr.CmdTree.CreateUnaryMinusExpression(ConvertArithmeticArgs(bltInExpr, sr).Left);
if (TypeSemantics.IsUnsignedNumericType(unaryExpr.ResultType))
{
TypeUsage closestPromotableType = null;
if (TypeHelpers.TryGetClosestPromotableType(unaryExpr.ResultType, out closestPromotableType))
{
unaryExpr = sr.CmdTree.CreateCastExpression(unaryExpr, closestPromotableType);
}
else
{
throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.InvalidUnsignedTypeForUnaryMinusOperation(unaryExpr.ResultType.EdmType.FullName));
}
}
return unaryExpr;
});
#endregion
//
// + e
//
#region + e
builtInExprConverter.Add(BuiltInKind.UnaryPlus, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return ConvertArithmeticArgs(bltInExpr, sr).Left;
});
#endregion
////////////////////////////
// Logical Expressions
////////////////////////////
//
// e1 AND e2
// e1 && e2
//
#region e1 AND e2
builtInExprConverter.Add(BuiltInKind.And, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr);
return sr.CmdTree.CreateAndExpression(args.Left, args.Right);
});
#endregion
//
// e1 OR e2
// e1 || e2
//
#region e1 OR e2
builtInExprConverter.Add(BuiltInKind.Or, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr);
return sr.CmdTree.CreateOrExpression(args.Left, args.Right);
});
#endregion
//
// NOT e
// ! e
//
#region NOT e
builtInExprConverter.Add(BuiltInKind.Not, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return sr.CmdTree.CreateNotExpression(ConvertLogicalArgs(bltInExpr, sr).Left);
});
#endregion
////////////////////////////
// Comparison Expressions
////////////////////////////
//
// e1 == e2 | e1 = e2
//
#region e1 == e2
builtInExprConverter.Add(BuiltInKind.Equal, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertEqualCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateEqualsExpression(args.Left, args.Right);
});
#endregion
//
// e1 != e2 | e1 <> e2
//
#region e1 != e2
builtInExprConverter.Add(BuiltInKind.NotEqual, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertEqualCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateEqualsExpression(args.Left, args.Right));
});
#endregion
//
// e1 >= e2
//
#region e1 >= e2
builtInExprConverter.Add(BuiltInKind.GreaterEqual, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateGreaterThanOrEqualsExpression(args.Left, args.Right);
});
#endregion
//
// e1 > e2
//
#region e1 > e2
builtInExprConverter.Add(BuiltInKind.GreaterThan, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateGreaterThanExpression(args.Left, args.Right);
});
#endregion
//
// e1 <= e2
//
#region e1 <= e2
builtInExprConverter.Add(BuiltInKind.LessEqual, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateLessThanOrEqualsExpression(args.Left, args.Right);
});
#endregion
//
// e1 < e2
//
#region e1 < e2
builtInExprConverter.Add(BuiltInKind.LessThan, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertOrderCompArgs(bltInExpr, sr);
return sr.CmdTree.CreateLessThanExpression(args.Left, args.Right);
});
#endregion
////////////////////////////
// SET EXPRESSIONS
////////////////////////////
//
// e1 UNION e2
//
#region e1 UNION e2
builtInExprConverter.Add(BuiltInKind.Union, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateDistinctExpression(sr.CmdTree.CreateUnionAllExpression(args.Left, args.Right));
});
#endregion
//
// e1 UNION ALL e2
//
#region e1 UNION ALL e2
builtInExprConverter.Add(BuiltInKind.UnionAll, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateUnionAllExpression(args.Left, args.Right);
});
#endregion
//
// e1 INTERSECT e2
//
#region e1 INTERSECT e2
builtInExprConverter.Add(BuiltInKind.Intersect, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateIntersectExpression(args.Left, args.Right);
});
#endregion
//
// e1 OVERLAPS e2
//
#region e1 OVERLAPS e1
builtInExprConverter.Add(BuiltInKind.Overlaps, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateIsEmptyExpression(
sr.CmdTree.CreateIntersectExpression(args.Left, args.Right)));
});
#endregion
//
// ANYELEMENT( e )
//
#region ANYELEMENT( e )
builtInExprConverter.Add(BuiltInKind.AnyElement, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return sr.CmdTree.CreateElementExpression(ConvertSetArgs(bltInExpr, sr).Left);
});
#endregion
//
// ELEMENT( e )
//
#region ELEMENT( e ) - NOT SUPPORTED IN ORCAS TIMEFRAME
builtInExprConverter.Add(BuiltInKind.Element, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.ElementOperatorIsNotSupported);
});
#endregion
//
// e1 EXCEPT e2
//
#region e1 EXCEPT e2
builtInExprConverter.Add(BuiltInKind.Except, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateExceptExpression(args.Left, args.Right);
});
#endregion
//
// EXISTS( e )
//
#region EXISTS( e )
builtInExprConverter.Add(BuiltInKind.Exists, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
return sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateIsEmptyExpression(ConvertSetArgs(bltInExpr, sr).Left));
});
#endregion
//
// FLATTEN( e )
//
#region FLATTEN( e )
builtInExprConverter.Add(BuiltInKind.Flatten, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
DbExpression elemExpr = Convert(bltInExpr.Arg1, sr);
if (!TypeSemantics.IsCollectionType(elemExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidFlattenArgument);
}
if (!TypeSemantics.IsCollectionType(TypeHelpers.GetElementTypeUsage(elemExpr.ResultType)))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidFlattenArgument);
}
DbExpressionBinding leftExpr = sr.CmdTree.CreateExpressionBinding(elemExpr, sr.GenerateInternalName("l_flatten"));
DbExpressionBinding rightExpr = sr.CmdTree.CreateExpressionBinding(leftExpr.Variable, sr.GenerateInternalName("r_flatten"));
DbExpressionBinding applyBinding = sr.CmdTree.CreateExpressionBinding(
sr.CmdTree.CreateCrossApplyExpression(leftExpr, rightExpr),
sr.GenerateInternalName("flatten"));
return sr.CmdTree.CreateProjectExpression(applyBinding, sr.CmdTree.CreatePropertyExpression(rightExpr.VariableName, applyBinding.Variable));
});
#endregion
//
// e1 IN e2
//
#region e1 IN e2
builtInExprConverter.Add(BuiltInKind.In, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertInExprArgs(bltInExpr, sr);
// [....]: Temporary workaround for 2/3 milestone.
// Convert "x in multiset(y1, y2, ..., yn)" into
// x = y1 or x = y2 or x = y3 ...
//
if (args.Right.ExpressionKind == DbExpressionKind.NewInstance)
{
return ConvertSimpleInExpression(sr, args.Left, args.Right);
}
else
{
DbExpressionBinding rSet = sr.CmdTree.CreateExpressionBinding(args.Right, sr.GenerateInternalName("in-filter"));
DbExpression leftIn = args.Left;
DbExpression rightSet = rSet.Variable;
DbExpression exists = sr.CmdTree.CreateNotExpression(
sr.CmdTree.CreateIsEmptyExpression(
sr.CmdTree.CreateFilterExpression(
rSet,
sr.CmdTree.CreateEqualsExpression(leftIn, rightSet))));
List whenExpr = new List(1);
whenExpr.Add(sr.CmdTree.CreateIsNullExpression(leftIn));
List thenExpr = new List(1);
thenExpr.Add(sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType));
DbExpression left = sr.CmdTree.CreateCaseExpression(
whenExpr,
thenExpr,
sr.CmdTree.CreateFalseExpression());
DbExpression converted = sr.CmdTree.CreateOrExpression(left, exists);
return converted;
}
});
#endregion
//
// e1 NOT IN e1
//
#region e1 NOT IN e1
builtInExprConverter.Add(BuiltInKind.NotIn, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertInExprArgs(bltInExpr, sr);
if (args.Right.ExpressionKind == DbExpressionKind.NewInstance)
{
return sr.CmdTree.CreateNotExpression(
ConvertSimpleInExpression(sr, args.Left, args.Right));
}
else
{
DbExpressionBinding rSet = sr.CmdTree.CreateExpressionBinding(args.Right, sr.GenerateInternalName("in-filter"));
DbExpression leftIn = args.Left;
DbExpression rightSet = rSet.Variable;
DbExpression exists = sr.CmdTree.CreateIsEmptyExpression(
sr.CmdTree.CreateFilterExpression(
rSet,
sr.CmdTree.CreateEqualsExpression(leftIn, rightSet)));
List whenExpr = new List(1);
whenExpr.Add(sr.CmdTree.CreateIsNullExpression(leftIn));
List thenExpr = new List(1);
thenExpr.Add(sr.CmdTree.CreateNullExpression(sr.TypeResolver.BooleanType));
DbExpression left = sr.CmdTree.CreateCaseExpression(
whenExpr,
thenExpr,
sr.CmdTree.CreateTrueExpression());
DbExpression converted = sr.CmdTree.CreateAndExpression(left, exists);
return converted;
}
});
#endregion
//
// SET( e ) - DISTINCT( e ) before
//
#region SET( e )
builtInExprConverter.Add(BuiltInKind.Distinct, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertSetArgs(bltInExpr, sr);
return sr.CmdTree.CreateDistinctExpression(args.Left);
});
#endregion
////////////////////////////
// Nullabity Expressions
////////////////////////////
//
// e IS NULL
//
#region e IS NULL
builtInExprConverter.Add(BuiltInKind.IsNull, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
DbExpression isNullExpr = Convert(bltInExpr.Arg1, sr);
//
// ensure expression type is valid for this operation
//
if (!TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.IsNullInvalidType);
}
return SemanticResolver.IsNullExpression(isNullExpr) ? sr.CmdTree.CreateTrueExpression() : (DbExpression)sr.CmdTree.CreateIsNullExpression(isNullExpr);
});
#endregion
//
// e IS NOT NULL
//
#region e IS NOT NULL
builtInExprConverter.Add(BuiltInKind.IsNotNull, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
DbExpression isNullExpr = Convert(bltInExpr.Arg1, sr);
//
// ensure expression type is valid for this operation
//
if (!TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.IsNullInvalidType);
}
isNullExpr = SemanticResolver.IsNullExpression(isNullExpr) ? sr.CmdTree.CreateTrueExpression() : (DbExpression)sr.CmdTree.CreateIsNullExpression(isNullExpr);
return sr.CmdTree.CreateNotExpression(isNullExpr);
});
#endregion
////////////////////////////
// Type Expressions
////////////////////////////
//
// e IS OF ( [ONLY] T )
//
#region e IS OF ( [ONLY] T )
builtInExprConverter.Add(BuiltInKind.IsOf, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
bool isOnly = (bool)((Literal)bltInExpr.ArgList[2]).Value;
bool isNot = (bool)((Literal)bltInExpr.ArgList[3]).Value;
bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
if (TypeSemantics.IsNullType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionCannotBeNull);
}
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeEntityType(System.Data.Entity.Strings.CtxIsOf,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeNominalType(System.Data.Entity.Strings.CtxIsOf,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.TypeMustBeEntityType(System.Data.Entity.Strings.CtxIsOf,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.TypeMustBeNominalType(System.Data.Entity.Strings.CtxIsOf,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
if (!TypeSemantics.IsPolymorphicType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeSemantics.IsPolymorphicType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeResolver.IsSubOrSuperType(args.Left.ResultType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx,
System.Data.Entity.Strings.NotASuperOrSubType(args.Left.ResultType.EdmType.FullName,
args.Right.EdmType.FullName));
}
args.Right = TypeHelpers.GetReadOnlyType(args.Right);
DbExpression retExpr = null;
if (isOnly)
{
retExpr = sr.CmdTree.CreateIsOfOnlyExpression(args.Left, args.Right);
}
else
{
retExpr = sr.CmdTree.CreateIsOfExpression(args.Left, args.Right);
}
if (isNot)
{
retExpr = sr.CmdTree.CreateNotExpression(retExpr);
}
return retExpr;
});
#endregion
//
// TREAT( e as T )
//
#region TREAT( e as T )
builtInExprConverter.Add(BuiltInKind.Treat, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeEntityType(System.Data.Entity.Strings.CtxTreat,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeNominalType(System.Data.Entity.Strings.CtxTreat,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
if (TypeSemantics.IsNullType(args.Left.ResultType))
{
args.Left = sr.CmdTree.CreateNullExpression(args.Right);
}
else if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeEntityType(System.Data.Entity.Strings.CtxTreat,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionTypeMustBeNominalType(System.Data.Entity.Strings.CtxTreat,
args.Left.ResultType.EdmType.BuiltInTypeKind.ToString(),
args.Left.ResultType.EdmType.FullName));
}
if (!TypeSemantics.IsPolymorphicType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeSemantics.IsPolymorphicType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeInheritableType);
}
if (!TypeResolver.IsSubOrSuperType(args.Left.ResultType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.NotASuperOrSubType(args.Left.ResultType.EdmType.FullName,
args.Right.EdmType.FullName));
}
return sr.CmdTree.CreateTreatExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
});
#endregion
//
// CAST( e AS T )
//
#region CAST( e AS T )
builtInExprConverter.Add(BuiltInKind.Cast, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
//
// ensure CAST target type is Scalar
//
if (!TypeSemantics.IsPrimitiveType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.InvalidCastType);
}
if (SemanticResolver.IsNullExpression(args.Left))
{
return sr.CmdTree.CreateCastExpression(
sr.CmdTree.CreateNullExpression(args.Right),
args.Right);
}
if (args.Left.ResultType.BuiltInTypeKind != BuiltInTypeKind.EnumType)
{
if (!TypeSemantics.IsPrimitiveType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.InvalidCastExpressionType);
}
if (!TypeSemantics.IsCastAllowed(args.Left.ResultType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.InvalidCast(
args.Left.ResultType.EdmType,
args.Right.EdmType.FullName));
}
}
return sr.CmdTree.CreateCastExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
});
#endregion
//
// OFTYPE( [ONLY] e, T )
//
#region OFTYPE( [ONLY] e, T )
builtInExprConverter.Add(BuiltInKind.OfType, delegate(BuiltInExpr bltInExpr, SemanticResolver sr)
{
Pair args = ConvertTypeExprArgs(bltInExpr, sr);
bool isOnly = (bool)((Literal)bltInExpr.ArgList[2]).Value;
bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
if (!TypeSemantics.IsCollectionType(args.Left.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.ExpressionMustBeCollection);
}
TypeUsage elementType = TypeHelpers.GetElementTypeUsage(args.Left.ResultType);
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(elementType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.OfTypeExpressionElementTypeMustBeEntityType(elementType.EdmType.BuiltInTypeKind.ToString(),
elementType));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(elementType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.OfTypeExpressionElementTypeMustBeNominalType(elementType.EdmType.BuiltInTypeKind.ToString(),
elementType));
}
if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeEntityType(System.Data.Entity.Strings.CtxOfType,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.TypeMustBeNominalType(System.Data.Entity.Strings.CtxOfType,
args.Right.EdmType.BuiltInTypeKind.ToString(),
args.Right.EdmType.FullName));
}
if (isOnly && args.Right.EdmType.Abstract)
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
System.Data.Entity.Strings.OfTypeOnlyTypeArgumentCannotBeAbstract(args.Right.EdmType.FullName));
}
if (!TypeResolver.IsSubOrSuperType(elementType, args.Right))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
System.Data.Entity.Strings.NotASuperOrSubType(elementType.EdmType.FullName,
args.Right.EdmType.FullName));
}
DbExpression ofTypeExpression = null;
if (isOnly)
{
ofTypeExpression = sr.CmdTree.CreateOfTypeOnlyExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
}
else
{
ofTypeExpression = sr.CmdTree.CreateOfTypeExpression(args.Left, TypeHelpers.GetReadOnlyType(args.Right));
}
return ofTypeExpression;
});
#endregion
//
// e LIKE pattern [ESCAPE escape]
//
#region e LIKE pattern [ESCAPE escape]
builtInExprConverter.Add(BuiltInKind.Like, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
DbExpression likeExpr = null;
DbExpression matchExpr = Convert(bltInExpr.Arg1, sr);
if (TypeSemantics.IsNullType(matchExpr.ResultType))
{
matchExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.StringType);
}
else if (!TypeResolver.IsStringType(matchExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, System.Data.Entity.Strings.LikeArgMustBeStringType);
}
DbExpression patternExpr = Convert(bltInExpr.Arg2, sr);
if (patternExpr is UntypedNullExpression)
{
patternExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.StringType);
}
else if (!TypeResolver.IsStringType(patternExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, System.Data.Entity.Strings.LikeArgMustBeStringType);
}
if (3 == bltInExpr.ArgCount)
{
DbExpression escapeExpr = Convert(bltInExpr.ArgList[2], sr);
if (escapeExpr is UntypedNullExpression)
{
escapeExpr = sr.CmdTree.CreateNullExpression(sr.TypeResolver.StringType);
}
else if (!TypeResolver.IsStringType(escapeExpr.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[2].ErrCtx, System.Data.Entity.Strings.LikeArgMustBeStringType);
}
likeExpr = sr.CmdTree.CreateLikeExpression(matchExpr, patternExpr, escapeExpr);
}
else
{
likeExpr = sr.CmdTree.CreateLikeExpression(matchExpr, patternExpr);
}
return likeExpr;
});
#endregion
//
// e BETWEEN e1 AND e2
//
#region e BETWEEN e1 AND e2
builtInExprConverter.Add(BuiltInKind.Between, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
Debug.Assert(bltInExpr.ArgCount == 3);
//
// convert lower and upper limits
//
Pair limitsExpr = sr.EnsureTypedNulls(
Convert(bltInExpr.ArgList[1], sr),
Convert(bltInExpr.ArgList[2], sr),
bltInExpr.ArgList[0].ErrCtx,
() => Strings.BetweenLimitsCannotBeUntypedNulls);
//
// Get and check common type for limits
//
TypeUsage rangeCommonType = TypeHelpers.GetCommonTypeUsage(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType);
if (null == rangeCommonType)
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[0].ErrCtx, System.Data.Entity.Strings.BetweenLimitsTypesAreNotCompatible(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName));
}
//
// check if limit types are order-comp
//
if (!TypeSemantics.IsOrderComparableTo(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType))
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[0].ErrCtx, System.Data.Entity.Strings.BetweenLimitsTypesAreNotOrderComparable(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName));
}
//
// convert value expression
//
DbExpression valueExpr = Convert(bltInExpr.ArgList[0], sr);
//
// Fixup value type if untyped
//
if (TypeSemantics.IsNullType(valueExpr.ResultType))
{
valueExpr = sr.CmdTree.CreateNullExpression(rangeCommonType);
}
//
// check if valueExpr is order-comparable to limits
//
if (!TypeSemantics.IsOrderComparableTo(valueExpr.ResultType, rangeCommonType))
{
throw EntityUtil.EntitySqlError(bltInExpr.ArgList[0].ErrCtx, System.Data.Entity.Strings.BetweenValueIsNotOrderComparable(valueExpr.ResultType.EdmType.FullName, rangeCommonType.EdmType.FullName));
}
return sr.CmdTree.CreateAndExpression(
sr.CmdTree.CreateGreaterThanOrEqualsExpression(valueExpr, limitsExpr.Left),
sr.CmdTree.CreateLessThanOrEqualsExpression(valueExpr, limitsExpr.Right));
});
#endregion
//
// e NOT BETWEEN e1 AND e2
//
#region e NOT BETWEEN e1 AND e2
builtInExprConverter.Add(BuiltInKind.NotBetween, delegate( BuiltInExpr bltInExpr, SemanticResolver sr )
{
bltInExpr.Kind = BuiltInKind.Between;
DbExpression converted = sr.CmdTree.CreateNotExpression(ConvertBuiltIn(bltInExpr, sr));
bltInExpr.Kind = BuiltInKind.NotBetween;
return converted;
});
#endregion
return builtInExprConverter;
}
#endregion
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ErrorFormatterPage.cs
- DataRelation.cs
- StateValidator.cs
- UnsafeNativeMethodsTablet.cs
- DataObjectFieldAttribute.cs
- TargetException.cs
- XmlBoundElement.cs
- ToolStripPanel.cs
- InputLanguageEventArgs.cs
- ProviderSettingsCollection.cs
- FlowLayout.cs
- AssemblyHelper.cs
- ZipIOExtraField.cs
- AdapterUtil.cs
- InvalidOperationException.cs
- ByteStack.cs
- HighContrastHelper.cs
- CommandBindingCollection.cs
- PropertyValueChangedEvent.cs
- Composition.cs
- RegexBoyerMoore.cs
- CustomAttributeSerializer.cs
- DataServiceContext.cs
- FrameworkReadOnlyPropertyMetadata.cs
- AutomationProperties.cs
- TextContainerHelper.cs
- EditorPart.cs
- EnumConverter.cs
- TagPrefixAttribute.cs
- InlineCollection.cs
- SingleResultAttribute.cs
- SessionEndedEventArgs.cs
- GroupByExpressionRewriter.cs
- SqlDataSourceTableQuery.cs
- DataRowChangeEvent.cs
- ExpressionParser.cs
- util.cs
- Interfaces.cs
- TextContainer.cs
- altserialization.cs
- WebPartDeleteVerb.cs
- CodeStatement.cs
- basecomparevalidator.cs
- Compensation.cs
- CharEntityEncoderFallback.cs
- WebBaseEventKeyComparer.cs
- MenuItem.cs
- PathTooLongException.cs
- WinEventQueueItem.cs
- Boolean.cs
- DataTableExtensions.cs
- EditorServiceContext.cs
- FlowDocument.cs
- OutputCacheProfileCollection.cs
- AesManaged.cs
- TextBox.cs
- BamlLocalizationDictionary.cs
- CharEntityEncoderFallback.cs
- ConfigXmlComment.cs
- RegisteredScript.cs
- ListViewInsertionMark.cs
- ControlFilterExpression.cs
- PreservationFileReader.cs
- ContainsRowNumberChecker.cs
- HttpServerChannel.cs
- ListBoxAutomationPeer.cs
- MetricEntry.cs
- TemplateXamlTreeBuilder.cs
- GregorianCalendar.cs
- Models.cs
- GlobalEventManager.cs
- Double.cs
- autovalidator.cs
- XmlAtomicValue.cs
- XsdDateTime.cs
- FieldToken.cs
- NotFiniteNumberException.cs
- CorrelationToken.cs
- AttributeConverter.cs
- ByteAnimation.cs
- TextElementEnumerator.cs
- TrustManagerMoreInformation.cs
- ValidatorCompatibilityHelper.cs
- RemoteWebConfigurationHostStream.cs
- TdsEnums.cs
- Encoder.cs
- NavigationExpr.cs
- XPathNodeList.cs
- BoundColumn.cs
- Crc32.cs
- ResizeGrip.cs
- FontSourceCollection.cs
- MobileCategoryAttribute.cs
- IIS7ConfigurationLoader.cs
- MonitoringDescriptionAttribute.cs
- WindowsClientElement.cs
- XmlNode.cs
- BindValidator.cs
- ComponentDispatcher.cs
- NamedElement.cs