1 //--------------------------------------------------------------------- 2 // <copyright file="SemanticAnalyzer.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 namespace System.Data.Common.EntitySql 11 { 12 using System; 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; 21 using System.Linq; 22 23 /// <summary> 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. 29 /// </summary> 30 internal sealed class SemanticAnalyzer 31 { 32 private SemanticResolver _sr; 33 34 /// <summary> 35 /// Initializes semantic analyzer 36 /// </summary> 37 /// <param name="sr">initialized SemanticResolver instance for a given typespace/type system</param> SemanticAnalyzer(SemanticResolver sr)38 internal SemanticAnalyzer(SemanticResolver sr) 39 { 40 Debug.Assert(sr != null, "sr must not be null"); 41 _sr = sr; 42 } 43 44 /// <summary> 45 /// Entry point to semantic analysis. Converts AST into a <see cref="DbCommandTree"/>. 46 /// </summary> 47 /// <param name="astExpr">ast command tree</param> 48 /// <remarks> 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> 52 /// </remarks> 53 /// <returns>ParseResult with a valid DbCommandTree</returns> AnalyzeCommand(AST.Node astExpr)54 internal ParseResult AnalyzeCommand(AST.Node astExpr) 55 { 56 // 57 // Ensure that the AST expression is a valid Command expression 58 // 59 AST.Command astCommandExpr = ValidateQueryCommandAst(astExpr); 60 61 // 62 // Convert namespace imports and add them to _sr.TypeResolver. 63 // 64 ConvertAndRegisterNamespaceImports(astCommandExpr.NamespaceImportList, astCommandExpr.ErrCtx, _sr); 65 66 // 67 // Convert the AST command root expression to a command tree using the appropriate converter 68 // 69 ParseResult parseResult = ConvertStatement(astCommandExpr.Statement, _sr); 70 71 Debug.Assert(parseResult != null, "ConvertStatement produced null parse result"); 72 Debug.Assert(parseResult.CommandTree != null, "ConvertStatement returned null command tree"); 73 74 return parseResult; 75 } 76 77 /// <summary> 78 /// Converts query command AST into a <see cref="DbExpression"/>. 79 /// </summary> 80 /// <param name="astExpr">ast command tree</param> 81 /// <remarks> 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> 85 /// </remarks> 86 /// <returns>DbExpression</returns> AnalyzeQueryCommand(AST.Node astExpr)87 internal DbLambda AnalyzeQueryCommand(AST.Node astExpr) 88 { 89 // 90 // Ensure that the AST expression is a valid query command expression 91 // (only a query command root expression can produce a standalone DbExpression) 92 // 93 AST.Command astQueryCommandExpr = ValidateQueryCommandAst(astExpr); 94 95 // 96 // Convert namespace imports and add them to _sr.TypeResolver. 97 // 98 ConvertAndRegisterNamespaceImports(astQueryCommandExpr.NamespaceImportList, astQueryCommandExpr.ErrCtx, _sr); 99 100 // 101 // Convert the AST of the query command root expression into a DbExpression 102 // 103 List<FunctionDefinition> functionDefs; 104 DbExpression expression = ConvertQueryStatementToDbExpression(astQueryCommandExpr.Statement, _sr, out functionDefs); 105 106 // Construct DbLambda from free variables and the expression 107 DbLambda lambda = DbExpressionBuilder.Lambda(expression, _sr.Variables.Values); 108 109 Debug.Assert(lambda != null, "AnalyzeQueryCommand returned null"); 110 111 return lambda; 112 } 113 ValidateQueryCommandAst(AST.Node astExpr)114 private AST.Command ValidateQueryCommandAst(AST.Node astExpr) 115 { 116 AST.Command astCommandExpr = astExpr as AST.Command; 117 if (null == astCommandExpr) 118 { 119 throw EntityUtil.Argument(Strings.UnknownAstCommandExpression); 120 } 121 122 if (!(astCommandExpr.Statement is AST.QueryStatement)) 123 throw EntityUtil.Argument(Strings.UnknownAstExpressionType); 124 125 return astCommandExpr; 126 } 127 128 /// <summary> 129 /// Converts namespace imports and adds them to the type resolver. 130 /// </summary> ConvertAndRegisterNamespaceImports(AST.NodeList<AST.NamespaceImport> nsImportList, ErrorContext cmdErrCtx, SemanticResolver sr)131 private static void ConvertAndRegisterNamespaceImports(AST.NodeList<AST.NamespaceImport> nsImportList, ErrorContext cmdErrCtx, SemanticResolver sr) 132 { 133 List<Tuple<string, MetadataNamespace, ErrorContext>> aliasedNamespaceImports = new List<Tuple<string, MetadataNamespace, ErrorContext>>(); 134 List<Tuple<MetadataNamespace, ErrorContext>> namespaceImports = new List<Tuple<MetadataNamespace, ErrorContext>>(); 135 136 // 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. 139 // 140 if (nsImportList != null) 141 { 142 foreach (AST.NamespaceImport namespaceImport in nsImportList) 143 { 144 string[] name = null; 145 146 AST.Identifier identifier = namespaceImport.NamespaceName as AST.Identifier; 147 if (identifier != null) 148 { 149 name = new string[] { identifier.Name }; 150 } 151 152 AST.DotExpr dotExpr = namespaceImport.NamespaceName as AST.DotExpr; 153 if (dotExpr != null && dotExpr.IsMultipartIdentifier(out name)) 154 { 155 Debug.Assert(name != null, "name != null"); 156 } 157 158 if (name == null) 159 { 160 throw EntityUtil.EntitySqlError(namespaceImport.NamespaceName.ErrCtx, Strings.InvalidMetadataMemberName); 161 } 162 163 string alias = namespaceImport.Alias != null ? namespaceImport.Alias.Name : null; 164 165 MetadataMember metadataMember = sr.ResolveMetadataMemberName(name, namespaceImport.NamespaceName.ErrCtx); 166 Debug.Assert(metadataMember != null, "metadata member name resolution must not return null"); 167 168 if (metadataMember.MetadataMemberClass == MetadataMemberClass.Namespace) 169 { 170 if (alias != null) 171 { 172 aliasedNamespaceImports.Add(Tuple.Create(alias, (MetadataNamespace)metadataMember, namespaceImport.ErrCtx)); 173 } 174 else 175 { 176 namespaceImports.Add(Tuple.Create((MetadataNamespace)metadataMember, namespaceImport.ErrCtx)); 177 } 178 } 179 else 180 { 181 throw EntityUtil.EntitySqlError(namespaceImport.NamespaceName.ErrCtx, Strings.InvalidMetadataMemberClassResolution( 182 metadataMember.Name, metadataMember.MetadataMemberClassName, MetadataNamespace.NamespaceClassName)); 183 } 184 } 185 } 186 187 // 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. 190 // 191 sr.TypeResolver.AddNamespaceImport(new MetadataNamespace(EdmConstants.EdmNamespace), nsImportList != null ? nsImportList.ErrCtx : cmdErrCtx); 192 foreach (var resolvedAliasedNamespaceImport in aliasedNamespaceImports) 193 { 194 sr.TypeResolver.AddAliasedNamespaceImport(resolvedAliasedNamespaceImport.Item1, resolvedAliasedNamespaceImport.Item2, resolvedAliasedNamespaceImport.Item3); 195 } 196 foreach (var resolvedNamespaceImport in namespaceImports) 197 { 198 sr.TypeResolver.AddNamespaceImport(resolvedNamespaceImport.Item1, resolvedNamespaceImport.Item2); 199 } 200 } 201 202 /// <summary> 203 /// Dispatches/Converts statement expressions. 204 /// </summary> 205 /// <param name="astStatement"></param> 206 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 207 /// <returns></returns> ConvertStatement(AST.Statement astStatement, SemanticResolver sr)208 private static ParseResult ConvertStatement(AST.Statement astStatement, SemanticResolver sr) 209 { 210 Debug.Assert(astStatement != null, "astStatement must not be null"); 211 212 StatementConverter statementConverter; 213 if (astStatement is AST.QueryStatement) 214 { 215 statementConverter = new StatementConverter(ConvertQueryStatementToDbCommandTree); 216 } 217 else 218 { 219 throw EntityUtil.Argument(Strings.UnknownAstExpressionType); 220 } 221 222 ParseResult converted = statementConverter(astStatement, sr); 223 224 Debug.Assert(converted != null, "statementConverter returned null"); 225 Debug.Assert(converted.CommandTree != null, "statementConverter produced null command tree"); 226 227 return converted; 228 } StatementConverter(AST.Statement astExpr, SemanticResolver sr)229 private delegate ParseResult StatementConverter(AST.Statement astExpr, SemanticResolver sr); 230 231 /// <summary> 232 /// Converts query statement AST to a <see cref="DbQueryCommandTree"/> 233 /// </summary> 234 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> ConvertQueryStatementToDbCommandTree(AST.Statement astStatement, SemanticResolver sr)235 private static ParseResult ConvertQueryStatementToDbCommandTree(AST.Statement astStatement, SemanticResolver sr) 236 { 237 Debug.Assert(astStatement != null, "astStatement must not be null"); 238 239 List<FunctionDefinition> functionDefs; 240 DbExpression converted = ConvertQueryStatementToDbExpression(astStatement, sr, out functionDefs); 241 242 Debug.Assert(converted != null, "ConvertQueryStatementToDbExpression returned null"); 243 Debug.Assert(functionDefs != null, "ConvertQueryStatementToDbExpression produced null functionDefs"); 244 245 return new ParseResult( 246 DbQueryCommandTree.FromValidExpression(sr.TypeResolver.Perspective.MetadataWorkspace, sr.TypeResolver.Perspective.TargetDataspace, converted), 247 functionDefs); 248 } 249 250 /// <summary> 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"/>. 254 /// </summary> 255 /// <param name="astStatement">The query statement</param> 256 /// <param name="sr">The <see cref="SemanticResolver"/>instance to use</param> 257 /// <returns> 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. 260 /// </returns> ConvertQueryStatementToDbExpression(AST.Statement astStatement, SemanticResolver sr, out List<FunctionDefinition> functionDefs)261 private static DbExpression ConvertQueryStatementToDbExpression(AST.Statement astStatement, SemanticResolver sr, out List<FunctionDefinition> functionDefs) 262 { 263 Debug.Assert(astStatement != null, "astStatement must not be null"); 264 265 AST.QueryStatement queryStatement = astStatement as AST.QueryStatement; 266 267 if (queryStatement == null) 268 { 269 throw EntityUtil.Argument(Strings.UnknownAstExpressionType); 270 } 271 272 // 273 // Convert query inline definitions and create parse result. 274 // Converted inline definitions are also added to the semantic resolver. 275 // 276 functionDefs = ConvertInlineFunctionDefinitions(queryStatement.FunctionDefList, sr); 277 278 // 279 // Convert top level expression 280 // 281 DbExpression converted = ConvertValueExpressionAllowUntypedNulls(queryStatement.Expr, sr); 282 if (converted == null) 283 { 284 // 285 // Ensure converted expression is not untyped null. 286 // Use error context of the top-level expression. 287 // 288 throw EntityUtil.EntitySqlError(queryStatement.Expr.ErrCtx, Strings.ResultingExpressionTypeCannotBeNull); 289 } 290 291 // 292 // Handle the "inline" projection case 293 // 294 if (converted is DbScanExpression) 295 { 296 DbExpressionBinding source = converted.BindAs(sr.GenerateInternalName("extent")); 297 298 converted = source.Project(source.Variable); 299 } 300 301 // 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. 306 // 307 if (sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.NormalMode) 308 { 309 ValidateQueryResultType(converted.ResultType, queryStatement.Expr.ErrCtx); 310 } 311 312 Debug.Assert(null != converted, "null != converted"); 313 314 return converted; 315 } 316 317 /// <summary> 318 /// Ensures that the result of a query expression is valid. 319 /// </summary> ValidateQueryResultType(TypeUsage resultType, ErrorContext errCtx)320 private static void ValidateQueryResultType(TypeUsage resultType, ErrorContext errCtx) 321 { 322 if (Helper.IsCollectionType(resultType.EdmType)) 323 { 324 ValidateQueryResultType(((CollectionType)resultType.EdmType).TypeUsage, errCtx); 325 } 326 else if (Helper.IsRowType(resultType.EdmType)) 327 { 328 foreach (EdmProperty property in ((RowType)resultType.EdmType).Properties) 329 { 330 ValidateQueryResultType(property.TypeUsage, errCtx); 331 } 332 } 333 else if (Helper.IsAssociationType(resultType.EdmType)) 334 { 335 throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidQueryResultType(resultType.EdmType.FullName)); 336 } 337 } 338 339 340 /// <summary> 341 /// Converts query inline function defintions. Returns empty list in case of no definitions. 342 /// </summary> ConvertInlineFunctionDefinitions(AST.NodeList<AST.FunctionDefinition> functionDefList, SemanticResolver sr)343 private static List<FunctionDefinition> ConvertInlineFunctionDefinitions(AST.NodeList<AST.FunctionDefinition> functionDefList, SemanticResolver sr) 344 { 345 List<FunctionDefinition> functionDefinitions = new List<FunctionDefinition>(); 346 347 if (functionDefList != null) 348 { 349 // 350 // Process inline function signatures, declare functions in the type resolver. 351 // 352 List<InlineFunctionInfo> inlineFunctionInfos = new List<InlineFunctionInfo>(); 353 foreach (AST.FunctionDefinition functionDefAst in functionDefList) 354 { 355 // 356 // Get and validate function name. 357 // 358 string name = functionDefAst.Name; 359 Debug.Assert(!String.IsNullOrEmpty(name), "function name must not be null or empty"); 360 361 // 362 // Process function parameters 363 // 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 366 367 // 368 // Register new function in the type resolver. 369 // 370 InlineFunctionInfo functionInfo = new InlineFunctionInfoImpl(functionDefAst, parameters); 371 inlineFunctionInfos.Add(functionInfo); 372 sr.TypeResolver.DeclareInlineFunction(name, functionInfo); 373 } 374 Debug.Assert(functionDefList.Count == inlineFunctionInfos.Count); 375 376 // 377 // Convert function defintions. 378 // 379 foreach (InlineFunctionInfo functionInfo in inlineFunctionInfos) 380 { 381 functionDefinitions.Add(new FunctionDefinition( 382 functionInfo.FunctionDefAst.Name, 383 functionInfo.GetLambda(sr), 384 functionInfo.FunctionDefAst.StartPosition, 385 functionInfo.FunctionDefAst.EndPosition)); 386 } 387 } 388 389 return functionDefinitions; 390 } 391 ConvertInlineFunctionParameterDefs(AST.NodeList<AST.PropDefinition> parameterDefs, SemanticResolver sr)392 private static List<DbVariableReferenceExpression> ConvertInlineFunctionParameterDefs(AST.NodeList<AST.PropDefinition> parameterDefs, SemanticResolver sr) 393 { 394 List<DbVariableReferenceExpression> paramList = new List<DbVariableReferenceExpression>(); 395 if (parameterDefs != null) 396 { 397 foreach (AST.PropDefinition paramDef in parameterDefs) 398 { 399 string name = paramDef.Name.Name; 400 401 // 402 // Validate param name 403 // 404 if (paramList.Exists((DbVariableReferenceExpression arg) => 405 sr.NameComparer.Compare(arg.VariableName, name) == 0)) 406 { 407 throw EntityUtil.EntitySqlError( 408 paramDef.ErrCtx, 409 Strings.MultipleDefinitionsOfParameter(name)); 410 } 411 412 // 413 // Convert parameter type 414 // 415 TypeUsage typeUsage = ConvertTypeDefinition(paramDef.Type, sr); 416 Debug.Assert(typeUsage != null, "typeUsage must not be null"); 417 418 // 419 // Create function parameter ref expression 420 // 421 DbVariableReferenceExpression paramRefExpr = new DbVariableReferenceExpression(typeUsage, name); 422 paramList.Add(paramRefExpr); 423 } 424 } 425 return paramList; 426 } 427 428 private sealed class InlineFunctionInfoImpl : InlineFunctionInfo 429 { 430 private DbLambda _convertedDefinition = null; 431 private bool _convertingDefinition = false; 432 InlineFunctionInfoImpl(AST.FunctionDefinition functionDef, List<DbVariableReferenceExpression> parameters)433 internal InlineFunctionInfoImpl(AST.FunctionDefinition functionDef, List<DbVariableReferenceExpression> parameters) 434 : base(functionDef, parameters) 435 { 436 } 437 GetLambda(SemanticResolver sr)438 internal override DbLambda GetLambda(SemanticResolver sr) 439 { 440 if (_convertedDefinition == null) 441 { 442 // 443 // Check for recursive definitions. 444 // 445 if (_convertingDefinition) 446 { 447 throw EntityUtil.EntitySqlError(FunctionDefAst.ErrCtx, Strings.Cqt_UDF_FunctionDefinitionWithCircularReference(FunctionDefAst.Name)); 448 } 449 450 // 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. 454 // 455 SemanticResolver srSandbox = sr.CloneForInlineFunctionConversion(); 456 457 _convertingDefinition = true; 458 _convertedDefinition = SemanticAnalyzer.ConvertInlineFunctionDefinition(this, srSandbox); 459 _convertingDefinition = false; 460 } 461 return _convertedDefinition; 462 } 463 } 464 ConvertInlineFunctionDefinition(InlineFunctionInfo functionInfo, SemanticResolver sr)465 private static DbLambda ConvertInlineFunctionDefinition(InlineFunctionInfo functionInfo, SemanticResolver sr) 466 { 467 // 468 // Push function definition scope. 469 // 470 sr.EnterScope(); 471 472 // 473 // Add function parameters to the scope. 474 // 475 functionInfo.Parameters.ForEach(p => sr.CurrentScope.Add(p.VariableName, new FreeVariableScopeEntry(p))); 476 477 // 478 // Convert function body expression 479 // 480 DbExpression body = ConvertValueExpression(functionInfo.FunctionDefAst.Body, sr); 481 482 // 483 // Pop function definition scope 484 // 485 sr.LeaveScope(); 486 487 // 488 // Create and return lambda representing the function body. 489 // 490 return DbExpressionBuilder.Lambda(body, functionInfo.Parameters); 491 } 492 493 /// <summary> 494 /// Converts general expressions (AST.Node) 495 /// </summary> Convert(AST.Node astExpr, SemanticResolver sr)496 private static ExpressionResolution Convert(AST.Node astExpr, SemanticResolver sr) 497 { 498 AstExprConverter converter = _astExprConverters[astExpr.GetType()]; 499 if (converter == null) 500 { 501 throw EntityUtil.EntitySqlError(Strings.UnknownAstExpressionType); 502 } 503 return converter(astExpr, sr); 504 } 505 506 /// <summary> 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. 511 /// </summary> ConvertValueExpression(AST.Node astExpr, SemanticResolver sr)512 private static DbExpression ConvertValueExpression(AST.Node astExpr, SemanticResolver sr) 513 { 514 var expr = ConvertValueExpressionAllowUntypedNulls(astExpr, sr); 515 if (expr == null) 516 { 517 throw EntityUtil.EntitySqlError(astExpr.ErrCtx, Strings.ExpressionCannotBeNull); 518 } 519 return expr; 520 } 521 522 /// <summary> 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. 527 /// </summary> ConvertValueExpressionAllowUntypedNulls(AST.Node astExpr, SemanticResolver sr)528 private static DbExpression ConvertValueExpressionAllowUntypedNulls(AST.Node astExpr, SemanticResolver sr) 529 { 530 ExpressionResolution resolution = Convert(astExpr, sr); 531 if (resolution.ExpressionClass == ExpressionResolutionClass.Value) 532 { 533 return ((ValueExpression)resolution).Value; 534 } 535 else if (resolution.ExpressionClass == ExpressionResolutionClass.MetadataMember) 536 { 537 var metadataMember = (MetadataMember)resolution; 538 if (metadataMember.MetadataMemberClass == MetadataMemberClass.EnumMember) 539 { 540 var enumMember = (MetadataEnumMember)metadataMember; 541 return enumMember.EnumType.Constant(enumMember.EnumMember.Value); 542 } 543 } 544 545 // 546 // The resolution is not a value and can not be converted to a value: report an error. 547 // 548 549 string errorMessage = Strings.InvalidExpressionResolutionClass(resolution.ExpressionClassName, ValueExpression.ValueClassName); 550 551 AST.Identifier identifier = astExpr as AST.Identifier; 552 if (identifier != null) 553 { 554 errorMessage = Strings.CouldNotResolveIdentifier(identifier.Name); 555 } 556 557 AST.DotExpr dotExpr = astExpr as AST.DotExpr; 558 string[] names; 559 if (dotExpr != null && dotExpr.IsMultipartIdentifier(out names)) 560 { 561 errorMessage = Strings.CouldNotResolveIdentifier(TypeResolver.GetFullName(names)); 562 } 563 564 throw EntityUtil.EntitySqlError(astExpr.ErrCtx, errorMessage); 565 } 566 567 /// <summary> 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. 570 /// </summary> ConvertValueExpressionsWithUntypedNulls(AST.Node leftAst, AST.Node rightAst, ErrorContext errCtx, Func<string> formatMessage, SemanticResolver sr)571 private static Pair<DbExpression, DbExpression> ConvertValueExpressionsWithUntypedNulls(AST.Node leftAst, 572 AST.Node rightAst, 573 ErrorContext errCtx, 574 Func<string> formatMessage, 575 SemanticResolver sr) 576 { 577 var leftExpr = leftAst != null ? ConvertValueExpressionAllowUntypedNulls(leftAst, sr) : null; 578 var rightExpr = rightAst != null ? ConvertValueExpressionAllowUntypedNulls(rightAst, sr) : null; 579 580 if (leftExpr == null) 581 { 582 if (rightExpr == null) 583 { 584 throw EntityUtil.EntitySqlError(errCtx, formatMessage()); 585 } 586 else 587 { 588 leftExpr = DbExpressionBuilder.Null(rightExpr.ResultType); 589 } 590 } 591 else if (rightExpr == null) 592 { 593 rightExpr = DbExpressionBuilder.Null(leftExpr.ResultType); 594 } 595 596 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr); 597 } 598 599 /// <summary> 600 /// Converts literal expression (AST.Literal) 601 /// </summary> ConvertLiteral(AST.Node expr, SemanticResolver sr)602 private static ExpressionResolution ConvertLiteral(AST.Node expr, SemanticResolver sr) 603 { 604 AST.Literal literal = (AST.Literal)expr; 605 606 if (literal.IsNullLiteral) 607 { 608 // 609 // If it is literal null, return the untyped null: the type will be inferred depending on the specific expression in which it participates. 610 // 611 return new ValueExpression(null); 612 } 613 else 614 { 615 return new ValueExpression(DbExpressionBuilder.Constant(GetLiteralTypeUsage(literal), literal.Value)); 616 } 617 } 618 GetLiteralTypeUsage(AST.Literal literal)619 private static TypeUsage GetLiteralTypeUsage(AST.Literal literal) 620 { 621 PrimitiveType primitiveType = null; 622 623 if (!ClrProviderManifest.Instance.TryGetPrimitiveType(literal.Type, out primitiveType)) 624 { 625 throw EntityUtil.EntitySqlError(literal.ErrCtx, Strings.LiteralTypeNotFoundInMetadata(literal.OriginalValue)); 626 } 627 TypeUsage literalTypeUsage = TypeHelpers.GetLiteralTypeUsage(primitiveType.PrimitiveTypeKind, literal.IsUnicodeString); 628 629 return literalTypeUsage; 630 } 631 632 /// <summary> 633 /// Converts identifier expression (Identifier) 634 /// </summary> ConvertIdentifier(AST.Node expr, SemanticResolver sr)635 private static ExpressionResolution ConvertIdentifier(AST.Node expr, SemanticResolver sr) 636 { 637 return ConvertIdentifier(((AST.Identifier)expr), false /* leftHandSideOfMemberAccess */, sr); 638 } 639 ConvertIdentifier(AST.Identifier identifier, bool leftHandSideOfMemberAccess, SemanticResolver sr)640 private static ExpressionResolution ConvertIdentifier(AST.Identifier identifier, bool leftHandSideOfMemberAccess, SemanticResolver sr) 641 { 642 return sr.ResolveSimpleName(((AST.Identifier)identifier).Name, leftHandSideOfMemberAccess, identifier.ErrCtx); 643 } 644 645 /// <summary> 646 /// Converts member access expression (AST.DotExpr) 647 /// </summary> ConvertDotExpr(AST.Node expr, SemanticResolver sr)648 private static ExpressionResolution ConvertDotExpr(AST.Node expr, SemanticResolver sr) 649 { 650 AST.DotExpr dotExpr = (AST.DotExpr)expr; 651 652 ValueExpression groupKeyResolution; 653 if (sr.TryResolveDotExprAsGroupKeyAlternativeName(dotExpr, out groupKeyResolution)) 654 { 655 return groupKeyResolution; 656 } 657 658 // 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. 662 // 663 ExpressionResolution leftResolution; 664 AST.Identifier leftIdentifier = dotExpr.Left as AST.Identifier; 665 if (leftIdentifier != null) 666 { 667 leftResolution = ConvertIdentifier(leftIdentifier, true /* leftHandSideOfMemberAccess */, sr); 668 } 669 else 670 { 671 leftResolution = Convert(dotExpr.Left, sr); 672 } 673 674 switch (leftResolution.ExpressionClass) 675 { 676 case ExpressionResolutionClass.Value: 677 return sr.ResolvePropertyAccess(((ValueExpression)leftResolution).Value, dotExpr.Identifier.Name, dotExpr.Identifier.ErrCtx); 678 679 case ExpressionResolutionClass.EntityContainer: 680 return sr.ResolveEntityContainerMemberAccess(((EntityContainerExpression)leftResolution).EntityContainer, dotExpr.Identifier.Name, dotExpr.Identifier.ErrCtx); 681 682 case ExpressionResolutionClass.MetadataMember: 683 return sr.ResolveMetadataMemberAccess((MetadataMember)leftResolution, dotExpr.Identifier.Name, dotExpr.Identifier.ErrCtx); 684 685 default: 686 throw EntityUtil.EntitySqlError(dotExpr.Left.ErrCtx, Strings.UnknownExpressionResolutionClass(leftResolution.ExpressionClass)); 687 } 688 } 689 690 /// <summary> 691 /// Converts paren expression (AST.ParenExpr) 692 /// </summary> ConvertParenExpr(AST.Node astExpr, SemanticResolver sr)693 private static ExpressionResolution ConvertParenExpr(AST.Node astExpr, SemanticResolver sr) 694 { 695 AST.Node innerExpr = ((AST.ParenExpr)astExpr).Expr; 696 697 // 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. 701 // 702 DbExpression converted = ConvertValueExpressionAllowUntypedNulls(innerExpr, sr); 703 return new ValueExpression(converted); 704 } 705 706 /// <summary> 707 /// Converts GROUPPARTITION expression (AST.GroupPartitionExpr). 708 /// </summary> ConvertGroupPartitionExpr(AST.Node astExpr, SemanticResolver sr)709 private static ExpressionResolution ConvertGroupPartitionExpr(AST.Node astExpr, SemanticResolver sr) 710 { 711 AST.GroupPartitionExpr groupAggregateExpr = (AST.GroupPartitionExpr)astExpr; 712 713 DbExpression converted = null; 714 715 // 716 // If ast node was annotated in a previous pass, means it contains a ready-to-use expression. 717 // 718 if (!TryConvertAsResolvedGroupAggregate(groupAggregateExpr, sr, out converted)) 719 { 720 // 721 // GROUPPARTITION is allowed only in the context of a group operation provided by a query expression (SELECT ...). 722 // 723 if (!sr.IsInAnyGroupScope()) 724 { 725 throw EntityUtil.EntitySqlError(astExpr.ErrCtx, Strings.GroupPartitionOutOfContext); 726 } 727 728 // 729 // Process aggregate argument. 730 // 731 DbExpression arg; 732 GroupPartitionInfo aggregateInfo; 733 using (sr.EnterGroupPartition(groupAggregateExpr, groupAggregateExpr.ErrCtx, out aggregateInfo)) 734 { 735 // 736 // Convert aggregate argument. 737 // 738 arg = ConvertValueExpressionAllowUntypedNulls(groupAggregateExpr.ArgExpr, sr); 739 } 740 741 // 742 // Ensure converted GROUPPARTITION argument expression is not untyped null. 743 // 744 if (arg == null) 745 { 746 throw EntityUtil.EntitySqlError(groupAggregateExpr.ArgExpr.ErrCtx, Strings.ResultingExpressionTypeCannotBeNull); 747 } 748 749 // 750 // Project the argument off the DbGroupAggregate binding. 751 // 752 DbExpression definition = aggregateInfo.EvaluatingScopeRegion.GroupAggregateBinding.Project(arg); 753 754 if (groupAggregateExpr.DistinctKind == AST.DistinctKind.Distinct) 755 { 756 ValidateDistinctProjection(definition.ResultType, groupAggregateExpr.ArgExpr.ErrCtx, null); 757 definition = definition.Distinct(); 758 } 759 760 // 761 // Add aggregate to aggreate list. 762 // 763 aggregateInfo.AttachToAstNode(sr.GenerateInternalName("groupPartition"), definition); 764 aggregateInfo.EvaluatingScopeRegion.GroupAggregateInfos.Add(aggregateInfo); 765 766 // 767 // Return stub expression with same type as the group aggregate. 768 // 769 converted = aggregateInfo.AggregateStubExpression; 770 } 771 772 Debug.Assert(null != converted, "null != converted"); 773 774 return new ValueExpression(converted); 775 } 776 777 #region ConvertMethodExpr implementation 778 /// <summary> 779 /// Converts invocation expression (AST.MethodExpr) 780 /// </summary> ConvertMethodExpr(AST.Node expr, SemanticResolver sr)781 private static ExpressionResolution ConvertMethodExpr(AST.Node expr, SemanticResolver sr) 782 { 783 return ConvertMethodExpr((AST.MethodExpr)expr, true /* includeInlineFunctions */, sr); 784 } 785 ConvertMethodExpr(AST.MethodExpr methodExpr, bool includeInlineFunctions, SemanticResolver sr)786 private static ExpressionResolution ConvertMethodExpr(AST.MethodExpr methodExpr, bool includeInlineFunctions, SemanticResolver sr) 787 { 788 // 789 // Resolve methodExpr.Expr 790 // 791 ExpressionResolution leftResolution; 792 using (sr.TypeResolver.EnterFunctionNameResolution(includeInlineFunctions)) 793 { 794 AST.Identifier simpleFunctionName = methodExpr.Expr as AST.Identifier; 795 if (simpleFunctionName != null) 796 { 797 leftResolution = sr.ResolveSimpleFunctionName(simpleFunctionName.Name, simpleFunctionName.ErrCtx); 798 } 799 else 800 { 801 // 802 // Convert methodExpr.Expr optionally entering special resolution modes. See ConvertMethodExpr_TryEnter methods for more info. 803 // 804 AST.DotExpr dotExpr = methodExpr.Expr as AST.DotExpr; 805 using (ConvertMethodExpr_TryEnterIgnoreEntityContainerNameResolution(dotExpr, sr)) 806 { 807 using (ConvertMethodExpr_TryEnterV1ViewGenBackwardCompatibilityResolution(dotExpr, sr)) 808 { 809 leftResolution = Convert(methodExpr.Expr, sr); 810 } 811 } 812 } 813 } 814 815 if (leftResolution.ExpressionClass == ExpressionResolutionClass.MetadataMember) 816 { 817 MetadataMember metadataMember = (MetadataMember)leftResolution; 818 819 // 820 // Try converting as inline function call. If it fails, continue and try to convert as a model-defined function/function import call. 821 // 822 ValueExpression inlineFunctionCall; 823 if (metadataMember.MetadataMemberClass == MetadataMemberClass.InlineFunctionGroup) 824 { 825 Debug.Assert(includeInlineFunctions, "includeInlineFunctions must be true, otherwise recursion does not stop"); 826 827 methodExpr.ErrCtx.ErrorContextInfo = Strings.CtxFunction(metadataMember.Name); 828 methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; 829 if (TryConvertInlineFunctionCall((InlineFunctionGroup)metadataMember, methodExpr, sr, out inlineFunctionCall)) 830 { 831 return inlineFunctionCall; 832 } 833 else 834 { 835 // Make another try ignoring inline functions. 836 return ConvertMethodExpr(methodExpr, false /* includeInlineFunctions */, sr); 837 } 838 } 839 840 switch (metadataMember.MetadataMemberClass) 841 { 842 case MetadataMemberClass.Type: 843 methodExpr.ErrCtx.ErrorContextInfo = Strings.CtxTypeCtor(metadataMember.Name); 844 methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; 845 return ConvertTypeConstructorCall((MetadataType)metadataMember, methodExpr, sr); 846 847 case MetadataMemberClass.FunctionGroup: 848 methodExpr.ErrCtx.ErrorContextInfo = Strings.CtxFunction(metadataMember.Name); 849 methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; 850 return ConvertModelFunctionCall((MetadataFunctionGroup)metadataMember, methodExpr, sr); 851 852 default: 853 throw EntityUtil.EntitySqlError(methodExpr.Expr.ErrCtx, Strings.CannotResolveNameToTypeOrFunction(metadataMember.Name)); 854 } 855 } 856 else 857 { 858 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.MethodInvocationNotSupported); 859 } 860 } 861 862 /// <summary> 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(...)". 866 /// </summary> ConvertMethodExpr_TryEnterIgnoreEntityContainerNameResolution(AST.DotExpr leftExpr, SemanticResolver sr)867 private static IDisposable ConvertMethodExpr_TryEnterIgnoreEntityContainerNameResolution(AST.DotExpr leftExpr, SemanticResolver sr) 868 { 869 return leftExpr != null && leftExpr.Left is AST.Identifier ? sr.EnterIgnoreEntityContainerNameResolution() : null; 870 } 871 872 /// <summary> 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 ... 881 /// </summary> ConvertMethodExpr_TryEnterV1ViewGenBackwardCompatibilityResolution(AST.DotExpr leftExpr, SemanticResolver sr)882 private static IDisposable ConvertMethodExpr_TryEnterV1ViewGenBackwardCompatibilityResolution(AST.DotExpr leftExpr, SemanticResolver sr) 883 { 884 if (leftExpr != null && leftExpr.Left is AST.Identifier && 885 (sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode || 886 sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.UserViewGenerationMode)) 887 { 888 var mappingCollection = 889 sr.TypeResolver.Perspective.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace) as StorageMappingItemCollection; 890 891 Debug.Assert(mappingCollection != null, "mappingCollection != null"); 892 893 if (mappingCollection.MappingVersion < XmlConstants.EdmVersionForV2) 894 { 895 return sr.TypeResolver.EnterBackwardCompatibilityResolution(); 896 } 897 } 898 return null; 899 } 900 901 /// <summary> 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. 906 /// </summary> TryConvertInlineFunctionCall( InlineFunctionGroup inlineFunctionGroup, AST.MethodExpr methodExpr, SemanticResolver sr, out ValueExpression inlineFunctionCall)907 private static bool TryConvertInlineFunctionCall( 908 InlineFunctionGroup inlineFunctionGroup, 909 AST.MethodExpr methodExpr, 910 SemanticResolver sr, 911 out ValueExpression inlineFunctionCall) 912 { 913 inlineFunctionCall = null; 914 915 // 916 // An inline function can't be a group aggregate, so if DistinctKind is specified then it is not an inline function call. 917 // 918 if (methodExpr.DistinctKind != AST.DistinctKind.None) 919 { 920 return false; 921 } 922 923 // 924 // Convert function arguments. 925 // 926 List<TypeUsage> argTypes; 927 var args = ConvertFunctionArguments(methodExpr.Args, sr, out argTypes); 928 929 // 930 // Find function overload match for the given argument types. 931 // 932 bool isAmbiguous = false; 933 InlineFunctionInfo overload = SemanticResolver.ResolveFunctionOverloads( 934 inlineFunctionGroup.FunctionMetadata, 935 argTypes, 936 (lambdaOverload) => lambdaOverload.Parameters, 937 (varRef) => varRef.ResultType, 938 (varRef) => ParameterMode.In, 939 false /* isGroupAggregateFunction */, 940 out isAmbiguous); 941 942 // 943 // If there is more than one overload that matches the given arguments, throw. 944 // 945 if (isAmbiguous) 946 { 947 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments); 948 } 949 950 // 951 // If null, means no overload matched. 952 // 953 if (overload == null) 954 { 955 return false; 956 } 957 958 // 959 // Convert untyped NULLs in arguments to typed nulls inferred from formals. 960 // 961 ConvertUntypedNullsInArguments(args, overload.Parameters, (formal) => formal.ResultType); 962 963 inlineFunctionCall = new ValueExpression(DbExpressionBuilder.Invoke(overload.GetLambda(sr), args)); 964 return true; 965 } 966 ConvertTypeConstructorCall(MetadataType metadataType, AST.MethodExpr methodExpr, SemanticResolver sr)967 private static ValueExpression ConvertTypeConstructorCall(MetadataType metadataType, AST.MethodExpr methodExpr, SemanticResolver sr) 968 { 969 // 970 // Ensure type has a contructor. 971 // 972 if (!TypeSemantics.IsComplexType(metadataType.TypeUsage) && 973 !TypeSemantics.IsEntityType(metadataType.TypeUsage) && 974 !TypeSemantics.IsRelationshipType(metadataType.TypeUsage)) 975 { 976 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidCtorUseOnType(metadataType.TypeUsage.EdmType.FullName)); 977 } 978 979 // 980 // Abstract types cannot be instantiated. 981 // 982 if (metadataType.TypeUsage.EdmType.Abstract) 983 { 984 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.CannotInstantiateAbstractType(metadataType.TypeUsage.EdmType.FullName)); 985 } 986 987 // 988 // DistinctKind must not be specified on a type constructor. 989 // 990 if (methodExpr.DistinctKind != AST.DistinctKind.None) 991 { 992 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInCtor); 993 } 994 995 // 996 // Convert relationships if present. 997 // 998 List<DbRelatedEntityRef> relshipExprList = null; 999 if (methodExpr.HasRelationships) 1000 { 1001 if (!(sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode || 1002 sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.UserViewGenerationMode)) 1003 { 1004 throw EntityUtil.EntitySqlError(methodExpr.Relationships.ErrCtx, Strings.InvalidModeForWithRelationshipClause); 1005 } 1006 1007 EntityType driverEntityType = metadataType.TypeUsage.EdmType as EntityType; 1008 if (driverEntityType == null) 1009 { 1010 throw EntityUtil.EntitySqlError(methodExpr.Relationships.ErrCtx, Strings.InvalidTypeForWithRelationshipClause); 1011 } 1012 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++) 1016 { 1017 AST.RelshipNavigationExpr relshipExpr = methodExpr.Relationships[i]; 1018 1019 DbRelatedEntityRef relshipTarget = ConvertRelatedEntityRef(relshipExpr, driverEntityType, sr); 1020 1021 string targetEndId = String.Join(":", new String[] { relshipTarget.TargetEnd.DeclaringType.Identity, relshipTarget.TargetEnd.Identity }); 1022 if (targetEnds.Contains(targetEndId)) 1023 { 1024 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipTargetMustBeUnique(targetEndId)); 1025 } 1026 1027 targetEnds.Add(targetEndId); 1028 1029 relshipExprList.Add(relshipTarget); 1030 } 1031 } 1032 1033 List<TypeUsage> argTypes; 1034 return new ValueExpression(CreateConstructorCallExpression(methodExpr, 1035 metadataType.TypeUsage, 1036 ConvertFunctionArguments(methodExpr.Args, sr, out argTypes), 1037 relshipExprList, 1038 sr)); 1039 } 1040 ConvertModelFunctionCall(MetadataFunctionGroup metadataFunctionGroup, AST.MethodExpr methodExpr, SemanticResolver sr)1041 private static ValueExpression ConvertModelFunctionCall(MetadataFunctionGroup metadataFunctionGroup, AST.MethodExpr methodExpr, SemanticResolver sr) 1042 { 1043 if (metadataFunctionGroup.FunctionMetadata.Any(f => !f.IsComposableAttribute)) 1044 { 1045 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.CannotCallNoncomposableFunction(metadataFunctionGroup.Name)); 1046 } 1047 1048 // 1049 // Decide if it is an ordinary function or group aggregate 1050 // 1051 if (TypeSemantics.IsAggregateFunction(metadataFunctionGroup.FunctionMetadata[0]) && sr.IsInAnyGroupScope()) 1052 { 1053 // 1054 // If it is an aggreagate function inside a group scope, dispatch to the expensive ConvertAggregateFunctionInGroupScope()... 1055 // 1056 return new ValueExpression(ConvertAggregateFunctionInGroupScope(methodExpr, metadataFunctionGroup, sr)); 1057 } 1058 else 1059 { 1060 // 1061 // Otherwise, it is just an ordinary function call (including aggregate functions outside of a group scope) 1062 // 1063 return new ValueExpression(CreateModelFunctionCallExpression(methodExpr, metadataFunctionGroup, sr)); 1064 } 1065 } 1066 1067 #region ConvertAggregateFunctionInGroupScope implementation 1068 /// <summary> 1069 /// Converts group aggregates. 1070 /// </summary> 1071 /// <remarks> 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 1074 /// to the scope 1075 /// Phase 2 - if ast node was annotated, just extract the precomputed expression from the scope. 1076 /// </remarks> ConvertAggregateFunctionInGroupScope(AST.MethodExpr methodExpr, MetadataFunctionGroup metadataFunctionGroup, SemanticResolver sr)1077 private static DbExpression ConvertAggregateFunctionInGroupScope(AST.MethodExpr methodExpr, MetadataFunctionGroup metadataFunctionGroup, SemanticResolver sr) 1078 { 1079 DbExpression converted = null; 1080 1081 // 1082 // First, check if methodExpr is already resolved as an aggregate... 1083 // 1084 if (TryConvertAsResolvedGroupAggregate(methodExpr, sr, out converted)) 1085 { 1086 return converted; 1087 } 1088 1089 // 1090 // ... then, try to convert as a collection function. 1091 // 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. 1107 // 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. 1112 // 1113 ScopeRegion savedInnermostReferencedScopeRegion = sr.CurrentGroupAggregateInfo != null ? sr.CurrentGroupAggregateInfo.InnermostReferencedScopeRegion : null; 1114 List<TypeUsage> argTypes; 1115 if (TryConvertAsCollectionFunction(methodExpr, metadataFunctionGroup, sr, out argTypes, out converted)) 1116 { 1117 return converted; 1118 } 1119 else if (sr.CurrentGroupAggregateInfo != null) 1120 { 1121 sr.CurrentGroupAggregateInfo.InnermostReferencedScopeRegion = savedInnermostReferencedScopeRegion; 1122 } 1123 Debug.Assert(argTypes != null, "argTypes != null"); 1124 1125 // 1126 // Finally, try to convert as a function group aggregate. 1127 // 1128 if (TryConvertAsFunctionAggregate(methodExpr, metadataFunctionGroup, argTypes, sr, out converted)) 1129 { 1130 return converted; 1131 } 1132 1133 // 1134 // If we reach this point, means the resolution failed. 1135 // 1136 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.FailedToResolveAggregateFunction(metadataFunctionGroup.Name)); 1137 } 1138 1139 /// <summary> 1140 /// Try to convert as pre resolved group aggregate. 1141 /// </summary> TryConvertAsResolvedGroupAggregate(AST.GroupAggregateExpr groupAggregateExpr, SemanticResolver sr, out DbExpression converted)1142 private static bool TryConvertAsResolvedGroupAggregate(AST.GroupAggregateExpr groupAggregateExpr, SemanticResolver sr, out DbExpression converted) 1143 { 1144 converted = null; 1145 1146 // 1147 // If ast node was annotated in a previous pass, means it contains a ready-to-use expression, 1148 // otherwise exit. 1149 // 1150 if (groupAggregateExpr.AggregateInfo == null) 1151 { 1152 return false; 1153 } 1154 1155 // 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. 1162 // 1163 groupAggregateExpr.AggregateInfo.SetContainingAggregate(sr.CurrentGroupAggregateInfo); 1164 1165 if (!sr.TryResolveInternalAggregateName(groupAggregateExpr.AggregateInfo.AggregateName, groupAggregateExpr.AggregateInfo.ErrCtx, out converted)) 1166 { 1167 Debug.Assert(groupAggregateExpr.AggregateInfo.AggregateStubExpression != null, "Resolved aggregate stub expression must not be null."); 1168 converted = groupAggregateExpr.AggregateInfo.AggregateStubExpression; 1169 } 1170 1171 Debug.Assert(converted != null, "converted != null"); 1172 1173 return true; 1174 } 1175 1176 /// <summary> 1177 /// Try convert method expr in a group scope as a collection aggregate 1178 /// </summary> 1179 /// <param name="argTypes">argTypes are returned regardless of the function result</param> TryConvertAsCollectionFunction(AST.MethodExpr methodExpr, MetadataFunctionGroup metadataFunctionGroup, SemanticResolver sr, out List<TypeUsage> argTypes, out DbExpression converted)1180 private static bool TryConvertAsCollectionFunction(AST.MethodExpr methodExpr, 1181 MetadataFunctionGroup metadataFunctionGroup, 1182 SemanticResolver sr, 1183 out List<TypeUsage> argTypes, 1184 out DbExpression converted) 1185 { 1186 // 1187 // Convert aggregate arguments. 1188 // 1189 var args = ConvertFunctionArguments(methodExpr.Args, sr, out argTypes); 1190 1191 // 1192 // Try to see if there is an overload match. 1193 // 1194 bool isAmbiguous = false; 1195 EdmFunction functionType = SemanticResolver.ResolveFunctionOverloads( 1196 metadataFunctionGroup.FunctionMetadata, 1197 argTypes, 1198 false /* isGroupAggregateFunction */, 1199 out isAmbiguous); 1200 1201 // 1202 // If there is more then one overload that matches given arguments, throw. 1203 // 1204 if (isAmbiguous) 1205 { 1206 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments); 1207 } 1208 1209 // 1210 // If not null, means a match was found as a collection aggregate (ordinary function). 1211 // 1212 if (functionType != null) 1213 { 1214 // 1215 // Convert untyped NULLs in arguments to typed nulls inferred from function parameters. 1216 // 1217 ConvertUntypedNullsInArguments(args, functionType.Parameters, (parameter) => parameter.TypeUsage); 1218 converted = functionType.Invoke(args); 1219 return true; 1220 } 1221 else 1222 { 1223 converted = null; 1224 return false; 1225 } 1226 } 1227 TryConvertAsFunctionAggregate(AST.MethodExpr methodExpr, MetadataFunctionGroup metadataFunctionGroup, List<TypeUsage> argTypes, SemanticResolver sr, out DbExpression converted)1228 private static bool TryConvertAsFunctionAggregate(AST.MethodExpr methodExpr, 1229 MetadataFunctionGroup metadataFunctionGroup, 1230 List<TypeUsage> argTypes, 1231 SemanticResolver sr, 1232 out DbExpression converted) 1233 { 1234 Debug.Assert(argTypes != null, "argTypes != null"); 1235 1236 converted = null; 1237 1238 // 1239 // Try to find an overload match as group aggregate 1240 // 1241 bool isAmbiguous = false; 1242 EdmFunction functionType = SemanticResolver.ResolveFunctionOverloads( 1243 metadataFunctionGroup.FunctionMetadata, 1244 argTypes, 1245 true /* isGroupAggregateFunction */, 1246 out isAmbiguous); 1247 1248 // 1249 // If there is more then one overload that matches given arguments, throw. 1250 // 1251 if (isAmbiguous) 1252 { 1253 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments); 1254 } 1255 1256 // 1257 // If it still null, then there is no overload as a group aggregate function. 1258 // 1259 if (null == functionType) 1260 { 1261 CqlErrorHelper.ReportFunctionOverloadError(methodExpr, metadataFunctionGroup.FunctionMetadata[0], argTypes); 1262 } 1263 // 1264 // Process aggregate argument. 1265 // 1266 List<DbExpression> args; 1267 FunctionAggregateInfo aggregateInfo; 1268 using (sr.EnterFunctionAggregate(methodExpr, methodExpr.ErrCtx, out aggregateInfo)) 1269 { 1270 List<TypeUsage> aggArgTypes; 1271 args = ConvertFunctionArguments(methodExpr.Args, sr, out aggArgTypes); 1272 // Sanity check - argument types must agree. 1273 Debug.Assert( 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"); 1277 } 1278 1279 // 1280 // Aggregate functions can have only one argument and of collection type 1281 // 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"); 1284 1285 // 1286 // Convert untyped NULLs in arguments to typed nulls inferred from function parameters. 1287 // 1288 ConvertUntypedNullsInArguments(args, functionType.Parameters, (parameter) => TypeHelpers.GetElementTypeUsage(parameter.TypeUsage)); 1289 1290 // 1291 // Create function aggregate expression. 1292 // 1293 DbFunctionAggregate functionAggregate; 1294 if (methodExpr.DistinctKind == AST.DistinctKind.Distinct) 1295 { 1296 functionAggregate = DbExpressionBuilder.AggregateDistinct(functionType, args[0]); 1297 } 1298 else 1299 { 1300 functionAggregate = DbExpressionBuilder.Aggregate(functionType, args[0]); 1301 } 1302 1303 // 1304 // Add aggregate to aggreate list. 1305 // 1306 aggregateInfo.AttachToAstNode(sr.GenerateInternalName("groupAgg" + functionType.Name), functionAggregate); 1307 aggregateInfo.EvaluatingScopeRegion.GroupAggregateInfos.Add(aggregateInfo); 1308 1309 // 1310 // Return stub expression with same type as the aggregate function. 1311 // 1312 converted = aggregateInfo.AggregateStubExpression; 1313 1314 Debug.Assert(converted != null, "converted != null"); 1315 1316 return true; 1317 } 1318 #endregion ConvertAggregateFunctionInGroupScope implementation 1319 1320 /// <summary> 1321 /// Creates <see cref="DbExpression"/> representing a new instance of the given type. 1322 /// Validates and infers argument types. 1323 /// </summary> CreateConstructorCallExpression(AST.MethodExpr methodExpr, TypeUsage type, List<DbExpression> args, List<DbRelatedEntityRef> relshipExprList, SemanticResolver sr)1324 private static DbExpression CreateConstructorCallExpression(AST.MethodExpr methodExpr, 1325 TypeUsage type, 1326 List<DbExpression> args, 1327 List<DbRelatedEntityRef> relshipExprList, 1328 SemanticResolver sr) 1329 { 1330 Debug.Assert(TypeSemantics.IsComplexType(type) || TypeSemantics.IsEntityType(type) || TypeSemantics.IsRelationshipType(type), "type must have a constructor"); 1331 1332 DbExpression newInstance = null; 1333 int idx = 0; 1334 int argCount = args.Count; 1335 1336 // 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. 1339 // 1340 StructuralType stype = (StructuralType)type.EdmType; 1341 foreach (EdmMember member in TypeHelpers.GetAllStructuralMembers(stype)) 1342 { 1343 TypeUsage memberModelTypeUsage = Helper.GetModelTypeUsage(member); 1344 1345 Debug.Assert(memberModelTypeUsage.EdmType.DataSpace == DataSpace.CSpace, "member space must be CSpace"); 1346 1347 // 1348 // Ensure given arguments are not less than 'formal' constructor arguments. 1349 // 1350 if (argCount <= idx) 1351 { 1352 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.NumberOfTypeCtorIsLessThenFormalSpec(member.Name)); 1353 } 1354 1355 // 1356 // If the given argument is the untyped null, infer type from the ctor formal argument type. 1357 // 1358 if (args[idx] == null) 1359 { 1360 EdmProperty edmProperty = member as EdmProperty; 1361 if (edmProperty != null && !edmProperty.Nullable) 1362 { 1363 throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, 1364 Strings.InvalidNullLiteralForNonNullableMember(member.Name, stype.FullName)); 1365 } 1366 args[idx] = DbExpressionBuilder.Null(memberModelTypeUsage); 1367 } 1368 1369 // 1370 // Ensure the given argument type is promotable to the formal ctor argument type. 1371 // 1372 bool isPromotable = TypeSemantics.IsPromotableTo(args[idx].ResultType, memberModelTypeUsage); 1373 if (ParserOptions.CompilationMode.RestrictedViewGenerationMode == sr.ParserOptions.ParserCompilationMode || 1374 ParserOptions.CompilationMode.UserViewGenerationMode == sr.ParserOptions.ParserCompilationMode) 1375 { 1376 if (!isPromotable && !TypeSemantics.IsPromotableTo(memberModelTypeUsage, args[idx].ResultType)) 1377 { 1378 throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, 1379 Strings.InvalidCtorArgumentType( 1380 args[idx].ResultType.EdmType.FullName, 1381 member.Name, 1382 memberModelTypeUsage.EdmType.FullName)); 1383 } 1384 1385 if (Helper.IsPrimitiveType(memberModelTypeUsage.EdmType) && 1386 !TypeSemantics.IsSubTypeOf(args[idx].ResultType, memberModelTypeUsage)) 1387 { 1388 args[idx] = args[idx].CastTo(memberModelTypeUsage); 1389 } 1390 } 1391 else 1392 { 1393 if (!isPromotable) 1394 { 1395 throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, 1396 Strings.InvalidCtorArgumentType( 1397 args[idx].ResultType.EdmType.FullName, 1398 member.Name, 1399 memberModelTypeUsage.EdmType.FullName)); 1400 } 1401 } 1402 1403 idx++; 1404 } 1405 1406 // 1407 // Ensure all given arguments and all ctor formals were considered and properly checked. 1408 // 1409 if (idx != argCount) 1410 { 1411 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.NumberOfTypeCtorIsMoreThenFormalSpec(stype.FullName)); 1412 } 1413 1414 // 1415 // Finally, create expression 1416 // 1417 if (relshipExprList != null && relshipExprList.Count > 0) 1418 { 1419 EntityType entityType = (EntityType)type.EdmType; 1420 newInstance = DbExpressionBuilder.CreateNewEntityWithRelationshipsExpression(entityType, args, relshipExprList); 1421 } 1422 else 1423 { 1424 newInstance = DbExpressionBuilder.New(TypeHelpers.GetReadOnlyType(type), args); 1425 } 1426 Debug.Assert(null != newInstance, "null != newInstance"); 1427 1428 return newInstance; 1429 } 1430 1431 /// <summary> 1432 /// Creates <see cref="DbFunctionExpression"/> representing a model function call. 1433 /// Validates overloads. 1434 /// </summary> CreateModelFunctionCallExpression(AST.MethodExpr methodExpr, MetadataFunctionGroup metadataFunctionGroup, SemanticResolver sr)1435 private static DbFunctionExpression CreateModelFunctionCallExpression(AST.MethodExpr methodExpr, 1436 MetadataFunctionGroup metadataFunctionGroup, 1437 SemanticResolver sr) 1438 { 1439 DbFunctionExpression functionExpression = null; 1440 bool isAmbiguous = false; 1441 1442 // 1443 // DistinctKind must not be specified on a regular function call. 1444 // 1445 if (methodExpr.DistinctKind != AST.DistinctKind.None) 1446 { 1447 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInNonAggFunction); 1448 } 1449 1450 // 1451 // Convert function arguments. 1452 // 1453 List<TypeUsage> argTypes; 1454 var args = ConvertFunctionArguments(methodExpr.Args, sr, out argTypes); 1455 1456 // 1457 // Find function overload match for given argument types. 1458 // 1459 EdmFunction functionType = SemanticResolver.ResolveFunctionOverloads( 1460 metadataFunctionGroup.FunctionMetadata, 1461 argTypes, 1462 false /* isGroupAggregateFunction */, 1463 out isAmbiguous); 1464 1465 // 1466 // If there is more than one overload that matches given arguments, throw. 1467 // 1468 if (isAmbiguous) 1469 { 1470 throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments); 1471 } 1472 1473 // 1474 // If null, means no overload matched. 1475 // 1476 if (null == functionType) 1477 { 1478 CqlErrorHelper.ReportFunctionOverloadError(methodExpr, metadataFunctionGroup.FunctionMetadata[0], argTypes); 1479 } 1480 1481 // 1482 // Convert untyped NULLs in arguments to typed nulls inferred from function parameters. 1483 // 1484 ConvertUntypedNullsInArguments(args, functionType.Parameters, (parameter) => parameter.TypeUsage); 1485 1486 // 1487 // Finally, create expression 1488 // 1489 functionExpression = functionType.Invoke(args); 1490 1491 Debug.Assert(null != functionExpression, "null != functionExpression"); 1492 1493 return functionExpression; 1494 } 1495 1496 /// <summary> 1497 /// Converts function call arguments into a list of <see cref="DbExpression"/>s. 1498 /// In case of no arguments returns an empty list. 1499 /// </summary> ConvertFunctionArguments(AST.NodeList<AST.Node> astExprList, SemanticResolver sr, out List<TypeUsage> argTypes)1500 private static List<DbExpression> ConvertFunctionArguments(AST.NodeList<AST.Node> astExprList, SemanticResolver sr, out List<TypeUsage> argTypes) 1501 { 1502 List<DbExpression> convertedArgs = new List<DbExpression>(); 1503 1504 if (null != astExprList) 1505 { 1506 for (int i = 0; i < astExprList.Count; i++) 1507 { 1508 convertedArgs.Add(ConvertValueExpressionAllowUntypedNulls(astExprList[i], sr)); 1509 } 1510 } 1511 1512 argTypes = convertedArgs.Select(a => a != null ? a.ResultType : null).ToList(); 1513 return convertedArgs; 1514 } 1515 ConvertUntypedNullsInArguments( List<DbExpression> args, IList<TParameterMetadata> parametersMetadata, Func<TParameterMetadata, TypeUsage> getParameterTypeUsage)1516 private static void ConvertUntypedNullsInArguments<TParameterMetadata>( 1517 List<DbExpression> args, 1518 IList<TParameterMetadata> parametersMetadata, 1519 Func<TParameterMetadata, TypeUsage> getParameterTypeUsage) 1520 { 1521 for (int i = 0; i < args.Count; i++) 1522 { 1523 if (args[i] == null) 1524 { 1525 args[i] = DbExpressionBuilder.Null(getParameterTypeUsage(parametersMetadata[i])); 1526 } 1527 } 1528 } 1529 #endregion ConvertMethodExpr implementation 1530 1531 /// <summary> 1532 /// Converts command parameter reference expression (AST.QueryParameter) 1533 /// </summary> ConvertParameter(AST.Node expr, SemanticResolver sr)1534 private static ExpressionResolution ConvertParameter(AST.Node expr, SemanticResolver sr) 1535 { 1536 AST.QueryParameter parameter = (AST.QueryParameter)expr; 1537 1538 DbParameterReferenceExpression paramRef; 1539 if (null == sr.Parameters || !sr.Parameters.TryGetValue(parameter.Name, out paramRef)) 1540 { 1541 throw EntityUtil.EntitySqlError(parameter.ErrCtx, Strings.ParameterWasNotDefined(parameter.Name)); 1542 } 1543 1544 return new ValueExpression(paramRef); 1545 } 1546 1547 /// <summary> 1548 /// Converts WITH RELATIONSHIP (AST.RelshipNavigationExpr) 1549 /// </summary> 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> ConvertRelatedEntityRef(AST.RelshipNavigationExpr relshipExpr, EntityType driverEntityType, SemanticResolver sr)1554 private static DbRelatedEntityRef ConvertRelatedEntityRef(AST.RelshipNavigationExpr relshipExpr, EntityType driverEntityType, SemanticResolver sr) 1555 { 1556 // 1557 // Resolve relationship type name. 1558 // 1559 var edmType = ConvertTypeName(relshipExpr.TypeName, sr).EdmType; 1560 var relationshipType = edmType as RelationshipType; 1561 if (relationshipType == null) 1562 { 1563 throw EntityUtil.EntitySqlError(relshipExpr.TypeName.ErrCtx, Strings.RelationshipTypeExpected(edmType.FullName)); 1564 } 1565 1566 // 1567 // Convert target instance expression. 1568 // 1569 var targetEntityRef = ConvertValueExpression(relshipExpr.RefExpr, sr); 1570 1571 // 1572 // Make sure it is a ref type. 1573 // 1574 var refType = targetEntityRef.ResultType.EdmType as RefType; 1575 if (refType == null) 1576 { 1577 throw EntityUtil.EntitySqlError(relshipExpr.RefExpr.ErrCtx, Strings.RelatedEndExprTypeMustBeReference); 1578 } 1579 1580 // 1581 // Convert To end if explicitly defined, derive if implicit. 1582 // 1583 RelationshipEndMember toEnd; 1584 if (relshipExpr.ToEndIdentifier != null) 1585 { 1586 toEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.ToEndIdentifier.Name, StringComparison.OrdinalIgnoreCase)); 1587 if (toEnd == null) 1588 { 1589 throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.ToEndIdentifier.Name, relationshipType.FullName)); 1590 } 1591 // 1592 // ensure is *..{0|1} 1593 // 1594 if (toEnd.RelationshipMultiplicity != RelationshipMultiplicity.One && toEnd.RelationshipMultiplicity != RelationshipMultiplicity.ZeroOrOne) 1595 { 1596 throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, 1597 Strings.InvalidWithRelationshipTargetEndMultiplicity(toEnd.Name, toEnd.RelationshipMultiplicity.ToString())); 1598 } 1599 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(refType, toEnd.TypeUsage.EdmType)) 1600 { 1601 throw EntityUtil.EntitySqlError(relshipExpr.RefExpr.ErrCtx, Strings.RelatedEndExprTypeMustBePromotoableToToEnd(refType.FullName, toEnd.TypeUsage.EdmType.FullName)); 1602 } 1603 } 1604 else 1605 { 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) 1611 { 1612 case 1: 1613 toEnd = toEndCandidates[0]; 1614 break; 1615 case 0: 1616 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipToEnd(relationshipType.FullName)); 1617 default: 1618 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipToEndIsAmbiguos); 1619 } 1620 } 1621 Debug.Assert(toEnd != null, "toEnd must be resolved."); 1622 1623 // 1624 // Convert From end if explicitly defined, derive if implicit. 1625 // 1626 RelationshipEndMember fromEnd; 1627 if (relshipExpr.FromEndIdentifier != null) 1628 { 1629 fromEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.FromEndIdentifier.Name, StringComparison.OrdinalIgnoreCase)); 1630 if (fromEnd == null) 1631 { 1632 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.FromEndIdentifier.Name, relationshipType.FullName)); 1633 } 1634 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(driverEntityType.GetReferenceType(), fromEnd.TypeUsage.EdmType)) 1635 { 1636 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx, 1637 Strings.SourceTypeMustBePromotoableToFromEndRelationType(driverEntityType.FullName, fromEnd.TypeUsage.EdmType.FullName)); 1638 } 1639 if (fromEnd.EdmEquals(toEnd)) 1640 { 1641 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos); 1642 } 1643 } 1644 else 1645 { 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) 1650 { 1651 case 1: 1652 fromEnd = fromEndCandidates[0]; 1653 break; 1654 case 0: 1655 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipFromEnd(relationshipType.FullName)); 1656 default: 1657 Debug.Fail("N-ary relationship? N > 2"); 1658 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos); 1659 } 1660 } 1661 Debug.Assert(fromEnd != null, "fromEnd must be resolved."); 1662 1663 return DbExpressionBuilder.CreateRelatedEntityRef(fromEnd, toEnd, targetEntityRef); 1664 } 1665 1666 /// <summary> 1667 /// Converts relationship navigation expression (AST.RelshipNavigationExpr) 1668 /// </summary> ConvertRelshipNavigationExpr(AST.Node astExpr, SemanticResolver sr)1669 private static ExpressionResolution ConvertRelshipNavigationExpr(AST.Node astExpr, SemanticResolver sr) 1670 { 1671 AST.RelshipNavigationExpr relshipExpr = (AST.RelshipNavigationExpr)astExpr; 1672 1673 // 1674 // Resolve relationship type name. 1675 // 1676 var edmType = ConvertTypeName(relshipExpr.TypeName, sr).EdmType; 1677 var relationshipType = edmType as RelationshipType; 1678 if (relationshipType == null) 1679 { 1680 throw EntityUtil.EntitySqlError(relshipExpr.TypeName.ErrCtx, Strings.RelationshipTypeExpected(edmType.FullName)); 1681 } 1682 1683 // 1684 // Convert source instance expression. 1685 // 1686 var sourceEntityRef = ConvertValueExpression(relshipExpr.RefExpr, sr); 1687 1688 // 1689 // Make sure it is a ref type. Convert to ref if possible. 1690 // 1691 var sourceRefType = sourceEntityRef.ResultType.EdmType as RefType; 1692 if (sourceRefType == null) 1693 { 1694 var entityType = sourceEntityRef.ResultType.EdmType as EntityType; 1695 if (entityType != null) 1696 { 1697 sourceEntityRef = DbExpressionBuilder.GetEntityRef(sourceEntityRef); 1698 sourceRefType = (RefType)sourceEntityRef.ResultType.EdmType; 1699 } 1700 else 1701 { 1702 throw EntityUtil.EntitySqlError(relshipExpr.RefExpr.ErrCtx, Strings.RelatedEndExprTypeMustBeReference); 1703 } 1704 } 1705 1706 // 1707 // Convert To end if explicitly defined. Derive if implicit later, after From end processing. 1708 // 1709 RelationshipEndMember toEnd; 1710 if (relshipExpr.ToEndIdentifier != null) 1711 { 1712 toEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.ToEndIdentifier.Name, StringComparison.OrdinalIgnoreCase)); 1713 if (toEnd == null) 1714 { 1715 throw EntityUtil.EntitySqlError(relshipExpr.ToEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.ToEndIdentifier.Name, relationshipType.FullName)); 1716 } 1717 } 1718 else 1719 { 1720 toEnd = null; 1721 } 1722 1723 // 1724 // Convert From end if explicitly defined, derive if implicit. 1725 // 1726 RelationshipEndMember fromEnd; 1727 if (relshipExpr.FromEndIdentifier != null) 1728 { 1729 fromEnd = (RelationshipEndMember)relationshipType.Members.FirstOrDefault(m => m.Name.Equals(relshipExpr.FromEndIdentifier.Name, StringComparison.OrdinalIgnoreCase)); 1730 if (fromEnd == null) 1731 { 1732 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx, Strings.InvalidRelationshipMember(relshipExpr.FromEndIdentifier.Name, relationshipType.FullName)); 1733 } 1734 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(sourceRefType, fromEnd.TypeUsage.EdmType)) 1735 { 1736 throw EntityUtil.EntitySqlError(relshipExpr.FromEndIdentifier.ErrCtx, 1737 Strings.SourceTypeMustBePromotoableToFromEndRelationType(sourceRefType.FullName, fromEnd.TypeUsage.EdmType.FullName)); 1738 } 1739 if (toEnd != null && fromEnd.EdmEquals(toEnd)) 1740 { 1741 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos); 1742 } 1743 } 1744 else 1745 { 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) 1750 { 1751 case 1: 1752 fromEnd = fromEndCandidates[0]; 1753 break; 1754 case 0: 1755 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipFromEnd(relationshipType.FullName)); 1756 default: 1757 Debug.Assert(toEnd == null, "N-ary relationship? N > 2"); 1758 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipFromEndIsAmbiguos); 1759 } 1760 } 1761 Debug.Assert(fromEnd != null, "fromEnd must be resolved."); 1762 1763 // 1764 // Derive To end if implicit. 1765 // 1766 if (toEnd == null) 1767 { 1768 var toEndCandidates = relationshipType.Members.Select(m => (RelationshipEndMember)m) 1769 .Where (e => !e.EdmEquals(fromEnd)).ToArray(); 1770 switch (toEndCandidates.Length) 1771 { 1772 case 1: 1773 toEnd = toEndCandidates[0]; 1774 break; 1775 case 0: 1776 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.InvalidImplicitRelationshipToEnd(relationshipType.FullName)); 1777 default: 1778 Debug.Fail("N-ary relationship? N > 2"); 1779 throw EntityUtil.EntitySqlError(relshipExpr.ErrCtx, Strings.RelationshipToEndIsAmbiguos); 1780 } 1781 } 1782 Debug.Assert(toEnd != null, "toEnd must be resolved."); 1783 1784 // 1785 // Create cqt expression. 1786 // 1787 DbExpression converted = sourceEntityRef.Navigate(fromEnd, toEnd); 1788 Debug.Assert(null != converted, "null != converted"); 1789 1790 return new ValueExpression(converted); 1791 } 1792 1793 /// <summary> 1794 /// Converts REF expression (AST.RefExpr) 1795 /// </summary> ConvertRefExpr(AST.Node astExpr, SemanticResolver sr)1796 private static ExpressionResolution ConvertRefExpr(AST.Node astExpr, SemanticResolver sr) 1797 { 1798 AST.RefExpr refExpr = (AST.RefExpr)astExpr; 1799 1800 DbExpression converted = ConvertValueExpression(refExpr.ArgExpr, sr); 1801 1802 // 1803 // check if is entity type 1804 // 1805 if (!TypeSemantics.IsEntityType(converted.ResultType)) 1806 { 1807 throw EntityUtil.EntitySqlError(refExpr.ArgExpr.ErrCtx, Strings.RefArgIsNotOfEntityType(converted.ResultType.EdmType.FullName)); 1808 } 1809 1810 // 1811 // create ref expression 1812 // 1813 converted = converted.GetEntityRef(); 1814 Debug.Assert(null != converted, "null != converted"); 1815 1816 return new ValueExpression(converted); 1817 } 1818 1819 /// <summary> 1820 /// Converts DEREF expression (AST.DerefExpr) 1821 /// </summary> ConvertDeRefExpr(AST.Node astExpr, SemanticResolver sr)1822 private static ExpressionResolution ConvertDeRefExpr(AST.Node astExpr, SemanticResolver sr) 1823 { 1824 AST.DerefExpr deRefExpr = (AST.DerefExpr)astExpr; 1825 1826 DbExpression converted = null; 1827 1828 converted = ConvertValueExpression(deRefExpr.ArgExpr, sr); 1829 1830 // 1831 // check if return type is RefType 1832 // 1833 if (!TypeSemantics.IsReferenceType(converted.ResultType)) 1834 { 1835 throw EntityUtil.EntitySqlError(deRefExpr.ArgExpr.ErrCtx, Strings.DeRefArgIsNotOfRefType(converted.ResultType.EdmType.FullName)); 1836 } 1837 1838 // 1839 // create DeRef expression 1840 // 1841 converted = converted.Deref(); 1842 Debug.Assert(null != converted, "null != converted"); 1843 1844 return new ValueExpression(converted); 1845 } 1846 1847 /// <summary> 1848 /// Converts CREATEREF expression (AST.CreateRefExpr) 1849 /// </summary> ConvertCreateRefExpr(AST.Node astExpr, SemanticResolver sr)1850 private static ExpressionResolution ConvertCreateRefExpr(AST.Node astExpr, SemanticResolver sr) 1851 { 1852 AST.CreateRefExpr createRefExpr = (AST.CreateRefExpr)astExpr; 1853 1854 DbExpression converted = null; 1855 1856 // 1857 // Convert the entity set, also, ensure that we get back an extent expression 1858 // 1859 DbScanExpression entitySetExpr = ConvertValueExpression(createRefExpr.EntitySet, sr) as DbScanExpression; 1860 if (entitySetExpr == null) 1861 { 1862 throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, Strings.ExprIsNotValidEntitySetForCreateRef); 1863 } 1864 1865 // 1866 // Ensure that the extent is an entity set 1867 // 1868 EntitySet entitySet = entitySetExpr.Target as EntitySet; 1869 if (entitySet == null) 1870 { 1871 throw EntityUtil.EntitySqlError(createRefExpr.EntitySet.ErrCtx, Strings.ExprIsNotValidEntitySetForCreateRef); 1872 } 1873 1874 DbExpression keyRowExpression = ConvertValueExpression(createRefExpr.Keys, sr); 1875 1876 RowType inputKeyRowType = keyRowExpression.ResultType.EdmType as RowType; 1877 if (null == inputKeyRowType) 1878 { 1879 throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, Strings.InvalidCreateRefKeyType); 1880 } 1881 1882 RowType entityKeyRowType = TypeHelpers.CreateKeyRowType(entitySet.ElementType); 1883 1884 if (entityKeyRowType.Members.Count != inputKeyRowType.Members.Count) 1885 { 1886 throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, Strings.ImcompatibleCreateRefKeyType); 1887 } 1888 1889 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(keyRowExpression.ResultType, TypeUsage.Create(entityKeyRowType))) 1890 { 1891 throw EntityUtil.EntitySqlError(createRefExpr.Keys.ErrCtx, Strings.ImcompatibleCreateRefKeyElementType); 1892 } 1893 1894 // 1895 // if CREATEREF specifies a type, resolve and validate the type 1896 // 1897 if (null != createRefExpr.TypeIdentifier) 1898 { 1899 TypeUsage targetTypeUsage = ConvertTypeName(createRefExpr.TypeIdentifier, sr); 1900 1901 // 1902 // ensure type is entity 1903 // 1904 if (!TypeSemantics.IsEntityType(targetTypeUsage)) 1905 { 1906 1907 throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx, 1908 Strings.CreateRefTypeIdentifierMustSpecifyAnEntityType( 1909 targetTypeUsage.EdmType.FullName, 1910 targetTypeUsage.EdmType.BuiltInTypeKind.ToString())); 1911 } 1912 1913 if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, targetTypeUsage.EdmType)) 1914 { 1915 throw EntityUtil.EntitySqlError(createRefExpr.TypeIdentifier.ErrCtx, 1916 Strings.CreateRefTypeIdentifierMustBeASubOrSuperType( 1917 entitySet.ElementType.FullName, 1918 targetTypeUsage.EdmType.FullName)); 1919 } 1920 1921 converted = DbExpressionBuilder.RefFromKey(entitySet, keyRowExpression, (EntityType)targetTypeUsage.EdmType); 1922 } 1923 else 1924 { 1925 // 1926 // finally creates the expression 1927 // 1928 converted = DbExpressionBuilder.RefFromKey(entitySet, keyRowExpression); 1929 } 1930 1931 Debug.Assert(null != converted, "null != converted"); 1932 1933 return new ValueExpression(converted); 1934 } 1935 1936 /// <summary> 1937 /// Converts KEY expression (AST.KeyExpr) 1938 /// </summary> ConvertKeyExpr(AST.Node astExpr, SemanticResolver sr)1939 private static ExpressionResolution ConvertKeyExpr(AST.Node astExpr, SemanticResolver sr) 1940 { 1941 AST.KeyExpr keyExpr = (AST.KeyExpr)astExpr; 1942 1943 DbExpression converted = ConvertValueExpression(keyExpr.ArgExpr, sr); 1944 1945 if (TypeSemantics.IsEntityType(converted.ResultType)) 1946 { 1947 converted = converted.GetEntityRef(); 1948 } 1949 else if (!TypeSemantics.IsReferenceType(converted.ResultType)) 1950 { 1951 throw EntityUtil.EntitySqlError(keyExpr.ArgExpr.ErrCtx, Strings.InvalidKeyArgument(converted.ResultType.EdmType.FullName)); 1952 } 1953 1954 converted = converted.GetRefKey(); 1955 Debug.Assert(null != converted, "null != converted"); 1956 1957 return new ValueExpression(converted); 1958 } 1959 1960 /// <summary> 1961 /// Converts a builtin expression (AST.BuiltInExpr). 1962 /// </summary> ConvertBuiltIn(AST.Node astExpr, SemanticResolver sr)1963 private static ExpressionResolution ConvertBuiltIn(AST.Node astExpr, SemanticResolver sr) 1964 { 1965 AST.BuiltInExpr bltInExpr = (AST.BuiltInExpr)astExpr; 1966 1967 BuiltInExprConverter builtInConverter = _builtInExprConverter[bltInExpr.Kind]; 1968 if (builtInConverter == null) 1969 { 1970 throw EntityUtil.EntitySqlError(Strings.UnknownBuiltInAstExpressionType); 1971 } 1972 1973 return new ValueExpression(builtInConverter(bltInExpr, sr)); 1974 } 1975 1976 /// <summary> 1977 /// Converts Arithmetic Expressions Args 1978 /// </summary> 1979 /// <param name="astBuiltInExpr"></param> 1980 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 1981 /// <returns></returns> ConvertArithmeticArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)1982 private static Pair<DbExpression, DbExpression> ConvertArithmeticArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 1983 { 1984 var operands = ConvertValueExpressionsWithUntypedNulls( 1985 astBuiltInExpr.Arg1, 1986 astBuiltInExpr.Arg2, 1987 astBuiltInExpr.ErrCtx, 1988 () => Strings.InvalidNullArithmetic, 1989 sr); 1990 1991 if (!TypeSemantics.IsNumericType(operands.Left.ResultType)) 1992 { 1993 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionMustBeNumericType); 1994 } 1995 1996 if (operands.Right != null) 1997 { 1998 if (!TypeSemantics.IsNumericType(operands.Right.ResultType)) 1999 { 2000 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.ExpressionMustBeNumericType); 2001 } 2002 2003 if (null == TypeHelpers.GetCommonTypeUsage(operands.Left.ResultType, operands.Right.ResultType)) 2004 { 2005 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible( 2006 operands.Left.ResultType.EdmType.FullName, operands.Right.ResultType.EdmType.FullName)); 2007 } 2008 } 2009 2010 return operands; 2011 } 2012 2013 /// <summary> 2014 /// Converts Plus Args - specific case since string type is an allowed type for '+' 2015 /// </summary> 2016 /// <param name="astBuiltInExpr"></param> 2017 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 2018 /// <returns></returns> ConvertPlusOperands(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)2019 private static Pair<DbExpression, DbExpression> ConvertPlusOperands(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 2020 { 2021 var operands = ConvertValueExpressionsWithUntypedNulls( 2022 astBuiltInExpr.Arg1, 2023 astBuiltInExpr.Arg2, 2024 astBuiltInExpr.ErrCtx, 2025 () => Strings.InvalidNullArithmetic, 2026 sr); 2027 2028 if (!TypeSemantics.IsNumericType(operands.Left.ResultType) && !TypeSemantics.IsPrimitiveType(operands.Left.ResultType, PrimitiveTypeKind.String)) 2029 { 2030 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.PlusLeftExpressionInvalidType); 2031 } 2032 2033 if (!TypeSemantics.IsNumericType(operands.Right.ResultType) && !TypeSemantics.IsPrimitiveType(operands.Right.ResultType, PrimitiveTypeKind.String)) 2034 { 2035 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.PlusRightExpressionInvalidType); 2036 } 2037 2038 if (TypeHelpers.GetCommonTypeUsage(operands.Left.ResultType, operands.Right.ResultType) == null) 2039 { 2040 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible( 2041 operands.Left.ResultType.EdmType.FullName, operands.Right.ResultType.EdmType.FullName)); 2042 } 2043 2044 return operands; 2045 } 2046 2047 /// <summary> 2048 /// Converts Logical Expression Args 2049 /// </summary> 2050 /// <param name="astBuiltInExpr"></param> 2051 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 2052 /// <returns></returns> ConvertLogicalArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)2053 private static Pair<DbExpression, DbExpression> ConvertLogicalArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 2054 { 2055 DbExpression leftExpr = ConvertValueExpressionAllowUntypedNulls(astBuiltInExpr.Arg1, sr); 2056 if (leftExpr == null) 2057 { 2058 leftExpr = DbExpressionBuilder.Null(sr.TypeResolver.BooleanType); 2059 } 2060 2061 DbExpression rightExpr = null; 2062 if (astBuiltInExpr.Arg2 != null) 2063 { 2064 rightExpr = ConvertValueExpressionAllowUntypedNulls(astBuiltInExpr.Arg2, sr); 2065 if (rightExpr == null) 2066 { 2067 rightExpr = DbExpressionBuilder.Null(sr.TypeResolver.BooleanType); 2068 } 2069 } 2070 2071 // 2072 // ensure left expression type is boolean 2073 // 2074 if (!IsBooleanType(leftExpr.ResultType)) 2075 { 2076 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionTypeMustBeBoolean); 2077 } 2078 2079 // 2080 // ensure right expression type is boolean 2081 // 2082 if (null != rightExpr && !IsBooleanType(rightExpr.ResultType)) 2083 { 2084 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.ExpressionTypeMustBeBoolean); 2085 } 2086 2087 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr); 2088 } 2089 2090 /// <summary> 2091 /// Converts Equal Comparison Expression Args 2092 /// </summary> 2093 /// <param name="astBuiltInExpr"></param> 2094 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 2095 /// <returns></returns> ConvertEqualCompArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)2096 private static Pair<DbExpression, DbExpression> ConvertEqualCompArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 2097 { 2098 // 2099 // convert left and right types and infer null types 2100 // 2101 Pair<DbExpression, DbExpression> compArgs = ConvertValueExpressionsWithUntypedNulls( 2102 astBuiltInExpr.Arg1, 2103 astBuiltInExpr.Arg2, 2104 astBuiltInExpr.ErrCtx, 2105 () => Strings.InvalidNullComparison, 2106 sr); 2107 2108 // 2109 // ensure both operand types are equal-comparable 2110 // 2111 if (!TypeSemantics.IsEqualComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType)) 2112 { 2113 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible( 2114 compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName)); 2115 } 2116 2117 return compArgs; 2118 } 2119 2120 /// <summary> 2121 /// Converts Order Comparison Expression Args 2122 /// </summary> 2123 /// <param name="astBuiltInExpr"></param> 2124 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 2125 /// <returns></returns> ConvertOrderCompArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)2126 private static Pair<DbExpression, DbExpression> ConvertOrderCompArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 2127 { 2128 Pair<DbExpression, DbExpression> compArgs = ConvertValueExpressionsWithUntypedNulls( 2129 astBuiltInExpr.Arg1, 2130 astBuiltInExpr.Arg2, 2131 astBuiltInExpr.ErrCtx, 2132 () => Strings.InvalidNullComparison, 2133 sr); 2134 2135 // 2136 // ensure both operand types are order-comparable 2137 // 2138 if (!TypeSemantics.IsOrderComparableTo(compArgs.Left.ResultType, compArgs.Right.ResultType)) 2139 { 2140 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.ArgumentTypesAreIncompatible( 2141 compArgs.Left.ResultType.EdmType.FullName, compArgs.Right.ResultType.EdmType.FullName)); 2142 } 2143 2144 return compArgs; 2145 } 2146 2147 /// <summary> 2148 /// Converts Set Expression Args 2149 /// </summary> 2150 /// <param name="astBuiltInExpr"></param> 2151 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 2152 /// <returns></returns> ConvertSetArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)2153 private static Pair<DbExpression, DbExpression> ConvertSetArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 2154 { 2155 // 2156 // convert left expression 2157 // 2158 DbExpression leftExpr = ConvertValueExpression(astBuiltInExpr.Arg1, sr); 2159 2160 // 2161 // convert right expression if binary set op kind 2162 // 2163 DbExpression rightExpr = null; 2164 if (null != astBuiltInExpr.Arg2) 2165 { 2166 // 2167 // binary set op 2168 // 2169 2170 // 2171 // make sure left expression type is of sequence type (ICollection or Extent) 2172 // 2173 if (!TypeSemantics.IsCollectionType(leftExpr.ResultType)) 2174 { 2175 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.LeftSetExpressionArgsMustBeCollection); 2176 } 2177 2178 // 2179 // convert right expression 2180 // 2181 rightExpr = ConvertValueExpression(astBuiltInExpr.Arg2, sr); 2182 2183 // 2184 // make sure right expression type is of sequence type (ICollection or Extent) 2185 // 2186 if (!TypeSemantics.IsCollectionType(rightExpr.ResultType)) 2187 { 2188 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.RightSetExpressionArgsMustBeCollection); 2189 } 2190 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)) 2195 { 2196 CqlErrorHelper.ReportIncompatibleCommonType(astBuiltInExpr.ErrCtx, leftElemType, rightElemType); 2197 } 2198 2199 if (astBuiltInExpr.Kind != AST.BuiltInKind.UnionAll) 2200 { 2201 // 2202 // ensure left argument is set op comparable 2203 // 2204 if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType))) 2205 { 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)); 2211 } 2212 2213 // 2214 // ensure right argument is set op comparable 2215 // 2216 if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(rightExpr.ResultType))) 2217 { 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)); 2223 } 2224 } 2225 else 2226 { 2227 if (Helper.IsAssociationType(leftElemType.EdmType)) 2228 { 2229 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.InvalidAssociationTypeForUnion(leftElemType.EdmType.FullName)); 2230 } 2231 2232 if (Helper.IsAssociationType(rightElemType.EdmType)) 2233 { 2234 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.InvalidAssociationTypeForUnion(rightElemType.EdmType.FullName)); 2235 } 2236 } 2237 } 2238 else 2239 { 2240 // 2241 // unary set op 2242 // 2243 2244 // 2245 // make sure expression type is of sequence type (ICollection or Extent) 2246 // 2247 if (!TypeSemantics.IsCollectionType(leftExpr.ResultType)) 2248 { 2249 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.InvalidUnarySetOpArgument(astBuiltInExpr.Name)); 2250 } 2251 2252 // 2253 // make sure that if is distinct unary operator, arg element type must be equal-comparable 2254 // 2255 if (astBuiltInExpr.Kind == AST.BuiltInKind.Distinct && !TypeHelpers.IsValidDistinctOpType(TypeHelpers.GetElementTypeUsage(leftExpr.ResultType))) 2256 { 2257 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionTypeMustBeEqualComparable); 2258 } 2259 } 2260 2261 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr); 2262 } 2263 2264 2265 /// <summary> 2266 /// Converts Set 'IN' expression args 2267 /// </summary> 2268 /// <param name="astBuiltInExpr"></param> 2269 /// <param name="sr">SemanticResolver instance relative to a especif typespace/system</param> 2270 /// <returns></returns> ConvertInExprArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr)2271 private static Pair<DbExpression, DbExpression> ConvertInExprArgs(AST.BuiltInExpr astBuiltInExpr, SemanticResolver sr) 2272 { 2273 DbExpression rightExpr = ConvertValueExpression(astBuiltInExpr.Arg2, sr); 2274 if (!TypeSemantics.IsCollectionType(rightExpr.ResultType)) 2275 { 2276 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg2.ErrCtx, Strings.RightSetExpressionArgsMustBeCollection); 2277 } 2278 2279 DbExpression leftExpr = ConvertValueExpressionAllowUntypedNulls(astBuiltInExpr.Arg1, sr); 2280 if (leftExpr == null) 2281 { 2282 // 2283 // If left expression type is null, infer its type from the collection element type. 2284 // 2285 TypeUsage elementType = TypeHelpers.GetElementTypeUsage(rightExpr.ResultType); 2286 ValidateTypeForNullExpression(elementType, astBuiltInExpr.Arg1.ErrCtx); 2287 leftExpr = DbExpressionBuilder.Null(elementType); 2288 } 2289 2290 if (TypeSemantics.IsCollectionType(leftExpr.ResultType)) 2291 { 2292 throw EntityUtil.EntitySqlError(astBuiltInExpr.Arg1.ErrCtx, Strings.ExpressionTypeMustNotBeCollection); 2293 } 2294 2295 // 2296 // Ensure that if left and right are typed expressions then their types must be comparable for IN op. 2297 // 2298 TypeUsage commonElemType = TypeHelpers.GetCommonTypeUsage(leftExpr.ResultType, TypeHelpers.GetElementTypeUsage(rightExpr.ResultType)); 2299 if (null == commonElemType || !TypeHelpers.IsValidInOpType(commonElemType)) 2300 { 2301 throw EntityUtil.EntitySqlError(astBuiltInExpr.ErrCtx, Strings.InvalidInExprArgs(leftExpr.ResultType.EdmType.FullName, rightExpr.ResultType.EdmType.FullName)); 2302 } 2303 2304 return new Pair<DbExpression, DbExpression>(leftExpr, rightExpr); 2305 } 2306 ValidateTypeForNullExpression(TypeUsage type, ErrorContext errCtx)2307 private static void ValidateTypeForNullExpression(TypeUsage type, ErrorContext errCtx) 2308 { 2309 if (TypeSemantics.IsCollectionType(type)) 2310 { 2311 throw EntityUtil.EntitySqlError(errCtx, Strings.NullLiteralCannotBePromotedToCollectionOfNulls); 2312 } 2313 } 2314 2315 /// <summary> 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. 2321 /// </summary> ConvertTypeName(AST.Node typeName, SemanticResolver sr)2322 private static TypeUsage ConvertTypeName(AST.Node typeName, SemanticResolver sr) 2323 { 2324 Debug.Assert(typeName != null, "typeName != null"); 2325 2326 string[] name = null; 2327 AST.NodeList<AST.Node> typeSpecArgs = null; 2328 2329 // 2330 // Process AST.MethodExpr - reduce it to an identifier with type spec arguments 2331 // 2332 AST.MethodExpr methodExpr = typeName as AST.MethodExpr; 2333 if (methodExpr != null) 2334 { 2335 typeName = methodExpr.Expr; 2336 typeName.ErrCtx.ErrorContextInfo = methodExpr.ErrCtx.ErrorContextInfo; 2337 typeName.ErrCtx.UseContextInfoAsResourceIdentifier = methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier; 2338 2339 typeSpecArgs = methodExpr.Args; 2340 } 2341 2342 // 2343 // Try as AST.Identifier 2344 // 2345 AST.Identifier identifier = typeName as AST.Identifier; 2346 if (identifier != null) 2347 { 2348 name = new string[] { identifier.Name }; 2349 } 2350 2351 // 2352 // Try as AST.DotExpr 2353 // 2354 AST.DotExpr dotExpr = typeName as AST.DotExpr; 2355 if (dotExpr != null && dotExpr.IsMultipartIdentifier(out name)) 2356 { 2357 Debug.Assert(name != null, "name != null for a multipart identifier"); 2358 } 2359 2360 if (name == null) 2361 { 2362 Debug.Fail("Unexpected AST.Node in the type name"); 2363 throw EntityUtil.EntitySqlError(typeName.ErrCtx, Strings.InvalidMetadataMemberName); 2364 } 2365 2366 MetadataMember metadataMember = sr.ResolveMetadataMemberName(name, typeName.ErrCtx); 2367 Debug.Assert(metadataMember != null, "metadata member name resolution must not return null"); 2368 2369 switch (metadataMember.MetadataMemberClass) 2370 { 2371 case MetadataMemberClass.Type: 2372 { 2373 TypeUsage typeUsage = ((MetadataType)metadataMember).TypeUsage; 2374 2375 if (typeSpecArgs != null) 2376 { 2377 typeUsage = ConvertTypeSpecArgs(typeUsage, typeSpecArgs, typeName.ErrCtx, sr); 2378 } 2379 2380 return typeUsage; 2381 } 2382 2383 case MetadataMemberClass.Namespace: 2384 throw EntityUtil.EntitySqlError(typeName.ErrCtx, Strings.TypeNameNotFound(metadataMember.Name)); 2385 2386 default: 2387 throw EntityUtil.EntitySqlError(typeName.ErrCtx, Strings.InvalidMetadataMemberClassResolution( 2388 metadataMember.Name, metadataMember.MetadataMemberClassName, MetadataType.TypeClassName)); 2389 } 2390 } 2391 ConvertTypeSpecArgs(TypeUsage parameterizedType, AST.NodeList<AST.Node> typeSpecArgs, ErrorContext errCtx, SemanticResolver sr)2392 private static TypeUsage ConvertTypeSpecArgs(TypeUsage parameterizedType, AST.NodeList<AST.Node> typeSpecArgs, ErrorContext errCtx, SemanticResolver sr) 2393 { 2394 Debug.Assert(typeSpecArgs != null && typeSpecArgs.Count > 0, "typeSpecArgs must be null or a non-empty list"); 2395 2396 // 2397 // Type arguments must be literals. 2398 // 2399 foreach (AST.Node arg in typeSpecArgs) 2400 { 2401 if (!(arg is AST.Literal)) 2402 { 2403 throw EntityUtil.EntitySqlError(arg.ErrCtx, Strings.TypeArgumentMustBeLiteral); 2404 } 2405 } 2406 2407 // 2408 // The only parameterized type supported is Edm.Decimal 2409 // 2410 PrimitiveType primitiveType = parameterizedType.EdmType as PrimitiveType; 2411 if (primitiveType == null || primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Decimal) 2412 { 2413 throw EntityUtil.EntitySqlError(errCtx, Strings.TypeDoesNotSupportSpec(primitiveType.FullName)); 2414 } 2415 2416 // 2417 // Edm.Decimal has two optional parameters: precision and scale. 2418 // 2419 if (typeSpecArgs.Count > 2) 2420 { 2421 throw EntityUtil.EntitySqlError(errCtx, Strings.TypeArgumentCountMismatch(primitiveType.FullName, 2)); 2422 } 2423 2424 // 2425 // Get precision value for Edm.Decimal 2426 // 2427 byte precision; 2428 ConvertTypeFacetValue(primitiveType, (AST.Literal)typeSpecArgs[0], DbProviderManifest.PrecisionFacetName, out precision); 2429 2430 // 2431 // Get scale value for Edm.Decimal 2432 // 2433 byte scale = 0; 2434 if (typeSpecArgs.Count == 2) 2435 { 2436 ConvertTypeFacetValue(primitiveType, (AST.Literal)typeSpecArgs[1], DbProviderManifest.ScaleFacetName, out scale); 2437 } 2438 2439 // 2440 // Ensure P >= S 2441 // 2442 if (precision < scale) 2443 { 2444 throw EntityUtil.EntitySqlError(typeSpecArgs[0].ErrCtx, Strings.PrecisionMustBeGreaterThanScale(precision, scale)); 2445 } 2446 2447 return TypeUsage.CreateDecimalTypeUsage(primitiveType, precision, scale); 2448 } 2449 ConvertTypeFacetValue(PrimitiveType type, AST.Literal value, string facetName, out byte byteValue)2450 private static void ConvertTypeFacetValue(PrimitiveType type, AST.Literal value, string facetName, out byte byteValue) 2451 { 2452 FacetDescription facetDescription = Helper.GetFacet(type.ProviderManifest.GetFacetDescriptions(type), facetName); 2453 if (facetDescription == null) 2454 { 2455 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeDoesNotSupportFacet(type.FullName, facetName)); 2456 } 2457 2458 if (value.IsNumber && Byte.TryParse(value.OriginalValue, out byteValue)) 2459 { 2460 if (facetDescription.MaxValue.HasValue && byteValue > facetDescription.MaxValue.Value) 2461 { 2462 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeArgumentExceedsMax(facetName)); 2463 } 2464 2465 if (facetDescription.MinValue.HasValue && byteValue < facetDescription.MinValue.Value) 2466 { 2467 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeArgumentBelowMin(facetName)); 2468 } 2469 } 2470 else 2471 { 2472 throw EntityUtil.EntitySqlError(value.ErrCtx, Strings.TypeArgumentIsNotValid); 2473 } 2474 } 2475 ConvertTypeDefinition(AST.Node typeDefinitionExpr, SemanticResolver sr)2476 private static TypeUsage ConvertTypeDefinition(AST.Node typeDefinitionExpr, SemanticResolver sr) 2477 { 2478 Debug.Assert(typeDefinitionExpr != null, "typeDefinitionExpr != null"); 2479 2480 TypeUsage converted = null; 2481 2482 AST.CollectionTypeDefinition collTypeDefExpr = typeDefinitionExpr as AST.CollectionTypeDefinition; 2483 AST.RefTypeDefinition refTypeDefExpr = typeDefinitionExpr as AST.RefTypeDefinition; 2484 AST.RowTypeDefinition rowTypeDefExpr = typeDefinitionExpr as AST.RowTypeDefinition; 2485 2486 if (collTypeDefExpr != null) 2487 { 2488 TypeUsage elementType = ConvertTypeDefinition(collTypeDefExpr.ElementTypeDef, sr); 2489 converted = TypeHelpers.CreateCollectionTypeUsage(elementType, true /* readOnly */); 2490 } 2491 else if (refTypeDefExpr != null) 2492 { 2493 TypeUsage targetTypeUsage = ConvertTypeName(refTypeDefExpr.RefTypeIdentifier, sr); 2494 2495 // 2496 // Ensure type is entity 2497 // 2498 if (!TypeSemantics.IsEntityType(targetTypeUsage)) 2499 { 2500 2501 throw EntityUtil.EntitySqlError(refTypeDefExpr.RefTypeIdentifier.ErrCtx, 2502 Strings.RefTypeIdentifierMustSpecifyAnEntityType( 2503 targetTypeUsage.EdmType.FullName, 2504 targetTypeUsage.EdmType.BuiltInTypeKind.ToString())); 2505 } 2506 2507 converted = TypeHelpers.CreateReferenceTypeUsage((EntityType)targetTypeUsage.EdmType); 2508 } 2509 else if (rowTypeDefExpr != null) 2510 { 2511 Debug.Assert(rowTypeDefExpr.Properties != null && rowTypeDefExpr.Properties.Count > 0, "rowTypeDefExpr.Properties must be a non-empty collection"); 2512 2513 converted = TypeHelpers.CreateRowTypeUsage( 2514 rowTypeDefExpr.Properties.Select(p => new KeyValuePair<string, TypeUsage>(p.Name.Name, ConvertTypeDefinition(p.Type, sr))), 2515 true /* readOnly */); 2516 } 2517 else 2518 { 2519 converted = ConvertTypeName(typeDefinitionExpr, sr); 2520 } 2521 2522 Debug.Assert(converted != null, "Type definition conversion yielded null"); 2523 2524 return converted; 2525 } 2526 2527 /// <summary> 2528 /// Converts row constructor expression (AST.RowConstructorExpr) 2529 /// </summary> ConvertRowConstructor(AST.Node expr, SemanticResolver sr)2530 private static ExpressionResolution ConvertRowConstructor(AST.Node expr, SemanticResolver sr) 2531 { 2532 AST.RowConstructorExpr rowExpr = (AST.RowConstructorExpr)expr; 2533 2534 Dictionary<string, TypeUsage> rowColumns = new Dictionary<string, TypeUsage>(sr.NameComparer); 2535 List<DbExpression> fieldExprs = new List<DbExpression>(rowExpr.AliasedExprList.Count); 2536 2537 for (int i = 0; i < rowExpr.AliasedExprList.Count; i++) 2538 { 2539 AST.AliasedExpr aliasExpr = rowExpr.AliasedExprList[i]; 2540 2541 DbExpression colExpr = ConvertValueExpressionAllowUntypedNulls(aliasExpr.Expr, sr); 2542 if (colExpr == null) 2543 { 2544 throw EntityUtil.EntitySqlError(aliasExpr.Expr.ErrCtx, Strings.RowCtorElementCannotBeNull); 2545 } 2546 2547 string aliasName = sr.InferAliasName(aliasExpr, colExpr); 2548 2549 if (rowColumns.ContainsKey(aliasName)) 2550 { 2551 if (aliasExpr.Alias != null) 2552 { 2553 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasExpr.Alias.ErrCtx, Strings.InRowCtor); 2554 } 2555 else 2556 { 2557 aliasName = sr.GenerateInternalName("autoRowCol"); 2558 } 2559 } 2560 2561 rowColumns.Add(aliasName, colExpr.ResultType); 2562 2563 fieldExprs.Add(colExpr); 2564 } 2565 2566 return new ValueExpression(DbExpressionBuilder.New(TypeHelpers.CreateRowTypeUsage(rowColumns, true /* readOnly */), fieldExprs)); 2567 } 2568 2569 /// <summary> 2570 /// Converts multiset constructor expression (AST.MultisetConstructorExpr) 2571 /// </summary> ConvertMultisetConstructor(AST.Node expr, SemanticResolver sr)2572 private static ExpressionResolution ConvertMultisetConstructor(AST.Node expr, SemanticResolver sr) 2573 { 2574 AST.MultisetConstructorExpr msetCtor = (AST.MultisetConstructorExpr)expr; 2575 2576 if (null == msetCtor.ExprList) 2577 { 2578 throw EntityUtil.EntitySqlError(expr.ErrCtx, Strings.CannotCreateEmptyMultiset); 2579 } 2580 2581 var mSetExprs = msetCtor.ExprList.Select(e => ConvertValueExpressionAllowUntypedNulls(e, sr)).ToArray(); 2582 2583 var multisetTypes = mSetExprs.Where(e => e != null).Select(e => e.ResultType).ToArray(); 2584 2585 // 2586 // Ensure common type is not an untyped null. 2587 // 2588 if (multisetTypes.Length == 0) 2589 { 2590 throw EntityUtil.EntitySqlError(expr.ErrCtx, Strings.CannotCreateMultisetofNulls); 2591 } 2592 2593 TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(multisetTypes); 2594 2595 // 2596 // Ensure all elems have a common type. 2597 // 2598 if (commonType == null) 2599 { 2600 throw EntityUtil.EntitySqlError(expr.ErrCtx, Strings.MultisetElemsAreNotTypeCompatible); 2601 } 2602 2603 commonType = TypeHelpers.GetReadOnlyType(commonType); 2604 2605 // 2606 // Fixup untyped nulls. 2607 // 2608 for (int i = 0; i < mSetExprs.Length; i++) 2609 { 2610 if (mSetExprs[i] == null) 2611 { 2612 ValidateTypeForNullExpression(commonType, msetCtor.ExprList[i].ErrCtx); 2613 mSetExprs[i] = DbExpressionBuilder.Null(commonType); 2614 } 2615 } 2616 2617 return new ValueExpression(DbExpressionBuilder.New(TypeHelpers.CreateCollectionTypeUsage(commonType, true /* readOnly */), mSetExprs)); 2618 } 2619 2620 /// <summary> 2621 /// Converts case-when-then expression (AST.CaseExpr) 2622 /// </summary> ConvertCaseExpr(AST.Node expr, SemanticResolver sr)2623 private static ExpressionResolution ConvertCaseExpr(AST.Node expr, SemanticResolver sr) 2624 { 2625 AST.CaseExpr caseExpr = (AST.CaseExpr)expr; 2626 2627 List<DbExpression> whenExprList = new List<DbExpression>(caseExpr.WhenThenExprList.Count); 2628 List<DbExpression> thenExprList = new List<DbExpression>(caseExpr.WhenThenExprList.Count); 2629 2630 // 2631 // Convert when/then expressions. 2632 // 2633 for (int i = 0; i < caseExpr.WhenThenExprList.Count; i++) 2634 { 2635 AST.WhenThenExpr whenThenExpr = caseExpr.WhenThenExprList[i]; 2636 2637 DbExpression whenExpression = ConvertValueExpression(whenThenExpr.WhenExpr, sr); 2638 2639 if (!IsBooleanType(whenExpression.ResultType)) 2640 { 2641 throw EntityUtil.EntitySqlError(whenThenExpr.WhenExpr.ErrCtx, Strings.ExpressionTypeMustBeBoolean); 2642 } 2643 2644 whenExprList.Add(whenExpression); 2645 2646 DbExpression thenExpression = ConvertValueExpressionAllowUntypedNulls(whenThenExpr.ThenExpr, sr); 2647 2648 thenExprList.Add(thenExpression); 2649 } 2650 2651 // 2652 // Convert else if present. 2653 // 2654 DbExpression elseExpr = caseExpr.ElseExpr != null ? ConvertValueExpressionAllowUntypedNulls(caseExpr.ElseExpr, sr) : null; 2655 2656 // 2657 // Collect result types from THENs and the ELSE. 2658 // 2659 var resultTypes = thenExprList.Where(e => e != null).Select(e => e.ResultType).ToList(); 2660 if (elseExpr != null) 2661 { 2662 resultTypes.Add(elseExpr.ResultType); 2663 } 2664 if (resultTypes.Count == 0) 2665 { 2666 throw EntityUtil.EntitySqlError(caseExpr.ElseExpr.ErrCtx, Strings.InvalidCaseWhenThenNullType); 2667 } 2668 2669 // 2670 // Derive common return type. 2671 // 2672 TypeUsage resultType = TypeHelpers.GetCommonTypeUsage(resultTypes); 2673 if (resultType == null) 2674 { 2675 throw EntityUtil.EntitySqlError(caseExpr.WhenThenExprList[0].ThenExpr.ErrCtx, Strings.InvalidCaseResultTypes); 2676 } 2677 2678 // 2679 // Fixup untyped nulls 2680 // 2681 for (int i = 0; i < thenExprList.Count; i++) 2682 { 2683 if (thenExprList[i] == null) 2684 { 2685 ValidateTypeForNullExpression(resultType, caseExpr.WhenThenExprList[i].ThenExpr.ErrCtx); 2686 thenExprList[i] = DbExpressionBuilder.Null(resultType); 2687 } 2688 } 2689 if (elseExpr == null) 2690 { 2691 if (caseExpr.ElseExpr == null && TypeSemantics.IsCollectionType(resultType)) 2692 { 2693 // 2694 // If ELSE was omitted and common return type is a collection, 2695 // then use empty collection for elseExpr. 2696 // 2697 elseExpr = DbExpressionBuilder.NewEmptyCollection(resultType); 2698 } 2699 else 2700 { 2701 ValidateTypeForNullExpression(resultType, (caseExpr.ElseExpr ?? caseExpr).ErrCtx); 2702 elseExpr = DbExpressionBuilder.Null(resultType); 2703 } 2704 } 2705 2706 return new ValueExpression(DbExpressionBuilder.Case(whenExprList, thenExprList, elseExpr)); 2707 } 2708 2709 /// <summary> 2710 /// Converts query expression (AST.QueryExpr) 2711 /// </summary> ConvertQueryExpr(AST.Node expr, SemanticResolver sr)2712 private static ExpressionResolution ConvertQueryExpr(AST.Node expr, SemanticResolver sr) 2713 { 2714 AST.QueryExpr queryExpr = (AST.QueryExpr)expr; 2715 2716 DbExpression converted = null; 2717 2718 bool isRestrictedViewGenerationMode = (ParserOptions.CompilationMode.RestrictedViewGenerationMode == sr.ParserOptions.ParserCompilationMode); 2719 2720 // 2721 // Validate & Compensate Query 2722 // 2723 if (null != queryExpr.HavingClause && null == queryExpr.GroupByClause) 2724 { 2725 throw EntityUtil.EntitySqlError(queryExpr.ErrCtx, Strings.HavingRequiresGroupClause); 2726 } 2727 if (queryExpr.SelectClause.TopExpr != null) 2728 { 2729 if (queryExpr.OrderByClause != null && queryExpr.OrderByClause.LimitSubClause != null) 2730 { 2731 throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, Strings.TopAndLimitCannotCoexist); 2732 } 2733 2734 if (queryExpr.OrderByClause != null && queryExpr.OrderByClause.SkipSubClause != null) 2735 { 2736 throw EntityUtil.EntitySqlError(queryExpr.SelectClause.TopExpr.ErrCtx, Strings.TopAndSkipCannotCoexist); 2737 } 2738 } 2739 2740 // 2741 // Create Source Scope Region 2742 // 2743 using (sr.EnterScopeRegion()) 2744 { 2745 // 2746 // Process From Clause 2747 // 2748 DbExpressionBinding sourceExpr = ProcessFromClause(queryExpr.FromClause, sr); 2749 2750 // 2751 // Process Where Clause 2752 // 2753 sourceExpr = ProcessWhereClause(sourceExpr, queryExpr.WhereClause, sr); 2754 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"); 2758 2759 bool queryProjectionProcessed = false; 2760 if (!isRestrictedViewGenerationMode) 2761 { 2762 // 2763 // Process GroupBy Clause 2764 // 2765 sourceExpr = ProcessGroupByClause(sourceExpr, queryExpr, sr); 2766 2767 // 2768 // Process Having Clause 2769 // 2770 sourceExpr = ProcessHavingClause(sourceExpr, queryExpr.HavingClause, sr); 2771 2772 // 2773 // Process OrderBy Clause 2774 // 2775 sourceExpr = ProcessOrderByClause(sourceExpr, queryExpr, out queryProjectionProcessed, sr); 2776 } 2777 2778 // 2779 // Process Projection Clause 2780 // 2781 converted = ProcessSelectClause(sourceExpr, queryExpr, queryProjectionProcessed, sr); 2782 2783 } // end query scope region 2784 2785 return new ValueExpression(converted); 2786 } 2787 2788 /// <summary> 2789 /// Process Select Clause 2790 /// </summary> ProcessSelectClause(DbExpressionBinding source, AST.QueryExpr queryExpr, bool queryProjectionProcessed, SemanticResolver sr)2791 private static DbExpression ProcessSelectClause(DbExpressionBinding source, AST.QueryExpr queryExpr, bool queryProjectionProcessed, SemanticResolver sr) 2792 { 2793 AST.SelectClause selectClause = queryExpr.SelectClause; 2794 2795 DbExpression projectExpression; 2796 if (queryProjectionProcessed) 2797 { 2798 projectExpression = source.Expression; 2799 } 2800 else 2801 { 2802 // 2803 // Convert projection items. 2804 // 2805 var projectionItems = ConvertSelectClauseItems(queryExpr, sr); 2806 2807 // 2808 // Create project expression off the projectionItems. 2809 // 2810 projectExpression = CreateProjectExpression(source, selectClause, projectionItems); 2811 } 2812 2813 // 2814 // Handle TOP/LIMIT sub-clauses. 2815 // 2816 if (selectClause.TopExpr != null || (queryExpr.OrderByClause != null && queryExpr.OrderByClause.LimitSubClause != null)) 2817 { 2818 AST.Node limitExpr; 2819 string exprName; 2820 if (selectClause.TopExpr != null) 2821 { 2822 Debug.Assert(queryExpr.OrderByClause == null || queryExpr.OrderByClause.LimitSubClause == null, "TOP and LIMIT in the same query are not allowed"); 2823 limitExpr = selectClause.TopExpr; 2824 exprName = "TOP"; 2825 } 2826 else 2827 { 2828 limitExpr = queryExpr.OrderByClause.LimitSubClause; 2829 exprName = "LIMIT"; 2830 } 2831 2832 // 2833 // Convert the expression. 2834 // 2835 DbExpression convertedLimit = ConvertValueExpression(limitExpr, sr); 2836 2837 // 2838 // Ensure the converted expression is in the range of values. 2839 // 2840 ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(convertedLimit, limitExpr.ErrCtx, exprName, sr); 2841 2842 // 2843 // Create the project expression with the limit. 2844 // 2845 projectExpression = projectExpression.Limit(convertedLimit); 2846 } 2847 2848 Debug.Assert(null != projectExpression, "null != projectExpression"); 2849 return projectExpression; 2850 } 2851 ConvertSelectClauseItems(AST.QueryExpr queryExpr, SemanticResolver sr)2852 private static List<KeyValuePair<string, DbExpression>> ConvertSelectClauseItems(AST.QueryExpr queryExpr, SemanticResolver sr) 2853 { 2854 AST.SelectClause selectClause = queryExpr.SelectClause; 2855 2856 // 2857 // Validate SELECT VALUE projection list. 2858 // 2859 if (selectClause.SelectKind == AST.SelectKind.Value) 2860 { 2861 if (selectClause.Items.Count != 1) 2862 { 2863 throw EntityUtil.EntitySqlError(selectClause.ErrCtx, Strings.InvalidSelectValueList); 2864 } 2865 2866 // 2867 // Aliasing is not allowed in the SELECT VALUE case, except when the ORDER BY clause is present. 2868 // 2869 if (selectClause.Items[0].Alias != null && queryExpr.OrderByClause == null) 2870 { 2871 throw EntityUtil.EntitySqlError(selectClause.Items[0].ErrCtx, Strings.InvalidSelectValueAliasedExpression); 2872 } 2873 } 2874 2875 // 2876 // Converts projection list 2877 // 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++) 2881 { 2882 AST.AliasedExpr projectionItem = selectClause.Items[i]; 2883 2884 DbExpression converted = ConvertValueExpression(projectionItem.Expr, sr); 2885 2886 // 2887 // Infer projection item alias. 2888 // 2889 string aliasName = sr.InferAliasName(projectionItem, converted); 2890 2891 // 2892 // Ensure the alias is not already used. 2893 // 2894 if (projectionAliases.Contains(aliasName)) 2895 { 2896 if (projectionItem.Alias != null) 2897 { 2898 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, projectionItem.Alias.ErrCtx, Strings.InSelectProjectionList); 2899 } 2900 else 2901 { 2902 aliasName = sr.GenerateInternalName("autoProject"); 2903 } 2904 } 2905 2906 projectionAliases.Add(aliasName); 2907 projectionItems.Add(new KeyValuePair<string, DbExpression>(aliasName, converted)); 2908 } 2909 2910 Debug.Assert(projectionItems.Count > 0, "projectionItems.Count > 0"); 2911 return projectionItems; 2912 } 2913 CreateProjectExpression(DbExpressionBinding source, AST.SelectClause selectClause, List<KeyValuePair<string, DbExpression>> projectionItems)2914 private static DbExpression CreateProjectExpression(DbExpressionBinding source, AST.SelectClause selectClause, List<KeyValuePair<string, DbExpression>> projectionItems) 2915 { 2916 // 2917 // Create DbProjectExpression off the projectionItems. 2918 // 2919 DbExpression projectExpression; 2920 if (selectClause.SelectKind == AST.SelectKind.Value) 2921 { 2922 Debug.Assert(projectionItems.Count == 1, "projectionItems.Count must be 1 for SELECT VALUE"); 2923 projectExpression = source.Project(projectionItems[0].Value); 2924 } 2925 else 2926 { 2927 projectExpression = source.Project(DbExpressionBuilder.NewRow(projectionItems)); 2928 } 2929 2930 // 2931 // Handle DISTINCT modifier - create DbDistinctExpression over the current projectExpression. 2932 // 2933 if (selectClause.DistinctKind == AST.DistinctKind.Distinct) 2934 { 2935 // 2936 // Ensure element type is equal-comparable. 2937 // 2938 ValidateDistinctProjection(projectExpression.ResultType, selectClause); 2939 2940 // 2941 // Create distinct expression. 2942 // 2943 projectExpression = projectExpression.Distinct(); 2944 } 2945 2946 return projectExpression; 2947 } 2948 ValidateDistinctProjection(TypeUsage projectExpressionResultType, AST.SelectClause selectClause)2949 private static void ValidateDistinctProjection(TypeUsage projectExpressionResultType, AST.SelectClause selectClause) 2950 { 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); 2956 } 2957 ValidateDistinctProjection(TypeUsage projectExpressionResultType, ErrorContext defaultErrCtx, List<ErrorContext> projectionItemErrCtxs)2958 private static void ValidateDistinctProjection(TypeUsage projectExpressionResultType, ErrorContext defaultErrCtx, List<ErrorContext> projectionItemErrCtxs) 2959 { 2960 TypeUsage projectionType = TypeHelpers.GetElementTypeUsage(projectExpressionResultType); 2961 if (!TypeHelpers.IsValidDistinctOpType(projectionType)) 2962 { 2963 ErrorContext errCtx = defaultErrCtx; 2964 if (projectionItemErrCtxs != null && TypeSemantics.IsRowType(projectionType)) 2965 { 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++) 2969 { 2970 if (!TypeHelpers.IsValidDistinctOpType(rowType.Members[i].TypeUsage)) 2971 { 2972 errCtx = projectionItemErrCtxs[i]; 2973 break; 2974 } 2975 } 2976 } 2977 throw EntityUtil.EntitySqlError(errCtx, Strings.SelectDistinctMustBeEqualComparable); 2978 } 2979 } 2980 ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(DbExpression expr, ErrorContext errCtx, string exprName, SemanticResolver sr)2981 private static void ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(DbExpression expr, ErrorContext errCtx, string exprName, SemanticResolver sr) 2982 { 2983 if (expr.ExpressionKind != DbExpressionKind.Constant && 2984 expr.ExpressionKind != DbExpressionKind.ParameterReference) 2985 { 2986 throw EntityUtil.EntitySqlError(errCtx, Strings.PlaceholderExpressionMustBeConstant(exprName)); 2987 } 2988 2989 if (!TypeSemantics.IsPromotableTo(expr.ResultType, sr.TypeResolver.Int64Type)) 2990 { 2991 throw EntityUtil.EntitySqlError(errCtx, Strings.PlaceholderExpressionMustBeCompatibleWithEdm64(exprName, expr.ResultType.EdmType.FullName)); 2992 } 2993 2994 DbConstantExpression constExpr = expr as DbConstantExpression; 2995 if (constExpr != null && System.Convert.ToInt64(constExpr.Value, CultureInfo.InvariantCulture) < 0) 2996 { 2997 throw EntityUtil.EntitySqlError(errCtx, Strings.PlaceholderExpressionMustBeGreaterThanOrEqualToZero(exprName)); 2998 } 2999 } 3000 3001 /// <summary> 3002 /// Process FROM clause. 3003 /// </summary> ProcessFromClause(AST.FromClause fromClause, SemanticResolver sr)3004 private static DbExpressionBinding ProcessFromClause(AST.FromClause fromClause, SemanticResolver sr) 3005 { 3006 DbExpressionBinding fromBinding = null; 3007 3008 // 3009 // Process each FROM clause item. 3010 // If there is more than one of them, then assemble them in a string from APPLYs. 3011 // 3012 List<SourceScopeEntry> fromClauseEntries = new List<SourceScopeEntry>(); 3013 for (int i = 0; i < fromClause.FromClauseItems.Count; i++) 3014 { 3015 // 3016 // Convert FROM clause item. 3017 // 3018 List<SourceScopeEntry> fromClauseItemEntries; 3019 DbExpressionBinding currentItemBinding = ProcessFromClauseItem(fromClause.FromClauseItems[i], sr, out fromClauseItemEntries); 3020 fromClauseEntries.AddRange(fromClauseItemEntries); 3021 3022 if (fromBinding == null) 3023 { 3024 fromBinding = currentItemBinding; 3025 } 3026 else 3027 { 3028 fromBinding = fromBinding.CrossApply(currentItemBinding).BindAs(sr.GenerateInternalName("lcapply")); 3029 3030 // 3031 // Adjust scope entries with the new binding. 3032 // 3033 fromClauseEntries.ForEach(scopeEntry => scopeEntry.AddParentVar(fromBinding.Variable)); 3034 } 3035 } 3036 3037 Debug.Assert(fromBinding != null, "fromBinding != null"); 3038 3039 return fromBinding; 3040 } 3041 3042 /// <summary> 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. 3045 /// </summary> ProcessFromClauseItem(AST.FromClauseItem fromClauseItem, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)3046 private static DbExpressionBinding ProcessFromClauseItem(AST.FromClauseItem fromClauseItem, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries) 3047 { 3048 DbExpressionBinding fromItemBinding = null; 3049 3050 switch (fromClauseItem.FromClauseItemKind) 3051 { 3052 case AST.FromClauseItemKind.AliasedFromClause: 3053 fromItemBinding = ProcessAliasedFromClauseItem((AST.AliasedExpr)fromClauseItem.FromExpr, sr, out scopeEntries); 3054 break; 3055 3056 case AST.FromClauseItemKind.JoinFromClause: 3057 fromItemBinding = ProcessJoinClauseItem((AST.JoinClauseItem)fromClauseItem.FromExpr, sr, out scopeEntries); 3058 break; 3059 3060 default: 3061 Debug.Assert(fromClauseItem.FromClauseItemKind == AST.FromClauseItemKind.ApplyFromClause, "AST.FromClauseItemKind.ApplyFromClause expected"); 3062 fromItemBinding = ProcessApplyClauseItem((AST.ApplyClauseItem)fromClauseItem.FromExpr, sr, out scopeEntries); 3063 break; 3064 } 3065 3066 Debug.Assert(fromItemBinding != null, "fromItemBinding != null"); 3067 3068 return fromItemBinding; 3069 } 3070 3071 /// <summary> 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. 3074 /// </summary> ProcessAliasedFromClauseItem(AST.AliasedExpr aliasedExpr, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)3075 private static DbExpressionBinding ProcessAliasedFromClauseItem(AST.AliasedExpr aliasedExpr, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries) 3076 { 3077 DbExpressionBinding aliasedBinding = null; 3078 3079 // 3080 // Convert the item expression. 3081 // 3082 DbExpression converted = ConvertValueExpression(aliasedExpr.Expr, sr); 3083 3084 // 3085 // Validate it is of collection type. 3086 // 3087 if (!TypeSemantics.IsCollectionType(converted.ResultType)) 3088 { 3089 throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, Strings.ExpressionMustBeCollection); 3090 } 3091 3092 // 3093 // Infer source var alias name. 3094 // 3095 string aliasName = sr.InferAliasName(aliasedExpr, converted); 3096 3097 // 3098 // Validate the name was not used yet. 3099 // 3100 if (sr.CurrentScope.Contains(aliasName)) 3101 { 3102 if (aliasedExpr.Alias != null) 3103 { 3104 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, aliasedExpr.Alias.ErrCtx, Strings.InFromClause); 3105 } 3106 else 3107 { 3108 aliasName = sr.GenerateInternalName("autoFrom"); 3109 } 3110 } 3111 3112 // 3113 // Create CQT expression. 3114 // 3115 aliasedBinding = converted.BindAs(aliasName); 3116 3117 // 3118 // Add source var to the _scopeEntries list and to the current scope. 3119 // 3120 SourceScopeEntry sourceScopeEntry = new SourceScopeEntry(aliasedBinding.Variable); 3121 sr.CurrentScope.Add(aliasedBinding.Variable.VariableName, sourceScopeEntry); 3122 scopeEntries = new List<SourceScopeEntry>(); 3123 scopeEntries.Add(sourceScopeEntry); 3124 3125 Debug.Assert(aliasedBinding != null, "aliasedBinding != null"); 3126 3127 return aliasedBinding; 3128 } 3129 3130 /// <summary> 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. 3133 /// </summary> ProcessJoinClauseItem(AST.JoinClauseItem joinClause, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)3134 private static DbExpressionBinding ProcessJoinClauseItem(AST.JoinClauseItem joinClause, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries) 3135 { 3136 DbExpressionBinding joinBinding = null; 3137 3138 // 3139 // Make sure inner join has ON predicate AND cross join has no ON predicate. 3140 // 3141 if (null == joinClause.OnExpr) 3142 { 3143 if (AST.JoinKind.Inner == joinClause.JoinKind) 3144 { 3145 throw EntityUtil.EntitySqlError(joinClause.ErrCtx, Strings.InnerJoinMustHaveOnPredicate); 3146 } 3147 } 3148 else 3149 { 3150 if (AST.JoinKind.Cross == joinClause.JoinKind) 3151 { 3152 throw EntityUtil.EntitySqlError(joinClause.OnExpr.ErrCtx, Strings.InvalidPredicateForCrossJoin); 3153 } 3154 } 3155 3156 // 3157 // Process left expression. 3158 // 3159 List<SourceScopeEntry> leftExprScopeEntries; 3160 DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(joinClause.LeftExpr, sr, out leftExprScopeEntries); 3161 3162 // 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. 3166 // Examples: 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 3170 // 3171 leftExprScopeEntries.ForEach(scopeEntry => scopeEntry.IsJoinClauseLeftExpr = true); 3172 3173 // 3174 // Process right expression 3175 // 3176 List<SourceScopeEntry> rightExprScopeEntries; 3177 DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(joinClause.RightExpr, sr, out rightExprScopeEntries); 3178 3179 // 3180 // Unmark scope entries from the left expression to allow their usage. 3181 // 3182 leftExprScopeEntries.ForEach(scopeEntry => scopeEntry.IsJoinClauseLeftExpr = false); 3183 3184 3185 // 3186 // Switch right outer to left outer. 3187 // 3188 if (joinClause.JoinKind == AST.JoinKind.RightOuter) 3189 { 3190 joinClause.JoinKind = AST.JoinKind.LeftOuter; 3191 DbExpressionBinding tmpExpr = leftBindingExpr; 3192 leftBindingExpr = rightBindingExpr; 3193 rightBindingExpr = tmpExpr; 3194 } 3195 3196 // 3197 // Resolve JoinType. 3198 // 3199 DbExpressionKind joinKind = MapJoinKind(joinClause.JoinKind); 3200 3201 // 3202 // Resolve ON. 3203 // 3204 DbExpression onExpr = null; 3205 if (null == joinClause.OnExpr) 3206 { 3207 if (DbExpressionKind.CrossJoin != joinKind) 3208 { 3209 onExpr = DbExpressionBuilder.True; 3210 } 3211 } 3212 else 3213 { 3214 onExpr = ConvertValueExpression(joinClause.OnExpr, sr); 3215 } 3216 3217 // 3218 // Create New Join 3219 // 3220 joinBinding = 3221 DbExpressionBuilder.CreateJoinExpressionByKind( 3222 joinKind, onExpr, leftBindingExpr, rightBindingExpr).BindAs(sr.GenerateInternalName("join")); 3223 3224 // 3225 // Combine left and right scope entries and adjust with the new binding. 3226 // 3227 scopeEntries = leftExprScopeEntries; 3228 scopeEntries.AddRange(rightExprScopeEntries); 3229 scopeEntries.ForEach(scopeEntry => scopeEntry.AddParentVar(joinBinding.Variable)); 3230 3231 Debug.Assert(joinBinding != null, "joinBinding != null"); 3232 3233 return joinBinding; 3234 } 3235 3236 /// <summary> 3237 /// Maps <see cref="AST.JoinKind"/> to <see cref="DbExpressionKind"/>. 3238 /// </summary> MapJoinKind(AST.JoinKind joinKind)3239 private static DbExpressionKind MapJoinKind(AST.JoinKind joinKind) 3240 { 3241 Debug.Assert(joinKind != AST.JoinKind.RightOuter, "joinKind != JoinKind.RightOuter"); 3242 return joinMap[(int)joinKind]; 3243 } 3244 private static readonly DbExpressionKind[] joinMap = { DbExpressionKind.CrossJoin, DbExpressionKind.InnerJoin, DbExpressionKind.LeftOuterJoin, DbExpressionKind.FullOuterJoin }; 3245 3246 /// <summary> 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. 3249 /// </summary> ProcessApplyClauseItem(AST.ApplyClauseItem applyClause, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries)3250 private static DbExpressionBinding ProcessApplyClauseItem(AST.ApplyClauseItem applyClause, SemanticResolver sr, out List<SourceScopeEntry> scopeEntries) 3251 { 3252 DbExpressionBinding applyBinding = null; 3253 3254 // 3255 // Resolve left expression. 3256 // 3257 List<SourceScopeEntry> leftExprScopeEntries; 3258 DbExpressionBinding leftBindingExpr = ProcessFromClauseItem(applyClause.LeftExpr, sr, out leftExprScopeEntries); 3259 3260 // 3261 // Resolve right expression. 3262 // 3263 List<SourceScopeEntry> rightExprScopeEntries; 3264 DbExpressionBinding rightBindingExpr = ProcessFromClauseItem(applyClause.RightExpr, sr, out rightExprScopeEntries); 3265 3266 // 3267 // Create Apply. 3268 // 3269 applyBinding = 3270 DbExpressionBuilder.CreateApplyExpressionByKind( 3271 MapApplyKind(applyClause.ApplyKind), 3272 leftBindingExpr, 3273 rightBindingExpr).BindAs(sr.GenerateInternalName("apply")); 3274 3275 // 3276 // Combine left and right scope entries and adjust with the new binding. 3277 // 3278 scopeEntries = leftExprScopeEntries; 3279 scopeEntries.AddRange(rightExprScopeEntries); 3280 scopeEntries.ForEach(scopeEntry => scopeEntry.AddParentVar(applyBinding.Variable)); 3281 3282 Debug.Assert(applyBinding != null, "applyBinding != null"); 3283 3284 return applyBinding; 3285 } 3286 3287 /// <summary> 3288 /// Maps <see cref="AST.ApplyKind"/> to <see cref="DbExpressionKind"/>. 3289 /// </summary> MapApplyKind(AST.ApplyKind applyKind)3290 private static DbExpressionKind MapApplyKind(AST.ApplyKind applyKind) 3291 { 3292 return applyMap[(int)applyKind]; 3293 } 3294 private static readonly DbExpressionKind[] applyMap = { DbExpressionKind.CrossApply, DbExpressionKind.OuterApply }; 3295 3296 /// <summary> 3297 /// Process WHERE clause. 3298 /// </summary> ProcessWhereClause(DbExpressionBinding source, AST.Node whereClause, SemanticResolver sr)3299 private static DbExpressionBinding ProcessWhereClause(DbExpressionBinding source, AST.Node whereClause, SemanticResolver sr) 3300 { 3301 if (whereClause == null) 3302 { 3303 return source; 3304 } 3305 return ProcessWhereHavingClausePredicate(source, whereClause, whereClause.ErrCtx, "where", sr); 3306 } 3307 3308 /// <summary> 3309 /// Process HAVING clause. 3310 /// </summary> ProcessHavingClause(DbExpressionBinding source, AST.HavingClause havingClause, SemanticResolver sr)3311 private static DbExpressionBinding ProcessHavingClause(DbExpressionBinding source, AST.HavingClause havingClause, SemanticResolver sr) 3312 { 3313 if (havingClause == null) 3314 { 3315 return source; 3316 } 3317 return ProcessWhereHavingClausePredicate(source, havingClause.HavingPredicate, havingClause.ErrCtx, "having", sr); 3318 } 3319 3320 /// <summary> 3321 /// Process WHERE or HAVING clause predicate. 3322 /// </summary> ProcessWhereHavingClausePredicate(DbExpressionBinding source, AST.Node predicate, ErrorContext errCtx, string bindingNameTemplate, SemanticResolver sr)3323 private static DbExpressionBinding ProcessWhereHavingClausePredicate(DbExpressionBinding source, AST.Node predicate, ErrorContext errCtx, string bindingNameTemplate, SemanticResolver sr) 3324 { 3325 Debug.Assert(predicate != null, "predicate != null"); 3326 3327 DbExpressionBinding whereBinding = null; 3328 3329 // 3330 // Convert the predicate. 3331 // 3332 DbExpression filterConditionExpr = ConvertValueExpression(predicate, sr); 3333 3334 // 3335 // Ensure the predicate type is boolean. 3336 // 3337 if (!IsBooleanType(filterConditionExpr.ResultType)) 3338 { 3339 throw EntityUtil.EntitySqlError(errCtx, Strings.ExpressionTypeMustBeBoolean); 3340 } 3341 3342 // 3343 // Create new filter binding. 3344 // 3345 whereBinding = source.Filter(filterConditionExpr).BindAs(sr.GenerateInternalName(bindingNameTemplate)); 3346 3347 // 3348 // Fixup Bindings. 3349 // 3350 sr.CurrentScopeRegion.ApplyToScopeEntries(scopeEntry => 3351 { 3352 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef, 3353 "scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef"); 3354 3355 if (scopeEntry.EntryKind == ScopeEntryKind.SourceVar) 3356 { 3357 ((SourceScopeEntry)scopeEntry).ReplaceParentVar(whereBinding.Variable); 3358 } 3359 }); 3360 3361 Debug.Assert(whereBinding != null, "whereBinding != null"); 3362 3363 return whereBinding; 3364 } 3365 3366 /// <summary> 3367 /// Process Group By Clause 3368 /// </summary> ProcessGroupByClause(DbExpressionBinding source, AST.QueryExpr queryExpr, SemanticResolver sr)3369 private static DbExpressionBinding ProcessGroupByClause(DbExpressionBinding source, AST.QueryExpr queryExpr, SemanticResolver sr) 3370 { 3371 AST.GroupByClause groupByClause = queryExpr.GroupByClause; 3372 3373 Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == groupByClause : true, "GROUP BY clause must be null in RestrictedViewGenerationMode"); 3374 3375 // 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. 3378 // 3379 int groupKeysCount = groupByClause != null ? groupByClause.GroupItems.Count : 0; 3380 bool isImplicitGroup = groupKeysCount == 0; 3381 if (isImplicitGroup && !queryExpr.HasMethodCall) 3382 { 3383 return source; 3384 } 3385 3386 // 3387 // Create input binding for DbGroupByExpression. 3388 // 3389 DbGroupExpressionBinding groupInputBinding = source.Expression.GroupBindAs(sr.GenerateInternalName("geb"), sr.GenerateInternalName("group")); 3390 3391 // 3392 // Create group partition (DbGroupAggregate) and projection template. 3393 // 3394 DbGroupAggregate groupAggregateDefinition = groupInputBinding.GroupAggregate; 3395 DbVariableReferenceExpression groupAggregateVarRef = groupAggregateDefinition.ResultType.Variable(sr.GenerateInternalName("groupAggregate")); 3396 DbExpressionBinding groupAggregateBinding = groupAggregateVarRef.BindAs(sr.GenerateInternalName("groupPartitionItem")); 3397 3398 // 3399 // Flag that we perform group operation. 3400 // 3401 sr.CurrentScopeRegion.EnterGroupOperation(groupAggregateBinding); 3402 3403 // 3404 // Update group input bindings. 3405 // 3406 sr.CurrentScopeRegion.ApplyToScopeEntries((scopeEntry) => 3407 { 3408 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar, "scopeEntry.EntryKind == ScopeEntryKind.SourceVar"); 3409 ((SourceScopeEntry)scopeEntry).AdjustToGroupVar(groupInputBinding.Variable, groupInputBinding.GroupVariable, groupAggregateBinding.Variable); 3410 }); 3411 3412 // 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. 3415 // 3416 HashSet<string> groupPropertyNames = new HashSet<string>(sr.NameComparer); 3417 3418 // 3419 // Convert group keys. 3420 // 3421 #region Convert group key definitions 3422 List<GroupKeyInfo> groupKeys = new List<GroupKeyInfo>(groupKeysCount); 3423 if (!isImplicitGroup) 3424 { 3425 Debug.Assert(null != groupByClause, "groupByClause must not be null at this point"); 3426 for (int i = 0; i < groupKeysCount; i++) 3427 { 3428 AST.AliasedExpr aliasedExpr = groupByClause.GroupItems[i]; 3429 3430 sr.CurrentScopeRegion.WasResolutionCorrelated = false; 3431 3432 // 3433 // Convert key expression relative to groupInputBinding.Variable. 3434 // This expression will be used as key definition during construction of DbGroupByExpression. 3435 // 3436 DbExpression keyExpr; 3437 GroupKeyAggregateInfo groupKeyAggregateInfo; 3438 using (sr.EnterGroupKeyDefinition(GroupAggregateKind.GroupKey, aliasedExpr.ErrCtx, out groupKeyAggregateInfo)) 3439 { 3440 keyExpr = ConvertValueExpression(aliasedExpr.Expr, sr); 3441 } 3442 3443 // 3444 // Ensure group key expression is correlated. 3445 // If resolution was correlated, then the following should be true for groupKeyAggregateInfo: ESR == DSR 3446 // 3447 if (!sr.CurrentScopeRegion.WasResolutionCorrelated) 3448 { 3449 throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, Strings.KeyMustBeCorrelated("GROUP BY")); 3450 } 3451 Debug.Assert(groupKeyAggregateInfo.EvaluatingScopeRegion == groupKeyAggregateInfo.DefiningScopeRegion, "Group key must evaluate on the scope it was defined on."); 3452 3453 // 3454 // Ensure key is valid. 3455 // 3456 if (!TypeHelpers.IsValidGroupKeyType(keyExpr.ResultType)) 3457 { 3458 throw EntityUtil.EntitySqlError(aliasedExpr.Expr.ErrCtx, Strings.GroupingKeysMustBeEqualComparable); 3459 } 3460 3461 // 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. 3464 // 3465 DbExpression keyExprForFunctionAggregates; 3466 GroupKeyAggregateInfo functionAggregateInfo; 3467 using (sr.EnterGroupKeyDefinition(GroupAggregateKind.Function, aliasedExpr.ErrCtx, out functionAggregateInfo)) 3468 { 3469 keyExprForFunctionAggregates = ConvertValueExpression(aliasedExpr.Expr, sr); 3470 } 3471 Debug.Assert(functionAggregateInfo.EvaluatingScopeRegion == functionAggregateInfo.DefiningScopeRegion, "Group key must evaluate on the scope it was defined on."); 3472 3473 // 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. 3476 // 3477 DbExpression keyExprForGroupPartitions; 3478 GroupKeyAggregateInfo groupPartitionInfo; 3479 using (sr.EnterGroupKeyDefinition(GroupAggregateKind.Partition, aliasedExpr.ErrCtx, out groupPartitionInfo)) 3480 { 3481 keyExprForGroupPartitions = ConvertValueExpression(aliasedExpr.Expr, sr); 3482 } 3483 Debug.Assert(groupPartitionInfo.EvaluatingScopeRegion == groupPartitionInfo.DefiningScopeRegion, "Group key must evaluate on the scope it was defined on."); 3484 3485 // 3486 // Infer group key alias name. 3487 // 3488 string groupKeyAlias = sr.InferAliasName(aliasedExpr, keyExpr); 3489 3490 // 3491 // Check if alias was already used. 3492 // 3493 if (groupPropertyNames.Contains(groupKeyAlias)) 3494 { 3495 if (aliasedExpr.Alias != null) 3496 { 3497 CqlErrorHelper.ReportAliasAlreadyUsedError(groupKeyAlias, aliasedExpr.Alias.ErrCtx, Strings.InGroupClause); 3498 } 3499 else 3500 { 3501 groupKeyAlias = sr.GenerateInternalName("autoGroup"); 3502 } 3503 } 3504 3505 // 3506 // Add alias to dictionary. 3507 // 3508 groupPropertyNames.Add(groupKeyAlias); 3509 3510 // 3511 // Add key to keys collection. 3512 // 3513 GroupKeyInfo groupKeyInfo = new GroupKeyInfo(groupKeyAlias, keyExpr, keyExprForFunctionAggregates, keyExprForGroupPartitions); 3514 groupKeys.Add(groupKeyInfo); 3515 3516 // 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 3521 // 3522 if (aliasedExpr.Alias == null) 3523 { 3524 AST.DotExpr dotExpr = aliasedExpr.Expr as AST.DotExpr; 3525 string[] alternativeName; 3526 if (null != dotExpr && dotExpr.IsMultipartIdentifier(out alternativeName)) 3527 { 3528 groupKeyInfo.AlternativeName = alternativeName; 3529 3530 string alternativeFullName = TypeResolver.GetFullName(alternativeName); 3531 if (groupPropertyNames.Contains(alternativeFullName)) 3532 { 3533 CqlErrorHelper.ReportAliasAlreadyUsedError(alternativeFullName, dotExpr.ErrCtx, Strings.InGroupClause); 3534 } 3535 3536 groupPropertyNames.Add(alternativeFullName); 3537 } 3538 } 3539 } 3540 } 3541 #endregion 3542 3543 // 3544 // Save scope. It will be used to rollback the temporary group scope created below. 3545 // 3546 int groupInputScope = sr.CurrentScopeIndex; 3547 3548 // 3549 // Push temporary group scope. 3550 // 3551 sr.EnterScope(); 3552 3553 // 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. 3556 // 3557 foreach (GroupKeyInfo groupKeyInfo in groupKeys) 3558 { 3559 sr.CurrentScope.Add( 3560 groupKeyInfo.Name, 3561 new GroupKeyDefinitionScopeEntry( 3562 groupKeyInfo.VarBasedKeyExpr, 3563 groupKeyInfo.GroupVarBasedKeyExpr, 3564 groupKeyInfo.GroupAggBasedKeyExpr, 3565 null)); 3566 3567 if (groupKeyInfo.AlternativeName != null) 3568 { 3569 string strAlternativeName = TypeResolver.GetFullName(groupKeyInfo.AlternativeName); 3570 sr.CurrentScope.Add( 3571 strAlternativeName, 3572 new GroupKeyDefinitionScopeEntry( 3573 groupKeyInfo.VarBasedKeyExpr, 3574 groupKeyInfo.GroupVarBasedKeyExpr, 3575 groupKeyInfo.GroupAggBasedKeyExpr, 3576 groupKeyInfo.AlternativeName)); 3577 } 3578 } 3579 3580 // 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. 3585 // 3586 3587 #region Search for group aggregates (functions and GROUPPARTITIONs) 3588 // 3589 // Search for aggregates in HAVING clause. 3590 // 3591 if (null != queryExpr.HavingClause && queryExpr.HavingClause.HasMethodCall) 3592 { 3593 DbExpression converted = ConvertValueExpression(queryExpr.HavingClause.HavingPredicate, sr); 3594 } 3595 3596 // 3597 // Search for aggregates in SELECT clause. 3598 // 3599 Dictionary<string, DbExpression> projectionExpressions = null; 3600 if (null != queryExpr.OrderByClause || queryExpr.SelectClause.HasMethodCall) 3601 { 3602 projectionExpressions = new Dictionary<string, DbExpression>(queryExpr.SelectClause.Items.Count, sr.NameComparer); 3603 for (int i = 0; i < queryExpr.SelectClause.Items.Count; i++) 3604 { 3605 AST.AliasedExpr aliasedExpr = queryExpr.SelectClause.Items[i]; 3606 3607 // 3608 // Convert projection item expression. 3609 // 3610 DbExpression converted = ConvertValueExpression(aliasedExpr.Expr, sr); 3611 3612 // 3613 // Create Null Expression with actual type. 3614 // 3615 converted = converted.ExpressionKind == CommandTrees.DbExpressionKind.Null ? converted : converted.ResultType.Null(); 3616 3617 // 3618 // Infer alias. 3619 // 3620 string aliasName = sr.InferAliasName(aliasedExpr, converted); 3621 3622 if (projectionExpressions.ContainsKey(aliasName)) 3623 { 3624 if (aliasedExpr.Alias != null) 3625 { 3626 CqlErrorHelper.ReportAliasAlreadyUsedError(aliasName, 3627 aliasedExpr.Alias.ErrCtx, 3628 Strings.InSelectProjectionList); 3629 } 3630 else 3631 { 3632 aliasName = sr.GenerateInternalName("autoProject"); 3633 } 3634 } 3635 3636 projectionExpressions.Add(aliasName, converted); 3637 } 3638 } 3639 3640 // 3641 // Search for aggregates in ORDER BY clause. 3642 // 3643 if (null != queryExpr.OrderByClause && queryExpr.OrderByClause.HasMethodCall) 3644 { 3645 // 3646 // Push temporary projection scope. 3647 // 3648 sr.EnterScope(); 3649 3650 // 3651 // Add projection items to the temporary scope (items may be used in ORDER BY). 3652 // 3653 foreach (KeyValuePair<string, DbExpression> kvp in projectionExpressions) 3654 { 3655 sr.CurrentScope.Add(kvp.Key, new ProjectionItemDefinitionScopeEntry(kvp.Value)); 3656 } 3657 3658 // 3659 // Search for aggregates in ORDER BY clause. 3660 // 3661 for (int i = 0; i < queryExpr.OrderByClause.OrderByClauseItem.Count; i++) 3662 { 3663 AST.OrderByClauseItem orderItem = queryExpr.OrderByClause.OrderByClauseItem[i]; 3664 3665 sr.CurrentScopeRegion.WasResolutionCorrelated = false; 3666 3667 DbExpression converted = ConvertValueExpression(orderItem.OrderExpr, sr); 3668 3669 // 3670 // Ensure key expression is correlated. 3671 // 3672 if (!sr.CurrentScopeRegion.WasResolutionCorrelated) 3673 { 3674 throw EntityUtil.EntitySqlError(orderItem.ErrCtx, Strings.KeyMustBeCorrelated("ORDER BY")); 3675 } 3676 } 3677 3678 // 3679 // Pop temporary projection scope. 3680 // 3681 sr.LeaveScope(); 3682 } 3683 #endregion 3684 3685 // 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. 3691 // 3692 if (isImplicitGroup) 3693 { 3694 if (0 == sr.CurrentScopeRegion.GroupAggregateInfos.Count) 3695 { 3696 #region Implicit Group Rollback 3697 // 3698 // Rollback the temporary group scope. 3699 // 3700 sr.RollbackToScope(groupInputScope); 3701 3702 // 3703 // Undo any group source fixups: re-applying the source var and remove the group var. 3704 // 3705 sr.CurrentScopeRegion.ApplyToScopeEntries((scopeEntry) => 3706 { 3707 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar, "scopeEntry.EntryKind == ScopeEntryKind.SourceVar"); 3708 ((SourceScopeEntry)scopeEntry).RollbackAdjustmentToGroupVar(source.Variable); 3709 }); 3710 3711 // 3712 // Remove the group operation flag. 3713 // 3714 sr.CurrentScopeRegion.RollbackGroupOperation(); 3715 #endregion 3716 // 3717 // Return the original source var binding. 3718 // 3719 return source; 3720 } 3721 } 3722 3723 // 3724 // Prepare list of aggregate definitions and their internal names. 3725 // 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) 3729 { 3730 switch (groupAggregateInfo.AggregateKind) 3731 { 3732 case GroupAggregateKind.Function: 3733 aggregates.Add(new KeyValuePair<string, DbAggregate>( 3734 groupAggregateInfo.AggregateName, 3735 ((FunctionAggregateInfo)groupAggregateInfo).AggregateDefinition)); 3736 break; 3737 3738 case GroupAggregateKind.Partition: 3739 groupPartitionRefFound = true; 3740 break; 3741 3742 default: 3743 Debug.Fail("Unexpected group aggregate kind:" + groupAggregateInfo.AggregateKind.ToString()); 3744 break; 3745 } 3746 } 3747 if (groupPartitionRefFound) 3748 { 3749 // 3750 // Add DbAggregate to support GROUPPARTITION definitions. 3751 // 3752 aggregates.Add(new KeyValuePair<string, DbAggregate>(groupAggregateVarRef.VariableName, groupAggregateDefinition)); 3753 } 3754 3755 // 3756 // Create GroupByExpression and a binding to it. 3757 // 3758 DbGroupByExpression groupBy = groupInputBinding.GroupBy( 3759 groupKeys.Select(keyInfo => new KeyValuePair<string, DbExpression>(keyInfo.Name, keyInfo.VarBasedKeyExpr)), 3760 aggregates); 3761 DbExpressionBinding groupBinding = groupBy.BindAs(sr.GenerateInternalName("group")); 3762 3763 // 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. 3767 // 3768 if (groupPartitionRefFound) 3769 { 3770 // 3771 // All GROUPPARTITION definitions reference groupAggregateVarRef, make sure the variable is properly defined in the groupBy expression. 3772 // 3773 Debug.Assert(aggregates.Any((aggregate) => String.CompareOrdinal(aggregate.Key, groupAggregateVarRef.VariableName) == 0), 3774 "DbAggregate is not defined"); 3775 3776 // 3777 // Get projection of GROUPPARTITION definitions. 3778 // This method may return null if all GROUPPARTITION definitions are reduced to the value of groupAggregateVarRef. 3779 // 3780 List<KeyValuePair<string, DbExpression>> projectionItems = ProcessGroupPartitionDefinitions( 3781 sr.CurrentScopeRegion.GroupAggregateInfos, 3782 groupAggregateVarRef, 3783 groupBinding); 3784 3785 if (projectionItems != null) 3786 { 3787 // 3788 // Project group keys along with GROUPPARTITION definitions. 3789 // 3790 projectionItems.AddRange(groupKeys.Select(keyInfo => 3791 new KeyValuePair<string, DbExpression>(keyInfo.Name, groupBinding.Variable.Property(keyInfo.Name)))); 3792 3793 // 3794 // Project function group aggregates along with GROUPPARTITION definitions and group keys. 3795 // 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)))); 3801 3802 DbExpression projectExpression = DbExpressionBuilder.NewRow(projectionItems); 3803 groupBinding = groupBinding.Project(projectExpression).BindAs(sr.GenerateInternalName("groupPartitionDefs")); 3804 } 3805 } 3806 3807 // 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. 3811 // 3812 sr.RollbackToScope(groupInputScope); 3813 sr.CurrentScopeRegion.ApplyToScopeEntries((scopeEntry) => 3814 { 3815 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar, "scopeEntry.EntryKind == ScopeEntryKind.SourceVar"); 3816 return new InvalidGroupInputRefScopeEntry(); 3817 }); 3818 3819 // 3820 // Add final group scope. 3821 // 3822 sr.EnterScope(); 3823 3824 // 3825 // Add group keys to the group scope. 3826 // 3827 foreach (GroupKeyInfo groupKeyInfo in groupKeys) 3828 { 3829 // 3830 // Add new scope entry 3831 // 3832 sr.CurrentScope.Add( 3833 groupKeyInfo.VarRef.VariableName, 3834 new SourceScopeEntry(groupKeyInfo.VarRef).AddParentVar(groupBinding.Variable)); 3835 3836 // 3837 // Handle the alternative name entry. 3838 // 3839 if (groupKeyInfo.AlternativeName != null) 3840 { 3841 // 3842 // We want two scope entries with keys as groupKeyInfo.VarRef.VariableName and groupKeyInfo.AlternativeName, 3843 // both pointing to the same variable (groupKeyInfo.VarRef). 3844 // 3845 string strAlternativeName = TypeResolver.GetFullName(groupKeyInfo.AlternativeName); 3846 sr.CurrentScope.Add( 3847 strAlternativeName, 3848 new SourceScopeEntry(groupKeyInfo.VarRef, groupKeyInfo.AlternativeName).AddParentVar(groupBinding.Variable)); 3849 } 3850 } 3851 3852 // 3853 // Add group aggregates to the scope. 3854 // 3855 foreach (GroupAggregateInfo groupAggregateInfo in sr.CurrentScopeRegion.GroupAggregateInfos) 3856 { 3857 DbVariableReferenceExpression aggVarRef = groupAggregateInfo.AggregateStubExpression.ResultType.Variable(groupAggregateInfo.AggregateName); 3858 3859 Debug.Assert( 3860 !sr.CurrentScope.Contains(aggVarRef.VariableName) || 3861 groupAggregateInfo.AggregateKind == GroupAggregateKind.Partition, "DbFunctionAggregate's with duplicate names are not allowed."); 3862 3863 if (!sr.CurrentScope.Contains(aggVarRef.VariableName)) 3864 { 3865 sr.CurrentScope.Add( 3866 aggVarRef.VariableName, 3867 new SourceScopeEntry(aggVarRef).AddParentVar(groupBinding.Variable)); 3868 sr.CurrentScopeRegion.RegisterGroupAggregateName(aggVarRef.VariableName); 3869 } 3870 3871 // 3872 // Cleanup the stub expression as it must not be used after this point. 3873 // 3874 groupAggregateInfo.AggregateStubExpression = null; 3875 } 3876 3877 return groupBinding; 3878 } 3879 3880 /// <summary> 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. 3885 /// </summary> ProcessGroupPartitionDefinitions( List<GroupAggregateInfo> groupAggregateInfos, DbVariableReferenceExpression groupAggregateVarRef, DbExpressionBinding groupBinding)3886 private static List<KeyValuePair<string, DbExpression>> ProcessGroupPartitionDefinitions( 3887 List<GroupAggregateInfo> groupAggregateInfos, 3888 DbVariableReferenceExpression groupAggregateVarRef, 3889 DbExpressionBinding groupBinding) 3890 { 3891 var gpExpressionLambdaVariables = new System.Collections.ObjectModel.ReadOnlyCollection<DbVariableReferenceExpression>( 3892 new DbVariableReferenceExpression[] { groupAggregateVarRef }); 3893 3894 List<KeyValuePair<string, DbExpression>> groupPartitionDefinitions = new List<KeyValuePair<string, DbExpression>>(); 3895 bool foundTrivialGroupAggregateProjection = false; 3896 foreach (GroupAggregateInfo groupAggregateInfo in groupAggregateInfos) 3897 { 3898 if (groupAggregateInfo.AggregateKind == GroupAggregateKind.Partition) 3899 { 3900 DbExpression aggregateDefinition = ((GroupPartitionInfo)groupAggregateInfo).AggregateDefinition; 3901 if (IsTrivialInputProjection(groupAggregateVarRef, aggregateDefinition)) 3902 { 3903 // 3904 // Reduce the case of the trivial projection of input to the value of groupAggregateVarRef. 3905 // 3906 groupAggregateInfo.AggregateName = groupAggregateVarRef.VariableName; 3907 foundTrivialGroupAggregateProjection = true; 3908 } 3909 else 3910 { 3911 // 3912 // Build a projection item for the non-trivial definition. 3913 // 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)))); 3918 } 3919 } 3920 } 3921 3922 if (foundTrivialGroupAggregateProjection) 3923 { 3924 if (groupPartitionDefinitions.Count > 0) 3925 { 3926 // 3927 // Add projection item for groupAggregateVarRef if there are reduced definitions. 3928 // 3929 groupPartitionDefinitions.Add(new KeyValuePair<string, DbExpression>( 3930 groupAggregateVarRef.VariableName, 3931 groupBinding.Variable.Property(groupAggregateVarRef.VariableName))); 3932 } 3933 else 3934 { 3935 // 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. 3939 // 3940 groupPartitionDefinitions = null; 3941 } 3942 } 3943 3944 return groupPartitionDefinitions; 3945 } 3946 3947 /// <summary> 3948 /// Returns true if lambda accepts a collection variable and trivially projects out its elements. 3949 /// </summary> IsTrivialInputProjection(DbVariableReferenceExpression lambdaVariable, DbExpression lambdaBody)3950 private static bool IsTrivialInputProjection(DbVariableReferenceExpression lambdaVariable, DbExpression lambdaBody) 3951 { 3952 if (lambdaBody.ExpressionKind != DbExpressionKind.Project) 3953 { 3954 return false; 3955 } 3956 DbProjectExpression projectExpression = (DbProjectExpression)lambdaBody; 3957 3958 if (projectExpression.Input.Expression != lambdaVariable) 3959 { 3960 return false; 3961 } 3962 3963 Debug.Assert(TypeSemantics.IsCollectionType(lambdaVariable.ResultType)); 3964 3965 if (projectExpression.Projection.ExpressionKind == DbExpressionKind.VariableReference) 3966 { 3967 DbVariableReferenceExpression projectionExpression = (DbVariableReferenceExpression)projectExpression.Projection; 3968 return projectionExpression == projectExpression.Input.Variable; 3969 } 3970 else if (projectExpression.Projection.ExpressionKind == DbExpressionKind.NewInstance && 3971 TypeSemantics.IsRowType(projectExpression.Projection.ResultType)) 3972 { 3973 if (!TypeSemantics.IsEqual(projectExpression.Projection.ResultType, projectExpression.Input.Variable.ResultType)) 3974 { 3975 return false; 3976 } 3977 3978 IBaseList<EdmMember> inputVariableTypeProperties = TypeHelpers.GetAllStructuralMembers(projectExpression.Input.Variable.ResultType); 3979 3980 DbNewInstanceExpression projectionExpression = (DbNewInstanceExpression)projectExpression.Projection; 3981 3982 Debug.Assert(projectionExpression.Arguments.Count == inputVariableTypeProperties.Count, "projectionExpression.Arguments.Count == inputVariableTypeProperties.Count"); 3983 for (int i = 0; i < projectionExpression.Arguments.Count; ++i) 3984 { 3985 if (projectionExpression.Arguments[i].ExpressionKind != DbExpressionKind.Property) 3986 { 3987 return false; 3988 } 3989 DbPropertyExpression propertyRef = (DbPropertyExpression)projectionExpression.Arguments[i]; 3990 3991 if (propertyRef.Instance != projectExpression.Input.Variable || 3992 propertyRef.Property != inputVariableTypeProperties[i]) 3993 { 3994 return false; 3995 } 3996 } 3997 3998 return true; 3999 } 4000 4001 return false; 4002 } 4003 4004 private sealed class GroupKeyInfo 4005 { GroupKeyInfo(string name, DbExpression varBasedKeyExpr, DbExpression groupVarBasedKeyExpr, DbExpression groupAggBasedKeyExpr)4006 internal GroupKeyInfo(string name, DbExpression varBasedKeyExpr, DbExpression groupVarBasedKeyExpr, DbExpression groupAggBasedKeyExpr) 4007 { 4008 Name = name; 4009 VarRef = varBasedKeyExpr.ResultType.Variable(name); 4010 VarBasedKeyExpr = varBasedKeyExpr; 4011 GroupVarBasedKeyExpr = groupVarBasedKeyExpr; 4012 GroupAggBasedKeyExpr = groupAggBasedKeyExpr; 4013 } 4014 4015 /// <summary> 4016 /// The primary name of the group key. It is used to refer to the key from other expressions. 4017 /// </summary> 4018 internal readonly string Name; 4019 4020 /// <summary> 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. 4025 /// </summary> 4026 internal string[] AlternativeName 4027 { 4028 get { return _alternativeName; } 4029 set 4030 { 4031 Debug.Assert(_alternativeName == null, "GroupKeyInfo.AlternativeName can not be reset"); 4032 _alternativeName = value; 4033 } 4034 } 4035 private string[] _alternativeName; 4036 4037 internal readonly DbVariableReferenceExpression VarRef; 4038 4039 internal readonly DbExpression VarBasedKeyExpr; 4040 4041 internal readonly DbExpression GroupVarBasedKeyExpr; 4042 4043 internal readonly DbExpression GroupAggBasedKeyExpr; 4044 } 4045 4046 /// <summary> 4047 /// Process ORDER BY clause. 4048 /// </summary> ProcessOrderByClause(DbExpressionBinding source, AST.QueryExpr queryExpr, out bool queryProjectionProcessed, SemanticResolver sr)4049 private static DbExpressionBinding ProcessOrderByClause(DbExpressionBinding source, AST.QueryExpr queryExpr, out bool queryProjectionProcessed, SemanticResolver sr) 4050 { 4051 Debug.Assert((sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode) ? null == queryExpr.OrderByClause : true, "ORDER BY clause must be null in RestrictedViewGenerationMode"); 4052 4053 queryProjectionProcessed = false; 4054 4055 if (queryExpr.OrderByClause == null) 4056 { 4057 return source; 4058 } 4059 4060 DbExpressionBinding sortBinding = null; 4061 AST.OrderByClause orderByClause = queryExpr.OrderByClause; 4062 AST.SelectClause selectClause = queryExpr.SelectClause; 4063 4064 // 4065 // Convert SKIP sub-clause if exists before adding projection expressions to the scope. 4066 // 4067 DbExpression convertedSkip = null; 4068 #region 4069 if (orderByClause.SkipSubClause != null) 4070 { 4071 // 4072 // Convert the skip expression. 4073 // 4074 convertedSkip = ConvertValueExpression(orderByClause.SkipSubClause, sr); 4075 4076 // 4077 // Ensure the converted expression is in the range of values. 4078 // 4079 ValidateExpressionIsCommandParamOrNonNegativeIntegerConstant(convertedSkip, orderByClause.SkipSubClause.ErrCtx, "SKIP", sr); 4080 } 4081 #endregion 4082 4083 // 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. 4090 // - Otherwise: 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). 4093 // 4094 var projectionItems = ConvertSelectClauseItems(queryExpr, sr); 4095 4096 if (selectClause.DistinctKind == AST.DistinctKind.Distinct) 4097 { 4098 // 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. 4103 // 4104 sr.CurrentScopeRegion.RollbackAllScopes(); 4105 } 4106 4107 // 4108 // Create temporary scope for SELECT clause items and add the items to the scope. 4109 // 4110 int savedScope = sr.CurrentScopeIndex; 4111 sr.EnterScope(); 4112 projectionItems.ForEach(projectionItem => sr.CurrentScope.Add(projectionItem.Key, new ProjectionItemDefinitionScopeEntry(projectionItem.Value))); 4113 4114 // 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. 4119 // 4120 if (selectClause.DistinctKind == AST.DistinctKind.Distinct) 4121 { 4122 // 4123 // Create distinct projection expression and bind to it. 4124 // 4125 DbExpression projectExpression = CreateProjectExpression(source, selectClause, projectionItems); 4126 Debug.Assert(projectExpression is DbDistinctExpression, "projectExpression is DbDistinctExpression"); 4127 source = projectExpression.BindAs(sr.GenerateInternalName("distinct")); 4128 4129 // 4130 // Replace SELECT clause item definitions with regular source scope entries pointing into the new source binding. 4131 // 4132 if (selectClause.SelectKind == AST.SelectKind.Value) 4133 { 4134 Debug.Assert(projectionItems.Count == 1, "projectionItems.Count == 1"); 4135 sr.CurrentScope.Replace(projectionItems[0].Key, new SourceScopeEntry(source.Variable)); 4136 } 4137 else 4138 { 4139 Debug.Assert(selectClause.SelectKind == AST.SelectKind.Row, "selectClause.SelectKind == AST.SelectKind.Row"); 4140 foreach (var projectionExpression in projectionItems) 4141 { 4142 DbVariableReferenceExpression projectionExpressionRef = projectionExpression.Value.ResultType.Variable(projectionExpression.Key); 4143 4144 sr.CurrentScope.Replace(projectionExpressionRef.VariableName, 4145 new SourceScopeEntry(projectionExpressionRef).AddParentVar(source.Variable)); 4146 } 4147 } 4148 4149 // 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. 4152 // 4153 queryProjectionProcessed = true; 4154 } 4155 4156 // 4157 // Convert sort keys. 4158 // 4159 List<DbSortClause> sortKeys = new List<DbSortClause>(orderByClause.OrderByClauseItem.Count); 4160 #region 4161 for (int i = 0; i < orderByClause.OrderByClauseItem.Count; i++) 4162 { 4163 AST.OrderByClauseItem orderClauseItem = orderByClause.OrderByClauseItem[i]; 4164 4165 sr.CurrentScopeRegion.WasResolutionCorrelated = false; 4166 4167 // 4168 // Convert order key expression. 4169 // 4170 DbExpression keyExpr = ConvertValueExpression(orderClauseItem.OrderExpr, sr); 4171 4172 // 4173 // Ensure key expression is correlated. 4174 // 4175 if (!sr.CurrentScopeRegion.WasResolutionCorrelated) 4176 { 4177 throw EntityUtil.EntitySqlError(orderClauseItem.ErrCtx, Strings.KeyMustBeCorrelated("ORDER BY")); 4178 } 4179 4180 // 4181 // Ensure key is order comparable. 4182 // 4183 if (!TypeHelpers.IsValidSortOpKeyType(keyExpr.ResultType)) 4184 { 4185 throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, Strings.OrderByKeyIsNotOrderComparable); 4186 } 4187 4188 // 4189 // Convert order direction. 4190 // 4191 bool ascSort = (orderClauseItem.OrderKind == AST.OrderKind.None) || (orderClauseItem.OrderKind == AST.OrderKind.Asc); 4192 4193 // 4194 // Convert collation. 4195 // 4196 string collation = null; 4197 if (orderClauseItem.Collation != null) 4198 { 4199 if (!IsStringType(keyExpr.ResultType)) 4200 { 4201 throw EntityUtil.EntitySqlError(orderClauseItem.OrderExpr.ErrCtx, Strings.InvalidKeyTypeForCollation(keyExpr.ResultType.EdmType.FullName)); 4202 } 4203 4204 collation = orderClauseItem.Collation.Name; 4205 } 4206 4207 // 4208 // Finish key conversion and add converted keys to key collection. 4209 // 4210 if (string.IsNullOrEmpty(collation)) 4211 { 4212 sortKeys.Add(ascSort ? keyExpr.ToSortClause() : keyExpr.ToSortClauseDescending()); 4213 } 4214 else 4215 { 4216 sortKeys.Add(ascSort ? keyExpr.ToSortClause(collation) : keyExpr.ToSortClauseDescending(collation)); 4217 } 4218 } 4219 #endregion 4220 4221 // 4222 // Remove the temporary projection scope with all the SELECT clause items on it. 4223 // 4224 sr.RollbackToScope(savedScope); 4225 4226 // 4227 // Create sort expression. 4228 // 4229 DbExpression sortSourceExpr = null; 4230 if (convertedSkip != null) 4231 { 4232 sortSourceExpr = source.Skip(sortKeys, convertedSkip); 4233 } 4234 else 4235 { 4236 sortSourceExpr = source.Sort(sortKeys); 4237 } 4238 4239 // 4240 // Create Sort Binding. 4241 // 4242 sortBinding = sortSourceExpr.BindAs(sr.GenerateInternalName("sort")); 4243 4244 // 4245 // Fixup Bindings. 4246 // 4247 if (queryProjectionProcessed) 4248 { 4249 Debug.Assert(sr.CurrentScopeIndex < sr.CurrentScopeRegion.FirstScopeIndex, "Current scope region is expected to have no scopes."); 4250 4251 /* 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). 4255 * 4256 4257 // 4258 // All the scopes of this current scope region have been rolled back. 4259 // Add new scope with all the projected items on it. 4260 // 4261 sr.EnterScope(); 4262 if (selectClause.SelectKind == AST.SelectKind.SelectRow) 4263 { 4264 foreach (var projectionExpression in projectionItems) 4265 { 4266 DbVariableReferenceExpression projectionExpressionRef = projectionExpression.Value.ResultType.Variable(projectionExpression.Key); 4267 sr.CurrentScope.Add(projectionExpressionRef.VariableName, 4268 new SourceScopeEntry(projectionExpressionRef).AddParentVar(sortBinding.Variable)); 4269 } 4270 } 4271 else 4272 { 4273 Debug.Assert(selectClause.SelectKind == AST.SelectKind.SelectValue, "selectClause.SelectKind == AST.SelectKind.SelectValue"); 4274 Debug.Assert(projectionItems.Count == 1, "projectionItems.Count == 1"); 4275 4276 sr.CurrentScope.Add(projectionItems[0].Key, new SourceScopeEntry(sortBinding.Variable)); 4277 }*/ 4278 } 4279 else 4280 { 4281 sr.CurrentScopeRegion.ApplyToScopeEntries(scopeEntry => 4282 { 4283 Debug.Assert(scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef, 4284 "scopeEntry.EntryKind == ScopeEntryKind.SourceVar || scopeEntry.EntryKind == ScopeEntryKind.InvalidGroupInputRef"); 4285 4286 if (scopeEntry.EntryKind == ScopeEntryKind.SourceVar) 4287 { 4288 ((SourceScopeEntry)scopeEntry).ReplaceParentVar(sortBinding.Variable); 4289 } 4290 }); 4291 } 4292 4293 Debug.Assert(null != sortBinding, "null != sortBinding"); 4294 4295 return sortBinding; 4296 } 4297 4298 /// <summary> 4299 /// Convert "x in multiset(y1, y2, ..., yn)" into 4300 /// x = y1 or x = y2 or x = y3 ... 4301 /// </summary> 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> ConvertSimpleInExpression(SemanticResolver sr, DbExpression left, DbExpression right)4306 private static DbExpression ConvertSimpleInExpression(SemanticResolver sr, DbExpression left, DbExpression right) 4307 { 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; 4311 4312 if (rightColl.Arguments.Count == 0) 4313 { 4314 return DbExpressionBuilder.False; 4315 } 4316 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)); 4320 4321 return orExpr; 4322 } 4323 IsStringType(TypeUsage type)4324 private static bool IsStringType(TypeUsage type) 4325 { 4326 return TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String); 4327 } 4328 IsBooleanType(TypeUsage type)4329 private static bool IsBooleanType(TypeUsage type) 4330 { 4331 return TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Boolean); 4332 } 4333 IsSubOrSuperType(TypeUsage type1, TypeUsage type2)4334 private static bool IsSubOrSuperType(TypeUsage type1, TypeUsage type2) 4335 { 4336 return TypeSemantics.IsStructurallyEqual(type1, type2) || type1.IsSubtypeOf(type2) || type2.IsSubtypeOf(type1); 4337 } 4338 4339 #region Expression converters 4340 AstExprConverter(AST.Node astExpr, SemanticResolver sr)4341 private delegate ExpressionResolution AstExprConverter(AST.Node astExpr, SemanticResolver sr); 4342 private static readonly Dictionary<Type, AstExprConverter> _astExprConverters = CreateAstExprConverters(); BuiltInExprConverter(AST.BuiltInExpr astBltInExpr, SemanticResolver sr)4343 private delegate DbExpression BuiltInExprConverter(AST.BuiltInExpr astBltInExpr, SemanticResolver sr); 4344 private static readonly Dictionary<AST.BuiltInKind, BuiltInExprConverter> _builtInExprConverter = CreateBuiltInExprConverter(); 4345 CreateAstExprConverters()4346 private static Dictionary<Type, AstExprConverter> CreateAstExprConverters() 4347 { 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; 4369 } 4370 CreateBuiltInExprConverter()4371 private static Dictionary<AST.BuiltInKind, BuiltInExprConverter> CreateBuiltInExprConverter() 4372 { 4373 Dictionary<AST.BuiltInKind, BuiltInExprConverter> builtInExprConverter = new Dictionary<AST.BuiltInKind, BuiltInExprConverter>(sizeof(AST.BuiltInKind)); 4374 4375 //////////////////////////// 4376 // Arithmetic Expressions 4377 //////////////////////////// 4378 4379 // 4380 // e1 + e2 4381 // 4382 #region e1 + e2 4383 builtInExprConverter.Add(AST.BuiltInKind.Plus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4384 { 4385 Pair<DbExpression, DbExpression> args = ConvertPlusOperands(bltInExpr, sr); 4386 4387 if (TypeSemantics.IsNumericType(args.Left.ResultType)) 4388 { 4389 return args.Left.Plus(args.Right); 4390 } 4391 else 4392 { 4393 // 4394 // fold '+' operator into concat canonical function 4395 // 4396 MetadataFunctionGroup function; 4397 if (!sr.TypeResolver.TryGetFunctionFromMetadata("Edm", "Concat", out function)) 4398 { 4399 throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, Strings.ConcatBuiltinNotSupported); 4400 } 4401 4402 List<TypeUsage> argTypes = new List<TypeUsage>(2); 4403 argTypes.Add(args.Left.ResultType); 4404 argTypes.Add(args.Right.ResultType); 4405 4406 bool isAmbiguous = false; 4407 EdmFunction concatFunction = SemanticResolver.ResolveFunctionOverloads( 4408 function.FunctionMetadata, 4409 argTypes, 4410 false /* isGroupAggregate */, 4411 out isAmbiguous); 4412 4413 if (null == concatFunction || isAmbiguous) 4414 { 4415 throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, Strings.ConcatBuiltinNotSupported); 4416 } 4417 4418 return concatFunction.Invoke(new[] { args.Left, args.Right }); 4419 } 4420 4421 }); 4422 #endregion 4423 4424 // 4425 // e1 - e2 4426 // 4427 #region e1 - e2 4428 builtInExprConverter.Add(AST.BuiltInKind.Minus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4429 { 4430 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr); 4431 4432 return args.Left.Minus(args.Right); 4433 }); 4434 #endregion 4435 4436 // 4437 // e1 * e2 4438 // 4439 #region e1 * e2 4440 builtInExprConverter.Add(AST.BuiltInKind.Multiply, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4441 { 4442 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr); 4443 4444 return args.Left.Multiply(args.Right); 4445 }); 4446 #endregion 4447 4448 // 4449 // e1 / e2 4450 // 4451 #region e1 / e2 4452 builtInExprConverter.Add(AST.BuiltInKind.Divide, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4453 { 4454 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr); 4455 4456 return args.Left.Divide(args.Right); 4457 }); 4458 #endregion 4459 4460 // 4461 // e1 % e2 4462 // 4463 #region e1 % e2 4464 builtInExprConverter.Add(AST.BuiltInKind.Modulus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4465 { 4466 Pair<DbExpression, DbExpression> args = ConvertArithmeticArgs(bltInExpr, sr); 4467 4468 return args.Left.Modulo(args.Right); 4469 }); 4470 #endregion 4471 4472 // 4473 // - e 4474 // 4475 #region - e 4476 builtInExprConverter.Add(AST.BuiltInKind.UnaryMinus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4477 { 4478 DbExpression argument = ConvertArithmeticArgs(bltInExpr, sr).Left; 4479 if (TypeSemantics.IsUnsignedNumericType(argument.ResultType)) 4480 { 4481 TypeUsage closestPromotableType = null; 4482 if (!TypeHelpers.TryGetClosestPromotableType(argument.ResultType, out closestPromotableType)) 4483 { 4484 throw EntityUtil.EntitySqlError(Strings.InvalidUnsignedTypeForUnaryMinusOperation(argument.ResultType.EdmType.FullName)); 4485 } 4486 } 4487 4488 DbExpression unaryExpr = argument.UnaryMinus(); 4489 return unaryExpr; 4490 }); 4491 #endregion 4492 4493 // 4494 // + e 4495 // 4496 #region + e 4497 builtInExprConverter.Add(AST.BuiltInKind.UnaryPlus, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4498 { 4499 return ConvertArithmeticArgs(bltInExpr, sr).Left; 4500 }); 4501 #endregion 4502 4503 //////////////////////////// 4504 // Logical Expressions 4505 //////////////////////////// 4506 4507 // 4508 // e1 AND e2 4509 // e1 && e2 4510 // 4511 #region e1 AND e2 4512 builtInExprConverter.Add(AST.BuiltInKind.And, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4513 { 4514 Pair<DbExpression, DbExpression> args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr); 4515 4516 return args.Left.And(args.Right); 4517 }); 4518 #endregion 4519 4520 // 4521 // e1 OR e2 4522 // e1 || e2 4523 // 4524 #region e1 OR e2 4525 builtInExprConverter.Add(AST.BuiltInKind.Or, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4526 { 4527 Pair<DbExpression, DbExpression> args = SemanticAnalyzer.ConvertLogicalArgs(bltInExpr, sr); 4528 4529 return args.Left.Or(args.Right); 4530 }); 4531 #endregion 4532 4533 // 4534 // NOT e 4535 // ! e 4536 // 4537 #region NOT e 4538 builtInExprConverter.Add(AST.BuiltInKind.Not, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4539 { 4540 return ConvertLogicalArgs(bltInExpr, sr).Left.Not(); 4541 }); 4542 #endregion 4543 4544 //////////////////////////// 4545 // Comparison Expressions 4546 //////////////////////////// 4547 4548 // 4549 // e1 == e2 | e1 = e2 4550 // 4551 #region e1 == e2 4552 builtInExprConverter.Add(AST.BuiltInKind.Equal, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4553 { 4554 Pair<DbExpression, DbExpression> args = ConvertEqualCompArgs(bltInExpr, sr); 4555 4556 return args.Left.Equal(args.Right); 4557 }); 4558 #endregion 4559 4560 // 4561 // e1 != e2 | e1 <> e2 4562 // 4563 #region e1 != e2 4564 builtInExprConverter.Add(AST.BuiltInKind.NotEqual, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4565 { 4566 Pair<DbExpression, DbExpression> args = ConvertEqualCompArgs(bltInExpr, sr); 4567 4568 // 4569 4570 return args.Left.Equal(args.Right).Not(); 4571 }); 4572 #endregion 4573 4574 // 4575 // e1 >= e2 4576 // 4577 #region e1 >= e2 4578 builtInExprConverter.Add(AST.BuiltInKind.GreaterEqual, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4579 { 4580 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr); 4581 4582 return args.Left.GreaterThanOrEqual(args.Right); 4583 }); 4584 #endregion 4585 4586 // 4587 // e1 > e2 4588 // 4589 #region e1 > e2 4590 builtInExprConverter.Add(AST.BuiltInKind.GreaterThan, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4591 { 4592 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr); 4593 4594 return args.Left.GreaterThan(args.Right); 4595 }); 4596 #endregion 4597 4598 // 4599 // e1 <= e2 4600 // 4601 #region e1 <= e2 4602 builtInExprConverter.Add(AST.BuiltInKind.LessEqual, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4603 { 4604 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr); 4605 4606 return args.Left.LessThanOrEqual(args.Right); 4607 }); 4608 #endregion 4609 4610 // 4611 // e1 < e2 4612 // 4613 #region e1 < e2 4614 builtInExprConverter.Add(AST.BuiltInKind.LessThan, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4615 { 4616 Pair<DbExpression, DbExpression> args = ConvertOrderCompArgs(bltInExpr, sr); 4617 4618 return args.Left.LessThan(args.Right); 4619 }); 4620 #endregion 4621 4622 4623 //////////////////////////// 4624 // SET EXPRESSIONS 4625 //////////////////////////// 4626 4627 4628 // 4629 // e1 UNION e2 4630 // 4631 #region e1 UNION e2 4632 builtInExprConverter.Add(AST.BuiltInKind.Union, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4633 { 4634 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr); 4635 4636 return args.Left.UnionAll(args.Right).Distinct(); 4637 }); 4638 #endregion 4639 4640 // 4641 // e1 UNION ALL e2 4642 // 4643 #region e1 UNION ALL e2 4644 builtInExprConverter.Add(AST.BuiltInKind.UnionAll, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4645 { 4646 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr); 4647 4648 return args.Left.UnionAll(args.Right); 4649 }); 4650 #endregion 4651 4652 // 4653 // e1 INTERSECT e2 4654 // 4655 #region e1 INTERSECT e2 4656 builtInExprConverter.Add(AST.BuiltInKind.Intersect, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4657 { 4658 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr); 4659 4660 return args.Left.Intersect(args.Right); 4661 }); 4662 #endregion 4663 4664 // 4665 // e1 OVERLAPS e2 4666 // 4667 #region e1 OVERLAPS e1 4668 builtInExprConverter.Add(AST.BuiltInKind.Overlaps, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4669 { 4670 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr); 4671 4672 return args.Left.Intersect(args.Right).IsEmpty().Not(); 4673 }); 4674 #endregion 4675 4676 // 4677 // ANYELEMENT( e ) 4678 // 4679 #region ANYELEMENT( e ) 4680 builtInExprConverter.Add(AST.BuiltInKind.AnyElement, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4681 { 4682 return ConvertSetArgs(bltInExpr, sr).Left.Element(); 4683 }); 4684 #endregion 4685 4686 // 4687 // ELEMENT( e ) 4688 // 4689 #region ELEMENT( e ) - NOT SUPPORTED IN ORCAS TIMEFRAME 4690 builtInExprConverter.Add(AST.BuiltInKind.Element, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4691 { 4692 throw EntityUtil.NotSupported(Strings.ElementOperatorIsNotSupported); 4693 }); 4694 #endregion 4695 4696 // 4697 // e1 EXCEPT e2 4698 // 4699 #region e1 EXCEPT e2 4700 builtInExprConverter.Add(AST.BuiltInKind.Except, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4701 { 4702 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr); 4703 4704 return args.Left.Except(args.Right); 4705 }); 4706 #endregion 4707 4708 // 4709 // EXISTS( e ) 4710 // 4711 #region EXISTS( e ) 4712 builtInExprConverter.Add(AST.BuiltInKind.Exists, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4713 { 4714 return ConvertSetArgs(bltInExpr, sr).Left.IsEmpty().Not(); 4715 }); 4716 #endregion 4717 4718 // 4719 // FLATTEN( e ) 4720 // 4721 #region FLATTEN( e ) 4722 builtInExprConverter.Add(AST.BuiltInKind.Flatten, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4723 { 4724 DbExpression elemExpr = ConvertValueExpression(bltInExpr.Arg1, sr); 4725 4726 if (!TypeSemantics.IsCollectionType(elemExpr.ResultType)) 4727 { 4728 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidFlattenArgument); 4729 } 4730 4731 if (!TypeSemantics.IsCollectionType(TypeHelpers.GetElementTypeUsage(elemExpr.ResultType))) 4732 { 4733 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidFlattenArgument); 4734 } 4735 4736 DbExpressionBinding leftExpr = elemExpr.BindAs(sr.GenerateInternalName("l_flatten")); 4737 4738 DbExpressionBinding rightExpr = leftExpr.Variable.BindAs(sr.GenerateInternalName("r_flatten")); 4739 4740 DbExpressionBinding applyBinding = leftExpr.CrossApply(rightExpr).BindAs(sr.GenerateInternalName("flatten")); 4741 4742 return applyBinding.Project(applyBinding.Variable.Property(rightExpr.VariableName)); 4743 }); 4744 #endregion 4745 4746 // 4747 // e1 IN e2 4748 // 4749 #region e1 IN e2 4750 builtInExprConverter.Add(AST.BuiltInKind.In, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4751 { 4752 Pair<DbExpression, DbExpression> args = ConvertInExprArgs(bltInExpr, sr); 4753 4754 // 4755 // Convert "x in multiset(y1, y2, ..., yn)" into x = y1 or x = y2 or x = y3 ... 4756 // 4757 if (args.Right.ExpressionKind == DbExpressionKind.NewInstance) 4758 { 4759 return ConvertSimpleInExpression(sr, args.Left, args.Right); 4760 } 4761 else 4762 { 4763 DbExpressionBinding rSet = args.Right.BindAs(sr.GenerateInternalName("in-filter")); 4764 4765 DbExpression leftIn = args.Left; 4766 DbExpression rightSet = rSet.Variable; 4767 4768 DbExpression exists = rSet.Filter(leftIn.Equal(rightSet)).IsEmpty().Not(); 4769 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)); 4774 4775 DbExpression left = DbExpressionBuilder.Case(whenExpr, thenExpr, DbExpressionBuilder.False); 4776 4777 DbExpression converted = left.Or(exists); 4778 4779 return converted; 4780 } 4781 }); 4782 #endregion 4783 4784 // 4785 // e1 NOT IN e1 4786 // 4787 #region e1 NOT IN e1 4788 builtInExprConverter.Add(AST.BuiltInKind.NotIn, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4789 { 4790 Pair<DbExpression, DbExpression> args = ConvertInExprArgs(bltInExpr, sr); 4791 4792 if (args.Right.ExpressionKind == DbExpressionKind.NewInstance) 4793 { 4794 return ConvertSimpleInExpression(sr, args.Left, args.Right).Not(); 4795 } 4796 else 4797 { 4798 DbExpressionBinding rSet = args.Right.BindAs(sr.GenerateInternalName("in-filter")); 4799 4800 DbExpression leftIn = args.Left; 4801 DbExpression rightSet = rSet.Variable; 4802 4803 DbExpression exists = rSet.Filter(leftIn.Equal(rightSet)).IsEmpty(); 4804 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)); 4809 4810 DbExpression left = DbExpressionBuilder.Case(whenExpr, thenExpr, DbExpressionBuilder.True); 4811 4812 DbExpression converted = left.And(exists); 4813 4814 return converted; 4815 } 4816 }); 4817 #endregion 4818 4819 // 4820 // SET( e ) - DISTINCT( e ) before 4821 // 4822 #region SET( e ) 4823 builtInExprConverter.Add(AST.BuiltInKind.Distinct, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4824 { 4825 Pair<DbExpression, DbExpression> args = ConvertSetArgs(bltInExpr, sr); 4826 4827 return args.Left.Distinct(); 4828 }); 4829 #endregion 4830 4831 4832 //////////////////////////// 4833 // Nullabity Expressions 4834 //////////////////////////// 4835 4836 // 4837 // e IS NULL 4838 // 4839 #region e IS NULL 4840 builtInExprConverter.Add(AST.BuiltInKind.IsNull, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4841 { 4842 DbExpression isNullExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr); 4843 4844 // 4845 // Ensure expression type is valid for this operation. 4846 // 4847 if (isNullExpr != null && !TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType)) 4848 { 4849 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.IsNullInvalidType); 4850 } 4851 4852 return isNullExpr != null ? (DbExpression)isNullExpr.IsNull() : DbExpressionBuilder.True; 4853 }); 4854 #endregion 4855 4856 // 4857 // e IS NOT NULL 4858 // 4859 #region e IS NOT NULL 4860 builtInExprConverter.Add(AST.BuiltInKind.IsNotNull, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4861 { 4862 DbExpression isNullExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr); 4863 4864 // 4865 // Ensure expression type is valid for this operation. 4866 // 4867 if (isNullExpr != null && !TypeHelpers.IsValidIsNullOpType(isNullExpr.ResultType)) 4868 { 4869 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.IsNullInvalidType); 4870 } 4871 4872 return isNullExpr != null ? (DbExpression)isNullExpr.IsNull().Not() : DbExpressionBuilder.False; 4873 }); 4874 #endregion 4875 4876 //////////////////////////// 4877 // Type Expressions 4878 //////////////////////////// 4879 4880 // 4881 // e IS OF ( [ONLY] T ) 4882 // 4883 #region e IS OF ( [ONLY] T ) 4884 builtInExprConverter.Add(AST.BuiltInKind.IsOf, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4885 { 4886 var exprToFilter = ConvertValueExpression(bltInExpr.Arg1, sr); 4887 var typeToFilterTo = ConvertTypeName(bltInExpr.Arg2, sr); 4888 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; 4892 4893 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(exprToFilter.ResultType)) 4894 { 4895 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, 4896 Strings.ExpressionTypeMustBeEntityType(Strings.CtxIsOf, 4897 exprToFilter.ResultType.EdmType.BuiltInTypeKind.ToString(), 4898 exprToFilter.ResultType.EdmType.FullName)); 4899 } 4900 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(exprToFilter.ResultType)) 4901 { 4902 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, 4903 Strings.ExpressionTypeMustBeNominalType(Strings.CtxIsOf, 4904 exprToFilter.ResultType.EdmType.BuiltInTypeKind.ToString(), 4905 exprToFilter.ResultType.EdmType.FullName)); 4906 } 4907 4908 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(typeToFilterTo)) 4909 { 4910 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeEntityType(Strings.CtxIsOf, 4911 typeToFilterTo.EdmType.BuiltInTypeKind.ToString(), 4912 typeToFilterTo.EdmType.FullName)); 4913 } 4914 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(typeToFilterTo)) 4915 { 4916 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeNominalType(Strings.CtxIsOf, 4917 typeToFilterTo.EdmType.BuiltInTypeKind.ToString(), 4918 typeToFilterTo.EdmType.FullName)); 4919 } 4920 4921 if (!TypeSemantics.IsPolymorphicType(exprToFilter.ResultType)) 4922 { 4923 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.TypeMustBeInheritableType); 4924 } 4925 4926 if (!TypeSemantics.IsPolymorphicType(typeToFilterTo)) 4927 { 4928 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeInheritableType); 4929 } 4930 4931 if (!IsSubOrSuperType(exprToFilter.ResultType, typeToFilterTo)) 4932 { 4933 throw EntityUtil.EntitySqlError(bltInExpr.ErrCtx, Strings.NotASuperOrSubType(exprToFilter.ResultType.EdmType.FullName, 4934 typeToFilterTo.EdmType.FullName)); 4935 } 4936 4937 typeToFilterTo = TypeHelpers.GetReadOnlyType(typeToFilterTo); 4938 4939 DbExpression retExpr = null; 4940 if (isOnly) 4941 { 4942 retExpr = exprToFilter.IsOfOnly(typeToFilterTo); 4943 } 4944 else 4945 { 4946 retExpr = exprToFilter.IsOf(typeToFilterTo); 4947 } 4948 4949 if (isNot) 4950 { 4951 retExpr = retExpr.Not(); 4952 } 4953 4954 return retExpr; 4955 }); 4956 #endregion 4957 4958 // 4959 // TREAT( e as T ) 4960 // 4961 #region TREAT( e as T ) 4962 builtInExprConverter.Add(AST.BuiltInKind.Treat, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 4963 { 4964 var exprToTreat = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr); 4965 var typeToTreatTo = ConvertTypeName(bltInExpr.Arg2, sr); 4966 4967 bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode; 4968 4969 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(typeToTreatTo)) 4970 { 4971 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, 4972 Strings.TypeMustBeEntityType(Strings.CtxTreat, 4973 typeToTreatTo.EdmType.BuiltInTypeKind.ToString(), 4974 typeToTreatTo.EdmType.FullName)); 4975 } 4976 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(typeToTreatTo)) 4977 { 4978 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, 4979 Strings.TypeMustBeNominalType(Strings.CtxTreat, 4980 typeToTreatTo.EdmType.BuiltInTypeKind.ToString(), 4981 typeToTreatTo.EdmType.FullName)); 4982 } 4983 4984 if (exprToTreat == null) 4985 { 4986 exprToTreat = DbExpressionBuilder.Null(typeToTreatTo); 4987 } 4988 else if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(exprToTreat.ResultType)) 4989 { 4990 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, 4991 Strings.ExpressionTypeMustBeEntityType(Strings.CtxTreat, 4992 exprToTreat.ResultType.EdmType.BuiltInTypeKind.ToString(), 4993 exprToTreat.ResultType.EdmType.FullName)); 4994 } 4995 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(exprToTreat.ResultType)) 4996 { 4997 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, 4998 Strings.ExpressionTypeMustBeNominalType(Strings.CtxTreat, 4999 exprToTreat.ResultType.EdmType.BuiltInTypeKind.ToString(), 5000 exprToTreat.ResultType.EdmType.FullName)); 5001 } 5002 5003 if (!TypeSemantics.IsPolymorphicType(exprToTreat.ResultType)) 5004 { 5005 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.TypeMustBeInheritableType); 5006 } 5007 5008 if (!TypeSemantics.IsPolymorphicType(typeToTreatTo)) 5009 { 5010 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.TypeMustBeInheritableType); 5011 } 5012 5013 if (!IsSubOrSuperType(exprToTreat.ResultType, typeToTreatTo)) 5014 { 5015 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.NotASuperOrSubType(exprToTreat.ResultType.EdmType.FullName, 5016 typeToTreatTo.EdmType.FullName)); 5017 } 5018 5019 return exprToTreat.TreatAs(TypeHelpers.GetReadOnlyType(typeToTreatTo)); 5020 }); 5021 #endregion 5022 5023 // 5024 // CAST( e AS T ) 5025 // 5026 #region CAST( e AS T ) 5027 builtInExprConverter.Add(AST.BuiltInKind.Cast, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 5028 { 5029 var exprToCast = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr); 5030 var typeToCastTo = ConvertTypeName(bltInExpr.Arg2, sr); 5031 5032 // 5033 // Ensure CAST target type is scalar. 5034 // 5035 if (!TypeSemantics.IsScalarType(typeToCastTo)) 5036 { 5037 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.InvalidCastType); 5038 } 5039 5040 if (exprToCast == null) 5041 { 5042 return DbExpressionBuilder.Null(typeToCastTo); 5043 } 5044 5045 // 5046 // Ensure CAST source type is scalar. 5047 // 5048 if (!TypeSemantics.IsScalarType(exprToCast.ResultType)) 5049 { 5050 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidCastExpressionType); 5051 } 5052 5053 if (!TypeSemantics.IsCastAllowed(exprToCast.ResultType, typeToCastTo)) 5054 { 5055 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.InvalidCast(exprToCast.ResultType.EdmType.FullName, typeToCastTo.EdmType.FullName)); 5056 } 5057 5058 return exprToCast.CastTo(TypeHelpers.GetReadOnlyType(typeToCastTo)); 5059 }); 5060 #endregion 5061 5062 // 5063 // OFTYPE( [ONLY] e, T ) 5064 // 5065 #region OFTYPE( [ONLY] e, T ) 5066 builtInExprConverter.Add(AST.BuiltInKind.OfType, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 5067 { 5068 var exprToFilter = ConvertValueExpression(bltInExpr.Arg1, sr); 5069 var typeToFilterTo = ConvertTypeName(bltInExpr.Arg2, sr); 5070 5071 bool isOnly = (bool)((AST.Literal)bltInExpr.Arg3).Value; 5072 5073 bool isNominalTypeAllowed = sr.ParserOptions.ParserCompilationMode == ParserOptions.CompilationMode.RestrictedViewGenerationMode; 5074 5075 if (!TypeSemantics.IsCollectionType(exprToFilter.ResultType)) 5076 { 5077 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.ExpressionMustBeCollection); 5078 } 5079 5080 TypeUsage elementType = TypeHelpers.GetElementTypeUsage(exprToFilter.ResultType); 5081 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(elementType)) 5082 { 5083 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, 5084 Strings.OfTypeExpressionElementTypeMustBeEntityType(elementType.EdmType.BuiltInTypeKind.ToString(), elementType)); 5085 } 5086 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(elementType)) 5087 { 5088 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, 5089 Strings.OfTypeExpressionElementTypeMustBeNominalType(elementType.EdmType.BuiltInTypeKind.ToString(), elementType)); 5090 } 5091 5092 if (!isNominalTypeAllowed && !TypeSemantics.IsEntityType(typeToFilterTo)) 5093 { 5094 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, 5095 Strings.TypeMustBeEntityType(Strings.CtxOfType, typeToFilterTo.EdmType.BuiltInTypeKind.ToString(), typeToFilterTo.EdmType.FullName)); 5096 } 5097 else if (isNominalTypeAllowed && !TypeSemantics.IsNominalType(typeToFilterTo)) 5098 { 5099 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, 5100 Strings.TypeMustBeNominalType(Strings.CtxOfType, typeToFilterTo.EdmType.BuiltInTypeKind.ToString(), typeToFilterTo.EdmType.FullName)); 5101 } 5102 5103 if (isOnly && typeToFilterTo.EdmType.Abstract) 5104 { 5105 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.OfTypeOnlyTypeArgumentCannotBeAbstract(typeToFilterTo.EdmType.FullName)); 5106 } 5107 5108 if (!IsSubOrSuperType(elementType, typeToFilterTo)) 5109 { 5110 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.NotASuperOrSubType(elementType.EdmType.FullName, typeToFilterTo.EdmType.FullName)); 5111 } 5112 5113 DbExpression ofTypeExpression = null; 5114 if (isOnly) 5115 { 5116 ofTypeExpression = exprToFilter.OfTypeOnly(TypeHelpers.GetReadOnlyType(typeToFilterTo)); 5117 } 5118 else 5119 { 5120 ofTypeExpression = exprToFilter.OfType(TypeHelpers.GetReadOnlyType(typeToFilterTo)); 5121 } 5122 5123 return ofTypeExpression; 5124 }); 5125 #endregion 5126 5127 // 5128 // e LIKE pattern [ESCAPE escape] 5129 // 5130 #region e LIKE pattern [ESCAPE escape] 5131 builtInExprConverter.Add(AST.BuiltInKind.Like, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 5132 { 5133 DbExpression likeExpr = null; 5134 5135 DbExpression matchExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr); 5136 if (matchExpr == null) 5137 { 5138 matchExpr = DbExpressionBuilder.Null(sr.TypeResolver.StringType); 5139 } 5140 else if (!IsStringType(matchExpr.ResultType)) 5141 { 5142 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.LikeArgMustBeStringType); 5143 } 5144 5145 DbExpression patternExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg2, sr); 5146 if (patternExpr == null) 5147 { 5148 patternExpr = DbExpressionBuilder.Null(sr.TypeResolver.StringType); 5149 } 5150 else if (!IsStringType(patternExpr.ResultType)) 5151 { 5152 throw EntityUtil.EntitySqlError(bltInExpr.Arg2.ErrCtx, Strings.LikeArgMustBeStringType); 5153 } 5154 5155 if (3 == bltInExpr.ArgCount) 5156 { 5157 DbExpression escapeExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg3, sr); 5158 if (escapeExpr == null) 5159 { 5160 escapeExpr = DbExpressionBuilder.Null(sr.TypeResolver.StringType); 5161 } 5162 else if (!IsStringType(escapeExpr.ResultType)) 5163 { 5164 throw EntityUtil.EntitySqlError(bltInExpr.Arg3.ErrCtx, Strings.LikeArgMustBeStringType); 5165 } 5166 5167 likeExpr = matchExpr.Like(patternExpr, escapeExpr); 5168 } 5169 else 5170 { 5171 likeExpr = matchExpr.Like(patternExpr); 5172 } 5173 5174 return likeExpr; 5175 }); 5176 #endregion 5177 5178 // 5179 // e BETWEEN e1 AND e2 5180 // 5181 #region e BETWEEN e1 AND e2 5182 builtInExprConverter.Add(AST.BuiltInKind.Between, ConvertBetweenExpr); 5183 #endregion 5184 5185 // 5186 // e NOT BETWEEN e1 AND e2 5187 // 5188 #region e NOT BETWEEN e1 AND e2 5189 builtInExprConverter.Add(AST.BuiltInKind.NotBetween, delegate(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 5190 { 5191 return ConvertBetweenExpr(bltInExpr, sr).Not(); 5192 }); 5193 #endregion 5194 5195 return builtInExprConverter; 5196 } 5197 ConvertBetweenExpr(AST.BuiltInExpr bltInExpr, SemanticResolver sr)5198 private static DbExpression ConvertBetweenExpr(AST.BuiltInExpr bltInExpr, SemanticResolver sr) 5199 { 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"); 5202 5203 // 5204 // convert lower and upper limits 5205 // 5206 Pair<DbExpression, DbExpression> limitsExpr = ConvertValueExpressionsWithUntypedNulls( 5207 bltInExpr.Arg2, 5208 bltInExpr.Arg3, 5209 bltInExpr.Arg1.ErrCtx, 5210 () => Strings.BetweenLimitsCannotBeUntypedNulls, 5211 sr); 5212 5213 // 5214 // Get and check common type for limits 5215 // 5216 TypeUsage rangeCommonType = TypeHelpers.GetCommonTypeUsage(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType); 5217 if (null == rangeCommonType) 5218 { 5219 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.BetweenLimitsTypesAreNotCompatible(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName)); 5220 } 5221 5222 // 5223 // check if limit types are order-comp 5224 // 5225 if (!TypeSemantics.IsOrderComparableTo(limitsExpr.Left.ResultType, limitsExpr.Right.ResultType)) 5226 { 5227 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.BetweenLimitsTypesAreNotOrderComparable(limitsExpr.Left.ResultType.EdmType.FullName, limitsExpr.Right.ResultType.EdmType.FullName)); 5228 } 5229 5230 // 5231 // convert value expression 5232 // 5233 DbExpression valueExpr = ConvertValueExpressionAllowUntypedNulls(bltInExpr.Arg1, sr); 5234 if (valueExpr == null) 5235 { 5236 valueExpr = DbExpressionBuilder.Null(rangeCommonType); 5237 } 5238 5239 // 5240 // check if valueExpr is order-comparable to limits 5241 // 5242 if (!TypeSemantics.IsOrderComparableTo(valueExpr.ResultType, rangeCommonType)) 5243 { 5244 throw EntityUtil.EntitySqlError(bltInExpr.Arg1.ErrCtx, Strings.BetweenValueIsNotOrderComparable(valueExpr.ResultType.EdmType.FullName, rangeCommonType.EdmType.FullName)); 5245 } 5246 5247 return valueExpr.GreaterThanOrEqual(limitsExpr.Left).And(valueExpr.LessThanOrEqual(limitsExpr.Right)); 5248 } 5249 #endregion 5250 } 5251 } 5252