1 //---------------------------------------------------------------------
2 // <copyright file="SemanticAnalyzer.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.EntitySql
13 using System.Collections.Generic;
14 using System.Data.Common.CommandTrees;
15 using System.Data.Common.CommandTrees.ExpressionBuilder;
16 using System.Data.Entity;
17 using System.Data.Mapping;
18 using System.Data.Metadata.Edm;
19 using System.Diagnostics;
20 using System.Globalization;
24 /// Implements Semantic Analysis and Conversion
25 /// Provides the translation service between an abstract syntax tree to a canonical command tree
26 /// For complete documentation of the language syntax and semantics, refer to http://sqlweb/default.asp?specDirId=764
27 /// 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
28 /// we rely on the assumption that metadata was pre-loaded and is relevant to the query.
30 internal sealed class SemanticAnalyzer
32 private SemanticResolver _sr;
35 /// Initializes semantic analyzer
37 /// <param name="sr">initialized SemanticResolver instance for a given typespace/type system</param>
38 internal SemanticAnalyzer(SemanticResolver sr)
40 Debug.Assert(sr != null, "sr must not be null");
45 /// Entry point to semantic analysis. Converts AST into a <see cref="DbCommandTree"/>.
47 /// <param name="astExpr">ast command tree</param>
49 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted</exception>
50 /// <exception cref="System.Data.MetadataException">Thrown when metadata related service requests fail</exception>
51 /// <exception cref="System.Data.MappingException">Thrown when mapping related service requests fail</exception>
53 /// <returns>ParseResult with a valid DbCommandTree</returns>
54 internal ParseResult AnalyzeCommand(AST.Node astExpr)
57 // Ensure that the AST expression is a valid Command expression
59 AST.Command astCommandExpr = ValidateQueryCommandAst(astExpr);
62 // Convert namespace imports and add them to _sr.TypeResolver.
64 ConvertAndRegisterNamespaceImports(astCommandExpr.NamespaceImportList, astCommandExpr.ErrCtx, _sr);
67 // Convert the AST command root expression to a command tree using the appropriate converter
69 ParseResult parseResult = ConvertStatement(astCommandExpr.Statement, _sr);
71 Debug.Assert(parseResult != null, "ConvertStatement produced null parse result");
72 Debug.Assert(parseResult.CommandTree != null, "ConvertStatement returned null command tree");
78 /// Converts query command AST into a <see cref="DbExpression"/>.
80 /// <param name="astExpr">ast command tree</param>
82 /// <exception cref="System.Data.EntityException">Thrown when Syntatic or Semantic rules are violated and the query cannot be accepted</exception>
83 /// <exception cref="System.Data.MetadataException">Thrown when metadata related service requests fail</exception>
84 /// <exception cref="System.Data.MappingException">Thrown when mapping related service requests fail</exception>
86 /// <returns>DbExpression</returns>
87 internal DbLambda AnalyzeQueryCommand(AST.Node astExpr)
90 // Ensure that the AST expression is a valid query command expression
91 // (only a query command root expression can produce a standalone DbExpression)
93 AST.Command astQueryCommandExpr = ValidateQueryCommandAst(astExpr);
96 // Convert namespace imports and add them to _sr.TypeResolver.
98 ConvertAndRegisterNamespaceImports(astQueryCommandExpr.NamespaceImportList, astQueryCommandExpr.ErrCtx, _sr);
101 // Convert the AST of the query command root expression into a DbExpression
103 List<FunctionDefinition> functionDefs;
104 DbExpression expression = ConvertQueryStatementToDbExpression(astQueryCommandExpr.Statement, _sr, out functionDefs);
106 // Construct DbLambda from free variables and the expression
107 DbLambda lambda = DbExpressionBuilder.Lambda(expression, _sr.Variables.Values);
109 Debug.Assert(lambda != null, "AnalyzeQueryCommand returned null");
114 private AST.Command ValidateQueryCommandAst(AST.Node astExpr)
116 AST.Command astCommandExpr = astExpr as AST.Command;
117 if (null == astCommandExpr)
119 throw EntityUtil.Argument(Strings.UnknownAstCommandExpression);
122 if (!(astCommandExpr.Statement is AST.QueryStatement))
123 throw EntityUtil.Argument(Strings.UnknownAstExpressionType);
125 return astCommandExpr;
129 /// Converts namespace imports and adds them to the type resolver.
131 private static void ConvertAndRegisterNamespaceImports(AST.NodeList<AST.NamespaceImport> nsImportList, ErrorContext cmdErrCtx, SemanticResolver sr)
133 List<Tuple<string, MetadataNamespace, ErrorContext>> aliasedNamespaceImports = new List<Tuple<string, MetadataNamespace, ErrorContext>>();
134 List<Tuple<MetadataNamespace, ErrorContext>> namespaceImports = new List<Tuple<MetadataNamespace, ErrorContext>>();
137 // Resolve all user-defined namespace imports to MetadataMember objects _before_ adding them to the type resolver,
138 // this is needed to keep resolution within the command prolog unaffected by previously resolved imports.
140 if (nsImportList != null)
142 foreach (AST.NamespaceImport namespaceImport in nsImportList)
144 string[] name = null;
146 AST.Identifier identifier = namespaceImport.NamespaceName as AST.Identifier;
147 if (identifier != null)
149 name = new string[] { identifier.Name };
152 AST.DotExpr dotExpr = namespaceImport.NamespaceName as AST.DotExpr;
153 if (dotExpr != null && dotExpr.IsMultipartIdentifier(out name))
155 Debug.Assert(name != null, "name != null");
160 throw EntityUtil.EntitySqlError(namespaceImport.NamespaceName.ErrCtx, Strings.InvalidMetadataMemberName);
163 string alias = namespaceImport.Alias != null ? namespaceImport.Alias.Name : null;
165 MetadataMember metadataMember = sr.ResolveMetadataMemberName(name, namespaceImport.NamespaceName.ErrCtx);
166 Debug.Assert(metadataMember != null, "metadata member name resolution must not return null");
168 if (metadataMember.MetadataMemberClass == MetadataMemberClass.Namespace)
172 aliasedNamespaceImports.Add(Tuple.Create(alias, (MetadataNamespace)metadataMember, namespaceImport.ErrCtx));
176 namespaceImports.Add(Tuple.Create((MetadataNamespace)metadataMember, namespaceImport.ErrCtx));
181 throw EntityUtil.EntitySqlError(namespaceImport.NamespaceName.ErrCtx, Strings.InvalidMetadataMemberClassResolution(
182 metadataMember.Name, metadataMember.MetadataMemberClassName, MetadataNamespace.NamespaceClassName));
188 // Add resolved user-defined imports to the type resolver.
189 // Before adding user-defined namespace imports, add EDM namespace import to make canonical functions and types available in the command text.
191 sr.TypeResolver.AddNamespaceImport(new MetadataNamespace(EdmConstants.EdmNamespace), nsImportList != null ? nsImportList.ErrCtx : cmdErrCtx);
192 foreach (var resolvedAliasedNamespaceImport in aliasedNamespaceImports)
194 sr.TypeResolver.AddAliasedNamespaceImport(resolvedAliasedNamespaceImport.Item1, resolvedAliasedNamespaceImport.Item2, resolvedAliasedNamespaceImport.Item3);
196 foreach (var resolvedNamespaceImport in namespaceImports)
198 sr.TypeResolver.AddNamespaceImport(resolvedNamespaceImport.Item1, resolvedNamespaceImport.Item2);
203 /// Dispatches/Converts statement expressions.
205 /// <param name="astStatement"></param>
206 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
207 /// <returns></returns>
208 private static ParseResult ConvertStatement(AST.Statement astStatement, SemanticResolver sr)
210 Debug.Assert(astStatement != null, "astStatement must not be null");
212 StatementConverter statementConverter;
213 if (astStatement is AST.QueryStatement)
215 statementConverter = new StatementConverter(ConvertQueryStatementToDbCommandTree);
219 throw EntityUtil.Argument(Strings.UnknownAstExpressionType);
222 ParseResult converted = statementConverter(astStatement, sr);
224 Debug.Assert(converted != null, "statementConverter returned null");
225 Debug.Assert(converted.CommandTree != null, "statementConverter produced null command tree");
229 private delegate ParseResult StatementConverter(AST.Statement astExpr, SemanticResolver sr);
232 /// Converts query statement AST to a <see cref="DbQueryCommandTree"/>
234 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
235 private static ParseResult ConvertQueryStatementToDbCommandTree(AST.Statement astStatement, SemanticResolver sr)
237 Debug.Assert(astStatement != null, "astStatement must not be null");
239 List<FunctionDefinition> functionDefs;
240 DbExpression converted = ConvertQueryStatementToDbExpression(astStatement, sr, out functionDefs);
242 Debug.Assert(converted != null, "ConvertQueryStatementToDbExpression returned null");
243 Debug.Assert(functionDefs != null, "ConvertQueryStatementToDbExpression produced null functionDefs");
245 return new ParseResult(
246 DbQueryCommandTree.FromValidExpression(sr.TypeResolver.Perspective.MetadataWorkspace, sr.TypeResolver.Perspective.TargetDataspace, converted),
251 /// Converts the query statement to a normalized and validated <see cref="DbExpression"/>.
252 /// This entry point to the semantic analysis phase is used when producing a
253 /// query command tree or producing only a <see cref="DbExpression"/>.
255 /// <param name="astStatement">The query statement</param>
256 /// <param name="sr">The <see cref="SemanticResolver"/>instance to use</param>
258 /// An instance of <see cref="DbExpression"/>, adjusted to handle 'inline' projections
259 /// and validated to produce a result type appropriate for the root of a query command tree.
261 private static DbExpression ConvertQueryStatementToDbExpression(AST.Statement astStatement, SemanticResolver sr, out List<FunctionDefinition> functionDefs)
263 Debug.Assert(astStatement != null, "astStatement must not be null");
265 AST.QueryStatement queryStatement = astStatement as AST.QueryStatement;
267 if (queryStatement == null)
269 throw EntityUtil.Argument(Strings.UnknownAstExpressionType);
273 // Convert query inline definitions and create parse result.
274 // Converted inline definitions are also added to the semantic resolver.
276 functionDefs = ConvertInlineFunctionDefinitions(queryStatement.FunctionDefList, sr);
279 // Convert top level expression
281 DbExpression converted = ConvertValueExpressionAllowUntypedNulls(queryStatement.Expr, sr);
282 if (converted == null)
285 // Ensure converted expression is not untyped null.
286 // Use error context of the top-level expression.
288 throw EntityUtil.EntitySqlError(queryStatement.Expr.ErrCtx, Strings.ResultingExpressionTypeCannotBeNull);
292 // Handle the "inline" projection case
294 if (converted is DbScanExpression)
296 DbExpressionBinding source = converted.BindAs(sr.GenerateInternalName("extent"));
298 converted = source.Project(source.Variable);
302 // Ensure return type is valid for query. For V1, association types are the only
303 // type that cannot be at 'top' level result. Note that this is only applicable in
304 // general queries and association types are valid in view gen mode queries.
305 // Use error context of the top-level expression.
307 if (sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.NormalMode)
309 ValidateQueryResultType(converted.ResultType, queryStatement.Expr.ErrCtx);
312 Debug.Assert(null != converted, "null != converted");
318 /// Ensures that the result of a query expression is valid.
320 private static void ValidateQueryResultType(TypeUsage resultType, ErrorContext errCtx)
322 if (Helper.IsCollectionType(resultType.EdmType))
324 ValidateQueryResultType(((CollectionType)resultType.EdmType).TypeUsage, errCtx);
326 else if (Helper.IsRowType(resultType.EdmType))
328 foreach (EdmProperty property in ((RowType)resultType.EdmType).Properties)
330 ValidateQueryResultType(property.TypeUsage, errCtx);
333 else if (Helper.IsAssociationType(resultType.EdmType))
335 throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidQueryResultType(resultType.EdmType.FullName));
341 /// Converts query inline function defintions. Returns empty list in case of no definitions.
343 private static List<FunctionDefinition> ConvertInlineFunctionDefinitions(AST.NodeList<AST.FunctionDefinition> functionDefList, SemanticResolver sr)
345 List<FunctionDefinition> functionDefinitions = new List<FunctionDefinition>();
347 if (functionDefList != null)
350 // Process inline function signatures, declare functions in the type resolver.
352 List<InlineFunctionInfo> inlineFunctionInfos = new List<InlineFunctionInfo>();
353 foreach (AST.FunctionDefinition functionDefAst in functionDefList)
356 // Get and validate function name.
358 string name = functionDefAst.Name;
359 Debug.Assert(!String.IsNullOrEmpty(name), "function name must not be null or empty");
362 // Process function parameters
364 List<DbVariableReferenceExpression> parameters = ConvertInlineFunctionParameterDefs(functionDefAst.Parameters, sr);
365 Debug.Assert(parameters != null, "parameters must not be null"); // should be empty collection if no parameters
368 // Register new function in the type resolver.
370 InlineFunctionInfo functionInfo = new InlineFunctionInfoImpl(functionDefAst, parameters);
371 inlineFunctionInfos.Add(functionInfo);
372 sr.TypeResolver.DeclareInlineFunction(name, functionInfo);
374 Debug.Assert(functionDefList.Count == inlineFunctionInfos.Count);
377 // Convert function defintions.
379 foreach (InlineFunctionInfo functionInfo in inlineFunctionInfos)
381 functionDefinitions.Add(new FunctionDefinition(
382 functionInfo.FunctionDefAst.Name,
383 functionInfo.GetLambda(sr),
384 functionInfo.FunctionDefAst.StartPosition,
385 functionInfo.FunctionDefAst.EndPosition));
389 return functionDefinitions;
392 private static List<DbVariableReferenceExpression> ConvertInlineFunctionParameterDefs(AST.NodeList<AST.PropDefinition> parameterDefs, SemanticResolver sr)
394 List<DbVariableReferenceExpression> paramList = new List<DbVariableReferenceExpression>();
395 if (parameterDefs != null)
397 foreach (AST.PropDefinition paramDef in parameterDefs)
399 string name = paramDef.Name.Name;
402 // Validate param name
404 if (paramList.Exists((DbVariableReferenceExpression arg) =>
405 sr.NameComparer.Compare(arg.VariableName, name) == 0))
407 throw EntityUtil.EntitySqlError(
409 Strings.MultipleDefinitionsOfParameter(name));
413 // Convert parameter type
415 TypeUsage typeUsage = ConvertTypeDefinition(paramDef.Type, sr);
416 Debug.Assert(typeUsage != null, "typeUsage must not be null");
419 // Create function parameter ref expression
421 DbVariableReferenceExpression paramRefExpr = new DbVariableReferenceExpression(typeUsage, name);
422 paramList.Add(paramRefExpr);
428 private sealed class InlineFunctionInfoImpl : InlineFunctionInfo
430 private DbLambda _convertedDefinition = null;
431 private bool _convertingDefinition = false;
433 internal InlineFunctionInfoImpl(AST.FunctionDefinition functionDef, List<DbVariableReferenceExpression> parameters)
434 : base(functionDef, parameters)
438 internal override DbLambda GetLambda(SemanticResolver sr)
440 if (_convertedDefinition == null)
443 // Check for recursive definitions.
445 if (_convertingDefinition)
447 throw EntityUtil.EntitySqlError(FunctionDefAst.ErrCtx, Strings.Cqt_UDF_FunctionDefinitionWithCircularReference(FunctionDefAst.Name));
451 // Create a copy of semantic resolver without query scope entries to guarantee proper variable bindings inside the function body.
452 // The srSandbox shares InlineFunctionInfo objects with the original semantic resolver (sr), hence all the indirect conversions of
453 // inline functions (in addition to this direct one) will also be visible in the original semantic resolver.
455 SemanticResolver srSandbox = sr.CloneForInlineFunctionConversion();
457 _convertingDefinition = true;
458 _convertedDefinition = SemanticAnalyzer.ConvertInlineFunctionDefinition(this, srSandbox);
459 _convertingDefinition = false;
461 return _convertedDefinition;
465 private static DbLambda ConvertInlineFunctionDefinition(InlineFunctionInfo functionInfo, SemanticResolver sr)
468 // Push function definition scope.
473 // Add function parameters to the scope.
475 functionInfo.Parameters.ForEach(p => sr.CurrentScope.Add(p.VariableName, new FreeVariableScopeEntry(p)));
478 // Convert function body expression
480 DbExpression body = ConvertValueExpression(functionInfo.FunctionDefAst.Body, sr);
483 // Pop function definition scope
488 // Create and return lambda representing the function body.
490 return DbExpressionBuilder.Lambda(body, functionInfo.Parameters);
494 /// Converts general expressions (AST.Node)
496 private static ExpressionResolution Convert(AST.Node astExpr, SemanticResolver sr)
498 AstExprConverter converter = _astExprConverters[astExpr.GetType()];
499 if (converter == null)
501 throw EntityUtil.EntitySqlError(Strings.UnknownAstExpressionType);
503 return converter(astExpr, sr);
507 /// Converts general expressions (AST.Node) to a <see cref="ValueExpression"/>.
508 /// Returns <see cref="ValueExpression.Value"/>.
509 /// Throws if conversion resulted an a non <see cref="ValueExpression"/> resolution.
510 /// Throws if conversion resulted in the untyped null.
512 private static DbExpression ConvertValueExpression(AST.Node astExpr, SemanticResolver sr)
514 var expr = ConvertValueExpressionAllowUntypedNulls(astExpr, sr);
517 throw EntityUtil.EntitySqlError(astExpr.ErrCtx, Strings.ExpressionCannotBeNull);
523 /// Converts general expressions (AST.Node) to a <see cref="ValueExpression"/>.
524 /// Returns <see cref="ValueExpression.Value"/>.
525 /// Returns null if expression is the untyped null.
526 /// Throws if conversion resulted an a non <see cref="ValueExpression"/> resolution.
528 private static DbExpression ConvertValueExpressionAllowUntypedNulls(AST.Node astExpr, SemanticResolver sr)
530 ExpressionResolution resolution = Convert(astExpr, sr);
531 if (resolution.ExpressionClass == ExpressionResolutionClass.Value)
533 return ((ValueExpression)resolution).Value;
535 else if (resolution.ExpressionClass == ExpressionResolutionClass.MetadataMember)
537 var metadataMember = (MetadataMember)resolution;
538 if (metadataMember.MetadataMemberClass == MetadataMemberClass.EnumMember)
540 var enumMember = (MetadataEnumMember)metadataMember;
541 return enumMember.EnumType.Constant(enumMember.EnumMember.Value);
546 // The resolution is not a value and can not be converted to a value: report an error.
549 string errorMessage = Strings.InvalidExpressionResolutionClass(resolution.ExpressionClassName, ValueExpression.ValueClassName);
551 AST.Identifier identifier = astExpr as AST.Identifier;
552 if (identifier != null)
554 errorMessage = Strings.CouldNotResolveIdentifier(identifier.Name);
557 AST.DotExpr dotExpr = astExpr as AST.DotExpr;
559 if (dotExpr != null && dotExpr.IsMultipartIdentifier(out names))
561 errorMessage = Strings.CouldNotResolveIdentifier(TypeResolver.GetFullName(names));
564 throw EntityUtil.EntitySqlError(astExpr.ErrCtx, errorMessage);
568 /// Converts left and right expressions. If any of them is the untyped null, derives the type and converts to a typed null.
569 /// Throws <see cref="EntitySqlException"/> if conversion is not possible.
571 private static Pair<DbExpression, DbExpression> ConvertValueExpressionsWithUntypedNulls(AST.Node leftAst,
574 Func<string> formatMessage,
577 var leftExpr = leftAst != null ? ConvertValueExpressionAllowUntypedNulls(leftAst, sr) : null;
578 var rightExpr = rightAst != null ? ConvertValueExpressionAllowUntypedNulls(rightAst, sr) : null;
580 if (leftExpr == null)
582 if (rightExpr == null)
584 throw EntityUtil.EntitySqlError(errCtx, formatMessage());
588 leftExpr = DbExpressionBuilder.Null(rightExpr.ResultType);
591 else if (rightExpr == null)
593 rightExpr = DbExpressionBuilder.Null(leftExpr.ResultType);
596 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr);
600 /// Converts literal expression (AST.Literal)
602 private static ExpressionResolution ConvertLiteral(AST.Node expr, SemanticResolver sr)
604 AST.Literal literal = (AST.Literal)expr;
606 if (literal.IsNullLiteral)
609 // If it is literal null, return the untyped null: the type will be inferred depending on the specific expression in which it participates.
611 return new ValueExpression(null);
615 return new ValueExpression(DbExpressionBuilder.Constant(GetLiteralTypeUsage(literal), literal.Value));
619 private static TypeUsage GetLiteralTypeUsage(AST.Literal literal)
621 PrimitiveType primitiveType = null;
623 if (!ClrProviderManifest.Instance.TryGetPrimitiveType(literal.Type, out primitiveType))
625 throw EntityUtil.EntitySqlError(literal.ErrCtx, Strings.LiteralTypeNotFoundInMetadata(literal.OriginalValue));
627 TypeUsage literalTypeUsage = TypeHelpers.GetLiteralTypeUsage(primitiveType.PrimitiveTypeKind, literal.IsUnicodeString);
629 return literalTypeUsage;
633 /// Converts identifier expression (Identifier)
635 private static ExpressionResolution ConvertIdentifier(AST.Node expr, SemanticResolver sr)
637 return ConvertIdentifier(((AST.Identifier)expr), false /* leftHandSideOfMemberAccess */, sr);
640 private static ExpressionResolution ConvertIdentifier(AST.Identifier identifier, bool leftHandSideOfMemberAccess, SemanticResolver sr)
642 return sr.ResolveSimpleName(((AST.Identifier)identifier).Name, leftHandSideOfMemberAccess, identifier.ErrCtx);
646 /// Converts member access expression (AST.DotExpr)
648 private static ExpressionResolution ConvertDotExpr(AST.Node expr, SemanticResolver sr)
650 AST.DotExpr dotExpr = (AST.DotExpr)expr;
652 ValueExpression groupKeyResolution;
653 if (sr.TryResolveDotExprAsGroupKeyAlternativeName(dotExpr, out groupKeyResolution))
655 return groupKeyResolution;
659 // If dotExpr.Left is an identifier, then communicate to the resolution mechanism
660 // that the identifier might be an unqualified name in the context of a qualified name.
661 // Otherwise convert the expr normally.
663 ExpressionResolution leftResolution;
664 AST.Identifier leftIdentifier = dotExpr.Left as AST.Identifier;
665 if (leftIdentifier != null)
667 leftResolution = ConvertIdentifier(leftIdentifier, true /* leftHandSideOfMemberAccess */, sr);
671 leftResolution = Convert(dotExpr.Left, sr);
674 switch (leftResolution.ExpressionClass)
676 case ExpressionResolutionClass.Value:
677 return sr.ResolvePropertyAccess(((ValueExpression)leftResolution).Value, dotExpr.Identifier.Name, dotExpr.Identifier.ErrCtx);
679 case ExpressionResolutionClass.EntityContainer:
680 return sr.ResolveEntityContainerMemberAccess(((EntityContainerExpression)leftResolution).EntityContainer, dotExpr.Identifier.Name, dotExpr.Identifier.ErrCtx);
682 case ExpressionResolutionClass.MetadataMember:
683 return sr.ResolveMetadataMemberAccess((MetadataMember)leftResolution, dotExpr.Identifier.Name, dotExpr.Identifier.ErrCtx);
686 throw EntityUtil.EntitySqlError(dotExpr.Left.ErrCtx, Strings.UnknownExpressionResolutionClass(leftResolution.ExpressionClass));
691 /// Converts paren expression (AST.ParenExpr)
693 private static ExpressionResolution ConvertParenExpr(AST.Node astExpr, SemanticResolver sr)
695 AST.Node innerExpr = ((AST.ParenExpr)astExpr).Expr;
698 // Convert the inner expression.
699 // Note that we allow it to be an untyped null: the consumer of this expression will handle it.
700 // The reason to allow untyped nulls is that "(null)" is a common construct for tool-generated eSQL.
702 DbExpression converted = ConvertValueExpressionAllowUntypedNulls(innerExpr, sr);
703 return new ValueExpression(converted);
707 /// Converts GROUPPARTITION expression (AST.GroupPartitionExpr).
709 private static ExpressionResolution ConvertGroupPartitionExpr(AST.Node astExpr, SemanticResolver sr)
711 AST.GroupPartitionExpr groupAggregateExpr = (AST.GroupPartitionExpr)astExpr;
713 DbExpression converted = null;
716 // If ast node was annotated in a previous pass, means it contains a ready-to-use expression.
718 if (!TryConvertAsResolvedGroupAggregate(groupAggregateExpr, sr, out converted))
721 // GROUPPARTITION is allowed only in the context of a group operation provided by a query expression (SELECT ...).
723 if (!sr.IsInAnyGroupScope())
725 throw EntityUtil.EntitySqlError(astExpr.ErrCtx, Strings.GroupPartitionOutOfContext);
729 // Process aggregate argument.
732 GroupPartitionInfo aggregateInfo;
733 using (sr.EnterGroupPartition(groupAggregateExpr, groupAggregateExpr.ErrCtx, out aggregateInfo))
736 // Convert aggregate argument.
738 arg = ConvertValueExpressionAllowUntypedNulls(groupAggregateExpr.ArgExpr, sr);
742 // Ensure converted GROUPPARTITION argument expression is not untyped null.
746 throw EntityUtil.EntitySqlError(groupAggregateExpr.ArgExpr.ErrCtx, Strings.ResultingExpressionTypeCannotBeNull);
750 // Project the argument off the DbGroupAggregate binding.
752 DbExpression definition = aggregateInfo.EvaluatingScopeRegion.GroupAggregateBinding.Project(arg);
754 if (groupAggregateExpr.DistinctKind == AST.DistinctKind.Distinct)
756 ValidateDistinctProjection(definition.ResultType, groupAggregateExpr.ArgExpr.ErrCtx, null);
757 definition = definition.Distinct();
761 // Add aggregate to aggreate list.
763 aggregateInfo.AttachToAstNode(sr.GenerateInternalName("groupPartition"), definition);
764 aggregateInfo.EvaluatingScopeRegion.GroupAggregateInfos.Add(aggregateInfo);
767 // Return stub expression with same type as the group aggregate.
769 converted = aggregateInfo.AggregateStubExpression;
772 Debug.Assert(null != converted, "null != converted");
774 return new ValueExpression(converted);
777 #region ConvertMethodExpr implementation
779 /// Converts invocation expression (AST.MethodExpr)
781 private static ExpressionResolution ConvertMethodExpr(AST.Node expr, SemanticResolver sr)
783 return ConvertMethodExpr((AST.MethodExpr)expr, true /* includeInlineFunctions */, sr);
786 private static ExpressionResolution ConvertMethodExpr(AST.MethodExpr methodExpr, bool includeInlineFunctions, SemanticResolver sr)
789 // Resolve methodExpr.Expr
791 ExpressionResolution leftResolution;
792 using (sr.TypeResolver.EnterFunctionNameResolution(includeInlineFunctions))
794 AST.Identifier simpleFunctionName = methodExpr.Expr as AST.Identifier;
795 if (simpleFunctionName != null)
797 leftResolution = sr.ResolveSimpleFunctionName(simpleFunctionName.Name, simpleFunctionName.ErrCtx);
802 // Convert methodExpr.Expr optionally entering special resolution modes. See ConvertMethodExpr_TryEnter methods for more info.
804 AST.DotExpr dotExpr = methodExpr.Expr as AST.DotExpr;
805 using (ConvertMethodExpr_TryEnterIgnoreEntityContainerNameResolution(dotExpr, sr))
807 using (ConvertMethodExpr_TryEnterV1ViewGenBackwardCompatibilityResolution(dotExpr, sr))
809 leftResolution = Convert(methodExpr.Expr, sr);
815 if (leftResolution.ExpressionClass == ExpressionResolutionClass.MetadataMember)
817 MetadataMember metadataMember = (MetadataMember)leftResolution;
820 // Try converting as inline function call. If it fails, continue and try to convert as a model-defined function/function import call.
822 ValueExpression inlineFunctionCall;
823 if (metadataMember.MetadataMemberClass == MetadataMemberClass.InlineFunctionGroup)
825 Debug.Assert(includeInlineFunctions, "includeInlineFunctions must be true, otherwise recursion does not stop");
827 methodExpr.ErrCtx.ErrorContextInfo = Strings.CtxFunction(metadataMember.Name);
828 methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false;
829 if (TryConvertInlineFunctionCall((InlineFunctionGroup)metadataMember, methodExpr, sr, out inlineFunctionCall))
831 return inlineFunctionCall;
835 // Make another try ignoring inline functions.
836 return ConvertMethodExpr(methodExpr, false /* includeInlineFunctions */, sr);
840 switch (metadataMember.MetadataMemberClass)
842 case MetadataMemberClass.Type:
843 methodExpr.ErrCtx.ErrorContextInfo = Strings.CtxTypeCtor(metadataMember.Name);
844 methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false;
845 return ConvertTypeConstructorCall((MetadataType)metadataMember, methodExpr, sr);
847 case MetadataMemberClass.FunctionGroup:
848 methodExpr.ErrCtx.ErrorContextInfo = Strings.CtxFunction(metadataMember.Name);
849 methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false;
850 return ConvertModelFunctionCall((MetadataFunctionGroup)metadataMember, methodExpr, sr);
853 throw EntityUtil.EntitySqlError(methodExpr.Expr.ErrCtx, Strings.CannotResolveNameToTypeOrFunction(metadataMember.Name));
858 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.MethodInvocationNotSupported);
863 /// If methodExpr.Expr is in the form of "Name1.Name2(...)" then ignore entity containers during resolution of the left expression
864 /// in the context of the invocation: "EntityContainer.EntitySet(...)" is not a valid expression and it should not shadow
865 /// a potentially valid interpretation as "Namespace.EntityType/Function(...)".
867 private static IDisposable ConvertMethodExpr_TryEnterIgnoreEntityContainerNameResolution(AST.DotExpr leftExpr, SemanticResolver sr)
869 return leftExpr != null && leftExpr.Left is AST.Identifier ? sr.EnterIgnoreEntityContainerNameResolution() : null;
873 /// If methodExpr.Expr is in the form of "Name1.Name2(...)"
874 /// and we are in the view generation mode
875 /// and schema version is less than V2
876 /// then ignore types in the resolution of Name1.
877 /// This is needed in order to support the following V1 case:
878 /// C-space type: AdventureWorks.Store
879 /// S-space type: [AdventureWorks.Store].Customer
880 /// query: select [AdventureWorks.Store].Customer(1, 2, 3) from ...
882 private static IDisposable ConvertMethodExpr_TryEnterV1ViewGenBackwardCompatibilityResolution(AST.DotExpr leftExpr, SemanticResolver sr)
884 if (leftExpr != null && leftExpr.Left is AST.Identifier &&
885 (sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode ||
886 sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.UserViewGenerationMode))
888 var mappingCollection =
889 sr.TypeResolver.Perspective.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace) as StorageMappingItemCollection;
891 Debug.Assert(mappingCollection != null, "mappingCollection != null");
893 if (mappingCollection.MappingVersion < XmlConstants.EdmVersionForV2)
895 return sr.TypeResolver.EnterBackwardCompatibilityResolution();
902 /// Attempts to create a <see cref="ValueExpression"/> representing the inline function call.
903 /// Returns false if <paramref name="methodExpr"/>.DistinctKind != <see see="AST.Method.DistinctKind"/>.None.
904 /// Returns false if no one of the overloads matched the given arguments.
905 /// Throws if given arguments cause overload resolution ambiguity.
907 private static bool TryConvertInlineFunctionCall(
908 InlineFunctionGroup inlineFunctionGroup,
909 AST.MethodExpr methodExpr,
911 out ValueExpression inlineFunctionCall)
913 inlineFunctionCall = null;
916 // An inline function can't be a group aggregate, so if DistinctKind is specified then it is not an inline function call.
918 if (methodExpr.DistinctKind != AST.DistinctKind.None)
924 // Convert function arguments.
926 List<TypeUsage> argTypes;
927 var args = ConvertFunctionArguments(methodExpr.Args, sr, out argTypes);
930 // Find function overload match for the given argument types.
932 bool isAmbiguous = false;
933 InlineFunctionInfo overload = SemanticResolver.ResolveFunctionOverloads(
934 inlineFunctionGroup.FunctionMetadata,
936 (lambdaOverload) => lambdaOverload.Parameters,
937 (varRef) => varRef.ResultType,
938 (varRef) => ParameterMode.In,
939 false /* isGroupAggregateFunction */,
943 // If there is more than one overload that matches the given arguments, throw.
947 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments);
951 // If null, means no overload matched.
953 if (overload == null)
959 // Convert untyped NULLs in arguments to typed nulls inferred from formals.
961 ConvertUntypedNullsInArguments(args, overload.Parameters, (formal) => formal.ResultType);
963 inlineFunctionCall = new ValueExpression(DbExpressionBuilder.Invoke(overload.GetLambda(sr), args));
967 private static ValueExpression ConvertTypeConstructorCall(MetadataType metadataType, AST.MethodExpr methodExpr, SemanticResolver sr)
970 // Ensure type has a contructor.
972 if (!TypeSemantics.IsComplexType(metadataType.TypeUsage) &&
973 !TypeSemantics.IsEntityType(metadataType.TypeUsage) &&
974 !TypeSemantics.IsRelationshipType(metadataType.TypeUsage))
976 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidCtorUseOnType(metadataType.TypeUsage.EdmType.FullName));
980 // Abstract types cannot be instantiated.
982 if (metadataType.TypeUsage.EdmType.Abstract)
984 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.CannotInstantiateAbstractType(metadataType.TypeUsage.EdmType.FullName));
988 // DistinctKind must not be specified on a type constructor.
990 if (methodExpr.DistinctKind != AST.DistinctKind.None)
992 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInCtor);
996 // Convert relationships if present.
998 List<DbRelatedEntityRef> relshipExprList = null;
999 if (methodExpr.HasRelationships)
1001 if (!(sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode ||
1002 sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.UserViewGenerationMode))
1004 throw EntityUtil.EntitySqlError(methodExpr.Relationships.ErrCtx, Strings.InvalidModeForWithRelationshipClause);
1007 EntityType driverEntityType = metadataType.TypeUsage.EdmType as EntityType;
1008 if (driverEntityType == null)
1010 throw EntityUtil.EntitySqlError(methodExpr.Relationships.ErrCtx, Strings.InvalidTypeForWithRelationshipClause);
1013 HashSet<string> targetEnds = new HashSet<string>();
1014 relshipExprList = new List<DbRelatedEntityRef>(methodExpr.Relationships.Count);
1015 for (int i = 0; i < methodExpr.Relationships.Count; i++)
1017 AST.RelshipNavigationExpr relshipExpr = methodExpr.Relationships[i];
1019 DbRelatedEntityRef relshipTarget = ConvertRelatedEntityRef(relshipExpr, driverEntityType, sr);
1021 string targetEndId = String.Join(":", new String[] { relshipTarget.TargetEnd.DeclaringType.Identity, relshipTarget.TargetEnd.Identity });
1022 if (targetEnds.Contains(targetEndId))
1024 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipTargetMustBeUnique(targetEndId));
1027 targetEnds.Add(targetEndId);
1029 relshipExprList.Add(relshipTarget);
1033 List<TypeUsage> argTypes;
1034 return new ValueExpression(CreateConstructorCallExpression(methodExpr,
1035 metadataType.TypeUsage,
1036 ConvertFunctionArguments(methodExpr.Args, sr, out argTypes),
1041 private static ValueExpression ConvertModelFunctionCall(MetadataFunctionGroup metadataFunctionGroup, AST.MethodExpr methodExpr, SemanticResolver sr)
1043 if (metadataFunctionGroup.FunctionMetadata.Any(f => !f.IsComposableAttribute))
1045 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.CannotCallNoncomposableFunction(metadataFunctionGroup.Name));
1049 // Decide if it is an ordinary function or group aggregate
1051 if (TypeSemantics.IsAggregateFunction(metadataFunctionGroup.FunctionMetadata[0]) && sr.IsInAnyGroupScope())
1054 // If it is an aggreagate function inside a group scope, dispatch to the expensive ConvertAggregateFunctionInGroupScope()...
1056 return new ValueExpression(ConvertAggregateFunctionInGroupScope(methodExpr, metadataFunctionGroup, sr));
1061 // Otherwise, it is just an ordinary function call (including aggregate functions outside of a group scope)
1063 return new ValueExpression(CreateModelFunctionCallExpression(methodExpr, metadataFunctionGroup, sr));
1067 #region ConvertAggregateFunctionInGroupScope implementation
1069 /// Converts group aggregates.
1072 /// This method converts group aggregates in two phases:
1073 /// Phase 1 - it will resolve the actual inner (argument) expression and then anotate the ast node and add the resolved aggregate
1075 /// Phase 2 - if ast node was annotated, just extract the precomputed expression from the scope.
1077 private static DbExpression ConvertAggregateFunctionInGroupScope(AST.MethodExpr methodExpr, MetadataFunctionGroup metadataFunctionGroup, SemanticResolver sr)
1079 DbExpression converted = null;
1082 // First, check if methodExpr is already resolved as an aggregate...
1084 if (TryConvertAsResolvedGroupAggregate(methodExpr, sr, out converted))
1090 // ... then, try to convert as a collection function.
1092 // Note that if methodExpr represents a group aggregate,
1093 // then the argument conversion performed inside of TryConvertAsCollectionFunction(...) is thrown away.
1094 // Throwing the argument conversion however is not possible in a clean way as the argument conversion has few side-effects:
1095 // 1. For each group aggregate within the argument a new GroupAggregateInfo object is created and:
1096 // a. Some of the aggregates are assigned to outer scope regions for evaluation, which means their aggregate info objects are
1097 // - enlisted in the outer scope regions,
1098 // - remain attached to the corresponding AST nodes, see GroupAggregateInfo.AttachToAstNode(...) for more info.
1099 // These aggregate info objects will be reused when the aggregates are revisited, see TryConvertAsResolvedGroupAggregate(...) method for more info.
1100 // b. The aggregate info objects of closest aggregates are wired to sr.CurrentGroupAggregateInfo object as contained/containing.
1101 // 2. sr.CurrentGroupAggregateInfo.InnermostReferencedScopeRegion value is adjusted with all the scope entry references outside of nested aggregates.
1102 // Hence when the conversion as a collection function fails, these side-effects must be mitigated:
1103 // (1.a) does not cause any issues.
1104 // (1.b) requires rewiring which is handled by the GroupAggregateInfo.SetContainingAggregate(...) mechanism invoked by
1105 // TryConvertAsResolvedGroupAggregate(...) method.
1106 // (2) requires saving and restoring the InnermostReferencedScopeRegion value, which is handled in the code below.
1108 // Note: we also do a throw-away conversions in other places, such as inline function attempt and processing of projection items in order by clause,
1109 // but this method is the only place where conversion attempts differ in the way how converted argument expression is processed.
1110 // This method is the only place that affects sr.CurrentGroupAggregateInfo with regard to the converted argument expression.
1111 // Hence the side-effect mitigation is needed only here.
1113 ScopeRegion savedInnermostReferencedScopeRegion = sr.CurrentGroupAggregateInfo != null ? sr.CurrentGroupAggregateInfo.InnermostReferencedScopeRegion : null;
1114 List<TypeUsage> argTypes;
1115 if (TryConvertAsCollectionFunction(methodExpr, metadataFunctionGroup, sr, out argTypes, out converted))
1119 else if (sr.CurrentGroupAggregateInfo != null)
1121 sr.CurrentGroupAggregateInfo.InnermostReferencedScopeRegion = savedInnermostReferencedScopeRegion;
1123 Debug.Assert(argTypes != null, "argTypes != null");
1126 // Finally, try to convert as a function group aggregate.
1128 if (TryConvertAsFunctionAggregate(methodExpr, metadataFunctionGroup, argTypes, sr, out converted))
1134 // If we reach this point, means the resolution failed.
1136 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.FailedToResolveAggregateFunction(metadataFunctionGroup.Name));
1140 /// Try to convert as pre resolved group aggregate.
1142 private static bool TryConvertAsResolvedGroupAggregate(AST.GroupAggregateExpr groupAggregateExpr, SemanticResolver sr, out DbExpression converted)
1147 // If ast node was annotated in a previous pass, means it contains a ready-to-use expression,
1150 if (groupAggregateExpr.AggregateInfo == null)
1156 // Wire up groupAggregateExpr.AggregateInfo to the sr.CurrentGroupAggregateInfo.
1157 // This is needed in the following case: ... select max(x + max(b)) ...
1158 // The outer max(...) is first processed as collection function, so when the nested max(b) is processed as an aggregate, it does not
1159 // see the outer function as a containing aggregate, so it does not wire to it.
1160 // Later, when the outer max(...) is processed as an aggregate, processing of the inner max(...) gets into TryConvertAsResolvedGroupAggregate(...)
1161 // and at this point we finally wire up the two aggregates.
1163 groupAggregateExpr.AggregateInfo.SetContainingAggregate(sr.CurrentGroupAggregateInfo);
1165 if (!sr.TryResolveInternalAggregateName(groupAggregateExpr.AggregateInfo.AggregateName, groupAggregateExpr.AggregateInfo.ErrCtx, out converted))
1167 Debug.Assert(groupAggregateExpr.AggregateInfo.AggregateStubExpression != null, "Resolved aggregate stub expression must not be null.");
1168 converted = groupAggregateExpr.AggregateInfo.AggregateStubExpression;
1171 Debug.Assert(converted != null, "converted != null");
1177 /// Try convert method expr in a group scope as a collection aggregate
1179 /// <param name="argTypes">argTypes are returned regardless of the function result</param>
1180 private static bool TryConvertAsCollectionFunction(AST.MethodExpr methodExpr,
1181 MetadataFunctionGroup metadataFunctionGroup,
1182 SemanticResolver sr,
1183 out List<TypeUsage> argTypes,
1184 out DbExpression converted)
1187 // Convert aggregate arguments.
1189 var args = ConvertFunctionArguments(methodExpr.Args, sr, out argTypes);
1192 // Try to see if there is an overload match.
1194 bool isAmbiguous = false;
1195 EdmFunction functionType = SemanticResolver.ResolveFunctionOverloads(
1196 metadataFunctionGroup.FunctionMetadata,
1198 false /* isGroupAggregateFunction */,
1202 // If there is more then one overload that matches given arguments, throw.
1206 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments);
1210 // If not null, means a match was found as a collection aggregate (ordinary function).
1212 if (functionType != null)
1215 // Convert untyped NULLs in arguments to typed nulls inferred from function parameters.
1217 ConvertUntypedNullsInArguments(args, functionType.Parameters, (parameter) => parameter.TypeUsage);
1218 converted = functionType.Invoke(args);
1228 private static bool TryConvertAsFunctionAggregate(AST.MethodExpr methodExpr,
1229 MetadataFunctionGroup metadataFunctionGroup,
1230 List<TypeUsage> argTypes,
1231 SemanticResolver sr,
1232 out DbExpression converted)
1234 Debug.Assert(argTypes != null, "argTypes != null");
1239 // Try to find an overload match as group aggregate
1241 bool isAmbiguous = false;
1242 EdmFunction functionType = SemanticResolver.ResolveFunctionOverloads(
1243 metadataFunctionGroup.FunctionMetadata,
1245 true /* isGroupAggregateFunction */,
1249 // If there is more then one overload that matches given arguments, throw.
1253 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments);
1257 // If it still null, then there is no overload as a group aggregate function.
1259 if (null == functionType)
1261 CqlErrorHelper.ReportFunctionOverloadError(methodExpr, metadataFunctionGroup.FunctionMetadata[0], argTypes);
1264 // Process aggregate argument.
1266 List<DbExpression> args;
1267 FunctionAggregateInfo aggregateInfo;
1268 using (sr.EnterFunctionAggregate(methodExpr, methodExpr.ErrCtx, out aggregateInfo))
1270 List<TypeUsage> aggArgTypes;
1271 args = ConvertFunctionArguments(methodExpr.Args, sr, out aggArgTypes);
1272 // Sanity check - argument types must agree.
1274 argTypes.Count == aggArgTypes.Count &&
1275 argTypes.Zip(aggArgTypes).All(types => types.Key == null && types.Value == null || TypeSemantics.IsStructurallyEqual(types.Key, types.Value)),
1276 "argument types resolved for the collection aggregate calls must match");
1280 // Aggregate functions can have only one argument and of collection type
1282 Debug.Assert((1 == functionType.Parameters.Count), "(1 == functionType.Parameters.Count)"); // we only support monadic aggregate functions
1283 Debug.Assert(TypeSemantics.IsCollectionType(functionType.Parameters[0].TypeUsage), "functionType.Parameters[0].Type is CollectionType");
1286 // Convert untyped NULLs in arguments to typed nulls inferred from function parameters.
1288 ConvertUntypedNullsInArguments(args, functionType.Parameters, (parameter) => TypeHelpers.GetElementTypeUsage(parameter.TypeUsage));
1291 // Create function aggregate expression.
1293 DbFunctionAggregate functionAggregate;
1294 if (methodExpr.DistinctKind == AST.DistinctKind.Distinct)
1296 functionAggregate = DbExpressionBuilder.AggregateDistinct(functionType, args[0]);
1300 functionAggregate = DbExpressionBuilder.Aggregate(functionType, args[0]);
1304 // Add aggregate to aggreate list.
1306 aggregateInfo.AttachToAstNode(sr.GenerateInternalName("groupAgg" + functionType.Name), functionAggregate);
1307 aggregateInfo.EvaluatingScopeRegion.GroupAggregateInfos.Add(aggregateInfo);
1310 // Return stub expression with same type as the aggregate function.
1312 converted = aggregateInfo.AggregateStubExpression;
1314 Debug.Assert(converted != null, "converted != null");
1318 #endregion ConvertAggregateFunctionInGroupScope implementation
1321 /// Creates <see cref="DbExpression"/> representing a new instance of the given type.
1322 /// Validates and infers argument types.
1324 private static DbExpression CreateConstructorCallExpression(AST.MethodExpr methodExpr,
1326 List<DbExpression> args,
1327 List<DbRelatedEntityRef> relshipExprList,
1328 SemanticResolver sr)
1330 Debug.Assert(TypeSemantics.IsComplexType(type) || TypeSemantics.IsEntityType(type) || TypeSemantics.IsRelationshipType(type), "type must have a constructor");
1332 DbExpression newInstance = null;
1334 int argCount = args.Count;
1337 // Find overloads by searching members in order of its definition.
1338 // Each member will be considered as a formal argument type in the order of its definition.
1340 StructuralType stype = (StructuralType)type.EdmType;
1341 foreach (EdmMember member in TypeHelpers.GetAllStructuralMembers(stype))
1343 TypeUsage memberModelTypeUsage = Helper.GetModelTypeUsage(member);
1345 Debug.Assert(memberModelTypeUsage.EdmType.DataSpace == DataSpace.CSpace, "member space must be CSpace");
1348 // Ensure given arguments are not less than 'formal' constructor arguments.
1350 if (argCount <= idx)
1352 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.NumberOfTypeCtorIsLessThenFormalSpec(member.Name));
1356 // If the given argument is the untyped null, infer type from the ctor formal argument type.
1358 if (args[idx] == null)
1360 EdmProperty edmProperty = member as EdmProperty;
1361 if (edmProperty != null && !edmProperty.Nullable)
1363 throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx,
1364 Strings.InvalidNullLiteralForNonNullableMember(member.Name, stype.FullName));
1366 args[idx] = DbExpressionBuilder.Null(memberModelTypeUsage);
1370 // Ensure the given argument type is promotable to the formal ctor argument type.
1372 bool isPromotable = TypeSemantics.IsPromotableTo(args[idx].ResultType, memberModelTypeUsage);
1373 if (ParserOptions.CompilationMode.RestrictedViewGenerationMode == sr.ParserOptions.ParserCompilationMode ||
1374 ParserOptions.CompilationMode.UserViewGenerationMode == sr.ParserOptions.ParserCompilationMode)
1376 if (!isPromotable && !TypeSemantics.IsPromotableTo(memberModelTypeUsage, args[idx].ResultType))
1378 throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx,
1379 Strings.InvalidCtorArgumentType(
1380 args[idx].ResultType.EdmType.FullName,
1382 memberModelTypeUsage.EdmType.FullName));
1385 if (Helper.IsPrimitiveType(memberModelTypeUsage.EdmType) &&
1386 !TypeSemantics.IsSubTypeOf(args[idx].ResultType, memberModelTypeUsage))
1388 args[idx] = args[idx].CastTo(memberModelTypeUsage);
1395 throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx,
1396 Strings.InvalidCtorArgumentType(
1397 args[idx].ResultType.EdmType.FullName,
1399 memberModelTypeUsage.EdmType.FullName));
1407 // Ensure all given arguments and all ctor formals were considered and properly checked.
1409 if (idx != argCount)
1411 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.NumberOfTypeCtorIsMoreThenFormalSpec(stype.FullName));
1415 // Finally, create expression
1417 if (relshipExprList != null && relshipExprList.Count > 0)
1419 EntityType entityType = (EntityType)type.EdmType;
1420 newInstance = DbExpressionBuilder.CreateNewEntityWithRelationshipsExpression(entityType, args, relshipExprList);
1424 newInstance = DbExpressionBuilder.New(TypeHelpers.GetReadOnlyType(type), args);
1426 Debug.Assert(null != newInstance, "null != newInstance");
1432 /// Creates <see cref="DbFunctionExpression"/> representing a model function call.
1433 /// Validates overloads.
1435 private static DbFunctionExpression CreateModelFunctionCallExpression(AST.MethodExpr methodExpr,
1436 MetadataFunctionGroup metadataFunctionGroup,
1437 SemanticResolver sr)
1439 DbFunctionExpression functionExpression = null;
1440 bool isAmbiguous = false;
1443 // DistinctKind must not be specified on a regular function call.
1445 if (methodExpr.DistinctKind != AST.DistinctKind.None)
1447 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInNonAggFunction);
1451 // Convert function arguments.
1453 List<TypeUsage> argTypes;
1454 var args = ConvertFunctionArguments(methodExpr.Args, sr, out argTypes);
1457 // Find function overload match for given argument types.
1459 EdmFunction functionType = SemanticResolver.ResolveFunctionOverloads(
1460 metadataFunctionGroup.FunctionMetadata,
1462 false /* isGroupAggregateFunction */,
1466 // If there is more than one overload that matches given arguments, throw.
1470 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments);
1474 // If null, means no overload matched.
1476 if (null == functionType)
1478 CqlErrorHelper.ReportFunctionOverloadError(methodExpr, metadataFunctionGroup.FunctionMetadata[0], argTypes);
1482 // Convert untyped NULLs in arguments to typed nulls inferred from function parameters.
1484 ConvertUntypedNullsInArguments(args, functionType.Parameters, (parameter) => parameter.TypeUsage);
1487 // Finally, create expression
1489 functionExpression = functionType.Invoke(args);
1491 Debug.Assert(null != functionExpression, "null != functionExpression");
1493 return functionExpression;
1497 /// Converts function call arguments into a list of <see cref="DbExpression"/>s.
1498 /// In case of no arguments returns an empty list.
1500 private static List<DbExpression> ConvertFunctionArguments(AST.NodeList<AST.Node> astExprList, SemanticResolver sr, out List<TypeUsage> argTypes)
1502 List<DbExpression> convertedArgs = new List<DbExpression>();
1504 if (null != astExprList)
1506 for (int i = 0; i < astExprList.Count; i++)
1508 convertedArgs.Add(ConvertValueExpressionAllowUntypedNulls(astExprList[i], sr));
1512 argTypes = convertedArgs.Select(a => a != null ? a.ResultType : null).ToList();
1513 return convertedArgs;
1516 private static void ConvertUntypedNullsInArguments<TParameterMetadata>(
1517 List<DbExpression> args,
1518 IList<TParameterMetadata> parametersMetadata,
1519 Func<TParameterMetadata, TypeUsage> getParameterTypeUsage)
1521 for (int i = 0; i < args.Count; i++)
1523 if (args[i] == null)
1525 args[i] = DbExpressionBuilder.Null(getParameterTypeUsage(parametersMetadata[i]));
1529 #endregion ConvertMethodExpr implementation
1532 /// Converts command parameter reference expression (AST.QueryParameter)
1534 private static ExpressionResolution ConvertParameter(AST.Node expr, SemanticResolver sr)
1536 AST.QueryParameter parameter = (AST.QueryParameter)expr;
1538 DbParameterReferenceExpression paramRef;
1539 if (null == sr.Parameters || !sr.Parameters.TryGetValue(parameter.Name, out paramRef))
1541 throw EntityUtil.EntitySqlError(parameter.ErrCtx, Strings.ParameterWasNotDefined(parameter.Name));
1544 return new ValueExpression(paramRef);
1548 /// Converts WITH RELATIONSHIP (AST.RelshipNavigationExpr)
1550 /// <param name="driverEntityType">The entity that is being constructed for with this RELATIONSHIP clause is processed.</param>
1551 /// <param name="relshipExpr">the ast expression</param>
1552 /// <param name="sr">the Semantic Resolver context</param>
1553 /// <returns>a DbRelatedEntityRef instance</returns>
1554 private static DbRelatedEntityRef ConvertRelatedEntityRef(AST.RelshipNavigationExpr relshipExpr, EntityType driverEntityType, SemanticResolver sr)
1557 // Resolve relationship type name.
1559 var edmType = ConvertTypeName(relshipExpr.TypeName, sr).EdmType;
1560 var relationshipType = edmType as RelationshipType;
1561 if (relationshipType == null)
1563 throw EntityUtil.EntitySqlError(relshipExpr.TypeName.ErrCtx, Strings.RelationshipTypeExpected(edmType.FullName));
1567 // Convert target instance expression.
1569 var targetEntityRef = ConvertValueExpression(relshipExpr.RefExpr, sr);
1572 // Make sure it is a ref type.
1574 var refType = targetEntityRef.ResultType.EdmType as RefType;
1575 if (refType == null)
1577 throw EntityUtil.EntitySqlError(relshipExpr.RefExpr.ErrCtx, Strings.RelatedEndExprTypeMustBeReference);
1581 // Convert To end if explicitly defined, derive if implicit.
1583 RelationshipEndMember toEnd;
1584 if (relshipExpr.ToEndIdentifier != null)
1586 toEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.ToEndIdentifier.Name, StringComparison.OrdinalIgnoreCase));
1589 throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.ToEndIdentifier.Name, relationshipType.FullName));
1592 // ensure is *..{0|1}
1594 if (toEnd.RelationshipMultiplicity != RelationshipMultiplicity.One && toEnd.RelationshipMultiplicity != RelationshipMultiplicity.ZeroOrOne)
1596 throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx,
1597 Strings.InvalidWithRelationshipTargetEndMultiplicity(toEnd.Name, toEnd.RelationshipMultiplicity.ToString()));
1599 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(refType, toEnd.TypeUsage.EdmType))
1601 throw EntityUtil.EntitySqlError(relshipExpr.RefExpr.ErrCtx, Strings.RelatedEndExprTypeMustBePromotoableToToEnd(refType.FullName, toEnd.TypeUsage.EdmType.FullName));
1606 var toEndCandidates = relationshipType.Members.Select(m => (RelationshipEndMember)m)
1607 .Where (e => TypeSemantics.IsStructurallyEqualOrPromotableTo(refType, e.TypeUsage.EdmType) &&
1608 (e.RelationshipMultiplicity == RelationshipMultiplicity.One ||
1609 e.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne)).ToArray();
1610 switch (toEndCandidates.Length)
1613 toEnd = toEndCandidates[0];
1616 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipToEnd(relationshipType.FullName));
1618 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipToEndIsAmbiguos);
1621 Debug.Assert(toEnd != null, "toEnd must be resolved.");
1624 // Convert From end if explicitly defined, derive if implicit.
1626 RelationshipEndMember fromEnd;
1627 if (relshipExpr.FromEndIdentifier != null)
1629 fromEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.FromEndIdentifier.Name, StringComparison.OrdinalIgnoreCase));
1630 if (fromEnd == null)
1632 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.FromEndIdentifier.Name, relationshipType.FullName));
1634 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(driverEntityType.GetReferenceType(), fromEnd.TypeUsage.EdmType))
1636 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx,
1637 Strings.SourceTypeMustBePromotoableToFromEndRelationType(driverEntityType.FullName, fromEnd.TypeUsage.EdmType.FullName));
1639 if (fromEnd.EdmEquals(toEnd))
1641 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos);
1646 var fromEndCandidates = relationshipType.Members.Select(m => (RelationshipEndMember)m)
1647 .Where(e => TypeSemantics.IsStructurallyEqualOrPromotableTo(driverEntityType.GetReferenceType(), e.TypeUsage.EdmType) &&
1648 !e.EdmEquals(toEnd)).ToArray();
1649 switch (fromEndCandidates.Length)
1652 fromEnd = fromEndCandidates[0];
1655 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipFromEnd(relationshipType.FullName));
1657 Debug.Fail("N-ary relationship? N > 2");
1658 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos);
1661 Debug.Assert(fromEnd != null, "fromEnd must be resolved.");
1663 return DbExpressionBuilder.CreateRelatedEntityRef(fromEnd, toEnd, targetEntityRef);
1667 /// Converts relationship navigation expression (AST.RelshipNavigationExpr)
1669 private static ExpressionResolution ConvertRelshipNavigationExpr(AST.Node astExpr, SemanticResolver sr)
1671 AST.RelshipNavigationExpr relshipExpr = (AST.RelshipNavigationExpr)astExpr;
1674 // Resolve relationship type name.
1676 var edmType = ConvertTypeName(relshipExpr.TypeName, sr).EdmType;
1677 var relationshipType = edmType as RelationshipType;
1678 if (relationshipType == null)
1680 throw EntityUtil.EntitySqlError(relshipExpr.TypeName.ErrCtx, Strings.RelationshipTypeExpected(edmType.FullName));
1684 // Convert source instance expression.
1686 var sourceEntityRef = ConvertValueExpression(relshipExpr.RefExpr, sr);
1689 // Make sure it is a ref type. Convert to ref if possible.
1691 var sourceRefType = sourceEntityRef.ResultType.EdmType as RefType;
1692 if (sourceRefType == null)
1694 var entityType = sourceEntityRef.ResultType.EdmType as EntityType;
1695 if (entityType != null)
1697 sourceEntityRef = DbExpressionBuilder.GetEntityRef(sourceEntityRef);
1698 sourceRefType = (RefType)sourceEntityRef.ResultType.EdmType;
1702 throw EntityUtil.EntitySqlError(relshipExpr.RefExpr.ErrCtx, Strings.RelatedEndExprTypeMustBeReference);
1707 // Convert To end if explicitly defined. Derive if implicit later, after From end processing.
1709 RelationshipEndMember toEnd;
1710 if (relshipExpr.ToEndIdentifier != null)
1712 toEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.ToEndIdentifier.Name, StringComparison.OrdinalIgnoreCase));
1715 throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.ToEndIdentifier.Name, relationshipType.FullName));
1724 // Convert From end if explicitly defined, derive if implicit.
1726 RelationshipEndMember fromEnd;
1727 if (relshipExpr.FromEndIdentifier != null)
1729 fromEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.FromEndIdentifier.Name, StringComparison.OrdinalIgnoreCase));
1730 if (fromEnd == null)
1732 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.FromEndIdentifier.Name, relationshipType.FullName));
1734 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(sourceRefType, fromEnd.TypeUsage.EdmType))
1736 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx,
1737 Strings.SourceTypeMustBePromotoableToFromEndRelationType(sourceRefType.FullName, fromEnd.TypeUsage.EdmType.FullName));
1739 if (toEnd != null && fromEnd.EdmEquals(toEnd))
1741 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos);
1746 var fromEndCandidates = relationshipType.Members.Select(m => (RelationshipEndMember)m)
1747 .Where (e => TypeSemantics.IsStructurallyEqualOrPromotableTo(sourceRefType, e.TypeUsage.EdmType) &&
1748 (toEnd == null || !e.EdmEquals(toEnd))).ToArray();
1749 switch (fromEndCandidates.Length)
1752 fromEnd = fromEndCandidates[0];
1755 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipFromEnd(relationshipType.FullName));
1757 Debug.Assert(toEnd == null, "N-ary relationship? N > 2");
1758 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos);
1761 Debug.Assert(fromEnd != null, "fromEnd must be resolved.");
1764 // Derive To end if implicit.
1768 var toEndCandidates = relationshipType.Members.Select(m => (RelationshipEndMember)m)
1769 .Where (e => !e.EdmEquals(fromEnd)).ToArray();
1770 switch (toEndCandidates.Length)
1773 toEnd = toEndCandidates[0];
1776 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipToEnd(relationshipType.FullName));
1778 Debug.Fail("N-ary relationship? N > 2");
1779 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipToEndIsAmbiguos);
1782 Debug.Assert(toEnd != null, "toEnd must be resolved.");
1785 // Create cqt expression.
1787 DbExpression converted = sourceEntityRef.Navigate(fromEnd, toEnd);
1788 Debug.Assert(null != converted, "null != converted");
1790 return new ValueExpression(converted);
1794 /// Converts REF expression (AST.RefExpr)
1796 private static ExpressionResolution ConvertRefExpr(AST.Node astExpr, SemanticResolver sr)
1798 AST.RefExpr refExpr = (AST.RefExpr)astExpr;
1800 DbExpression converted = ConvertValueExpression(refExpr.ArgExpr, sr);
1803 // check if is entity type
1805 if (!TypeSemantics.IsEntityType(converted.ResultType))
1807 throw EntityUtil.EntitySqlError(refExpr.ArgExpr.ErrCtx, Strings.RefArgIsNotOfEntityType(converted.ResultType.EdmType.FullName));
1811 // create ref expression
1813 converted = converted.GetEntityRef();
1814 Debug.Assert(null != converted, "null != converted");
1816 return new ValueExpression(converted);
1820 /// Converts DEREF expression (AST.DerefExpr)
1822 private static ExpressionResolution ConvertDeRefExpr(AST.Node astExpr, SemanticResolver sr)
1824 AST.DerefExpr deRefExpr = (AST.DerefExpr)astExpr;
1826 DbExpression converted = null;
1828 converted = ConvertValueExpression(deRefExpr.ArgExpr, sr);
1831 // check if return type is RefType
1833 if (!TypeSemantics.IsReferenceType(converted.ResultType))
1835 throw EntityUtil.EntitySqlError(deRefExpr.ArgExpr.ErrCtx, Strings.DeRefArgIsNotOfRefType(converted.ResultType.EdmType.FullName));
1839 // create DeRef expression
1841 converted = converted.Deref();
1842 Debug.Assert(null != converted, "null != converted");
1844 return new ValueExpression(converted);
1848 /// Converts CREATEREF expression (AST.CreateRefExpr)
1850 private static ExpressionResolution ConvertCreateRefExpr(AST.Node astExpr, SemanticResolver sr)
1852 AST.CreateRefExpr createRefExpr = (AST.CreateRefExpr)astExpr;
1854 DbExpression converted = null;
1857 // Convert the entity set, also, ensure that we get back an extent expression
1859 DbScanExpression entitySetExpr = ConvertValueExpression(createRefExpr.EntitySet, sr) as DbScanExpression;
1860 if (entitySetExpr == null)
1862 throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, Strings.ExprIsNotValidEntitySetForCreateRef);
1866 // Ensure that the extent is an entity set
1868 EntitySet entitySet = entitySetExpr.Target as EntitySet;
1869 if (entitySet == null)
1871 throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, Strings.ExprIsNotValidEntitySetForCreateRef);
1874 DbExpression keyRowExpression = ConvertValueExpression(createRefExpr.Keys, sr);
1876 RowType inputKeyRowType = keyRowExpression.ResultType.EdmType as RowType;
1877 if (null == inputKeyRowType)
1879 throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, Strings.InvalidCreateRefKeyType);
1882 RowType entityKeyRowType = TypeHelpers.CreateKeyRowType(entitySet.ElementType);
1884 if (entityKeyRowType.Members.Count != inputKeyRowType.Members.Count)
1886 throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, Strings.ImcompatibleCreateRefKeyType);
1889 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(keyRowExpression.ResultType, TypeUsage.Create(entityKeyRowType)))
1891 throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, Strings.ImcompatibleCreateRefKeyElementType);
1895 // if CREATEREF specifies a type, resolve and validate the type
1897 if (null != createRefExpr.TypeIdentifier)
1899 TypeUsage targetTypeUsage = ConvertTypeName(createRefExpr.TypeIdentifier, sr);
1902 // ensure type is entity
1904 if (!TypeSemantics.IsEntityType(targetTypeUsage))
1907 throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx,
1908 Strings.CreateRefTypeIdentifierMustSpecifyAnEntityType(
1909 targetTypeUsage.EdmType.FullName,
1910 targetTypeUsage.EdmType.BuiltInTypeKind.ToString()));
1913 if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, targetTypeUsage.EdmType))
1915 throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx,
1916 Strings.CreateRefTypeIdentifierMustBeASubOrSuperType(
1917 entitySet.ElementType.FullName,
1918 targetTypeUsage.EdmType.FullName));
1921 converted = DbExpressionBuilder.RefFromKey(entitySet, keyRowExpression, (EntityType)targetTypeUsage.EdmType);
1926 // finally creates the expression
1928 converted = DbExpressionBuilder.RefFromKey(entitySet, keyRowExpression);
1931 Debug.Assert(null != converted, "null != converted");
1933 return new ValueExpression(converted);
1937 /// Converts KEY expression (AST.KeyExpr)
1939 private static ExpressionResolution ConvertKeyExpr(AST.Node astExpr, SemanticResolver sr)
1941 AST.KeyExpr keyExpr = (AST.KeyExpr)astExpr;
1943 DbExpression converted = ConvertValueExpression(keyExpr.ArgExpr, sr);
1945 if (TypeSemantics.IsEntityType(converted.ResultType))
1947 converted = converted.GetEntityRef();
1949 else if (!TypeSemantics.IsReferenceType(converted.ResultType))
1951 throw EntityUtil.EntitySqlError(keyExpr.ArgExpr.ErrCtx, Strings.InvalidKeyArgument(converted.ResultType.EdmType.FullName));
1954 converted = converted.GetRefKey();
1955 Debug.Assert(null != converted, "null != converted");
1957 return new ValueExpression(converted);
1961 /// Converts a builtin expression (AST.BuiltInExpr).
1963 private static ExpressionResolution ConvertBuiltIn(AST.Node astExpr, SemanticResolver sr)
1965 AST.BuiltInExpr bltInExpr = (AST.BuiltInExpr)astExpr;
1967 BuiltInExprConverter builtInConverter = _builtInExprConverter[bltInExpr.Kind];
1968 if (builtInConverter == null)
1970 throw EntityUtil.EntitySqlError(Strings.UnknownBuiltInAstExpressionType);
1973 return new ValueExpression(builtInConverter(bltInExpr, sr));
1977 /// Converts Arithmetic Expressions Args
1979 /// <param name="astBuiltInExpr"></param>
1980 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
1981 /// <returns></returns>
1982 private static Pair<DbExpression, DbExpression> ConvertArithmeticArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
1984 var operands = ConvertValueExpressionsWithUntypedNulls(
1985 astBuiltInExpr.Arg1,
1986 astBuiltInExpr.Arg2,
1987 astBuiltInExpr.ErrCtx,
1988 () => Strings.InvalidNullArithmetic,
1991 if (!TypeSemantics.IsNumericType(operands.Left.ResultType))
1993 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionMustBeNumericType);
1996 if (operands.Right != null)
1998 if (!TypeSemantics.IsNumericType(operands.Right.ResultType))
2000 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.ExpressionMustBeNumericType);
2003 if (null == TypeHelpers.GetCommonTypeUsage(operands.Left.ResultType, operands.Right.ResultType))
2005 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible(
2006 operands.Left.ResultType.EdmType.FullName, operands.Right.ResultType.EdmType.FullName));
2014 /// Converts Plus Args - specific case since string type is an allowed type for '+'
2016 /// <param name="astBuiltInExpr"></param>
2017 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
2018 /// <returns></returns>
2019 private static Pair<DbExpression, DbExpression> ConvertPlusOperands(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
2021 var operands = ConvertValueExpressionsWithUntypedNulls(
2022 astBuiltInExpr.Arg1,
2023 astBuiltInExpr.Arg2,
2024 astBuiltInExpr.ErrCtx,
2025 () => Strings.InvalidNullArithmetic,
2028 if (!TypeSemantics.IsNumericType(operands.Left.ResultType) && !TypeSemantics.IsPrimitiveType(operands.Left.ResultType, PrimitiveTypeKind.String))
2030 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.PlusLeftExpressionInvalidType);
2033 if (!TypeSemantics.IsNumericType(operands.Right.ResultType) && !TypeSemantics.IsPrimitiveType(operands.Right.ResultType, PrimitiveTypeKind.String))
2035 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.PlusRightExpressionInvalidType);
2038 if (TypeHelpers.GetCommonTypeUsage(operands.Left.ResultType, operands.Right.ResultType) == null)
2040 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible(
2041 operands.Left.ResultType.EdmType.FullName, operands.Right.ResultType.EdmType.FullName));
2048 /// Converts Logical Expression Args
2050 /// <param name="astBuiltInExpr"></param>
2051 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
2052 /// <returns></returns>
2053 private static Pair<DbExpression, DbExpression> ConvertLogicalArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
2055 DbExpression leftExpr = ConvertValueExpressionAllowUntypedNulls(astBuiltInExpr.Arg1, sr);
2056 if (leftExpr == null)
2058 leftExpr = DbExpressionBuilder.Null(sr.TypeResolver.BooleanType);
2061 DbExpression rightExpr = null;
2062 if (astBuiltInExpr.Arg2 != null)
2064 rightExpr = ConvertValueExpressionAllowUntypedNulls(astBuiltInExpr.Arg2, sr);
2065 if (rightExpr == null)
2067 rightExpr = DbExpressionBuilder.Null(sr.TypeResolver.BooleanType);
2072 // ensure left expression type is boolean
2074 if (!IsBooleanType(leftExpr.ResultType))
2076 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionTypeMustBeBoolean);
2080 // ensure right expression type is boolean
2082 if (null != rightExpr && !IsBooleanType(rightExpr.ResultType))
2084 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.ExpressionTypeMustBeBoolean);
2087 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr);
2091 /// Converts Equal Comparison Expression Args
2093 /// <param name="astBuiltInExpr"></param>
2094 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
2095 /// <returns></returns>
2096 private static Pair<DbExpression, DbExpression> ConvertEqualCompArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
2099 // convert left and right types and infer null types
2101 Pair<DbExpression, DbExpression> compArgs = ConvertValueExpressionsWithUntypedNulls(
2102 astBuiltInExpr.Arg1,
2103 astBuiltInExpr.Arg2,
2104 astBuiltInExpr.ErrCtx,
2105 () => Strings.InvalidNullComparison,
2109 // ensure both operand types are equal-comparable
2111 if (!TypeSemantics.IsEqualComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType))
2113 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible(
2114 compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName));
2121 /// Converts Order Comparison Expression Args
2123 /// <param name="astBuiltInExpr"></param>
2124 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
2125 /// <returns></returns>
2126 private static Pair<DbExpression, DbExpression> ConvertOrderCompArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
2128 Pair<DbExpression, DbExpression> compArgs = ConvertValueExpressionsWithUntypedNulls(
2129 astBuiltInExpr.Arg1,
2130 astBuiltInExpr.Arg2,
2131 astBuiltInExpr.ErrCtx,
2132 () => Strings.InvalidNullComparison,
2136 // ensure both operand types are order-comparable
2138 if (!TypeSemantics.IsOrderComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType))
2140 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible(
2141 compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName));
2148 /// Converts Set Expression Args
2150 /// <param name="astBuiltInExpr"></param>
2151 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
2152 /// <returns></returns>
2153 private static Pair<DbExpression, DbExpression> ConvertSetArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
2156 // convert left expression
2158 DbExpression leftExpr = ConvertValueExpression(astBuiltInExpr.Arg1, sr);
2161 // convert right expression if binary set op kind
2163 DbExpression rightExpr = null;
2164 if (null != astBuiltInExpr.Arg2)
2171 // make sure left expression type is of sequence type (ICollection or Extent)
2173 if (!TypeSemantics.IsCollectionType(leftExpr.ResultType))
2175 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.LeftSetExpressionArgsMustBeCollection);
2179 // convert right expression
2181 rightExpr = ConvertValueExpression(astBuiltInExpr.Arg2, sr);
2184 // make sure right expression type is of sequence type (ICollection or Extent)
2186 if (!TypeSemantics.IsCollectionType(rightExpr.ResultType))
2188 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.RightSetExpressionArgsMustBeCollection);
2191 TypeUsage commonType;
2192 TypeUsage leftElemType = TypeHelpers.GetElementTypeUsage(leftExpr.ResultType);
2193 TypeUsage rightElemType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType);
2194 if (!TypeSemantics.TryGetCommonType(leftElemType, rightElemType, out commonType))
2196 CqlErrorHelper.ReportIncompatibleCommonType(astBuiltInExpr.ErrCtx, leftElemType, rightElemType);
2199 if (astBuiltInExpr.Kind != AST.BuiltInKind.UnionAll)
2202 // ensure left argument is set op comparable
2204 if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType)))
2206 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx,
2207 Strings.PlaceholderSetArgTypeIsNotEqualComparable(
2208 Strings.LocalizedLeft,
2209 astBuiltInExpr.Kind.ToString().ToUpperInvariant(),
2210 TypeHelpers.GetElementTypeUsage(leftExpr.ResultType).EdmType.FullName));
2214 // ensure right argument is set op comparable
2216 if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(rightExpr.ResultType)))
2218 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx,
2219 Strings.PlaceholderSetArgTypeIsNotEqualComparable(
2220 Strings.LocalizedRight,
2221 astBuiltInExpr.Kind.ToString().ToUpperInvariant(),
2222 TypeHelpers.GetElementTypeUsage(rightExpr.ResultType).EdmType.FullName));
2227 if (Helper.IsAssociationType(leftElemType.EdmType))
2229 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.InvalidAssociationTypeForUnion(leftElemType.EdmType.FullName));
2232 if (Helper.IsAssociationType(rightElemType.EdmType))
2234 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.InvalidAssociationTypeForUnion(rightElemType.EdmType.FullName));
2245 // make sure expression type is of sequence type (ICollection or Extent)
2247 if (!TypeSemantics.IsCollectionType(leftExpr.ResultType))
2249 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.InvalidUnarySetOpArgument(astBuiltInExpr.Name));
2253 // make sure that if is distinct unary operator, arg element type must be equal-comparable
2255 if (astBuiltInExpr.Kind == AST.BuiltInKind.Distinct && !TypeHelpers.IsValidDistinctOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType)))
2257 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionTypeMustBeEqualComparable);
2261 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr);
2266 /// Converts Set 'IN' expression args
2268 /// <param name="astBuiltInExpr"></param>
2269 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param>
2270 /// <returns></returns>
2271 private static Pair<DbExpression, DbExpression> ConvertInExprArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)
2273 DbExpression rightExpr = ConvertValueExpression(astBuiltInExpr.Arg2, sr);
2274 if (!TypeSemantics.IsCollectionType(rightExpr.ResultType))
2276 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.RightSetExpressionArgsMustBeCollection);
2279 DbExpression leftExpr = ConvertValueExpressionAllowUntypedNulls(astBuiltInExpr.Arg1, sr);
2280 if (leftExpr == null)
2283 // If left expression type is null, infer its type from the collection element type.
2285 TypeUsage elementType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType);
2286 ValidateTypeForNullExpression(elementType, astBuiltInExpr.Arg1.ErrCtx);
2287 leftExpr = DbExpressionBuilder.Null(elementType);
2290 if (TypeSemantics.IsCollectionType(leftExpr.ResultType))
2292 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionTypeMustNotBeCollection);
2296 // Ensure that if left and right are typed expressions then their types must be comparable for IN op.
2298 TypeUsage commonElemType = TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, TypeHelpers.GetElementTypeUsage(rightExpr.ResultType));
2299 if (null == commonElemType || !TypeHelpers.IsValidInOpType(commonElemType))
2301 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.InvalidInExprArgs(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName));
2304 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr);
2307 private static void ValidateTypeForNullExpression(TypeUsage type, ErrorContext errCtx)
2309 if (TypeSemantics.IsCollectionType(type))
2311 throw EntityUtil.EntitySqlError(errCtx, Strings.NullLiteralCannotBePromotedToCollectionOfNulls);
2316 /// Converts a type name.
2317 /// Type name can be represented by
2318 /// - AST.Identifier, such as "Product"
2319 /// - AST.DotExpr, such as "Northwind.Product"
2320 /// - AST.MethodExpr, such as "Edm.Decimal(10,4)", where "10" and "4" are type arguments.
2322 private static TypeUsage ConvertTypeName(AST.Node typeName, SemanticResolver sr)
2324 Debug.Assert(typeName != null, "typeName != null");
2326 string[] name = null;
2327 AST.NodeList<AST.Node> typeSpecArgs = null;
2330 // Process AST.MethodExpr - reduce it to an identifier with type spec arguments
2332 AST.MethodExpr methodExpr = typeName as AST.MethodExpr;
2333 if (methodExpr != null)
2335 typeName = methodExpr.Expr;
2336 typeName.ErrCtx.ErrorContextInfo = methodExpr.ErrCtx.ErrorContextInfo;
2337 typeName.ErrCtx.UseContextInfoAsResourceIdentifier = methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier;
2339 typeSpecArgs = methodExpr.Args;
2343 // Try as AST.Identifier
2345 AST.Identifier identifier = typeName as AST.Identifier;
2346 if (identifier != null)
2348 name = new string[] { identifier.Name };
2352 // Try as AST.DotExpr
2354 AST.DotExpr dotExpr = typeName as AST.DotExpr;
2355 if (dotExpr != null && dotExpr.IsMultipartIdentifier(out name))
2357 Debug.Assert(name != null, "name != null for a multipart identifier");
2362 Debug.Fail("Unexpected AST.Node in the type name");
2363 throw EntityUtil.EntitySqlError(typeName.ErrCtx, Strings.InvalidMetadataMemberName);
2366 MetadataMember metadataMember = sr.ResolveMetadataMemberName(name, typeName.ErrCtx);
2367 Debug.Assert(metadataMember != null, "metadata member name resolution must not return null");
2369 switch (metadataMember.MetadataMemberClass)
2371 case MetadataMemberClass.Type:
2373 TypeUsage typeUsage = ((MetadataType)metadataMember).TypeUsage;
2375 if (typeSpecArgs != null)
2377 typeUsage = ConvertTypeSpecArgs(typeUsage, typeSpecArgs, typeName.ErrCtx, sr);
2383 case MetadataMemberClass.Namespace:
2384 throw EntityUtil.EntitySqlError(typeName.ErrCtx, Strings.TypeNameNotFound(metadataMember.Name));
2387 throw EntityUtil.EntitySqlError(typeName.ErrCtx, Strings.InvalidMetadataMemberClassResolution(
2388 metadataMember.Name, metadataMember.MetadataMemberClassName, MetadataType.TypeClassName));
2392 private static TypeUsage ConvertTypeSpecArgs(TypeUsage parameterizedType, AST.NodeList<AST.Node> typeSpecArgs, ErrorContext errCtx, SemanticResolver sr)
2394 Debug.Assert(typeSpecArgs != null && typeSpecArgs.Count > 0, "typeSpecArgs must be null or a non-empty list");
2397 // Type arguments must be literals.
2399 foreach (AST.Node arg in typeSpecArgs)
2401 if (!(arg is AST.Literal))
2403 throw EntityUtil.EntitySqlError(arg.ErrCtx, Strings.TypeArgumentMustBeLiteral);
2408 // The only parameterized type supported is Edm.Decimal
2410 PrimitiveType primitiveType = parameterizedType.EdmType as PrimitiveType;
2411 if (primitiveType == null || primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Decimal)
2413 throw EntityUtil.EntitySqlError(errCtx, Strings.TypeDoesNotSupportSpec(primitiveType.FullName));
2417 // Edm.Decimal has two optional parameters: precision and scale.
2419 if (typeSpecArgs.Count > 2)
2421 throw EntityUtil.EntitySqlError(errCtx, Strings.TypeArgumentCountMismatch(primitiveType.FullName, 2));
2425 // Get precision value for Edm.Decimal
2428 ConvertTypeFacetValue(primitiveType, (AST.Literal)typeSpecArgs[0], DbProviderManifest.PrecisionFacetName, out precision);
2431 // Get scale value for Edm.Decimal
2434 if (typeSpecArgs.Count == 2)
2436 ConvertTypeFacetValue(primitiveType, (AST.Literal)typeSpecArgs[1], DbProviderManifest.ScaleFacetName, out scale);
2442 if (precision < scale)
2444 throw EntityUtil.EntitySqlError(typeSpecArgs[0].ErrCtx, Strings.PrecisionMustBeGreaterThanScale(precision, scale));
2447 return TypeUsage.CreateDecimalTypeUsage(primitiveType, precision, scale);
2450 private static void ConvertTypeFacetValue(PrimitiveType type, AST.Literal value, string facetName, out byte byteValue)
2452 FacetDescription facetDescription = Helper.GetFacet(type.ProviderManifest.GetFacetDescriptions(type), facetName);
2453 if (facetDescription == null)
2455 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeDoesNotSupportFacet(type.FullName, facetName));
2458 if (value.IsNumber && Byte.TryParse(value.OriginalValue, out byteValue))
2460 if (facetDescription.MaxValue.HasValue && byteValue > facetDescription.MaxValue.Value)
2462 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeArgumentExceedsMax(facetName));
2465 if (facetDescription.MinValue.HasValue && byteValue < facetDescription.MinValue.Value)
2467 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeArgumentBelowMin(facetName));
2472 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeArgumentIsNotValid);
2476 private static TypeUsage ConvertTypeDefinition(AST.Node typeDefinitionExpr, SemanticResolver sr)
2478 Debug.Assert(typeDefinitionExpr != null, "typeDefinitionExpr != null");
2480 TypeUsage converted = null;
2482 AST.CollectionTypeDefinition collTypeDefExpr = typeDefinitionExpr as AST.CollectionTypeDefinition;
2483 AST.RefTypeDefinition refTypeDefExpr = typeDefinitionExpr as AST.RefTypeDefinition;
2484 AST.RowTypeDefinition rowTypeDefExpr = typeDefinitionExpr as AST.RowTypeDefinition;
2486 if (collTypeDefExpr != null)
2488 TypeUsage elementType = ConvertTypeDefinition(collTypeDefExpr.ElementTypeDef, sr);
2489 converted = TypeHelpers.CreateCollectionTypeUsage(elementType, true /* readOnly */);
2491 else if (refTypeDefExpr != null)
2493 TypeUsage targetTypeUsage = ConvertTypeName(refTypeDefExpr.RefTypeIdentifier, sr);
2496 // Ensure type is entity
2498 if (!TypeSemantics.IsEntityType(targetTypeUsage))
2501 throw EntityUtil.EntitySqlError(refTypeDefExpr.RefTypeIdentifier.ErrCtx,
2502 Strings.RefTypeIdentifierMustSpecifyAnEntityType(
2503 targetTypeUsage.EdmType.FullName,
2504 targetTypeUsage.EdmType.BuiltInTypeKind.ToString()));
2507 converted = TypeHelpers.CreateReferenceTypeUsage((EntityType)targetTypeUsage.EdmType);
2509 else if (rowTypeDefExpr != null)
2511 Debug.Assert(rowTypeDefExpr.Properties != null && rowTypeDefExpr.Properties.Count > 0, "rowTypeDefExpr.Properties must be a non-empty collection");
2513 converted = TypeHelpers.CreateRowTypeUsage(
2514 rowTypeDefExpr.Properties.Select(p => new KeyValuePair<string, TypeUsage>(p.Name.Name, ConvertTypeDefinition(p.Type, sr))),
2515 true /* readOnly */);
2519 converted = ConvertTypeName(typeDefinitionExpr, sr);
2522 Debug.Assert(converted != null, "Type definition conversion yielded null");
2528 /// Converts row constructor expression (AST.RowConstructorExpr)
2530 private static ExpressionResolution ConvertRowConstructor(AST.Node expr, SemanticResolver sr)
2532 AST.RowConstructorExpr rowExpr = (AST.RowConstructorExpr)expr;
2534 Dictionary<string, TypeUsage> rowColumns = new Dictionary<string, TypeUsage>(sr.NameComparer);
2535 List<DbExpression> fieldExprs = new List<DbExpression>(rowExpr.AliasedExprList.Count);
2537 for (int i = 0; i < rowExpr.AliasedExprList.Count; i++)
2539 AST.AliasedExpr aliasExpr = rowExpr.AliasedExprList[i];
2541 DbExpression colExpr = ConvertValueExpressionAllowUntypedNulls(aliasExpr.Expr, sr);
2542 if (colExpr == null)
2544 throw EntityUtil.EntitySqlError(aliasExpr.Expr.ErrCtx, Strings.RowCtorElementCannotBeNull);
2547 string aliasName = sr.InferAliasName(aliasExpr, colExpr);
2549 if (rowColumns.ContainsKey(aliasName))
2551 if (aliasExpr.Alias != null)
2553 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasExpr.Alias.ErrCtx, Strings.InRowCtor);
2557 aliasName = sr.GenerateInternalName("autoRowCol");
2561 rowColumns.Add(aliasName, colExpr.ResultType);
2563 fieldExprs.Add(colExpr);
2566 return new ValueExpression(DbExpressionBuilder.New(TypeHelpers.CreateRowTypeUsage(rowColumns, true /* readOnly */), fieldExprs));
2570 /// Converts multiset constructor expression (AST.MultisetConstructorExpr)
2572 private static ExpressionResolution ConvertMultisetConstructor(AST.Node expr, SemanticResolver sr)
2574 AST.MultisetConstructorExpr msetCtor = (AST.MultisetConstructorExpr)expr;
2576 if (null == msetCtor.ExprList)
2578 throw EntityUtil.EntitySqlError(expr.ErrCtx, Strings.CannotCreateEmptyMultiset);
2581 var mSetExprs = msetCtor.ExprList.Select(e => ConvertValueExpressionAllowUntypedNulls(e, sr)).ToArray();
2583 var multisetTypes = mSetExprs.Where(e => e != null).Select(e => e.ResultType).ToArray();
2586 // Ensure common type is not an untyped null.
2588 if (multisetTypes.Length == 0)
2590 throw EntityUtil.EntitySqlError(expr.ErrCtx, Strings.CannotCreateMultisetofNulls);
2593 TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(multisetTypes);
2596 // Ensure all elems have a common type.
2598 if (commonType == null)
2600 throw EntityUtil.EntitySqlError(expr.ErrCtx, Strings.MultisetElemsAreNotTypeCompatible);
2603 commonType = TypeHelpers.GetReadOnlyType(commonType);
2606 // Fixup untyped nulls.
2608 for (int i = 0; i < mSetExprs.Length; i++)
2610 if (mSetExprs[i] == null)
2612 ValidateTypeForNullExpression(commonType, msetCtor.ExprList[i].ErrCtx);
2613 mSetExprs[i] = DbExpressionBuilder.Null(commonType);
2617 return new ValueExpression(DbExpressionBuilder.New(TypeHelpers.CreateCollectionTypeUsage(commonType, true /* readOnly */), mSetExprs));
2621 /// Converts case-when-then expression (AST.CaseExpr)
2623 private static ExpressionResolution ConvertCaseExpr(AST.Node expr, SemanticResolver sr)
2625 AST.CaseExpr caseExpr = (AST.CaseExpr)expr;
2627 List<DbExpression> whenExprList = new List<DbExpression>(caseExpr.WhenThenExprList.Count);
2628 List<DbExpression> thenExprList = new List<DbExpression>(caseExpr.WhenThenExprList.Count);
2631 // Convert when/then expressions.
2633 for (int i = 0; i < caseExpr.WhenThenExprList.Count; i++)
2635 AST.WhenThenExpr whenThenExpr = caseExpr.WhenThenExprList[i];
2637 DbExpression whenExpression = ConvertValueExpression(whenThenExpr.WhenExpr, sr);
2639 if (!IsBooleanType(whenExpression.ResultType))
2641 throw EntityUtil.EntitySqlError(whenThenExpr.WhenExpr.ErrCtx, Strings.ExpressionTypeMustBeBoolean);
2644 whenExprList.Add(whenExpression);
2646 DbExpression thenExpression = ConvertValueExpressionAllowUntypedNulls(whenThenExpr.ThenExpr, sr);
2648 thenExprList.Add(thenExpression);
2652 // Convert else if present.
2654 DbExpression elseExpr = caseExpr.ElseExpr != null ? ConvertValueExpressionAllowUntypedNulls(caseExpr.ElseExpr, sr) : null;
2657 // Collect result types from THENs and the ELSE.
2659 var resultTypes = thenExprList.Where(e => e != null).Select(e => e.ResultType).ToList();
2660 if (elseExpr != null)
2662 resultTypes.Add(elseExpr.ResultType);
2664 if (resultTypes.Count == 0)
2666 throw EntityUtil.EntitySqlError(caseExpr.ElseExpr.ErrCtx, Strings.InvalidCaseWhenThenNullType);
2670 // Derive common return type.
2672 TypeUsage resultType = TypeHelpers.GetCommonTypeUsage(resultTypes);
2673 if (resultType == null)
2675 throw EntityUtil.EntitySqlError(caseExpr.WhenThenExprList[0].ThenExpr.ErrCtx, Strings.InvalidCaseResultTypes);
2679 // Fixup untyped nulls
2681 for (int i = 0; i < thenExprList.Count; i++)
2683 if (thenExprList[i] == null)
2685 ValidateTypeForNullExpression(resultType, caseExpr.WhenThenExprList[i].ThenExpr.ErrCtx);
2686 thenExprList[i] = DbExpressionBuilder.Null(resultType);
2689 if (elseExpr == null)
2691 if (caseExpr.ElseExpr == null && TypeSemantics.IsCollectionType(resultType))
2694 // If ELSE was omitted and common return type is a collection,
2695 // then use empty collection for elseExpr.
2697 elseExpr = DbExpressionBuilder.NewEmptyCollection(resultType);
2701 ValidateTypeForNullExpression(resultType, (caseExpr.ElseExpr ?? caseExpr).ErrCtx);
2702 elseExpr = DbExpressionBuilder.Null(resultType);
2706 return new ValueExpression(DbExpressionBuilder.Case(whenExprList, thenExprList, elseExpr));
2710 /// Converts query expression (AST.QueryExpr)
2712 private static ExpressionResolution ConvertQueryExpr(AST.Node expr, SemanticResolver sr)
2714 AST.QueryExpr queryExpr = (AST.QueryExpr)expr;
2716 DbExpression converted = null;
2718 bool isRestrictedViewGenerationMode = (ParserOptions.CompilationMode.RestrictedViewGenerationMode == sr.ParserOptions.ParserCompilationMode);
2721 // Validate & Compensate Query
2723 if (null != queryExpr.HavingClause && null == queryExpr.GroupByClause)
2725 throw EntityUtil.EntitySqlError(queryExpr.ErrCtx, Strings.HavingRequiresGroupClause);
2727 if (queryExpr.SelectClause.TopExpr != null)
2729 if (queryExpr.OrderByClause != null && queryExpr.OrderByClause.LimitSubClause != null)
2731 throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, Strings.TopAndLimitCannotCoexist);
2734 if (queryExpr.OrderByClause != null && queryExpr.OrderByClause.SkipSubClause != null)
2736 throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, Strings.TopAndSkipCannotCoexist);
2741 // Create Source Scope Region
2743 using (sr.EnterScopeRegion())
2746 // Process From Clause
2748 DbExpressionBinding sourceExpr = ProcessFromClause(queryExpr.FromClause, sr);
2751 // Process Where Clause
2753 sourceExpr = ProcessWhereClause(sourceExpr, queryExpr.WhereClause, sr);
2755 Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.GroupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode");
2756 Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.HavingClause : true, "HAVING clause must be null in RestrictedViewGenerationMode");
2757 Debug.Assert(isRestrictedViewGenerationMode ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode");
2759 bool queryProjectionProcessed = false;
2760 if (!isRestrictedViewGenerationMode)
2763 // Process GroupBy Clause
2765 sourceExpr = ProcessGroupByClause(sourceExpr, queryExpr, sr);
2768 // Process Having Clause
2770 sourceExpr = ProcessHavingClause(sourceExpr, queryExpr.HavingClause, sr);
2773 // Process OrderBy Clause
2775 sourceExpr = ProcessOrderByClause(sourceExpr, queryExpr, out queryProjectionProcessed, sr);
2779 // Process Projection Clause
2781 converted = ProcessSelectClause(sourceExpr, queryExpr, queryProjectionProcessed, sr);
2783 } // end query scope region
2785 return new ValueExpression(converted);
2789 /// Process Select Clause
2791 private static DbExpression ProcessSelectClause(DbExpressionBinding source, AST.QueryExpr queryExpr, bool queryProjectionProcessed, SemanticResolver sr)
2793 AST.SelectClause selectClause = queryExpr.SelectClause;
2795 DbExpression projectExpression;
2796 if (queryProjectionProcessed)
2798 projectExpression = source.Expression;
2803 // Convert projection items.
2805 var projectionItems = ConvertSelectClauseItems(queryExpr, sr);
2808 // Create project expression off the projectionItems.
2810 projectExpression = CreateProjectExpression(source, selectClause, projectionItems);
2814 // Handle TOP/LIMIT sub-clauses.
2816 if (selectClause.TopExpr != null || (queryExpr.OrderByClause != null && queryExpr.OrderByClause.LimitSubClause != null))
2820 if (selectClause.TopExpr != null)
2822 Debug.Assert(queryExpr.OrderByClause == null || queryExpr.OrderByClause.LimitSubClause == null, "TOP and LIMIT in the same query are not allowed");
2823 limitExpr = selectClause.TopExpr;
2828 limitExpr = queryExpr.OrderByClause.LimitSubClause;
2833 // Convert the expression.
2835 DbExpression convertedLimit = ConvertValueExpression(limitExpr, sr);
2838 // Ensure the converted expression is in the range of values.
2840 ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(convertedLimit, limitExpr.ErrCtx, exprName, sr);
2843 // Create the project expression with the limit.
2845 projectExpression = projectExpression.Limit(convertedLimit);
2848 Debug.Assert(null != projectExpression, "null != projectExpression");
2849 return projectExpression;
2852 private static List<KeyValuePair<string, DbExpression>> ConvertSelectClauseItems(AST.QueryExpr queryExpr, SemanticResolver sr)
2854 AST.SelectClause selectClause = queryExpr.SelectClause;
2857 // Validate SELECT VALUE projection list.
2859 if (selectClause.SelectKind == AST.SelectKind.Value)
2861 if (selectClause.Items.Count != 1)
2863 throw EntityUtil.EntitySqlError(selectClause.ErrCtx, Strings.InvalidSelectValueList);
2867 // Aliasing is not allowed in the SELECT VALUE case, except when the ORDER BY clause is present.
2869 if (selectClause.Items[0].Alias != null && queryExpr.OrderByClause == null)
2871 throw EntityUtil.EntitySqlError(selectClause.Items[0].ErrCtx, Strings.InvalidSelectValueAliasedExpression);
2876 // Converts projection list
2878 HashSet<string> projectionAliases = new HashSet<string>(sr.NameComparer);
2879 List<KeyValuePair<string, DbExpression>> projectionItems = new List<KeyValuePair<string, DbExpression>>(selectClause.Items.Count);
2880 for (int i = 0; i < selectClause.Items.Count; i++)
2882 AST.AliasedExpr projectionItem = selectClause.Items[i];
2884 DbExpression converted = ConvertValueExpression(projectionItem.Expr, sr);
2887 // Infer projection item alias.
2889 string aliasName = sr.InferAliasName(projectionItem, converted);
2892 // Ensure the alias is not already used.
2894 if (projectionAliases.Contains(aliasName))
2896 if (projectionItem.Alias != null)
2898 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, projectionItem.Alias.ErrCtx, Strings.InSelectProjectionList);
2902 aliasName = sr.GenerateInternalName("autoProject");
2906 projectionAliases.Add(aliasName);
2907 projectionItems.Add(new KeyValuePair<string, DbExpression>(aliasName, converted));
2910 Debug.Assert(projectionItems.Count > 0, "projectionItems.Count > 0");
2911 return projectionItems;
2914 private static DbExpression CreateProjectExpression(DbExpressionBinding source, AST.SelectClause selectClause, List<KeyValuePair<string, DbExpression>> projectionItems)
2917 // Create DbProjectExpression off the projectionItems.
2919 DbExpression projectExpression;
2920 if (selectClause.SelectKind == AST.SelectKind.Value)
2922 Debug.Assert(projectionItems.Count == 1, "projectionItems.Count must be 1 for SELECT VALUE");
2923 projectExpression = source.Project(projectionItems[0].Value);
2927 projectExpression = source.Project(DbExpressionBuilder.NewRow(projectionItems));
2931 // Handle DISTINCT modifier - create DbDistinctExpression over the current projectExpression.
2933 if (selectClause.DistinctKind == AST.DistinctKind.Distinct)
2936 // Ensure element type is equal-comparable.
2938 ValidateDistinctProjection(projectExpression.ResultType, selectClause);
2941 // Create distinct expression.
2943 projectExpression = projectExpression.Distinct();
2946 return projectExpression;
2949 private static void ValidateDistinctProjection(TypeUsage projectExpressionResultType, AST.SelectClause selectClause)
2951 ValidateDistinctProjection(
2952 projectExpressionResultType,
2953 selectClause.Items[0].Expr.ErrCtx,
2954 selectClause.SelectKind == System.Data.Common.EntitySql.AST.SelectKind.Row ?
2955 new List<ErrorContext>(selectClause.Items.Select(item => item.Expr.ErrCtx)) : null);
2958 private static void ValidateDistinctProjection(TypeUsage projectExpressionResultType, ErrorContext defaultErrCtx, List<ErrorContext> projectionItemErrCtxs)
2960 TypeUsage projectionType = TypeHelpers.GetElementTypeUsage(projectExpressionResultType);
2961 if (!TypeHelpers.IsValidDistinctOpType(projectionType))
2963 ErrorContext errCtx = defaultErrCtx;
2964 if (projectionItemErrCtxs != null && TypeSemantics.IsRowType(projectionType))
2966 RowType rowType = projectionType.EdmType as RowType;
2967 Debug.Assert(projectionItemErrCtxs.Count == rowType.Members.Count);
2968 for (int i = 0; i < rowType.Members.Count; i++)
2970 if (!TypeHelpers.IsValidDistinctOpType(rowType.Members[i].TypeUsage))
2972 errCtx = projectionItemErrCtxs[i];
2977 throw EntityUtil.EntitySqlError(errCtx, Strings.SelectDistinctMustBeEqualComparable);
2981 private static void ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(DbExpression expr, ErrorContext errCtx, string exprName, SemanticResolver sr)
2983 if (expr.ExpressionKind != DbExpressionKind.Constant &&
2984 expr.ExpressionKind != DbExpressionKind.ParameterReference)
2986 throw EntityUtil.EntitySqlError(errCtx, Strings.PlaceholderExpressionMustBeConstant(exprName));
2989 if (!TypeSemantics.IsPromotableTo(expr.ResultType, sr.TypeResolver.Int64Type))
2991 throw EntityUtil.EntitySqlError(errCtx, Strings.PlaceholderExpressionMustBeCompatibleWithEdm64(exprName, expr.ResultType.EdmType.FullName));
2994 DbConstantExpression constExpr = expr as DbConstantExpression;
2995 if (constExpr != null && System.Convert.ToInt64(constExpr.Value, CultureInfo.InvariantCulture) < 0)
2997 throw EntityUtil.EntitySqlError(errCtx, Strings.PlaceholderExpressionMustBeGreaterThanOrEqualToZero(exprName));
3002 /// Process FROM clause.
3004 private static DbExpressionBinding ProcessFromClause(AST.FromClause fromClause, SemanticResolver sr)
3006 DbExpressionBinding fromBinding = null;
3009 // Process each FROM clause item.
3010 // If there is more than one of them, then assemble them in a string from APPLYs.
3012 List<SourceScopeEntry> fromClauseEntries = new List<SourceScopeEntry>();
3013 for (int i = 0; i < fromClause.FromClauseItems.Count; i++)
3016 // Convert FROM clause item.
3018 List<SourceScopeEntry> fromClauseItemEntries;
3019 DbExpressionBinding currentItemBinding = ProcessFromClauseItem(fromClause.FromClauseItems[i], sr, out fromClauseItemEntries);
3020 fromClauseEntries.AddRange(fromClauseItemEntries);
3022 if (fromBinding == null)
3024 fromBinding = currentItemBinding;
3028 fromBinding = fromBinding.CrossApply(currentItemBinding).BindAs(sr.GenerateInternalName("lcapply"));
3031 // Adjust scope entries with the new binding.
3033 fromClauseEntries.ForEach(scopeEntry => scopeEntry.AddParentVar(fromBinding.Variable));
3037 Debug.Assert(fromBinding != null, "fromBinding != null");
3043 /// Process generic FROM clause item: aliasedExpr, JoinClauseItem or ApplyClauseItem.
3044 /// Returns <see cref="DbExpressionBinding"/> and the <paramref name="scopeEntries"/> list with entries created by the clause item.
3046 private static DbExpressionBinding ProcessFromClauseItem(AST.FromClauseItem fromClauseItem, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)
3048 DbExpressionBinding fromItemBinding = null;
3050 switch (fromClauseItem.FromClauseItemKind)
3052 case AST.FromClauseItemKind.AliasedFromClause:
3053 fromItemBinding = ProcessAliasedFromClauseItem((AST.AliasedExpr)fromClauseItem.FromExpr, sr, out scopeEntries);
3056 case AST.FromClauseItemKind.JoinFromClause:
3057 fromItemBinding = ProcessJoinClauseItem((AST.JoinClauseItem)fromClauseItem.FromExpr, sr, out scopeEntries);
3061 Debug.Assert(fromClauseItem.FromClauseItemKind == AST.FromClauseItemKind.ApplyFromClause, "AST.FromClauseItemKind.ApplyFromClause expected");
3062 fromItemBinding = ProcessApplyClauseItem((AST.ApplyClauseItem)fromClauseItem.FromExpr, sr, out scopeEntries);
3066 Debug.Assert(fromItemBinding != null, "fromItemBinding != null");
3068 return fromItemBinding;
3072 /// Process a simple FROM clause item.
3073 /// Returns <see cref="DbExpressionBinding"/> and the <paramref name="scopeEntries"/> list with a single entry created for the clause item.
3075 private static DbExpressionBinding ProcessAliasedFromClauseItem(AST.AliasedExpr aliasedExpr, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)
3077 DbExpressionBinding aliasedBinding = null;
3080 // Convert the item expression.
3082 DbExpression converted = ConvertValueExpression(aliasedExpr.Expr, sr);
3085 // Validate it is of collection type.
3087 if (!TypeSemantics.IsCollectionType(converted.ResultType))
3089 throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, Strings.ExpressionMustBeCollection);
3093 // Infer source var alias name.
3095 string aliasName = sr.InferAliasName(aliasedExpr, converted);
3098 // Validate the name was not used yet.
3100 if (sr.CurrentScope.Contains(aliasName))
3102 if (aliasedExpr.Alias != null)
3104 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasedExpr.Alias.ErrCtx, Strings.InFromClause);
3108 aliasName = sr.GenerateInternalName("autoFrom");
3113 // Create CQT expression.
3115 aliasedBinding = converted.BindAs(aliasName);
3118 // Add source var to the _scopeEntries list and to the current scope.
3120 SourceScopeEntry sourceScopeEntry = new SourceScopeEntry(aliasedBinding.Variable);
3121 sr.CurrentScope.Add(aliasedBinding.Variable.VariableName, sourceScopeEntry);
3122 scopeEntries = new List<SourceScopeEntry>();
3123 scopeEntries.Add(sourceScopeEntry);
3125 Debug.Assert(aliasedBinding != null, "aliasedBinding != null");
3127 return aliasedBinding;
3131 /// Process a JOIN clause item.
3132 /// Returns <see cref="DbExpressionBinding"/> and the <paramref name="scopeEntries"/> list with a join-left and join-right entries created for the clause item.
3134 private static DbExpressionBinding ProcessJoinClauseItem(AST.JoinClauseItem joinClause, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)
3136 DbExpressionBinding joinBinding = null;
3139 // Make sure inner join has ON predicate AND cross join has no ON predicate.
3141 if (null == joinClause.OnExpr)
3143 if (AST.JoinKind.Inner == joinClause.JoinKind)
3145 throw EntityUtil.EntitySqlError(joinClause.ErrCtx, Strings.InnerJoinMustHaveOnPredicate);
3150 if (AST.JoinKind.Cross == joinClause.JoinKind)
3152 throw EntityUtil.EntitySqlError(joinClause.OnExpr.ErrCtx, Strings.InvalidPredicateForCrossJoin);
3157 // Process left expression.
3159 List<SourceScopeEntry> leftExprScopeEntries;
3160 DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(joinClause.LeftExpr, sr, out leftExprScopeEntries);
3163 // Mark scope entries from the left expression as such. This will disallow their usage inside of the right expression.
3164 // The left and right expressions of a join must be independent (they can not refer to variables in the other expression).
3165 // Join ON predicate may refer to variables defined in both expressions.
3167 // Select ... From A JOIN B JOIN A.x -> invalid
3168 // Select ... From A JOIN B JOIN C ON A.x = C.x -> valid
3169 // Select ... From A JOIN B, C JOIN A.x ... -> valid
3171 leftExprScopeEntries.ForEach(scopeEntry => scopeEntry.IsJoinClauseLeftExpr = true);
3174 // Process right expression
3176 List<SourceScopeEntry> rightExprScopeEntries;
3177 DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(joinClause.RightExpr, sr, out rightExprScopeEntries);
3180 // Unmark scope entries from the left expression to allow their usage.
3182 leftExprScopeEntries.ForEach(scopeEntry => scopeEntry.IsJoinClauseLeftExpr = false);
3186 // Switch right outer to left outer.
3188 if (joinClause.JoinKind == AST.JoinKind.RightOuter)
3190 joinClause.JoinKind = AST.JoinKind.LeftOuter;
3191 DbExpressionBinding tmpExpr = leftBindingExpr;
3192 leftBindingExpr = rightBindingExpr;
3193 rightBindingExpr = tmpExpr;
3197 // Resolve JoinType.
3199 DbExpressionKind joinKind = MapJoinKind(joinClause.JoinKind);
3204 DbExpression onExpr = null;
3205 if (null == joinClause.OnExpr)
3207 if (DbExpressionKind.CrossJoin != joinKind)
3209 onExpr = DbExpressionBuilder.True;
3214 onExpr = ConvertValueExpression(joinClause.OnExpr, sr);
3221 DbExpressionBuilder.CreateJoinExpressionByKind(
3222 joinKind, onExpr, leftBindingExpr, rightBindingExpr).BindAs(sr.GenerateInternalName("join"));
3225 // Combine left and right scope entries and adjust with the new binding.
3227 scopeEntries = leftExprScopeEntries;
3228 scopeEntries.AddRange(rightExprScopeEntries);
3229 scopeEntries.ForEach(scopeEntry => scopeEntry.AddParentVar(joinBinding.Variable));
3231 Debug.Assert(joinBinding != null, "joinBinding != null");
3237 /// Maps <see cref="AST.JoinKind"/> to <see cref="DbExpressionKind"/>.
3239 private static DbExpressionKind MapJoinKind(AST.JoinKind joinKind)
3241 Debug.Assert(joinKind != AST.JoinKind.RightOuter, "joinKind != JoinKind.RightOuter");
3242 return joinMap[(int)joinKind];
3244 private static readonly DbExpressionKind[] joinMap = { DbExpressionKind.CrossJoin, DbExpressionKind.InnerJoin, DbExpressionKind.LeftOuterJoin, DbExpressionKind.FullOuterJoin };
3247 /// Process an APPLY clause item.
3248 /// Returns <see cref="DbExpressionBinding"/> and the <paramref name="scopeEntries"/> list with an apply-left and apply-right entries created for the clause item.
3250 private static DbExpressionBinding ProcessApplyClauseItem(AST.ApplyClauseItem applyClause, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)
3252 DbExpressionBinding applyBinding = null;
3255 // Resolve left expression.
3257 List<SourceScopeEntry> leftExprScopeEntries;
3258 DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(applyClause.LeftExpr, sr, out leftExprScopeEntries);
3261 // Resolve right expression.
3263 List<SourceScopeEntry> rightExprScopeEntries;
3264 DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(applyClause.RightExpr, sr, out rightExprScopeEntries);
3270 DbExpressionBuilder.CreateApplyExpressionByKind(
3271 MapApplyKind(applyClause.ApplyKind),
3273 rightBindingExpr).BindAs(sr.GenerateInternalName("apply"));
3276 // Combine left and right scope entries and adjust with the new binding.
3278 scopeEntries = leftExprScopeEntries;
3279 scopeEntries.AddRange(rightExprScopeEntries);
3280 scopeEntries.ForEach(scopeEntry => scopeEntry.AddParentVar(applyBinding.Variable));
3282 Debug.Assert(applyBinding != null, "applyBinding != null");
3284 return applyBinding;
3288 /// Maps <see cref="AST.ApplyKind"/> to <see cref="DbExpressionKind"/>.
3290 private static DbExpressionKind MapApplyKind(AST.ApplyKind applyKind)
3292 return applyMap[(int)applyKind];
3294 private static readonly DbExpressionKind[] applyMap = { DbExpressionKind.CrossApply, DbExpressionKind.OuterApply };
3297 /// Process WHERE clause.
3299 private static DbExpressionBinding ProcessWhereClause(DbExpressionBinding source, AST.Node whereClause, SemanticResolver sr)
3301 if (whereClause == null)
3305 return ProcessWhereHavingClausePredicate(source, whereClause, whereClause.ErrCtx, "where", sr);
3309 /// Process HAVING clause.
3311 private static DbExpressionBinding ProcessHavingClause(DbExpressionBinding source, AST.HavingClause havingClause, SemanticResolver sr)
3313 if (havingClause == null)
3317 return ProcessWhereHavingClausePredicate(source, havingClause.HavingPredicate, havingClause.ErrCtx, "having", sr);
3321 /// Process WHERE or HAVING clause predicate.
3323 private static DbExpressionBinding ProcessWhereHavingClausePredicate(DbExpressionBinding source, AST.Node predicate, ErrorContext errCtx, string bindingNameTemplate, SemanticResolver sr)
3325 Debug.Assert(predicate != null, "predicate != null");
3327 DbExpressionBinding whereBinding = null;
3330 // Convert the predicate.
3332 DbExpression filterConditionExpr = ConvertValueExpression(predicate, sr);
3335 // Ensure the predicate type is boolean.
3337 if (!IsBooleanType(filterConditionExpr.ResultType))
3339 throw EntityUtil.EntitySqlError(errCtx, Strings.ExpressionTypeMustBeBoolean);
3343 // Create new filter binding.
3345 whereBinding = source.Filter(filterConditionExpr).BindAs(sr.GenerateInternalName(bindingNameTemplate));
3350 sr.CurrentScopeRegion.ApplyToScopeEntries(scopeEntry =>
3352 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef,
3353 "scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef");
3355 if (scopeEntry.EntryKind == ScopeEntryKind.SourceVar)
3357 ((SourceScopeEntry)scopeEntry).ReplaceParentVar(whereBinding.Variable);
3361 Debug.Assert(whereBinding != null, "whereBinding != null");
3363 return whereBinding;
3367 /// Process Group By Clause
3369 private static DbExpressionBinding ProcessGroupByClause(DbExpressionBinding source, AST.QueryExpr queryExpr, SemanticResolver sr)
3371 AST.GroupByClause groupByClause = queryExpr.GroupByClause;
3373 Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == groupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode");
3376 // If group expression is null, assume an implicit group and speculate that there are group aggregates in the remaining query expression.
3377 // If no group aggregate are found after partial evaluation of HAVING, ORDER BY and SELECT, rollback the implicit group.
3379 int groupKeysCount = groupByClause != null ? groupByClause.GroupItems.Count : 0;
3380 bool isImplicitGroup = groupKeysCount == 0;
3381 if (isImplicitGroup && !queryExpr.HasMethodCall)
3387 // Create input binding for DbGroupByExpression.
3389 DbGroupExpressionBinding groupInputBinding = source.Expression.GroupBindAs(sr.GenerateInternalName("geb"), sr.GenerateInternalName("group"));
3392 // Create group partition (DbGroupAggregate) and projection template.
3394 DbGroupAggregate groupAggregateDefinition = groupInputBinding.GroupAggregate;
3395 DbVariableReferenceExpression groupAggregateVarRef = groupAggregateDefinition.ResultType.Variable(sr.GenerateInternalName("groupAggregate"));
3396 DbExpressionBinding groupAggregateBinding = groupAggregateVarRef.BindAs(sr.GenerateInternalName("groupPartitionItem"));
3399 // Flag that we perform group operation.
3401 sr.CurrentScopeRegion.EnterGroupOperation(groupAggregateBinding);
3404 // Update group input bindings.
3406 sr.CurrentScopeRegion.ApplyToScopeEntries((scopeEntry) =>
3408 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar, "scopeEntry.EntryKind == ScopeEntryKind.SourceVar");
3409 ((SourceScopeEntry)scopeEntry).AdjustToGroupVar(groupInputBinding.Variable, groupInputBinding.GroupVariable, groupAggregateBinding.Variable);
3413 // This set will include names of keys, aggregates and the group partition name if specified.
3414 // All these properties become field names of the row type returned by the DbGroupByExpression.
3416 HashSet<string> groupPropertyNames = new HashSet<string>(sr.NameComparer);
3419 // Convert group keys.
3421 #region Convert group key definitions
3422 List<GroupKeyInfo> groupKeys = new List<GroupKeyInfo>(groupKeysCount);
3423 if (!isImplicitGroup)
3425 Debug.Assert(null != groupByClause, "groupByClause must not be null at this point");
3426 for (int i = 0; i < groupKeysCount; i++)
3428 AST.AliasedExpr aliasedExpr = groupByClause.GroupItems[i];
3430 sr.CurrentScopeRegion.WasResolutionCorrelated = false;
3433 // Convert key expression relative to groupInputBinding.Variable.
3434 // This expression will be used as key definition during construction of DbGroupByExpression.
3436 DbExpression keyExpr;
3437 GroupKeyAggregateInfo groupKeyAggregateInfo;
3438 using (sr.EnterGroupKeyDefinition(GroupAggregateKind.GroupKey, aliasedExpr.ErrCtx, out groupKeyAggregateInfo))
3440 keyExpr = ConvertValueExpression(aliasedExpr.Expr, sr);
3444 // Ensure group key expression is correlated.
3445 // If resolution was correlated, then the following should be true for groupKeyAggregateInfo: ESR == DSR
3447 if (!sr.CurrentScopeRegion.WasResolutionCorrelated)
3449 throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, Strings.KeyMustBeCorrelated("GROUP BY"));
3451 Debug.Assert(groupKeyAggregateInfo.EvaluatingScopeRegion == groupKeyAggregateInfo.DefiningScopeRegion, "Group key must evaluate on the scope it was defined on.");
3454 // Ensure key is valid.
3456 if (!TypeHelpers.IsValidGroupKeyType(keyExpr.ResultType))
3458 throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, Strings.GroupingKeysMustBeEqualComparable);
3462 // Convert key expression relative to groupInputBinding.GroupVariable.
3463 // keyExprForFunctionAggregates will be used inside of definitions of group aggregates resolved to the current scope region.
3465 DbExpression keyExprForFunctionAggregates;
3466 GroupKeyAggregateInfo functionAggregateInfo;
3467 using (sr.EnterGroupKeyDefinition(GroupAggregateKind.Function, aliasedExpr.ErrCtx, out functionAggregateInfo))
3469 keyExprForFunctionAggregates = ConvertValueExpression(aliasedExpr.Expr, sr);
3471 Debug.Assert(functionAggregateInfo.EvaluatingScopeRegion == functionAggregateInfo.DefiningScopeRegion, "Group key must evaluate on the scope it was defined on.");
3474 // Convert key expression relative to groupAggregateBinding.Variable.
3475 // keyExprForGroupPartitions will be used inside of definitions of GROUPPARTITION aggregates resolved to the current scope region.
3477 DbExpression keyExprForGroupPartitions;
3478 GroupKeyAggregateInfo groupPartitionInfo;
3479 using (sr.EnterGroupKeyDefinition(GroupAggregateKind.Partition, aliasedExpr.ErrCtx, out groupPartitionInfo))
3481 keyExprForGroupPartitions = ConvertValueExpression(aliasedExpr.Expr, sr);
3483 Debug.Assert(groupPartitionInfo.EvaluatingScopeRegion == groupPartitionInfo.DefiningScopeRegion, "Group key must evaluate on the scope it was defined on.");
3486 // Infer group key alias name.
3488 string groupKeyAlias = sr.InferAliasName(aliasedExpr, keyExpr);
3491 // Check if alias was already used.
3493 if (groupPropertyNames.Contains(groupKeyAlias))
3495 if (aliasedExpr.Alias != null)
3497 CqlErrorHelper.ReportAliasAlreadyUsedError(groupKeyAlias, aliasedExpr.Alias.ErrCtx, Strings.InGroupClause);
3501 groupKeyAlias = sr.GenerateInternalName("autoGroup");
3506 // Add alias to dictionary.
3508 groupPropertyNames.Add(groupKeyAlias);
3511 // Add key to keys collection.
3513 GroupKeyInfo groupKeyInfo = new GroupKeyInfo(groupKeyAlias, keyExpr, keyExprForFunctionAggregates, keyExprForGroupPartitions);
3514 groupKeys.Add(groupKeyInfo);
3517 // Group keys should be visible by their 'original' key expression name. The following three forms should be allowed:
3518 // SELECT k FROM ... as p GROUP BY p.Price as k (explicit key alias) - handled above by InferAliasName()
3519 // SELECT Price FROM ... as p GROUP BY p.Price (implicit alias - leading name) - handled above by InferAliasName()
3520 // SELECT p.Price FROM ... as p GROUP BY p.Price (original key expression) - case handled in the code bellow
3522 if (aliasedExpr.Alias == null)
3524 AST.DotExpr dotExpr = aliasedExpr.Expr as AST.DotExpr;
3525 string[] alternativeName;
3526 if (null != dotExpr && dotExpr.IsMultipartIdentifier(out alternativeName))
3528 groupKeyInfo.AlternativeName = alternativeName;
3530 string alternativeFullName = TypeResolver.GetFullName(alternativeName);
3531 if (groupPropertyNames.Contains(alternativeFullName))
3533 CqlErrorHelper.ReportAliasAlreadyUsedError(alternativeFullName, dotExpr.ErrCtx, Strings.InGroupClause);
3536 groupPropertyNames.Add(alternativeFullName);
3544 // Save scope. It will be used to rollback the temporary group scope created below.
3546 int groupInputScope = sr.CurrentScopeIndex;
3549 // Push temporary group scope.
3554 // Add scope entries for group keys and the group partition to the current scope,
3555 // this is needed for the aggregate search phase during which keys may be referenced.
3557 foreach (GroupKeyInfo groupKeyInfo in groupKeys)
3559 sr.CurrentScope.Add(
3561 new GroupKeyDefinitionScopeEntry(
3562 groupKeyInfo.VarBasedKeyExpr,
3563 groupKeyInfo.GroupVarBasedKeyExpr,
3564 groupKeyInfo.GroupAggBasedKeyExpr,
3567 if (groupKeyInfo.AlternativeName != null)
3569 string strAlternativeName = TypeResolver.GetFullName(groupKeyInfo.AlternativeName);
3570 sr.CurrentScope.Add(
3572 new GroupKeyDefinitionScopeEntry(
3573 groupKeyInfo.VarBasedKeyExpr,
3574 groupKeyInfo.GroupVarBasedKeyExpr,
3575 groupKeyInfo.GroupAggBasedKeyExpr,
3576 groupKeyInfo.AlternativeName));
3581 // Convert/Search Aggregates
3582 // since aggregates can be defined in Having, OrderBy and/or Select clauses must be resolved as part of the group expression.
3583 // The resolution of these clauses result in potential collection of resolved group aggregates and the actual resulting
3584 // expression is ignored. These clauses will be then resolved as usual on a second pass.
3587 #region Search for group aggregates (functions and GROUPPARTITIONs)
3589 // Search for aggregates in HAVING clause.
3591 if (null != queryExpr.HavingClause && queryExpr.HavingClause.HasMethodCall)
3593 DbExpression converted = ConvertValueExpression(queryExpr.HavingClause.HavingPredicate, sr);
3597 // Search for aggregates in SELECT clause.
3599 Dictionary<string, DbExpression> projectionExpressions = null;
3600 if (null != queryExpr.OrderByClause || queryExpr.SelectClause.HasMethodCall)
3602 projectionExpressions = new Dictionary<string, DbExpression>(queryExpr.SelectClause.Items.Count, sr.NameComparer);
3603 for (int i = 0; i < queryExpr.SelectClause.Items.Count; i++)
3605 AST.AliasedExpr aliasedExpr = queryExpr.SelectClause.Items[i];
3608 // Convert projection item expression.
3610 DbExpression converted = ConvertValueExpression(aliasedExpr.Expr, sr);
3613 // Create Null Expression with actual type.
3615 converted = converted.ExpressionKind == CommandTrees.DbExpressionKind.Null ? converted : converted.ResultType.Null();
3620 string aliasName = sr.InferAliasName(aliasedExpr, converted);
3622 if (projectionExpressions.ContainsKey(aliasName))
3624 if (aliasedExpr.Alias != null)
3626 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName,
3627 aliasedExpr.Alias.ErrCtx,
3628 Strings.InSelectProjectionList);
3632 aliasName = sr.GenerateInternalName("autoProject");
3636 projectionExpressions.Add(aliasName, converted);
3641 // Search for aggregates in ORDER BY clause.
3643 if (null != queryExpr.OrderByClause && queryExpr.OrderByClause.HasMethodCall)
3646 // Push temporary projection scope.
3651 // Add projection items to the temporary scope (items may be used in ORDER BY).
3653 foreach (KeyValuePair<string, DbExpression> kvp in projectionExpressions)
3655 sr.CurrentScope.Add(kvp.Key, new ProjectionItemDefinitionScopeEntry(kvp.Value));
3659 // Search for aggregates in ORDER BY clause.
3661 for (int i = 0; i < queryExpr.OrderByClause.OrderByClauseItem.Count; i++)
3663 AST.OrderByClauseItem orderItem = queryExpr.OrderByClause.OrderByClauseItem[i];
3665 sr.CurrentScopeRegion.WasResolutionCorrelated = false;
3667 DbExpression converted = ConvertValueExpression(orderItem.OrderExpr, sr);
3670 // Ensure key expression is correlated.
3672 if (!sr.CurrentScopeRegion.WasResolutionCorrelated)
3674 throw EntityUtil.EntitySqlError(orderItem.ErrCtx, Strings.KeyMustBeCorrelated("ORDER BY"));
3679 // Pop temporary projection scope.
3686 // If we introduced a fake group but did not find any group aggregates
3687 // on the first pass, then there is no need for creating an implicit group.
3688 // Rollback to the status before entering ProcessGroupByClause().
3689 // If we did find group aggregates, make sure all non-group aggregate function
3690 // expressions refer to group scope variables only.
3692 if (isImplicitGroup)
3694 if (0 == sr.CurrentScopeRegion.GroupAggregateInfos.Count)
3696 #region Implicit Group Rollback
3698 // Rollback the temporary group scope.
3700 sr.RollbackToScope(groupInputScope);
3703 // Undo any group source fixups: re-applying the source var and remove the group var.
3705 sr.CurrentScopeRegion.ApplyToScopeEntries((scopeEntry) =>
3707 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar, "scopeEntry.EntryKind == ScopeEntryKind.SourceVar");
3708 ((SourceScopeEntry)scopeEntry).RollbackAdjustmentToGroupVar(source.Variable);
3712 // Remove the group operation flag.
3714 sr.CurrentScopeRegion.RollbackGroupOperation();
3717 // Return the original source var binding.
3724 // Prepare list of aggregate definitions and their internal names.
3726 List<KeyValuePair<string, DbAggregate>> aggregates = new List<KeyValuePair<string, DbAggregate>>(sr.CurrentScopeRegion.GroupAggregateInfos.Count);
3727 bool groupPartitionRefFound = false;
3728 foreach (GroupAggregateInfo groupAggregateInfo in sr.CurrentScopeRegion.GroupAggregateInfos)
3730 switch (groupAggregateInfo.AggregateKind)
3732 case GroupAggregateKind.Function:
3733 aggregates.Add(new KeyValuePair<string, DbAggregate>(
3734 groupAggregateInfo.AggregateName,
3735 ((FunctionAggregateInfo)groupAggregateInfo).AggregateDefinition));
3738 case GroupAggregateKind.Partition:
3739 groupPartitionRefFound = true;
3743 Debug.Fail("Unexpected group aggregate kind:" + groupAggregateInfo.AggregateKind.ToString());
3747 if (groupPartitionRefFound)
3750 // Add DbAggregate to support GROUPPARTITION definitions.
3752 aggregates.Add(new KeyValuePair<string, DbAggregate>(groupAggregateVarRef.VariableName, groupAggregateDefinition));
3756 // Create GroupByExpression and a binding to it.
3758 DbGroupByExpression groupBy = groupInputBinding.GroupBy(
3759 groupKeys.Select(keyInfo => new KeyValuePair<string, DbExpression>(keyInfo.Name, keyInfo.VarBasedKeyExpr)),
3761 DbExpressionBinding groupBinding = groupBy.BindAs(sr.GenerateInternalName("group"));
3764 // If there are GROUPPARTITION expressions, then add an extra projection off the groupBinding to
3765 // - project all the keys and aggregates, except the DbGroupAggregate,
3766 // - project definitions of GROUPPARTITION expressions.
3768 if (groupPartitionRefFound)
3771 // All GROUPPARTITION definitions reference groupAggregateVarRef, make sure the variable is properly defined in the groupBy expression.
3773 Debug.Assert(aggregates.Any((aggregate) => String.CompareOrdinal(aggregate.Key, groupAggregateVarRef.VariableName) == 0),
3774 "DbAggregate is not defined");
3777 // Get projection of GROUPPARTITION definitions.
3778 // This method may return null if all GROUPPARTITION definitions are reduced to the value of groupAggregateVarRef.
3780 List<KeyValuePair<string, DbExpression>> projectionItems = ProcessGroupPartitionDefinitions(
3781 sr.CurrentScopeRegion.GroupAggregateInfos,
3782 groupAggregateVarRef,
3785 if (projectionItems != null)
3788 // Project group keys along with GROUPPARTITION definitions.
3790 projectionItems.AddRange(groupKeys.Select(keyInfo =>
3791 new KeyValuePair<string, DbExpression>(keyInfo.Name, groupBinding.Variable.Property(keyInfo.Name))));
3794 // Project function group aggregates along with GROUPPARTITION definitions and group keys.
3796 projectionItems.AddRange(sr.CurrentScopeRegion.GroupAggregateInfos
3797 .Where(groupAggregateInfo => groupAggregateInfo.AggregateKind == GroupAggregateKind.Function)
3798 .Select(groupAggregateInfo => new KeyValuePair<string, DbExpression>(
3799 groupAggregateInfo.AggregateName,
3800 groupBinding.Variable.Property(groupAggregateInfo.AggregateName))));
3802 DbExpression projectExpression = DbExpressionBuilder.NewRow(projectionItems);
3803 groupBinding = groupBinding.Project(projectExpression).BindAs(sr.GenerateInternalName("groupPartitionDefs"));
3808 // Remove the temporary group scope with group key definitions,
3809 // Replace all existing pre-group scope entries with InvalidGroupInputRefScopeEntry stubs -
3810 // they are no longer available for proper referencing and only to be used for user error messages.
3812 sr.RollbackToScope(groupInputScope);
3813 sr.CurrentScopeRegion.ApplyToScopeEntries((scopeEntry) =>
3815 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar, "scopeEntry.EntryKind == ScopeEntryKind.SourceVar");
3816 return new InvalidGroupInputRefScopeEntry();
3820 // Add final group scope.
3825 // Add group keys to the group scope.
3827 foreach (GroupKeyInfo groupKeyInfo in groupKeys)
3830 // Add new scope entry
3832 sr.CurrentScope.Add(
3833 groupKeyInfo.VarRef.VariableName,
3834 new SourceScopeEntry(groupKeyInfo.VarRef).AddParentVar(groupBinding.Variable));
3837 // Handle the alternative name entry.
3839 if (groupKeyInfo.AlternativeName != null)
3842 // We want two scope entries with keys as groupKeyInfo.VarRef.VariableName and groupKeyInfo.AlternativeName,
3843 // both pointing to the same variable (groupKeyInfo.VarRef).
3845 string strAlternativeName = TypeResolver.GetFullName(groupKeyInfo.AlternativeName);
3846 sr.CurrentScope.Add(
3848 new SourceScopeEntry(groupKeyInfo.VarRef, groupKeyInfo.AlternativeName).AddParentVar(groupBinding.Variable));
3853 // Add group aggregates to the scope.
3855 foreach (GroupAggregateInfo groupAggregateInfo in sr.CurrentScopeRegion.GroupAggregateInfos)
3857 DbVariableReferenceExpression aggVarRef = groupAggregateInfo.AggregateStubExpression.ResultType.Variable(groupAggregateInfo.AggregateName);
3860 !sr.CurrentScope.Contains(aggVarRef.VariableName) ||
3861 groupAggregateInfo.AggregateKind == GroupAggregateKind.Partition, "DbFunctionAggregate's with duplicate names are not allowed.");
3863 if (!sr.CurrentScope.Contains(aggVarRef.VariableName))
3865 sr.CurrentScope.Add(
3866 aggVarRef.VariableName,
3867 new SourceScopeEntry(aggVarRef).AddParentVar(groupBinding.Variable));
3868 sr.CurrentScopeRegion.RegisterGroupAggregateName(aggVarRef.VariableName);
3872 // Cleanup the stub expression as it must not be used after this point.
3874 groupAggregateInfo.AggregateStubExpression = null;
3877 return groupBinding;
3881 /// Generates the list of projections for GROUPPARTITION definitions.
3882 /// All GROUPPARTITION definitions over the trivial projection of input are reduced to the value of groupAggregateVarRef,
3883 /// only one projection item is created for such definitions.
3884 /// Returns null if all GROUPPARTITION definitions are reduced to the value of groupAggregateVarRef.
3886 private static List<KeyValuePair<string, DbExpression>> ProcessGroupPartitionDefinitions(
3887 List<GroupAggregateInfo> groupAggregateInfos,
3888 DbVariableReferenceExpression groupAggregateVarRef,
3889 DbExpressionBinding groupBinding)
3891 var gpExpressionLambdaVariables = new System.Collections.ObjectModel.ReadOnlyCollection<DbVariableReferenceExpression>(
3892 new DbVariableReferenceExpression[] { groupAggregateVarRef });
3894 List<KeyValuePair<string, DbExpression>> groupPartitionDefinitions = new List<KeyValuePair<string, DbExpression>>();
3895 bool foundTrivialGroupAggregateProjection = false;
3896 foreach (GroupAggregateInfo groupAggregateInfo in groupAggregateInfos)
3898 if (groupAggregateInfo.AggregateKind == GroupAggregateKind.Partition)
3900 DbExpression aggregateDefinition = ((GroupPartitionInfo)groupAggregateInfo).AggregateDefinition;
3901 if (IsTrivialInputProjection(groupAggregateVarRef, aggregateDefinition))
3904 // Reduce the case of the trivial projection of input to the value of groupAggregateVarRef.
3906 groupAggregateInfo.AggregateName = groupAggregateVarRef.VariableName;
3907 foundTrivialGroupAggregateProjection = true;
3912 // Build a projection item for the non-trivial definition.
3914 DbLambda gpExpressionLambda = new DbLambda(gpExpressionLambdaVariables, ((GroupPartitionInfo)groupAggregateInfo).AggregateDefinition);
3915 groupPartitionDefinitions.Add(new KeyValuePair<string, DbExpression>(
3916 groupAggregateInfo.AggregateName,
3917 gpExpressionLambda.Invoke(groupBinding.Variable.Property(groupAggregateVarRef.VariableName))));
3922 if (foundTrivialGroupAggregateProjection)
3924 if (groupPartitionDefinitions.Count > 0)
3927 // Add projection item for groupAggregateVarRef if there are reduced definitions.
3929 groupPartitionDefinitions.Add(new KeyValuePair<string, DbExpression>(
3930 groupAggregateVarRef.VariableName,
3931 groupBinding.Variable.Property(groupAggregateVarRef.VariableName)));
3936 // If all GROUPPARTITION definitions have been reduced, return null.
3937 // In this case the wrapping projection will not be created and
3938 // groupAggregateVarRef will be projected directly from the DbGroupByExpression.
3940 groupPartitionDefinitions = null;
3944 return groupPartitionDefinitions;
3948 /// Returns true if lambda accepts a collection variable and trivially projects out its elements.
3950 private static bool IsTrivialInputProjection(DbVariableReferenceExpression lambdaVariable, DbExpression lambdaBody)
3952 if (lambdaBody.ExpressionKind != DbExpressionKind.Project)
3956 DbProjectExpression projectExpression = (DbProjectExpression)lambdaBody;
3958 if (projectExpression.Input.Expression != lambdaVariable)
3963 Debug.Assert(TypeSemantics.IsCollectionType(lambdaVariable.ResultType));
3965 if (projectExpression.Projection.ExpressionKind == DbExpressionKind.VariableReference)
3967 DbVariableReferenceExpression projectionExpression = (DbVariableReferenceExpression)projectExpression.Projection;
3968 return projectionExpression == projectExpression.Input.Variable;
3970 else if (projectExpression.Projection.ExpressionKind == DbExpressionKind.NewInstance &&
3971 TypeSemantics.IsRowType(projectExpression.Projection.ResultType))
3973 if (!TypeSemantics.IsEqual(projectExpression.Projection.ResultType, projectExpression.Input.Variable.ResultType))
3978 IBaseList<EdmMember> inputVariableTypeProperties = TypeHelpers.GetAllStructuralMembers(projectExpression.Input.Variable.ResultType);
3980 DbNewInstanceExpression projectionExpression = (DbNewInstanceExpression)projectExpression.Projection;
3982 Debug.Assert(projectionExpression.Arguments.Count == inputVariableTypeProperties.Count, "projectionExpression.Arguments.Count == inputVariableTypeProperties.Count");
3983 for (int i = 0; i < projectionExpression.Arguments.Count; ++i)
3985 if (projectionExpression.Arguments[i].ExpressionKind != DbExpressionKind.Property)
3989 DbPropertyExpression propertyRef = (DbPropertyExpression)projectionExpression.Arguments[i];
3991 if (propertyRef.Instance != projectExpression.Input.Variable ||
3992 propertyRef.Property != inputVariableTypeProperties[i])
4004 private sealed class GroupKeyInfo
4006 internal GroupKeyInfo(string name, DbExpression varBasedKeyExpr, DbExpression groupVarBasedKeyExpr, DbExpression groupAggBasedKeyExpr)
4009 VarRef = varBasedKeyExpr.ResultType.Variable(name);
4010 VarBasedKeyExpr = varBasedKeyExpr;
4011 GroupVarBasedKeyExpr = groupVarBasedKeyExpr;
4012 GroupAggBasedKeyExpr = groupAggBasedKeyExpr;
4016 /// The primary name of the group key. It is used to refer to the key from other expressions.
4018 internal readonly string Name;
4021 /// Optional alternative name of the group key.
4022 /// Used to support the following scenario:
4023 /// SELECT Price, p.Price FROM ... as p GROUP BY p.Price
4024 /// In this case the group key Name is "Price" and the AlternativeName is "p.Price" as if it is coming as an escaped identifier.
4026 internal string[] AlternativeName
4028 get { return _alternativeName; }
4031 Debug.Assert(_alternativeName == null, "GroupKeyInfo.AlternativeName can not be reset");
4032 _alternativeName = value;
4035 private string[] _alternativeName;
4037 internal readonly DbVariableReferenceExpression VarRef;
4039 internal readonly DbExpression VarBasedKeyExpr;
4041 internal readonly DbExpression GroupVarBasedKeyExpr;
4043 internal readonly DbExpression GroupAggBasedKeyExpr;
4047 /// Process ORDER BY clause.
4049 private static DbExpressionBinding ProcessOrderByClause(DbExpressionBinding source, AST.QueryExpr queryExpr, out bool queryProjectionProcessed, SemanticResolver sr)
4051 Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode");
4053 queryProjectionProcessed = false;
4055 if (queryExpr.OrderByClause == null)
4060 DbExpressionBinding sortBinding = null;
4061 AST.OrderByClause orderByClause = queryExpr.OrderByClause;
4062 AST.SelectClause selectClause = queryExpr.SelectClause;
4065 // Convert SKIP sub-clause if exists before adding projection expressions to the scope.
4067 DbExpression convertedSkip = null;
4069 if (orderByClause.SkipSubClause != null)
4072 // Convert the skip expression.
4074 convertedSkip = ConvertValueExpression(orderByClause.SkipSubClause, sr);
4077 // Ensure the converted expression is in the range of values.
4079 ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(convertedSkip, orderByClause.SkipSubClause.ErrCtx, "SKIP", sr);
4084 // Convert SELECT clause items before processing the rest of the ORDER BY clause:
4085 // - If it is the SELECT DISTINCT case:
4086 // SELECT clause item definitions will be used to create DbDistinctExpression, which becomes the new source expression.
4087 // Sort keys can only reference:
4088 // a. SELECT clause items by their aliases (only these aliases are projected by the new source expression),
4089 // b. entries from outer scopes.
4091 // Sort keys may references any available scope entries, including SELECT clause items.
4092 // If a sort key references a SELECT clause item, the item _definition_ will be used as the sort key definition (not a variable ref).
4094 var projectionItems = ConvertSelectClauseItems(queryExpr, sr);
4096 if (selectClause.DistinctKind == AST.DistinctKind.Distinct)
4099 // SELECT DISTINCT ... ORDER BY case:
4100 // - All scope entries created below SELECT DISTINCT are not valid above it in this query, even for error messages, so remove them.
4101 // - The scope entries created by SELECT DISTINCT (the SELECT clause items) will be added to a temporary scope in the code below,
4102 // this will make them available for sort keys.
4104 sr.CurrentScopeRegion.RollbackAllScopes();
4108 // Create temporary scope for SELECT clause items and add the items to the scope.
4110 int savedScope = sr.CurrentScopeIndex;
4112 projectionItems.ForEach(projectionItem => sr.CurrentScope.Add(projectionItem.Key, new ProjectionItemDefinitionScopeEntry(projectionItem.Value)));
4115 // Process SELECT DISTINCT ... ORDER BY case:
4116 // - create projection expression: new Row(SELECT clause item defintions) or just the single SELECT clause item defintion;
4117 // - create DbDistinctExpression over the projection expression;
4118 // - set source expression to the binding to the distinct.
4120 if (selectClause.DistinctKind == AST.DistinctKind.Distinct)
4123 // Create distinct projection expression and bind to it.
4125 DbExpression projectExpression = CreateProjectExpression(source, selectClause, projectionItems);
4126 Debug.Assert(projectExpression is DbDistinctExpression, "projectExpression is DbDistinctExpression");
4127 source = projectExpression.BindAs(sr.GenerateInternalName("distinct"));
4130 // Replace SELECT clause item definitions with regular source scope entries pointing into the new source binding.
4132 if (selectClause.SelectKind == AST.SelectKind.Value)
4134 Debug.Assert(projectionItems.Count == 1, "projectionItems.Count == 1");
4135 sr.CurrentScope.Replace(projectionItems[0].Key, new SourceScopeEntry(source.Variable));
4139 Debug.Assert(selectClause.SelectKind == AST.SelectKind.Row, "selectClause.SelectKind == AST.SelectKind.Row");
4140 foreach (var projectionExpression in projectionItems)
4142 DbVariableReferenceExpression projectionExpressionRef = projectionExpression.Value.ResultType.Variable(projectionExpression.Key);
4144 sr.CurrentScope.Replace(projectionExpressionRef.VariableName,
4145 new SourceScopeEntry(projectionExpressionRef).AddParentVar(source.Variable));
4150 // At this point source contains all projected items, so query processing is mostly complete,
4151 // the only task remaining is processing of TOP/LIMIT subclauses, which happens in ProcessSelectClause(...) method.
4153 queryProjectionProcessed = true;
4157 // Convert sort keys.
4159 List<DbSortClause> sortKeys = new List<DbSortClause>(orderByClause.OrderByClauseItem.Count);
4161 for (int i = 0; i < orderByClause.OrderByClauseItem.Count; i++)
4163 AST.OrderByClauseItem orderClauseItem = orderByClause.OrderByClauseItem[i];
4165 sr.CurrentScopeRegion.WasResolutionCorrelated = false;
4168 // Convert order key expression.
4170 DbExpression keyExpr = ConvertValueExpression(orderClauseItem.OrderExpr, sr);
4173 // Ensure key expression is correlated.
4175 if (!sr.CurrentScopeRegion.WasResolutionCorrelated)
4177 throw EntityUtil.EntitySqlError(orderClauseItem.ErrCtx, Strings.KeyMustBeCorrelated("ORDER BY"));
4181 // Ensure key is order comparable.
4183 if (!TypeHelpers.IsValidSortOpKeyType(keyExpr.ResultType))
4185 throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, Strings.OrderByKeyIsNotOrderComparable);
4189 // Convert order direction.
4191 bool ascSort = (orderClauseItem.OrderKind == AST.OrderKind.None) || (orderClauseItem.OrderKind == AST.OrderKind.Asc);
4194 // Convert collation.
4196 string collation = null;
4197 if (orderClauseItem.Collation != null)
4199 if (!IsStringType(keyExpr.ResultType))
4201 throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, Strings.InvalidKeyTypeForCollation(keyExpr.ResultType.EdmType.FullName));
4204 collation = orderClauseItem.Collation.Name;
4208 // Finish key conversion and add converted keys to key collection.
4210 if (string.IsNullOrEmpty(collation))
4212 sortKeys.Add(ascSort ? keyExpr.ToSortClause() : keyExpr.ToSortClauseDescending());
4216 sortKeys.Add(ascSort ? keyExpr.ToSortClause(collation) : keyExpr.ToSortClauseDescending(collation));
4222 // Remove the temporary projection scope with all the SELECT clause items on it.
4224 sr.RollbackToScope(savedScope);
4227 // Create sort expression.
4229 DbExpression sortSourceExpr = null;
4230 if (convertedSkip != null)
4232 sortSourceExpr = source.Skip(sortKeys, convertedSkip);
4236 sortSourceExpr = source.Sort(sortKeys);
4240 // Create Sort Binding.
4242 sortBinding = sortSourceExpr.BindAs(sr.GenerateInternalName("sort"));
4247 if (queryProjectionProcessed)
4249 Debug.Assert(sr.CurrentScopeIndex < sr.CurrentScopeRegion.FirstScopeIndex, "Current scope region is expected to have no scopes.");
4252 * The following code illustrates definition of the projected output in the case of DISTINCT ORDER BY.
4253 * There is nothing above this point that should reference any scope entries produced by this query,
4254 * so we do not really add them to the scope region (hence the code is commented out).
4258 // All the scopes of this current scope region have been rolled back.
4259 // Add new scope with all the projected items on it.
4262 if (selectClause.SelectKind == AST.SelectKind.SelectRow)
4264 foreach (var projectionExpression in projectionItems)
4266 DbVariableReferenceExpression projectionExpressionRef = projectionExpression.Value.ResultType.Variable(projectionExpression.Key);
4267 sr.CurrentScope.Add(projectionExpressionRef.VariableName,
4268 new SourceScopeEntry(projectionExpressionRef).AddParentVar(sortBinding.Variable));
4273 Debug.Assert(selectClause.SelectKind == AST.SelectKind.SelectValue, "selectClause.SelectKind == AST.SelectKind.SelectValue");
4274 Debug.Assert(projectionItems.Count == 1, "projectionItems.Count == 1");
4276 sr.CurrentScope.Add(projectionItems[0].Key, new SourceScopeEntry(sortBinding.Variable));
4281 sr.CurrentScopeRegion.ApplyToScopeEntries(scopeEntry =>
4283 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef,
4284 "scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef");
4286 if (scopeEntry.EntryKind == ScopeEntryKind.SourceVar)
4288 ((SourceScopeEntry)scopeEntry).ReplaceParentVar(sortBinding.Variable);
4293 Debug.Assert(null != sortBinding, "null != sortBinding");
4299 /// Convert "x in multiset(y1, y2, ..., yn)" into
4300 /// x = y1 or x = y2 or x = y3 ...
4302 /// <param name="sr">semantic resolver</param>
4303 /// <param name="left">left-expression (the probe)</param>
4304 /// <param name="right">right expression (the collection)</param>
4305 /// <returns>Or tree of equality comparisons</returns>
4306 private static DbExpression ConvertSimpleInExpression(SemanticResolver sr, DbExpression left, DbExpression right)
4308 // Only handle cases when the right-side is a new instance expression
4309 Debug.Assert(right.ExpressionKind == DbExpressionKind.NewInstance, "right.ExpressionKind == DbExpressionKind.NewInstance");
4310 DbNewInstanceExpression rightColl = (DbNewInstanceExpression)right;
4312 if (rightColl.Arguments.Count == 0)
4314 return DbExpressionBuilder.False;
4317 var predicates = rightColl.Arguments.Select(arg => left.Equal(arg));
4318 List<DbExpression> args = new List<DbExpression>(predicates);
4319 DbExpression orExpr = Utils.Helpers.BuildBalancedTreeInPlace(args, (prev, next) => prev.Or(next));
4324 private static bool IsStringType(TypeUsage type)
4326 return TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String);
4329 private static bool IsBooleanType(TypeUsage type)
4331 return TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Boolean);
4334 private static bool IsSubOrSuperType(TypeUsage type1, TypeUsage type2)
4336 return TypeSemantics.IsStructurallyEqual(type1, type2) || type1.IsSubtypeOf(type2) || type2.IsSubtypeOf(type1);
4339 #region Expression converters
4341 private delegate ExpressionResolution AstExprConverter(AST.Node astExpr, SemanticResolver sr);
4342 private static readonly Dictionary<Type, AstExprConverter> _astExprConverters = CreateAstExprConverters();
4343 private delegate DbExpression BuiltInExprConverter(AST.BuiltInExpr astBltInExpr, SemanticResolver sr);
4344 private static readonly Dictionary<AST.BuiltInKind, BuiltInExprConverter> _builtInExprConverter = CreateBuiltInExprConverter();
4346 private static Dictionary<Type, AstExprConverter> CreateAstExprConverters()
4348 const int NumberOfElements = 17; // number of elements initialized by the dictionary
4349 Dictionary<Type, AstExprConverter> astExprConverters = new Dictionary<Type, AstExprConverter>(NumberOfElements);
4350 astExprConverters.Add(typeof(AST.Literal), new AstExprConverter(ConvertLiteral));
4351 astExprConverters.Add(typeof(AST.QueryParameter), new AstExprConverter(ConvertParameter));
4352 astExprConverters.Add(typeof(AST.Identifier), new AstExprConverter(ConvertIdentifier));
4353 astExprConverters.Add(typeof(AST.DotExpr), new AstExprConverter(ConvertDotExpr));
4354 astExprConverters.Add(typeof(AST.BuiltInExpr), new AstExprConverter(ConvertBuiltIn));
4355 astExprConverters.Add(typeof(AST.QueryExpr), new AstExprConverter(ConvertQueryExpr));
4356 astExprConverters.Add(typeof(AST.ParenExpr), new AstExprConverter(ConvertParenExpr));
4357 astExprConverters.Add(typeof(AST.RowConstructorExpr), new AstExprConverter(ConvertRowConstructor));
4358 astExprConverters.Add(typeof(AST.MultisetConstructorExpr), new AstExprConverter(ConvertMultisetConstructor));
4359 astExprConverters.Add(typeof(AST.CaseExpr), new AstExprConverter(ConvertCaseExpr));
4360 astExprConverters.Add(typeof(AST.RelshipNavigationExpr), new AstExprConverter(ConvertRelshipNavigationExpr));
4361 astExprConverters.Add(typeof(AST.RefExpr), new AstExprConverter(ConvertRefExpr));
4362 astExprConverters.Add(typeof(AST.DerefExpr), new AstExprConverter(ConvertDeRefExpr));
4363 astExprConverters.Add(typeof(AST.MethodExpr), new AstExprConverter(ConvertMethodExpr));
4364 astExprConverters.Add(typeof(AST.CreateRefExpr), new AstExprConverter(ConvertCreateRefExpr));
4365 astExprConverters.Add(typeof(AST.KeyExpr), new AstExprConverter(ConvertKeyExpr));
4366 astExprConverters.Add(typeof(AST.GroupPartitionExpr), new AstExprConverter(ConvertGroupPartitionExpr));
4367 Debug.Assert(NumberOfElements == astExprConverters.Count, "The number of elements and initial capacity don't match");
4368 return astExprConverters;
4371 private static Dictionary<AST.BuiltInKind, BuiltInExprConverter> CreateBuiltInExprConverter()
4373 Dictionary<AST.BuiltInKind, BuiltInExprConverter> builtInExprConverter = new Dictionary<AST.BuiltInKind, BuiltInExprConverter>(sizeof(AST.BuiltInKind));
4375 ////////////////////////////
4376 // Arithmetic Expressions
4377 ////////////////////////////
4383 builtInExprConverter.Add(AST.BuiltInKind.Plus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4385 Pair<DbExpression, DbExpression> args = ConvertPlusOperands(bltInExpr, sr);
4387 if (TypeSemantics.IsNumericType(args.Left.ResultType))
4389 return args.Left.Plus(args.Right);
4394 // fold '+' operator into concat canonical function
4396 MetadataFunctionGroup function;
4397 if (!sr.TypeResolver.TryGetFunctionFromMetadata("Edm", "Concat", out function))
4399 throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, Strings.ConcatBuiltinNotSupported);
4402 List<TypeUsage> argTypes = new List<TypeUsage>(2);
4403 argTypes.Add(args.Left.ResultType);
4404 argTypes.Add(args.Right.ResultType);
4406 bool isAmbiguous = false;
4407 EdmFunction concatFunction = SemanticResolver.ResolveFunctionOverloads(
4408 function.FunctionMetadata,
4410 false /* isGroupAggregate */,
4413 if (null == concatFunction || isAmbiguous)
4415 throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, Strings.ConcatBuiltinNotSupported);
4418 return concatFunction.Invoke(new[] { args.Left, args.Right });
4428 builtInExprConverter.Add(AST.BuiltInKind.Minus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4430 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr);
4432 return args.Left.Minus(args.Right);
4440 builtInExprConverter.Add(AST.BuiltInKind.Multiply, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4442 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr);
4444 return args.Left.Multiply(args.Right);
4452 builtInExprConverter.Add(AST.BuiltInKind.Divide, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4454 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr);
4456 return args.Left.Divide(args.Right);
4464 builtInExprConverter.Add(AST.BuiltInKind.Modulus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4466 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr);
4468 return args.Left.Modulo(args.Right);
4476 builtInExprConverter.Add(AST.BuiltInKind.UnaryMinus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4478 DbExpression argument = ConvertArithmeticArgs(bltInExpr, sr).Left;
4479 if (TypeSemantics.IsUnsignedNumericType(argument.ResultType))
4481 TypeUsage closestPromotableType = null;
4482 if (!TypeHelpers.TryGetClosestPromotableType(argument.ResultType, out closestPromotableType))
4484 throw EntityUtil.EntitySqlError(Strings.InvalidUnsignedTypeForUnaryMinusOperation(argument.ResultType.EdmType.FullName));
4488 DbExpression unaryExpr = argument.UnaryMinus();
4497 builtInExprConverter.Add(AST.BuiltInKind.UnaryPlus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4499 return ConvertArithmeticArgs(bltInExpr, sr).Left;
4503 ////////////////////////////
4504 // Logical Expressions
4505 ////////////////////////////
4512 builtInExprConverter.Add(AST.BuiltInKind.And, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4514 Pair<DbExpression, DbExpression> args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr);
4516 return args.Left.And(args.Right);
4525 builtInExprConverter.Add(AST.BuiltInKind.Or, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4527 Pair<DbExpression, DbExpression> args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr);
4529 return args.Left.Or(args.Right);
4538 builtInExprConverter.Add(AST.BuiltInKind.Not, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4540 return ConvertLogicalArgs(bltInExpr, sr).Left.Not();
4544 ////////////////////////////
4545 // Comparison Expressions
4546 ////////////////////////////
4549 // e1 == e2 | e1 = e2
4552 builtInExprConverter.Add(AST.BuiltInKind.Equal, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4554 Pair<DbExpression, DbExpression> args = ConvertEqualCompArgs(bltInExpr, sr);
4556 return args.Left.Equal(args.Right);
4561 // e1 != e2 | e1 <> e2
4564 builtInExprConverter.Add(AST.BuiltInKind.NotEqual, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4566 Pair<DbExpression, DbExpression> args = ConvertEqualCompArgs(bltInExpr, sr);
4570 return args.Left.Equal(args.Right).Not();
4578 builtInExprConverter.Add(AST.BuiltInKind.GreaterEqual, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4580 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr);
4582 return args.Left.GreaterThanOrEqual(args.Right);
4590 builtInExprConverter.Add(AST.BuiltInKind.GreaterThan, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4592 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr);
4594 return args.Left.GreaterThan(args.Right);
4602 builtInExprConverter.Add(AST.BuiltInKind.LessEqual, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4604 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr);
4606 return args.Left.LessThanOrEqual(args.Right);
4614 builtInExprConverter.Add(AST.BuiltInKind.LessThan, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4616 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr);
4618 return args.Left.LessThan(args.Right);
4623 ////////////////////////////
4625 ////////////////////////////
4632 builtInExprConverter.Add(AST.BuiltInKind.Union, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4634 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr);
4636 return args.Left.UnionAll(args.Right).Distinct();
4643 #region e1 UNION ALL e2
4644 builtInExprConverter.Add(AST.BuiltInKind.UnionAll, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4646 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr);
4648 return args.Left.UnionAll(args.Right);
4655 #region e1 INTERSECT e2
4656 builtInExprConverter.Add(AST.BuiltInKind.Intersect, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4658 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr);
4660 return args.Left.Intersect(args.Right);
4667 #region e1 OVERLAPS e1
4668 builtInExprConverter.Add(AST.BuiltInKind.Overlaps, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4670 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr);
4672 return args.Left.Intersect(args.Right).IsEmpty().Not();
4679 #region ANYELEMENT( e )
4680 builtInExprConverter.Add(AST.BuiltInKind.AnyElement, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4682 return ConvertSetArgs(bltInExpr, sr).Left.Element();
4689 #region ELEMENT( e ) - NOT SUPPORTED IN ORCAS TIMEFRAME
4690 builtInExprConverter.Add(AST.BuiltInKind.Element, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4692 throw EntityUtil.NotSupported(Strings.ElementOperatorIsNotSupported);
4699 #region e1 EXCEPT e2
4700 builtInExprConverter.Add(AST.BuiltInKind.Except, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4702 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr);
4704 return args.Left.Except(args.Right);
4712 builtInExprConverter.Add(AST.BuiltInKind.Exists, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4714 return ConvertSetArgs(bltInExpr, sr).Left.IsEmpty().Not();
4721 #region FLATTEN( e )
4722 builtInExprConverter.Add(AST.BuiltInKind.Flatten, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4724 DbExpression elemExpr = ConvertValueExpression(bltInExpr.Arg1, sr);
4726 if (!TypeSemantics.IsCollectionType(elemExpr.ResultType))
4728 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidFlattenArgument);
4731 if (!TypeSemantics.IsCollectionType(TypeHelpers.GetElementTypeUsage(elemExpr.ResultType)))
4733 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidFlattenArgument);
4736 DbExpressionBinding leftExpr = elemExpr.BindAs(sr.GenerateInternalName("l_flatten"));
4738 DbExpressionBinding rightExpr = leftExpr.Variable.BindAs(sr.GenerateInternalName("r_flatten"));
4740 DbExpressionBinding applyBinding = leftExpr.CrossApply(rightExpr).BindAs(sr.GenerateInternalName("flatten"));
4742 return applyBinding.Project(applyBinding.Variable.Property(rightExpr.VariableName));
4750 builtInExprConverter.Add(AST.BuiltInKind.In, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4752 Pair<DbExpression, DbExpression> args = ConvertInExprArgs(bltInExpr, sr);
4755 // Convert "x in multiset(y1, y2, ..., yn)" into x = y1 or x = y2 or x = y3 ...
4757 if (args.Right.ExpressionKind == DbExpressionKind.NewInstance)
4759 return ConvertSimpleInExpression(sr, args.Left, args.Right);
4763 DbExpressionBinding rSet = args.Right.BindAs(sr.GenerateInternalName("in-filter"));
4765 DbExpression leftIn = args.Left;
4766 DbExpression rightSet = rSet.Variable;
4768 DbExpression exists = rSet.Filter(leftIn.Equal(rightSet)).IsEmpty().Not();
4770 List<DbExpression> whenExpr = new List<DbExpression>(1);
4771 whenExpr.Add(leftIn.IsNull());
4772 List<DbExpression> thenExpr = new List<DbExpression>(1);
4773 thenExpr.Add(DbExpressionBuilder.Null(sr.TypeResolver.BooleanType));
4775 DbExpression left = DbExpressionBuilder.Case(whenExpr, thenExpr, DbExpressionBuilder.False);
4777 DbExpression converted = left.Or(exists);
4787 #region e1 NOT IN e1
4788 builtInExprConverter.Add(AST.BuiltInKind.NotIn, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4790 Pair<DbExpression, DbExpression> args = ConvertInExprArgs(bltInExpr, sr);
4792 if (args.Right.ExpressionKind == DbExpressionKind.NewInstance)
4794 return ConvertSimpleInExpression(sr, args.Left, args.Right).Not();
4798 DbExpressionBinding rSet = args.Right.BindAs(sr.GenerateInternalName("in-filter"));
4800 DbExpression leftIn = args.Left;
4801 DbExpression rightSet = rSet.Variable;
4803 DbExpression exists = rSet.Filter(leftIn.Equal(rightSet)).IsEmpty();
4805 List<DbExpression> whenExpr = new List<DbExpression>(1);
4806 whenExpr.Add(leftIn.IsNull());
4807 List<DbExpression> thenExpr = new List<DbExpression>(1);
4808 thenExpr.Add(DbExpressionBuilder.Null(sr.TypeResolver.BooleanType));
4810 DbExpression left = DbExpressionBuilder.Case(whenExpr, thenExpr, DbExpressionBuilder.True);
4812 DbExpression converted = left.And(exists);
4820 // SET( e ) - DISTINCT( e ) before
4823 builtInExprConverter.Add(AST.BuiltInKind.Distinct, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4825 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr);
4827 return args.Left.Distinct();
4832 ////////////////////////////
4833 // Nullabity Expressions
4834 ////////////////////////////
4840 builtInExprConverter.Add(AST.BuiltInKind.IsNull, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4842 DbExpression isNullExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr);
4845 // Ensure expression type is valid for this operation.
4847 if (isNullExpr != null && !TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType))
4849 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.IsNullInvalidType);
4852 return isNullExpr != null ? (DbExpression)isNullExpr.IsNull() : DbExpressionBuilder.True;
4859 #region e IS NOT NULL
4860 builtInExprConverter.Add(AST.BuiltInKind.IsNotNull, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4862 DbExpression isNullExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr);
4865 // Ensure expression type is valid for this operation.
4867 if (isNullExpr != null && !TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType))
4869 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.IsNullInvalidType);
4872 return isNullExpr != null ? (DbExpression)isNullExpr.IsNull().Not() : DbExpressionBuilder.False;
4876 ////////////////////////////
4878 ////////////////////////////
4881 // e IS OF ( [ONLY] T )
4883 #region e IS OF ( [ONLY] T )
4884 builtInExprConverter.Add(AST.BuiltInKind.IsOf, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4886 var exprToFilter = ConvertValueExpression(bltInExpr.Arg1, sr);
4887 var typeToFilterTo = ConvertTypeName(bltInExpr.Arg2, sr);
4889 bool isOnly = (bool)((AST.Literal)bltInExpr.Arg3).Value;
4890 bool isNot = (bool)((AST.Literal)bltInExpr.Arg4).Value;
4891 bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
4893 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(exprToFilter.ResultType))
4895 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
4896 Strings.ExpressionTypeMustBeEntityType(Strings.CtxIsOf,
4897 exprToFilter.ResultType.EdmType.BuiltInTypeKind.ToString(),
4898 exprToFilter.ResultType.EdmType.FullName));
4900 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(exprToFilter.ResultType))
4902 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
4903 Strings.ExpressionTypeMustBeNominalType(Strings.CtxIsOf,
4904 exprToFilter.ResultType.EdmType.BuiltInTypeKind.ToString(),
4905 exprToFilter.ResultType.EdmType.FullName));
4908 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(typeToFilterTo))
4910 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeEntityType(Strings.CtxIsOf,
4911 typeToFilterTo.EdmType.BuiltInTypeKind.ToString(),
4912 typeToFilterTo.EdmType.FullName));
4914 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(typeToFilterTo))
4916 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeNominalType(Strings.CtxIsOf,
4917 typeToFilterTo.EdmType.BuiltInTypeKind.ToString(),
4918 typeToFilterTo.EdmType.FullName));
4921 if (!TypeSemantics.IsPolymorphicType(exprToFilter.ResultType))
4923 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.TypeMustBeInheritableType);
4926 if (!TypeSemantics.IsPolymorphicType(typeToFilterTo))
4928 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeInheritableType);
4931 if (!IsSubOrSuperType(exprToFilter.ResultType, typeToFilterTo))
4933 throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, Strings.NotASuperOrSubType(exprToFilter.ResultType.EdmType.FullName,
4934 typeToFilterTo.EdmType.FullName));
4937 typeToFilterTo = TypeHelpers.GetReadOnlyType(typeToFilterTo);
4939 DbExpression retExpr = null;
4942 retExpr = exprToFilter.IsOfOnly(typeToFilterTo);
4946 retExpr = exprToFilter.IsOf(typeToFilterTo);
4951 retExpr = retExpr.Not();
4961 #region TREAT( e as T )
4962 builtInExprConverter.Add(AST.BuiltInKind.Treat, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
4964 var exprToTreat = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr);
4965 var typeToTreatTo = ConvertTypeName(bltInExpr.Arg2, sr);
4967 bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
4969 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(typeToTreatTo))
4971 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
4972 Strings.TypeMustBeEntityType(Strings.CtxTreat,
4973 typeToTreatTo.EdmType.BuiltInTypeKind.ToString(),
4974 typeToTreatTo.EdmType.FullName));
4976 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(typeToTreatTo))
4978 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
4979 Strings.TypeMustBeNominalType(Strings.CtxTreat,
4980 typeToTreatTo.EdmType.BuiltInTypeKind.ToString(),
4981 typeToTreatTo.EdmType.FullName));
4984 if (exprToTreat == null)
4986 exprToTreat = DbExpressionBuilder.Null(typeToTreatTo);
4988 else if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(exprToTreat.ResultType))
4990 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
4991 Strings.ExpressionTypeMustBeEntityType(Strings.CtxTreat,
4992 exprToTreat.ResultType.EdmType.BuiltInTypeKind.ToString(),
4993 exprToTreat.ResultType.EdmType.FullName));
4995 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(exprToTreat.ResultType))
4997 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
4998 Strings.ExpressionTypeMustBeNominalType(Strings.CtxTreat,
4999 exprToTreat.ResultType.EdmType.BuiltInTypeKind.ToString(),
5000 exprToTreat.ResultType.EdmType.FullName));
5003 if (!TypeSemantics.IsPolymorphicType(exprToTreat.ResultType))
5005 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.TypeMustBeInheritableType);
5008 if (!TypeSemantics.IsPolymorphicType(typeToTreatTo))
5010 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeInheritableType);
5013 if (!IsSubOrSuperType(exprToTreat.ResultType, typeToTreatTo))
5015 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.NotASuperOrSubType(exprToTreat.ResultType.EdmType.FullName,
5016 typeToTreatTo.EdmType.FullName));
5019 return exprToTreat.TreatAs(TypeHelpers.GetReadOnlyType(typeToTreatTo));
5026 #region CAST( e AS T )
5027 builtInExprConverter.Add(AST.BuiltInKind.Cast, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
5029 var exprToCast = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr);
5030 var typeToCastTo = ConvertTypeName(bltInExpr.Arg2, sr);
5033 // Ensure CAST target type is scalar.
5035 if (!TypeSemantics.IsScalarType(typeToCastTo))
5037 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.InvalidCastType);
5040 if (exprToCast == null)
5042 return DbExpressionBuilder.Null(typeToCastTo);
5046 // Ensure CAST source type is scalar.
5048 if (!TypeSemantics.IsScalarType(exprToCast.ResultType))
5050 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidCastExpressionType);
5053 if (!TypeSemantics.IsCastAllowed(exprToCast.ResultType, typeToCastTo))
5055 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidCast(exprToCast.ResultType.EdmType.FullName, typeToCastTo.EdmType.FullName));
5058 return exprToCast.CastTo(TypeHelpers.GetReadOnlyType(typeToCastTo));
5063 // OFTYPE( [ONLY] e, T )
5065 #region OFTYPE( [ONLY] e, T )
5066 builtInExprConverter.Add(AST.BuiltInKind.OfType, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
5068 var exprToFilter = ConvertValueExpression(bltInExpr.Arg1, sr);
5069 var typeToFilterTo = ConvertTypeName(bltInExpr.Arg2, sr);
5071 bool isOnly = (bool)((AST.Literal)bltInExpr.Arg3).Value;
5073 bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode;
5075 if (!TypeSemantics.IsCollectionType(exprToFilter.ResultType))
5077 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.ExpressionMustBeCollection);
5080 TypeUsage elementType = TypeHelpers.GetElementTypeUsage(exprToFilter.ResultType);
5081 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(elementType))
5083 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
5084 Strings.OfTypeExpressionElementTypeMustBeEntityType(elementType.EdmType.BuiltInTypeKind.ToString(), elementType));
5086 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(elementType))
5088 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx,
5089 Strings.OfTypeExpressionElementTypeMustBeNominalType(elementType.EdmType.BuiltInTypeKind.ToString(), elementType));
5092 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(typeToFilterTo))
5094 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
5095 Strings.TypeMustBeEntityType(Strings.CtxOfType, typeToFilterTo.EdmType.BuiltInTypeKind.ToString(), typeToFilterTo.EdmType.FullName));
5097 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(typeToFilterTo))
5099 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx,
5100 Strings.TypeMustBeNominalType(Strings.CtxOfType, typeToFilterTo.EdmType.BuiltInTypeKind.ToString(), typeToFilterTo.EdmType.FullName));
5103 if (isOnly && typeToFilterTo.EdmType.Abstract)
5105 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.OfTypeOnlyTypeArgumentCannotBeAbstract(typeToFilterTo.EdmType.FullName));
5108 if (!IsSubOrSuperType(elementType, typeToFilterTo))
5110 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.NotASuperOrSubType(elementType.EdmType.FullName, typeToFilterTo.EdmType.FullName));
5113 DbExpression ofTypeExpression = null;
5116 ofTypeExpression = exprToFilter.OfTypeOnly(TypeHelpers.GetReadOnlyType(typeToFilterTo));
5120 ofTypeExpression = exprToFilter.OfType(TypeHelpers.GetReadOnlyType(typeToFilterTo));
5123 return ofTypeExpression;
5128 // e LIKE pattern [ESCAPE escape]
5130 #region e LIKE pattern [ESCAPE escape]
5131 builtInExprConverter.Add(AST.BuiltInKind.Like, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
5133 DbExpression likeExpr = null;
5135 DbExpression matchExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr);
5136 if (matchExpr == null)
5138 matchExpr = DbExpressionBuilder.Null(sr.TypeResolver.StringType);
5140 else if (!IsStringType(matchExpr.ResultType))
5142 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.LikeArgMustBeStringType);
5145 DbExpression patternExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg2, sr);
5146 if (patternExpr == null)
5148 patternExpr = DbExpressionBuilder.Null(sr.TypeResolver.StringType);
5150 else if (!IsStringType(patternExpr.ResultType))
5152 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.LikeArgMustBeStringType);
5155 if (3 == bltInExpr.ArgCount)
5157 DbExpression escapeExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg3, sr);
5158 if (escapeExpr == null)
5160 escapeExpr = DbExpressionBuilder.Null(sr.TypeResolver.StringType);
5162 else if (!IsStringType(escapeExpr.ResultType))
5164 throw EntityUtil.EntitySqlError(bltInExpr.Arg3.ErrCtx, Strings.LikeArgMustBeStringType);
5167 likeExpr = matchExpr.Like(patternExpr, escapeExpr);
5171 likeExpr = matchExpr.Like(patternExpr);
5179 // e BETWEEN e1 AND e2
5181 #region e BETWEEN e1 AND e2
5182 builtInExprConverter.Add(AST.BuiltInKind.Between, ConvertBetweenExpr);
5186 // e NOT BETWEEN e1 AND e2
5188 #region e NOT BETWEEN e1 AND e2
5189 builtInExprConverter.Add(AST.BuiltInKind.NotBetween, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
5191 return ConvertBetweenExpr(bltInExpr, sr).Not();
5195 return builtInExprConverter;
5198 private static DbExpression ConvertBetweenExpr(AST.BuiltInExpr bltInExpr, SemanticResolver sr)
5200 Debug.Assert(bltInExpr.Kind == AST.BuiltInKind.Between || bltInExpr.Kind == AST.BuiltInKind.NotBetween, "bltInExpr.Kind must be Between or NotBetween");
5201 Debug.Assert(bltInExpr.ArgCount == 3, "bltInExpr.ArgCount == 3");
5204 // convert lower and upper limits
5206 Pair<DbExpression, DbExpression> limitsExpr = ConvertValueExpressionsWithUntypedNulls(
5209 bltInExpr.Arg1.ErrCtx,
5210 () => Strings.BetweenLimitsCannotBeUntypedNulls,
5214 // Get and check common type for limits
5216 TypeUsage rangeCommonType = TypeHelpers.GetCommonTypeUsage(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType);
5217 if (null == rangeCommonType)
5219 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.BetweenLimitsTypesAreNotCompatible(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName));
5223 // check if limit types are order-comp
5225 if (!TypeSemantics.IsOrderComparableTo(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType))
5227 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.BetweenLimitsTypesAreNotOrderComparable(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName));
5231 // convert value expression
5233 DbExpression valueExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr);
5234 if (valueExpr == null)
5236 valueExpr = DbExpressionBuilder.Null(rangeCommonType);
5240 // check if valueExpr is order-comparable to limits
5242 if (!TypeSemantics.IsOrderComparableTo(valueExpr.ResultType, rangeCommonType))
5244 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.BetweenValueIsNotOrderComparable(valueExpr.ResultType.EdmType.FullName, rangeCommonType.EdmType.FullName));
5247 return valueExpr.GreaterThanOrEqual(limitsExpr.Left).And(valueExpr.LessThanOrEqual(limitsExpr.Right));