Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Common / EntitySql / SemanticResolver.cs / 1 / SemanticResolver.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backup [....] //--------------------------------------------------------------------- namespace System.Data.Common.EntitySql { using System; using System.IO; using System.Globalization; using System.Collections.Generic; using System.Diagnostics; using System.Data.Mapping; using System.Data.Common.CommandTrees; using System.Data.Common; using System.Data.Metadata.Edm; using System.Data.Entity; ////// implements the semantic resolver in the context of a metadata workspace and typespace /// ///not thread safe internal sealed class SemanticResolver { private StaticContext _staticContext; private DbCommandTree _commandTree; private ParserOptions _parserOptions; private Dictionary_parameters; private Dictionary > _variables; private uint _namegenCounter = 0; private TypeResolver _typeResolver; private int _scopeIndexHighMark; private List _scopeRegionSavepoints = new List (); private List _scopeRegionFlags = new List (); private StringComparer _stringComparer; private List _aggregateAstNodes = new List (); /// /// Initializes semantic resolver /// /// /// options /// ordinary parameters /// variable parameters ////// internal SemanticResolver(Perspective perspective, ParserOptions parserOptions, Dictionary/// eSqlParameters, Dictionary variableParameters) { EntityUtil.CheckArgumentNull(perspective, "perspective"); EntityUtil.CheckArgumentNull(parserOptions, "parserOptions"); // // set parser options // _parserOptions = parserOptions; // // Initialize string comparer based on parser configuration // if (ParserOptions.CaseSensitiveness.CaseSensitive == _parserOptions.IdentifierCaseSensitiveness) { _stringComparer = StringComparer.Ordinal; } else { _stringComparer = StringComparer.OrdinalIgnoreCase; } // // Initialize Type Resolver // _typeResolver = new TypeResolver(perspective, _stringComparer); // // Creates Scope manager // _staticContext = new StaticContext(_stringComparer); // // Validate eSql parameters // _parameters = ValidateParameters(eSqlParameters); // // validate variable-representing // _variables = ValidateVariables(variableParameters); // // push a 'global' scope region // EnterScopeRegion(); // // set default scope visibility to All scopes // CurrentScopeRegionFlags.ScopeViewKind = ScopeViewKind.All; } /// /// returns command tree /// internal DbCommandTree CmdTree { get { return _commandTree; } } ////// returns parameters /// internal DictionaryParameters { get { return _parameters; } } /// /// returns variables /// internal Dictionary> Variables { get { return _variables; } } /// /// TypeSpace/Metadata/Perspective Dependent Type Resolver /// internal TypeResolver TypeResolver { get { return _typeResolver; } } ////// Returns current Parser Options /// internal ParserOptions ParserOptions { get { return _parserOptions; } } ////// returns the current string comparer /// internal StringComparer ScopeStringComparer { get { return _stringComparer; } } ////// sets the command tree factory /// /// ////// internal void SetCommandTreeFactory(CommandExpr astCommandExpr) { EntityUtil.CheckArgumentNull(astCommandExpr, "astCommandExpr"); if (null != _commandTree) { throw EntityUtil.EntitySqlError(Strings.CommandTreeCanOnlyBeSetOnce); } switch (astCommandExpr.QueryExpr.ExprKind) { case AstExprKind.Query: case AstExprKind.Generic: _commandTree = new DbQueryCommandTree(TypeResolver.Perspective.MetadataWorkspace, TypeResolver.Perspective.TargetDataspace); break; default: throw EntityUtil.Argument(Strings.UnknownAstExpressionType); } } ////// /// Sets the command tree factory using the specified command tree. /// When specifying a command tree, the AST command expression may /// only be a query/generic expression, since these are the only /// AST command expression that can be used to create a stand-alone /// The command expression that is the root of the AST /// The command tree to use when converting the AST into a. /// internal void SetCommandTreeFactory(CommandExpr astCommandExpr, DbCommandTree commandTree) { EntityUtil.CheckArgumentNull(astCommandExpr, "astCommandExpr"); EntityUtil.CheckArgumentNull(commandTree, "commandTree"); if (null != _commandTree) { throw EntityUtil.EntitySqlError(Strings.CommandTreeCanOnlyBeSetOnce); } DbQueryCommandTree queryTree = commandTree as DbQueryCommandTree; if (null == queryTree || (astCommandExpr.ExprKind != AstExprKind.Query && astCommandExpr.ExprKind != AstExprKind.Generic)) { throw EntityUtil.Argument(Strings.UnknownAstExpressionType); } _commandTree = commandTree; } /// /// declares and validates namespaces /// /// namespace expression list ////// internal void DeclareNamespaces(ExprList/// nsExprList) { if (null == nsExprList) { return; } for (int i = 0; i < nsExprList.Count; i++) { NamespaceExpr nsExpr = nsExprList[i]; string namespaceName = nsExpr.NamespaceName.FullName; if (namespaceName.Equals(EdmConstants.CanonicalFunctionNamespace, StringComparison.OrdinalIgnoreCase)) { throw EntityUtil.EntitySqlError(nsExpr.NamespaceName.ErrCtx, System.Data.Entity.Strings.CannotUseCanonicalNamespace(namespaceName)); } if (nsExpr.IsAliased) { string aliasName = nsExpr.AliasIdentifier.Name; if (!TypeResolver.TryAddAliasedNamespace(aliasName, namespaceName)) { throw EntityUtil.EntitySqlError(nsExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.NamespaceAliasAlreadyUsed(aliasName)); } } else { if (!TypeResolver.TryAddNamespace(namespaceName)) { throw EntityUtil.EntitySqlError(nsExpr.NamespaceName.ErrCtx, System.Data.Entity.Strings.NamespaceNameAlreadyDeclared(namespaceName)); } } } } /// /// Declares Canonical namespace in query scope /// internal void DeclareCanonicalNamespace() { if (!TypeResolver.TryAddNamespace(EdmConstants.CanonicalFunctionNamespace)) { throw EntityUtil.EntitySqlError(Strings.FailedToDeclareCanonicalNamespace); } } ////// defines scope visibility mode /// internal enum ScopeViewKind { All, /* default - all scopes plus types/extents */ CurrentContext, /* entire context - all stacked scopes */ CurrentScopeRegion, /* current scope region */ CurrentAndPreviousScope, /* top 2 scopes */ CurrentScope, /* current scope only */ GroupScope } ////// sets the current scope visibility /// /// internal void SetScopeView(ScopeViewKind viewKind) { CurrentScopeRegionFlags.ScopeViewKind = viewKind; switch (CurrentScopeRegionFlags.ScopeViewKind) { case ScopeViewKind.All: case ScopeViewKind.CurrentContext: case ScopeViewKind.GroupScope: _scopeIndexHighMark = 0; break; case ScopeViewKind.CurrentAndPreviousScope: _scopeIndexHighMark = CurrentScopeIndex - 1; break; case ScopeViewKind.CurrentScopeRegion: _scopeIndexHighMark = CurrentScopeRegionSavePoint.ScopeIndex; break; case ScopeViewKind.CurrentScope: _scopeIndexHighMark = CurrentScopeIndex; break; } Debug.Assert(_scopeIndexHighMark <= CurrentScopeIndex, "_scopeIndexHighMark <= CurrentScopeIndex"); } ////// returns current scope view kind /// ///internal ScopeViewKind GetScopeView() { return CurrentScopeRegionFlags.ScopeViewKind; } /// /// returns current scope index /// internal int CurrentScopeIndex { get { return _staticContext.CurrentScopeIndex; } } ////// Performs scope lookup returning the scope index entry /// /// /// ///internal bool TryScopeLookup(string key, out ScopeEntry scopeEntry) { int dummyVar; return TryScopeLookup(key, out scopeEntry, out dummyVar); } /// /// Performs scope lookup returning the scope index entry /// /// /// /// ///internal bool TryScopeLookup(string key, out ScopeEntry scopeEntry, out int scopeIndex) { return TryScopeLookup(key, false /* ignoreGroupScope */, out scopeEntry, out scopeIndex); } /// /// Performs scope lookup returning the scope index entry /// /// /// /// /// ///internal bool TryScopeLookup(string key, bool ignoreGroupScope, out ScopeEntry scopeEntry, out int scopeIndex) { scopeEntry = null; scopeIndex = -1; // assert that scope index is always greater or equal to current scope index // scopes grow top -> bottom with high mark always 'higher or equal' then the bottom // of the stack of scopes Debug.Assert(_scopeIndexHighMark <= CurrentScopeIndex, "_scopeIndexHighMark <= CurrentScopeIndex"); int i = 0; for (i = CurrentScopeIndex; i >= _scopeIndexHighMark; i--) { if (_staticContext.GetScopeByIndex(i).TryLookup(key, out scopeEntry)) { if (!ignoreGroupScope && CurrentScopeRegionFlags.IsInGroupScope && scopeEntry.VarKind == SourceVarKind.GroupInput) { return false; } scopeIndex = i; return true; } } return false; } /// /// returns the appropriate expression from a given scope entry. /// /// /// ///private DbExpression GetExpressionFromScopeEntry(ScopeEntry scopeEntry, int scopeIndex) { DbExpression innerExpr = null; innerExpr = scopeEntry.Expression; // // if it inside a group aggregate function, the 'base' expression is relative to // the groupVar, represented in the scope entry as AggregateExpression // if (CurrentScopeRegionFlags.IsInsideGroupAggregate) { if (_aggregateAstNodes.Count > 0) { _aggregateAstNodes[0].ScopeIndex = Math.Max(_aggregateAstNodes[0].ScopeIndex, scopeIndex); } SourceScopeEntry sse = scopeEntry as SourceScopeEntry; if (null != sse) { if (sse.GroupVarExpression != null) { return sse.AggregateExpression; } else { return innerExpr; } } DummyGroupVarScopeEntry dgv = scopeEntry as DummyGroupVarScopeEntry; if (null != dgv) { return dgv.AggregateExpression; } } Debug.Assert(null != innerExpr, "null != innerExpr"); return innerExpr; } /// /// resolve identifier or dotidentifier ast expression /// /// /// ////// ////// /// /// DbExpression internal DbExpression ResolveIdentifier(string[] names, ErrorContext errCtx) { Debug.Assert(CurrentScopeIndex >= _scopeIndexHighMark, "CurrentScopeIndex >= _scopeIndexHighMark FAILED"); Debug.Assert(_scopeRegionFlags.Count == _scopeRegionSavepoints.Count, "_scopeRegionFlags.Count == _scopeRegionSavepoints.Count FAILED"); TypeUsage definingType = null; DbExpression innerExpr = null; ScopeEntry scopeEntry; KeyValuePairvarInfo; int scopeIndex = -1; int suffixIndex = 0; // // try base type in scope first // if (names.Length > 1 && TryScopeLookup(TypeResolver.GetFullName(names), out scopeEntry, out scopeIndex)) { // // Sets correlation flag // SetScopeRegionCorrelationFlag(scopeIndex); return GetExpressionFromScopeEntry(scopeEntry, scopeIndex); } else if (TryScopeLookup(names[0], out scopeEntry, out scopeIndex)) { // // check for invalid left correlation // if (scopeEntry.Kind == ScopeEntryKind.JoinSourceVar && !CurrentScopeRegionFlags.IsInsideJoinOnPredicate) { throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidJoinLeftCorrelation); } // // Sets correlation flag // SetScopeRegionCorrelationFlag(scopeIndex); // // trim resolved prefix // names = TypeResolver.TrimNamesPrefix(names, 1); // // get inner expression from the scope entry // also verifies if a nested group aggregate is being referenced inside // innerExpr = GetExpressionFromScopeEntry(scopeEntry, scopeIndex); // // assume defining type as the expression type // definingType = innerExpr.ResultType; } // // else if (_variables.TryGetValue(names[0], out varInfo)) { // // Can the identifier be resolved as a variable? // names = TypeResolver.TrimNamesPrefix(names, 1); innerExpr = CmdTree.CreateVariableReferenceExpression(varInfo.Key, varInfo.Value); definingType = innerExpr.ResultType; } else { // // Try resolve as entity container/entity set // if (TryResolveAsEntitySet(names, errCtx, out suffixIndex, out innerExpr)) { //return innerExpr; definingType = innerExpr.ResultType; } else { // // try base type from metadata // int matchCount = 0; definingType = TypeResolver.ResolveBaseType(names, out suffixIndex, out matchCount); if (matchCount > 1) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.AmbiguousName(TypeResolver.GetFullName(names))); } } } if (null != definingType) { return ResolveIdentifierChain(names, suffixIndex, definingType, innerExpr, errCtx); } return null; } /// /// Resolves Identifier name chain relative to a base expression /// /// /// /// /// ////// ////// /// /// internal DbExpression ResolveIdentifierChain(string[] names, int suffixIndex, DbExpression innerExpr, ErrorContext errCtx) { return ResolveIdentifierChain(names, suffixIndex, innerExpr.ResultType, innerExpr, errCtx); } /// /// Resolve identifier chain of names /// /// /// /// /// /// ///private DbExpression ResolveIdentifierChain(string[] names, int suffixIndex, TypeUsage definingType, DbExpression innerExpr, ErrorContext errCtx) { Debug.Assert(null != names, "null != names"); Debug.Assert(null != definingType, "null != definingType"); DbExpression convertedExpr = innerExpr; if (names.Length > 0) { TypeUsage baseType = definingType; for (int i = suffixIndex; i < names.Length; i++) { convertedExpr = ResolveIdentifierElement(baseType, convertedExpr, names[i], errCtx); baseType = convertedExpr.ResultType; } } return convertedExpr; } /// /// resolve part of identifier /// /// /// /// /// ////// ////// /// /// internal DbExpression ResolveIdentifierElement(TypeUsage definingType, DbExpression innerExpr, string name, ErrorContext errCtx) { DbExpression converted = null; if (TryResolveAsProperty(name, (null == innerExpr ? definingType : innerExpr.ResultType), innerExpr, errCtx, out converted)) { return converted; } if (TryResolveAsRef(name, (null == innerExpr ? definingType : innerExpr.ResultType), innerExpr, errCtx, out converted)) { return converted; } throw CqlErrorHelper.ReportIdentifierElementError(errCtx, name, definingType); } /// /// Try resolve as EntitySet /// /// /// /// ///private bool TryResolveAsEntitySet(string[] names, ErrorContext errCtx, out int suffixIndex, out DbExpression convExpr) { Debug.Assert(names.Length > 0, "(names.Length > 0) assertion failed"); convExpr = null; suffixIndex = 0; EntityContainer entityContainer = null; // first see if there a default EC if (names.Length == 1) { entityContainer = TypeResolver.Perspective.GetDefaultContainer(); suffixIndex = 0; } else { if (TypeResolver.Perspective.TryGetEntityContainer(names[0], true /*ignoreCase*/, out entityContainer)) { suffixIndex = 1; } else { return false; } } if (null != entityContainer) { EntitySetBase entitySet = null; if (TypeResolver.Perspective.TryGetExtent(entityContainer, names[suffixIndex], true /*ignoreCase*/, out entitySet)) { convExpr = CmdTree.CreateScanExpression(entitySet); suffixIndex++; return true; } else if (names.Length > 1) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.EntitySetIsDoesNotBelongToEntityContainer(names[1], names[0])); } } if (names.Length == 1 && TypeResolver.Perspective.TryGetEntityContainer(names[0], true /*ignoreCase*/, out entityContainer)) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.MissingEntitySetName(names[0])); } return false; } /// /// Try resolve name as property /// /// /// /// /// /// ///private bool TryResolveAsProperty(string name, TypeUsage definingType, DbExpression innerExpr, ErrorContext errCtx, out DbExpression convExpr) { convExpr = null; if (Helper.IsStructuralType(definingType.EdmType)) { EdmMember member = null; if (TypeResolver.Perspective.TryGetMember((StructuralType)definingType.EdmType, name, true, out member)) { if (null != member) { Debug.Assert(name.Equals(member.Name, StringComparison.OrdinalIgnoreCase), "name.Equals(member.Name,StringComparison.OrdinalIgnoreCase)"); if (null == innerExpr) { // // if we dont have an instance, then it means it is a static member that is not supported in EDM // throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.StaticMembersAreNotSupported(TypeHelpers.GetFullName(definingType.EdmType), name)); } convExpr = CmdTree.CreatePropertyExpression(name, true /* case insenstive */, innerExpr); return true; } } } return false; } /// /// try resolve name as ref /// /// /// /// /// /// ///private bool TryResolveAsRef(string name, TypeUsage definingType, DbExpression innerExpr, ErrorContext errCtx, out DbExpression convExpr) { convExpr = null; if (TypeSemantics.IsReferenceType(definingType)) { convExpr = CmdTree.CreateDerefExpression(innerExpr); TypeUsage dereferencedType = convExpr.ResultType; if (TryResolveAsProperty(name, convExpr.ResultType, convExpr, errCtx, out convExpr)) { return true; } throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.InvalidDeRefProperty(name, TypeHelpers.GetFullName(dereferencedType), TypeHelpers.GetFullName(definingType))); } return false; } /// /// Resolve given name as Type /// /// /// ////// ////// /// /// TypeUsage internal TypeUsage ResolveNameAsType(string[] names, Expr astTypeExpression) { int matchCount = 0; ErrorContext errCtx = astTypeExpression.ErrCtx; TypeUsage typeUsage = TypeResolver.ResolveNameAsType(names, names.Length, out matchCount); // // if null, means there is no type with given name in the current typespace // if (null == typeUsage) { CqlErrorHelper.ReportTypeResolutionError(names, astTypeExpression, this); } // // if match count is greater then 1, means there are more then one match and is therefore ambiguous // if (matchCount > 1) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.AmbiguousTypeName(TypeResolver.GetFullName(names))); } // // check for parametrized types // MethodExpr methodExpr = astTypeExpression as MethodExpr; if (null != methodExpr) { typeUsage = HandleParametrizedType(typeUsage, methodExpr); } return typeUsage; } ////// Handles parametrized types such as Decimal, Decimal(p) and Decimal(p,s) /// /// typeusage of the resolved type name /// ast node with representing additional type parameters to be validated ///private TypeUsage HandleParametrizedType(TypeUsage typeUsage, MethodExpr methodExpr) { // // The only type in EDM that we support arguments is EDM.Decimal as of Sep 2007. // PrimitiveType primitiveType = typeUsage.EdmType as PrimitiveType; if (null == primitiveType || primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Decimal) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.TypeDoesNotSupportParameters(primitiveType.FullName)); } Debug.Assert(methodExpr.Args[0] is Literal, "first type argument must be a literal node"); Debug.Assert((methodExpr.Args.Count == 2) ? methodExpr.Args[1] is Literal : true, "second type argument must be a literal node"); // // Get valid Precision for given type from provider // byte precision = GetValidDecimalFacetValue(primitiveType, (Literal)methodExpr.Args[0], DbProviderManifest.PrecisionFacetName); // // Get valid Scale for given type from provider // byte scale = 0; if (2 == methodExpr.Args.Count) { scale = GetValidDecimalFacetValue(primitiveType, (Literal)methodExpr.Args[1], DbProviderManifest.ScaleFacetName); } // // Ensure P >= S // if (precision < scale) { Debug.Assert(2 == methodExpr.Args.Count, "decimal with precision and scale must have 2 arguments"); throw EntityUtil.EntitySqlError(methodExpr.Args[1].ErrCtx, System.Data.Entity.Strings.DecimalPrecisionMustBeGreaterThanScale(primitiveType.FullName)); } // // finally create TypeUsage with given precision and scale // return TypeUsage.CreateDecimalTypeUsage(primitiveType, precision, scale); } /// /// validates and returns the appropriate facet for decimal type /// /// /// /// ///private byte GetValidDecimalFacetValue(PrimitiveType primitiveType, Literal typeArg, string FacetName) { FacetDescription facetDescription = Helper.GetFacet(primitiveType.ProviderManifest.GetFacetDescriptions(primitiveType), FacetName); if (null == facetDescription) { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeDoesNotSupportPrecisionOrScale(primitiveType.FullName, FacetName)); } byte typeArgValue = 0; if (Byte.TryParse(typeArg.OriginalValue, out typeArgValue)) { if (typeArgValue > facetDescription.MaxValue) { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeSpecExceedsMax(FacetName)); } if (typeArgValue < facetDescription.MinValue) { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeSpecBellowMin(FacetName)); } } else { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeSpecIsNotValid); } return (byte)typeArgValue; } /// /// Handles expressions dotted id( List{e} ) that can be resolved to Static Methods, Type Constructors, Ordinary Functions and Aggregate Functions. /// this function ensures that one of the 3 posibilities must be resolved, otherwise an exception will be raised. /// Also, it ensures that the name is not ambiguous. /// /// /// /// /// ///internal void ResolveNameAsStaticMethodOrFunction(MethodExpr methodExpr, out TypeUsage constructorType, out TypeUsage staticMethodType, out IList functionType) { DotExpr dotExpr = methodExpr.MethodPrefixExpr; int matchCount = 0; int typeMatches = 0; constructorType = null; staticMethodType = null; functionType = null; // // Try Resolving as Type Constructor // constructorType = TypeResolver.ResolveNameAsType(dotExpr.Names, dotExpr.Names.Length, out matchCount); if (0 < matchCount) { typeMatches++; // // ensure type has contructor. one of: Entity, ComplexType or RelationType // if (!TypeSemantics.IsStructuralType(constructorType)) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidCtorUseOnType(TypeHelpers.GetFullName(constructorType))); } if (1 < matchCount) { dotExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericTypeCtor; throw EntityUtil.EntitySqlError(dotExpr.ErrCtx, System.Data.Entity.Strings.MultipleMatchesForName( System.Data.Entity.Strings.LocalizedType, dotExpr.FullName)); } } // // Try Resolving as EdmFunction // List foundNamespaces = null; functionType = TypeResolver.ResolveNameAsFunction(dotExpr.Names, true /* ignore case */, out matchCount, out foundNamespaces); if (0 < matchCount) { typeMatches++; if (1 < matchCount) { methodExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericFunctionCall; CqlErrorHelper.ReportAmbiguousFunctionError(methodExpr, foundNamespaces); } } #if SUPPORT_STATIC_METHODS // // Try Resolving as Static Method // staticMethodType = null; if (dotExpr.Names.Length > 1) { staticMethodType = TypeResolver.ResolveNameAsType(dotExpr.Names, dotExpr.Names.Length - 1, out matchCount); if (0 < matchCount) { typeMatches++; if (1 < matchCount) { dotExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxMethod; throw EntityUtil.QueryError(dotExpr.ErrCtx, System.Data.Entity.Strings.MultipleMatchesForName( System.Data.Entity.Strings.LocalizedType, dotExpr.FullName)); } } } #endif // // check if it is ambiguous. if the given method prefix name can be found as multiple types, then throw // if (1 < typeMatches) { throw EntityUtil.EntitySqlError(dotExpr.Identifier.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionMethodCtorName(dotExpr.FullName)); } // // Check if lookup was a miss // if (0 == typeMatches) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.CannotResolveNameToFunction(dotExpr.FullName)); } } /// /// Creates new instance of a given Type (IEntity, ComplexType or RelationType) /// Validates and infer argument types /// /// /// /// /// ////// ////// /// internal DbExpression CreateInstanceOfType(TypeUsage type, List args, List relshipExprList, MethodExpr methodExpr) { DbExpression newInstance = null; int idx = 0; int argCount = args.Count; methodExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericTypeCtor; // // abstract types cannot be instanciated // if (type.EdmType.Abstract) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.CannotInstantiateAbstractType(type.EdmType.FullName)); } // // ensure DISTINCT/ALL was not used on type constructors // if (methodExpr.DistinctKind != DistinctKind.None) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInCtor); } // // find overloads by searching members in order of its definition // each member will be considered as a formal argument type in the order of its definition // if (TypeSemantics.IsComplexType(type) || TypeSemantics.IsEntityType(type) || TypeSemantics.IsRelationshipType(type)) { StructuralType stype = (StructuralType)type.EdmType; foreach (EdmMember member in TypeHelpers.GetAllStructuralMembers(stype)) { TypeUsage memberModelTypeUsage = Helper.GetModelTypeUsage(member); Debug.Assert(memberModelTypeUsage.EdmType.DataSpace == DataSpace.CSpace, "member space must be CSpace"); // // ensure given arguments are not less than 'formal' constructor arguments // if (argCount <= idx) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.NumberOfTypeCtorIsLessThenFormalSpec(member.Name)); } // // if given argument is an untyped null, infer type the ctor formal argument list type // if (TypeSemantics.IsNullType(args[idx].ResultType)) { if (Helper.IsEdmProperty(member) && !((EdmProperty)member).Nullable) { throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, System.Data.Entity.Strings.InvalidNullLiteralForNonNullableMember( member.Name, TypeHelpers.GetFullName(stype))); } args[idx] = CmdTree.CreateNullExpression(memberModelTypeUsage); } // // if not, given argument is already typed, then ensure it is promotable to formal ctor argument type // bool isPromotable = TypeSemantics.IsPromotableTo(args[idx].ResultType, memberModelTypeUsage); if (ParserOptions.CompilationMode.RestrictedViewGenerationMode == _parserOptions.ParserCompilationMode || ParserOptions.CompilationMode.UserViewGenerationMode == _parserOptions.ParserCompilationMode ) { if (!isPromotable && !TypeSemantics.IsPromotableTo(memberModelTypeUsage, args[idx].ResultType)) { throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, System.Data.Entity.Strings.InvalidCtorArgumentType( args[idx].ResultType.Identity, member.Name, memberModelTypeUsage.Identity)); } if (Helper.IsPrimitiveType(memberModelTypeUsage.EdmType) && !TypeSemantics.IsSubTypeOf(args[idx].ResultType, memberModelTypeUsage)) { args[idx] = _commandTree.CreateCastExpression(args[idx], memberModelTypeUsage); } } else { if (!isPromotable) { throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, System.Data.Entity.Strings.InvalidCtorArgumentType( args[idx].ResultType.Identity, member.Name, memberModelTypeUsage.Identity)); } } idx++; } } // // the type does not support type constructors // else { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidCtorUseOnType( TypeHelpers.GetFullName(type))); } // // ensure all given arguments and all 'formal' ctor arguments were considered and properly checked // if (idx != argCount) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.NumberOfTypeCtorIsMoreThenFormalSpec( TypeHelpers.GetFullName(type))); } // // finally, create expression // if (relshipExprList != null && relshipExprList.Count > 0) { EntityType entityType = (EntityType)type.EdmType; newInstance = CmdTree.CreateNewEntityWithRelationshipsExpression(entityType, args, relshipExprList); } else { newInstance = CmdTree.CreateNewInstanceExpression(TypeHelpers.GetReadOnlyType(type), args); } Debug.Assert(null != newInstance, "null != newInstance"); return newInstance; } /// /// Creates DbFunctionExpression from metadata and arg list. validates overloads /// /// /// /// ////// ////// /// DbFunctionExpression internal DbExpression CreateFunction(IListfunctionTypeList, List args, MethodExpr methodExpr) { Debug.Assert(functionTypeList.Count > 0, "functionList.Count > 0"); DbExpression functionExpression = null; methodExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericFunctionCall; bool isAmbiguous = false; // // if DISTINCT was used, ensure function type and argument types are valid // if (methodExpr.DistinctKind != DistinctKind.None) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxFunction(functionTypeList[0].Name); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInNonAggFunction); } // // collect argument types from argument expression list // List argTypes = new List (args.Count); for (int i = 0; i < args.Count; i++) { argTypes.Add(args[i].ResultType); } // // Find function overload match for given argument types // EdmFunction functionType = TypeResolver.ResolveFunctionOverloads(functionTypeList, argTypes, false /* isGroupAggregateFunction */, out isAmbiguous); // // if there is more then one overload that matches given arguments, throw // if (isAmbiguous) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxFunction(functionTypeList[0].Name); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments); } // // if null, means no overload matched // if (null == functionType) { CqlErrorHelper.ReportFunctionOverloadError(methodExpr, functionTypeList[0], argTypes); } // // fixup untyped nulls in given arguments from formal method argument types // for (int i = 0; i < args.Count; i++) { if (TypeSemantics.IsNullType(args[i].ResultType)) { args[i] = CmdTree.CreateNullExpression(functionType.Parameters[i].TypeUsage); } } // // Finally, create expression // functionExpression = CmdTree.CreateFunctionExpression(functionType, args); Debug.Assert(null != functionExpression, "null != functionExpression"); return functionExpression; } /// /// Creates a static method expression /// /// /// /// ////// ////// /// MethodExpression internal static DbExpression CreateStaticMethod(TypeUsage definingType, Listargs, MethodExpr methodExpr) { return CreateMethod(definingType, args, methodExpr, null, true); } /// /// creates instance method expression /// /// /// /// ////// ////// /// InstanceMethodExpression or StaticMethodExpression internal static DbExpression CreateInstanceMethod(DbExpression instance, Listargs, MethodExpr methodExpr) { return CreateMethod(instance.ResultType, args, methodExpr, instance, false); } /// /// Creates a method expression. /// /// /// /// /// /// ///private static DbExpression CreateMethod(TypeUsage definingType, List args, MethodExpr methodExpr, DbExpression instance, bool isStatic) { #if METHOD_EXPRESSION Expression methodExpression = null; methodExpr.ErrCtx.ContextInfo = System.Data.Entity.Strings.CtxMethodTerm(methodExpr.MethodName); // // ensure definiting type is allowed for method calls // if (!TypeResolver.IsValidTypeForMethodCall(definingType)) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.DefiningTypeDoesNotSupportMethodCalls); } // // check if distinct/ALL was incorrectly used on methods // if (methodExpr.DistinctKind != DistinctKind.None) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidDistinctArgumentInMethod); } // // create list of arg types // List argTypes = new List (); foreach (Expression argExpr in args) { argTypes.Add(argExpr.ResultType); } // // Find function overload match for given args // bool isAmbiguous = false; MethodMetadata methodMetadata = TypeResolver.ResolveMethodOverloads(definingType, methodExpr.MethodName, argTypes, isStatic, out isAmbiguous); // // check if more than one overload match was found // if (isAmbiguous) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionArguments); } // // check if there is a match // if (null == methodMetadata) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.NoMethodOverloadMatch(methodExpr.MethodName, definingType.FullName)); } // // fixup untyped nulls in given arguments from formal method argument types // for (int i = 0 ; i < args.Count ; i++) { if (TypeResolver.IsNull(args[i].ResultType)) { args[i] = CmdTree.CreateNullExpression(methodMetadata.Parameters[i].Type); } } // // Finally, create expression // #if CALCULATED_MEMBERS ClrCalculatedMethod clrCalculatedMethod = methodMetadata as ClrCalculatedMethod; if (null != clrCalculatedMethod) { if (!isStatic) { args.Insert(0, instance); } methodExpression = clrCalculatedMethod.GetExpression(CmdTree, args); } else { #endif if (isStatic) { methodExpression = CmdTree.CreateStaticMethodExpression(methodMetadata, args); } else { methodExpression = CmdTree.CreateInstanceMethodExpression(methodMetadata, instance, args); } #if CALCULATED_MEMBERS } #endif Debug.Assert(null != methodExpression); return methodExpression; #endif throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.MethodInvocationNotSupported); } /// /// Ensure and typifies nulls if present /// ////// ////// Pair of Expressions with NullTypes inferred to the proper type internal PairEnsureTypedNulls(DbExpression leftExpr, DbExpression rightExpr, ErrorContext errCtx, Func formatMessage) { DbExpression newLeftExpr = leftExpr; DbExpression newRightExpr = rightExpr; UntypedNullExpression untypedLeftExpr = leftExpr as UntypedNullExpression; UntypedNullExpression untypedRightExpr = rightExpr as UntypedNullExpression; if (null != untypedLeftExpr) { if (null != untypedRightExpr || null == rightExpr) { throw EntityUtil.EntitySqlError(errCtx, formatMessage()); } else { newLeftExpr = CmdTree.CreateNullExpression(rightExpr.ResultType); } } else if (null != untypedRightExpr) { newRightExpr = CmdTree.CreateNullExpression(leftExpr.ResultType); } return new Pair (newLeftExpr, newRightExpr); } /// /// returns if an expresison is an untyped null expression /// /// ///internal static bool IsNullExpression(DbExpression expression) { return expression is UntypedNullExpression; } /// /// Set current source scope kind /// /// internal void SetCurrentScopeKind(ScopeEntryKind scopeKind) { CurrentScopeRegionFlags.ScopeEntryKind = scopeKind; } ////// Get current source scope kind /// internal ScopeEntryKind CurrentScopeKind { get { return CurrentScopeRegionFlags.ScopeEntryKind; } } ////// Generates internal name to be used in cqt /// /// ///internal string GenerateInternalName(string hint) { // string concat is faster than String.Join and much faster than String.Format return "_##" + hint + unchecked(_namegenCounter++).ToString(CultureInfo.InvariantCulture); } private static DbExpressionKind[] joinMap = { DbExpressionKind.CrossJoin, DbExpressionKind.InnerJoin, DbExpressionKind.LeftOuterJoin, DbExpressionKind.FullOuterJoin }; /// /// maps ast jointype to cqt join type /// /// ///internal static DbExpressionKind MapJoinKind(JoinKind joinKind) { Debug.Assert(joinKind != JoinKind.RightOuter, "joinKind != JoinKind.RightOuter"); return joinMap[(int)joinKind]; } private static DbExpressionKind[] applyMap = { DbExpressionKind.CrossApply, DbExpressionKind.OuterApply }; /// /// maps ast applytype to cqt apply type /// /// ///internal static DbExpressionKind MapApplyKind(ApplyKind applyKind) { return applyMap[(int)applyKind]; } /// /// returns current scope region savepoint /// internal SavePoint CurrentScopeRegionSavePoint { get { Debug.Assert(_scopeRegionSavepoints.Count > 0, "_scopeRegionSavepoints.Count > 0"); return _scopeRegionSavepoints[0]; } } ////// propagates new ebinding up the tree for the vars in scope /// /// internal void FixupSourceVarBindings(DbVariableReferenceExpression newSourceVar) { Debug.Assert(CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex, "CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex"); for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { SourceScopeEntry scopeEntry = scope.Value as SourceScopeEntry; if (null != scopeEntry) { scopeEntry.SetNewSourceBinding(newSourceVar); } } } } /// /// fixup new eb /// /// internal void FixupNamedSourceVarBindings(DbVariableReferenceExpression newSourceVar) { Debug.Assert(CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex, "CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex"); for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { if (scope.Value.Kind == CurrentScopeKind && !scope.Value.IsHidden) { SourceScopeEntry scopeEntry = scope.Value as SourceScopeEntry; if (null != scopeEntry && TreePathTagger.IsChildNode(CurrentScopeRegionFlags.PathTagger.Tag, scopeEntry.VarTag)) { scopeEntry.AddBindingPrefix(newSourceVar.VariableName); scopeEntry.SetNewSourceBinding(newSourceVar); } } } } } internal void MarkGroupInputVars() { for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePair scope in _staticContext.GetScopeByIndex(i)) { if (scope.Value.VarKind != SourceVarKind.GroupAggregate && scope.Value.VarKind != SourceVarKind.GroupKey) { scope.Value.VarKind = SourceVarKind.GroupInput; } } } } /// /// Fixes up source vars and adds groupvar in case the var is referenced in an group aggregate fucntion /// /// /// internal void FixupGroupSourceVarBindings(DbVariableReferenceExpression newSourceVar, DbVariableReferenceExpression newGroupVar) { InternalFixupGroupSourceVarBindings(newSourceVar, newGroupVar); } ////// undo group var /// /// internal void UndoFixupGroupSourceVarBindings(DbVariableReferenceExpression originalSourceVar) { InternalFixupGroupSourceVarBindings(originalSourceVar, null); } private void InternalFixupGroupSourceVarBindings(DbVariableReferenceExpression newSourceVar, DbVariableReferenceExpression newGroupVar) { Debug.Assert(CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex, "CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex"); for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { if (scope.Value.Kind == CurrentScopeKind && !scope.Value.IsHidden) { SourceScopeEntry scopeEntry = scope.Value as SourceScopeEntry; if (null != scopeEntry) { scopeEntry.SetNewSourceBinding(newSourceVar); scopeEntry.GroupVarExpression = newGroupVar; } } } } } /// /// Sets Current Scope Source Var Kind /// /// internal void SetCurrentScopeVarKind(FromClauseItemKind fromClauseItemKind) { if (fromClauseItemKind == FromClauseItemKind.JoinFromClause) { SetCurrentScopeKind(ScopeEntryKind.JoinSourceVar); } else if (fromClauseItemKind == FromClauseItemKind.ApplyFromClause) { SetCurrentScopeKind(ScopeEntryKind.ApplySourceVar); } else { SetCurrentScopeKind(ScopeEntryKind.SourceVar); } } ////// resets all source vars to default SourceVar Kind /// internal void ResetCurrentScopeVarKind() { for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { scope.Value.Kind = ScopeEntryKind.SourceVar; } } SetCurrentScopeKind(ScopeEntryKind.SourceVar); } /// /// Infers and Creates a new Alias name based on expr information /// /// ///private string CreateNewAlias(DbExpression expr) { DbScanExpression extent = expr as DbScanExpression; if (null != extent) { return extent.Target.Name; } DbPropertyExpression property = expr as DbPropertyExpression; if (null != property) { return property.Property.Name; } DbVariableReferenceExpression varRef = expr as DbVariableReferenceExpression; if (null != varRef) { return varRef.VariableName; } return GenerateInternalName(String.Empty); } /// /// infers alias name from astNode information and converted expression. /// /// /// ///internal string InferAliasName(AliasExpr aliasExpr, DbExpression convertedExpression) { if (aliasExpr.HasAlias) { return aliasExpr.AliasIdentifier.Name; } Identifier id = aliasExpr.Expr as Identifier; if (null != id) { return id.Name; } DotExpr dotExpr = aliasExpr.Expr as DotExpr; if (null != dotExpr && dotExpr.IsDottedIdentifier) { return dotExpr.Names[dotExpr.Length - 1]; } return CreateNewAlias(convertedExpression); } /// /// represents scope region disposer helper /// internal sealed class ScopeRegionDisposer : IDisposable { private SemanticResolver _semanticResolver; internal ScopeRegionDisposer(SemanticResolver semanticResolver) { _semanticResolver = semanticResolver; } public void Dispose() { _semanticResolver.LeaveScopeRegion(); } } ////// adds a scope entry to the scope /// /// /// internal void AddToScope(string key, ScopeEntry scopeEntry) { _staticContext.Add(key, scopeEntry); } ////// Removes an entry from scope /// /// internal void RemoveFromScope(string key) { _staticContext.RemoveFromScope(key); } ////// enters a scope region /// ///internal ScopeRegionDisposer EnterScopeRegion() { Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); // // push new savepoint // _scopeRegionSavepoints.Insert(0, CreateSavePoint()); // // push new scope // _staticContext.EnterScope(); // // push new scoperegion flags // _scopeRegionFlags.Insert(0, new ScopeRegionFlags()); Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); // // return disposer // return new ScopeRegionDisposer(this); } /// /// Leaves a scope region /// internal void LeaveScopeRegion() { Debug.Assert(_scopeRegionSavepoints.Count > 0, "_scopeRegionSavepoints.Count > 0"); Debug.Assert(_scopeRegionFlags.Count > 0, "_scopeRegionFlags.Count > 0"); Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); // // roll back scopes to the ScopeRegion savepoint // RollbackToSavepoint(CurrentScopeRegionSavePoint); // // pop top ScopeRegion savepoint // _scopeRegionSavepoints.RemoveAt(0); // // cleanup all aggregates related to this scope region // foreach (MethodExpr me in CurrentScopeRegionFlags.GroupAggregatesInfo.Keys) { me.ResetAggregateInfo(); } // // pop top ScopeRegion flags // _scopeRegionFlags.RemoveAt(0); // // restore scope view to the previously save view // SetScopeView(CurrentScopeRegionFlags.ScopeViewKind); Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); } ////// Creates a scope save point /// ///internal SavePoint CreateSavePoint() { return new SavePoint(CurrentScopeIndex); } /// /// rolls back the scope to a give savepoint /// /// internal void RollbackToSavepoint(SavePoint sp) { _staticContext.RollbackToSavepoint(sp); } ////// returns current scope region flags /// internal ScopeRegionFlags CurrentScopeRegionFlags { get { return _scopeRegionFlags[0]; } } ////// scope disposer /// internal sealed class ScopeDisposer : IDisposable { private SemanticResolver _semanticResolver; internal ScopeDisposer(SemanticResolver semanticResolver) { _semanticResolver = semanticResolver; } public void Dispose() { _semanticResolver.LeaveScope(); } } ////// entry scope /// ///internal ScopeDisposer EnterScope() { _staticContext.EnterScope(); return new ScopeDisposer(this); } /// /// leave scope /// internal void LeaveScope() { _staticContext.LeaveScope(); } ////// ensures the expression is typed /// /// /// ////// internal static void EnsureIsNotUntypedNull(DbExpression expression, ErrorContext errCtx) { if (expression is UntypedNullExpression) { throw EntityUtil.EntitySqlError(errCtx, Strings.ExpressionCannotBeNull); } } ////// /// checks if a name is in the current scope /// /// ///internal bool IsInCurrentScope(string key) { return _staticContext.IsInCurrentScope(key); } /// /// adds a source binding /// /// internal void AddSourceBinding(DbExpressionBinding sourceBinding) { _staticContext.AddSourceBinding(sourceBinding, this.CurrentScopeRegionFlags.ScopeEntryKind, this.CurrentScopeRegionFlags.PathTagger.Tag); } ////// adds dummy group key to be consumed during aggregate search pahse /// /// /// /// internal void AddDummyGroupKeyToScope(string groupKey, DbExpression varBasedExpression, DbExpression groupVarBasedExpression) { _staticContext.AddGroupDummyVar(groupKey, varBasedExpression, groupVarBasedExpression); } ////// Replaces dummy key added during the aggregate search phase by the real group key /// /// /// internal void ReplaceGroupVarInScope(string groupVarName, DbVariableReferenceExpression groupSourceBinding) { _staticContext.ReplaceGroupVarInScope(groupVarName, groupSourceBinding); } ////// Adds group aggregate vars to scope, including nested aggregate /// /// /// internal void AddGroupAggregateToScope(string aggregateName, DbVariableReferenceExpression sourceVar) { _staticContext.AddAggregateToScope(aggregateName, sourceVar); } ////// Validates that the specified parameters have valid, non-duplicated names /// /// The set of input parameter names and types ///A valid dictionary that maps parameter names to types using the current StringComparer private DictionaryValidateParameters(Dictionary paramDefs) { Dictionary retParams = new Dictionary (_stringComparer); if (paramDefs != null) { foreach (KeyValuePair paramDef in paramDefs) { if (retParams.ContainsKey(paramDef.Key)) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.MultipleDefinitionsOfParameter(paramDef.Key)); } if (!ValidateParameterType(paramDef.Value)) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.InvalidParameterType(paramDef.Key)); } retParams.Add(paramDef.Key, paramDef.Value); } } return retParams; } /// /// Validates that the specified variables have valid, non-duplicated names /// /// The set of input parameter names and types ///A valid dictionary that maps variable names to types using the current StringComparer private Dictionary> ValidateVariables(Dictionary varDefs) { Dictionary > retVars = new Dictionary >(_stringComparer); if (varDefs != null) { foreach (KeyValuePair varDef in varDefs) { if (retVars.ContainsKey(varDef.Key)) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.MultipleDefinitionsOfVariable(varDef.Key)); } retVars.Add(varDef.Key, new KeyValuePair (varDef.Key, varDef.Value)); } } return retVars; } private static bool ValidateParameterType(TypeUsage paramType) { return (paramType != null && paramType.EdmType != null && (TypeSemantics.IsPrimitiveType(paramType) || paramType.EdmType is EnumType)); } internal static void EnsureValidTypeForNullExpression(TypeUsage type, ErrorContext errCtx) { if (TypeSemantics.IsCollectionType(type)) { throw EntityUtil.EntitySqlError(errCtx, Strings.NullLiteralCannotBePromotedToCollectionOfNulls); } } /// /// Adds the group aggregate information to the innermost correlated scope. if the expression is not correlated /// then add to the innermost scope. /// /// /// /// /// internal void AddGroupAggregateInfoToScopeRegion(MethodExpr astNode, string aggregateName, DbAggregate aggregateExpression, int groupVarScopeIndex) { int scopeRegionIndex = 0; bool bFound = false; // if scope index is NonCorrelatedScope, it means the inner aggregate expression is not correlated and it is // just as constant expression if (groupVarScopeIndex == AggregateAstNodeInfo.NonCorrelatedScope) { bFound = true; scopeRegionIndex = 0; } else { // inner expression is correlated and we need to find the exact group var scope for (scopeRegionIndex = 0; scopeRegionIndex < _scopeRegionSavepoints.Count; scopeRegionIndex++) { if (_scopeRegionSavepoints[scopeRegionIndex].ContainsScope(groupVarScopeIndex)) { bFound = true; break; } } } if (bFound) { Debug.Assert(_scopeRegionFlags.Count > 0, "must have a scope region (_scopeRegionFlags.Count > 0)"); _scopeRegionFlags[scopeRegionIndex].AddGroupAggregateInfo(astNode, new GroupAggregateInfo(aggregateName, aggregateExpression)); } else { throw EntityUtil.EntitySqlError(Strings.GroupVarNotFoundInScope); } } ////// pushes aggregate ast node /// /// internal void PushAggregateAstNode(MethodExpr astNode) { _aggregateAstNodes.Insert(0, new AggregateAstNodeInfo(astNode)); } ////// removes aggregate ast node from the top of the stack /// ///internal AggregateAstNodeInfo PopAggregateAstNode() { Debug.Assert(_aggregateAstNodes.Count > 0, "_aggregateAstNodeInfo.Count must be greater than zero to pop"); AggregateAstNodeInfo result = _aggregateAstNodes[0]; _aggregateAstNodes.RemoveAt(0); return result; } /// /// returns true if any of the ScopeRegions from the closest to the outermost has a group scope /// ///internal bool IsInAnyGroupScope() { for (int i = 0; i < _scopeRegionFlags.Count; i++) { if (_scopeRegionFlags[i].IsInGroupScope) { return true; } } return false; } /// /// resets correlation flag in current scope region /// internal void ResetScopeRegionCorrelationFlag() { CurrentScopeRegionFlags.WasResolutionCorrelated = false; } ////// sets the correlation flag based on the input var scope index /// /// internal void SetScopeRegionCorrelationFlag(int scopeIndex) { Debug.Assert(_scopeRegionFlags.Count == _scopeRegionSavepoints.Count, "_scopeRegionFlags.Count == _scopeRegionSavepoints.Count pre-condition FAILED"); for (int i = 0; i < _scopeRegionFlags.Count; i++) { if (scopeIndex > _scopeRegionSavepoints[i].ScopeIndex) { _scopeRegionFlags[i].WasResolutionCorrelated = true; return; } } } ////// ensures limit expression (used in TOP and LIMIT) have valid pre-conditions /// NOTE that in M3.2 these preconditions are exactly the same for TOP and LIMIT. /// /// /// /// internal void EnsureValidLimitExpression(ErrorContext errCtx, DbExpression expr, string subclauseName) { // // ensure resolved expression have the right type // if (!TypeSemantics.IsPromotableTo(expr.ResultType, TypeResolver.Int64Type)) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeCompatibleWithEdm64(subclauseName, expr.ResultType.EdmType.FullName)); } // // if it is a literal, make sure it has the correct value // DbConstantExpression constantExpr = expr as DbConstantExpression; if (null != constantExpr && System.Convert.ToInt64(constantExpr.Value, CultureInfo.InvariantCulture) < 0) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeGreaterThanOrEqualToZero(subclauseName)); } } ////// Ensures Projection is valid for Distinct modifier /// /// /// internal static void ValidateDistinctProjection(SelectClause selectClause, TypeUsage projectionType) { TypeUsage elemType = TypeHelpers.GetElementTypeUsage(projectionType); if (!TypeHelpers.IsValidDistinctOpType(elemType)) { ErrorContext errCtx = selectClause.Items[0].Expr.ErrCtx; if (TypeSemantics.IsRowType(elemType)) { RowType rowType = elemType.EdmType as RowType; Debug.Assert(selectClause.Items.Count == rowType.Members.Count); for (int i = 0; i < rowType.Members.Count; i++) { if (!TypeSemantics.IsEqualComparable(rowType.Members[i].TypeUsage)) { errCtx = selectClause.Items[i].Expr.ErrCtx; break; } } } throw EntityUtil.EntitySqlError(errCtx, Strings.SelectDistinctMustBeEqualComparable); } } ////// Ensures that the result of a query expression is valid. /// /// /// internal static void ValidateQueryResultType(TypeUsage resultType, ErrorContext errCtx) { if (Helper.IsCollectionType(resultType.EdmType)) { ValidateQueryResultType(((CollectionType)resultType.EdmType).TypeUsage, errCtx); } else if (Helper.IsRowType(resultType.EdmType)) { foreach (EdmProperty property in ((RowType)resultType.EdmType).Properties) { ValidateQueryResultType(property.TypeUsage, errCtx); } } else if (Helper.IsAssociationType(resultType.EdmType)) { throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidQueryResultType(resultType.Identity)); } } } ////// represents a method ast node during aggregate resolution /// internal sealed class AggregateAstNodeInfo { internal const int NonCorrelatedScope = Int32.MinValue; private MethodExpr _methodExpr; private int _scopeIndex; internal AggregateAstNodeInfo( MethodExpr methodAstNode ) { _methodExpr = methodAstNode; _scopeIndex = NonCorrelatedScope; } internal void AssertMethodExprEquivalent(MethodExpr other) { Debug.Assert(_methodExpr == other, "method ast node from the top of the stack must be the same"); } internal int ScopeIndex { get { return _scopeIndex; } set { Debug.Assert(value >= 0); _scopeIndex = value; } } } ////// represents resolved group aggregate information /// internal sealed class GroupAggregateInfo { private DbAggregate _aggregateExpression = null; private string _aggregateName = null; internal GroupAggregateInfo( string aggregateName, DbAggregate aggregateExpression ) { _aggregateName = aggregateName; _aggregateExpression = aggregateExpression; } internal DbAggregate AggregateExpression { get { return _aggregateExpression; } } internal string AggregateName { get { return _aggregateName; } } } /// /// represents set of flags for a ScopeRegion /// internal sealed class ScopeRegionFlags { /// /// used to indicate when JoinSourceVars are visible within the scope of join sub-expresion tree resolution. /// Join right sub-expressions must not refer to join left sub-expressions (recursively). Of course, by definition /// ON predicate is allowed to refer to any previously defined join sources. General left correlation /// is allowed though. for instance: /// Select ... From A JOIN B JOIN A.x -> invalid /// Select ... From A JOIN B JOIN C ON A.x == C.x -> valid /// Select ... From A JOIN B, C JOIN A.x ... -> valid /// private bool _isInsideJoinOnPredicate = false; internal bool IsInsideJoinOnPredicate { get { return _isInsideJoinOnPredicate; } set { _isInsideJoinOnPredicate = value; } } /// /// indicates if input scope has group scope semantics /// private bool _isInGroupScope = false; internal bool IsInGroupScope { get { return _isInGroupScope; } set { _isInGroupScope = value; } } ////// indicates expression being resolved is a candidate for group aggregate and /// should reference groupVar instead of the sourceVar /// private bool _isInsideGroupAggregate = false; internal bool IsInsideGroupAggregate { get { return _isInsideGroupAggregate; } set { _isInsideGroupAggregate = value; } } ////// tracks group aggregate nesting calls /// private int _groupAggregateNestingCount = 0; internal int GroupAggregateNestingCount { get { return _groupAggregateNestingCount; } } ////// resets group aggregate function nesting call /// internal void ResetGroupAggregateNestingCount() { _groupAggregateNestingCount = 0; } internal void IncrementGroupAggregateNestingCount() { _groupAggregateNestingCount++; } internal void DecrementGroupAggregateNestingCount() { _groupAggregateNestingCount--; } private Dictionary_groupAggregatesInfo = new Dictionary (); internal void AddGroupAggregateInfo( MethodExpr astMethodNode, GroupAggregateInfo groupAggrInfo ) { _groupAggregatesInfo.Add(astMethodNode, groupAggrInfo); } internal Dictionary GroupAggregatesInfo { get { return _groupAggregatesInfo; } } /// /// keep group aggregates names /// private HashSet_groupAggregateNames = new HashSet (); internal bool ContainsGroupAggregate( string groupAggregateName ) { return _groupAggregateNames.Contains(groupAggregateName); } /// /// adds group aggregate name to scope region flags /// /// internal void AddGroupAggregateToScopeFlags( string groupAggregateName ) { Debug.Assert(!_groupAggregateNames.Contains(groupAggregateName),"!_groupAggregateNames.ContainsKey(groupAggregateName)"); _groupAggregateNames.Add(groupAggregateName); } ////// indicates if a nested group aggregate was referred by any sub-expression within /// a give group scope /// private bool _wasNestedGroupAggregateReferredByInnerExpressions = false; internal bool WasNestedGroupAggregateReferredByInnerExpressions { get { return _wasNestedGroupAggregateReferredByInnerExpressions; } set { _wasNestedGroupAggregateReferredByInnerExpressions = value; } } private SemanticResolver.ScopeViewKind _scopeViewKind = SemanticResolver.ScopeViewKind.All; internal SemanticResolver.ScopeViewKind ScopeViewKind { get { return _scopeViewKind; } set { _scopeViewKind = value; } } ////// indicates if current scope region holds an implicit group /// internal bool IsImplicitGroup { get { return _isImplicitGroup; } set { _isImplicitGroup = value; } } private bool _isImplicitGroup = false; ////// indicates the kind of the current scope entries /// internal ScopeEntryKind ScopeEntryKind { get { return _scopeEntryKind; } set { _scopeEntryKind = value; } } private ScopeEntryKind _scopeEntryKind = ScopeEntryKind.SourceVar; ////// defines if a given expression resolution is correlated /// internal bool WasResolutionCorrelated { get { return _wasResolutionCorrelated; } set { _wasResolutionCorrelated = value; } } private bool _wasResolutionCorrelated = false; ////// Represents the path tagger in the current input scope /// private TreePathTagger _treePathTagger = new TreePathTagger(); internal TreePathTagger PathTagger { get { return _treePathTagger; } } } ////// represents a pair of types to avoid uncessary enumerations to split kvp elements /// ////// internal sealed class Pair { internal Pair( L left, R right ) { Left = left; Right = right; } internal L Left; internal R Right; } /// /// represents a pair of types to avoid uncessary enumerations to split kvp lists /// ////// internal sealed class PairOfLists /* : IEnumerable > */ { private List _leftValues; internal List Left { get { return _leftValues; } } private List _rightValues; internal List Right { get { return _rightValues; } } internal PairOfLists() { _leftValues = new List (); _rightValues = new List (); } internal PairOfLists( List leftValues, List rightValues ) { _leftValues = leftValues; _rightValues = rightValues; } internal int Count { get { return _leftValues.Count; } } internal void Add( L left, R right ) { _leftValues.Add(left); _rightValues.Add(right); } internal Pair this[int index] { set { Left[index] = value.Left; Right[index] = value.Right; } } #if EXTRA_ENTITYSQL_PARSER_DEBUGGING #region GetEnumerator() public IEnumerator > GetEnumerator() { return new PairEnumerator(this); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region Enumerator internal struct PairEnumerator : IEnumerator > { PairOfLists _pair; int _pos; internal PairEnumerator( PairOfLists pair ) { _pair = pair; _pos = -1; } public Pair Current { get { return new Pair (_pair._leftValues[_pos], _pair._rightValues[_pos]); } } public bool MoveNext() { _pos++; if (_pos >= _pair.Count) { return false; } return true; } public void Dispose() { } public void Reset() { } object System.Collections.IEnumerator.Current { get { return null; } } } #endregion #endif } /// /// Helper class to enable tree path tagging in complex join/apply expressions /// Consider Moving to a bitmap or dictionary if needed. /// internal class TreePathTagger { private System.Text.StringBuilder _sb; ////// defauls constructor /// internal TreePathTagger() { _sb = new System.Text.StringBuilder(8); } ////// Marks a left visit in the path /// internal void VisitLeftNode() { _sb.Append('-'); } ////// Marks a right visit in the path /// internal void VisitRightNode() { _sb.Append('+'); } ////// leaves a path /// internal void LeaveNode() { _sb.Remove(_sb.Length - 1, 1); } ////// returns the current path tag /// internal string Tag { get { return _sb.ToString(); } } ////// Validates if a given path tag is a child of another path tag. /// /// ch /// ///static internal bool IsChildNode( string parentNodePrefix, string childNodePrefix ) { if (String.IsNullOrEmpty(childNodePrefix)) { return true; } return (childNodePrefix.Length > parentNodePrefix.Length && childNodePrefix.StartsWith(parentNodePrefix, StringComparison.Ordinal)); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backup [....] //--------------------------------------------------------------------- namespace System.Data.Common.EntitySql { using System; using System.IO; using System.Globalization; using System.Collections.Generic; using System.Diagnostics; using System.Data.Mapping; using System.Data.Common.CommandTrees; using System.Data.Common; using System.Data.Metadata.Edm; using System.Data.Entity; ////// implements the semantic resolver in the context of a metadata workspace and typespace /// ///not thread safe internal sealed class SemanticResolver { private StaticContext _staticContext; private DbCommandTree _commandTree; private ParserOptions _parserOptions; private Dictionary_parameters; private Dictionary > _variables; private uint _namegenCounter = 0; private TypeResolver _typeResolver; private int _scopeIndexHighMark; private List _scopeRegionSavepoints = new List (); private List _scopeRegionFlags = new List (); private StringComparer _stringComparer; private List _aggregateAstNodes = new List (); /// /// Initializes semantic resolver /// /// /// options /// ordinary parameters /// variable parameters ////// internal SemanticResolver(Perspective perspective, ParserOptions parserOptions, Dictionary/// eSqlParameters, Dictionary variableParameters) { EntityUtil.CheckArgumentNull(perspective, "perspective"); EntityUtil.CheckArgumentNull(parserOptions, "parserOptions"); // // set parser options // _parserOptions = parserOptions; // // Initialize string comparer based on parser configuration // if (ParserOptions.CaseSensitiveness.CaseSensitive == _parserOptions.IdentifierCaseSensitiveness) { _stringComparer = StringComparer.Ordinal; } else { _stringComparer = StringComparer.OrdinalIgnoreCase; } // // Initialize Type Resolver // _typeResolver = new TypeResolver(perspective, _stringComparer); // // Creates Scope manager // _staticContext = new StaticContext(_stringComparer); // // Validate eSql parameters // _parameters = ValidateParameters(eSqlParameters); // // validate variable-representing // _variables = ValidateVariables(variableParameters); // // push a 'global' scope region // EnterScopeRegion(); // // set default scope visibility to All scopes // CurrentScopeRegionFlags.ScopeViewKind = ScopeViewKind.All; } /// /// returns command tree /// internal DbCommandTree CmdTree { get { return _commandTree; } } ////// returns parameters /// internal DictionaryParameters { get { return _parameters; } } /// /// returns variables /// internal Dictionary> Variables { get { return _variables; } } /// /// TypeSpace/Metadata/Perspective Dependent Type Resolver /// internal TypeResolver TypeResolver { get { return _typeResolver; } } ////// Returns current Parser Options /// internal ParserOptions ParserOptions { get { return _parserOptions; } } ////// returns the current string comparer /// internal StringComparer ScopeStringComparer { get { return _stringComparer; } } ////// sets the command tree factory /// /// ////// internal void SetCommandTreeFactory(CommandExpr astCommandExpr) { EntityUtil.CheckArgumentNull(astCommandExpr, "astCommandExpr"); if (null != _commandTree) { throw EntityUtil.EntitySqlError(Strings.CommandTreeCanOnlyBeSetOnce); } switch (astCommandExpr.QueryExpr.ExprKind) { case AstExprKind.Query: case AstExprKind.Generic: _commandTree = new DbQueryCommandTree(TypeResolver.Perspective.MetadataWorkspace, TypeResolver.Perspective.TargetDataspace); break; default: throw EntityUtil.Argument(Strings.UnknownAstExpressionType); } } ////// /// Sets the command tree factory using the specified command tree. /// When specifying a command tree, the AST command expression may /// only be a query/generic expression, since these are the only /// AST command expression that can be used to create a stand-alone /// The command expression that is the root of the AST /// The command tree to use when converting the AST into a. /// internal void SetCommandTreeFactory(CommandExpr astCommandExpr, DbCommandTree commandTree) { EntityUtil.CheckArgumentNull(astCommandExpr, "astCommandExpr"); EntityUtil.CheckArgumentNull(commandTree, "commandTree"); if (null != _commandTree) { throw EntityUtil.EntitySqlError(Strings.CommandTreeCanOnlyBeSetOnce); } DbQueryCommandTree queryTree = commandTree as DbQueryCommandTree; if (null == queryTree || (astCommandExpr.ExprKind != AstExprKind.Query && astCommandExpr.ExprKind != AstExprKind.Generic)) { throw EntityUtil.Argument(Strings.UnknownAstExpressionType); } _commandTree = commandTree; } /// /// declares and validates namespaces /// /// namespace expression list ////// internal void DeclareNamespaces(ExprList/// nsExprList) { if (null == nsExprList) { return; } for (int i = 0; i < nsExprList.Count; i++) { NamespaceExpr nsExpr = nsExprList[i]; string namespaceName = nsExpr.NamespaceName.FullName; if (namespaceName.Equals(EdmConstants.CanonicalFunctionNamespace, StringComparison.OrdinalIgnoreCase)) { throw EntityUtil.EntitySqlError(nsExpr.NamespaceName.ErrCtx, System.Data.Entity.Strings.CannotUseCanonicalNamespace(namespaceName)); } if (nsExpr.IsAliased) { string aliasName = nsExpr.AliasIdentifier.Name; if (!TypeResolver.TryAddAliasedNamespace(aliasName, namespaceName)) { throw EntityUtil.EntitySqlError(nsExpr.AliasIdentifier.ErrCtx, System.Data.Entity.Strings.NamespaceAliasAlreadyUsed(aliasName)); } } else { if (!TypeResolver.TryAddNamespace(namespaceName)) { throw EntityUtil.EntitySqlError(nsExpr.NamespaceName.ErrCtx, System.Data.Entity.Strings.NamespaceNameAlreadyDeclared(namespaceName)); } } } } /// /// Declares Canonical namespace in query scope /// internal void DeclareCanonicalNamespace() { if (!TypeResolver.TryAddNamespace(EdmConstants.CanonicalFunctionNamespace)) { throw EntityUtil.EntitySqlError(Strings.FailedToDeclareCanonicalNamespace); } } ////// defines scope visibility mode /// internal enum ScopeViewKind { All, /* default - all scopes plus types/extents */ CurrentContext, /* entire context - all stacked scopes */ CurrentScopeRegion, /* current scope region */ CurrentAndPreviousScope, /* top 2 scopes */ CurrentScope, /* current scope only */ GroupScope } ////// sets the current scope visibility /// /// internal void SetScopeView(ScopeViewKind viewKind) { CurrentScopeRegionFlags.ScopeViewKind = viewKind; switch (CurrentScopeRegionFlags.ScopeViewKind) { case ScopeViewKind.All: case ScopeViewKind.CurrentContext: case ScopeViewKind.GroupScope: _scopeIndexHighMark = 0; break; case ScopeViewKind.CurrentAndPreviousScope: _scopeIndexHighMark = CurrentScopeIndex - 1; break; case ScopeViewKind.CurrentScopeRegion: _scopeIndexHighMark = CurrentScopeRegionSavePoint.ScopeIndex; break; case ScopeViewKind.CurrentScope: _scopeIndexHighMark = CurrentScopeIndex; break; } Debug.Assert(_scopeIndexHighMark <= CurrentScopeIndex, "_scopeIndexHighMark <= CurrentScopeIndex"); } ////// returns current scope view kind /// ///internal ScopeViewKind GetScopeView() { return CurrentScopeRegionFlags.ScopeViewKind; } /// /// returns current scope index /// internal int CurrentScopeIndex { get { return _staticContext.CurrentScopeIndex; } } ////// Performs scope lookup returning the scope index entry /// /// /// ///internal bool TryScopeLookup(string key, out ScopeEntry scopeEntry) { int dummyVar; return TryScopeLookup(key, out scopeEntry, out dummyVar); } /// /// Performs scope lookup returning the scope index entry /// /// /// /// ///internal bool TryScopeLookup(string key, out ScopeEntry scopeEntry, out int scopeIndex) { return TryScopeLookup(key, false /* ignoreGroupScope */, out scopeEntry, out scopeIndex); } /// /// Performs scope lookup returning the scope index entry /// /// /// /// /// ///internal bool TryScopeLookup(string key, bool ignoreGroupScope, out ScopeEntry scopeEntry, out int scopeIndex) { scopeEntry = null; scopeIndex = -1; // assert that scope index is always greater or equal to current scope index // scopes grow top -> bottom with high mark always 'higher or equal' then the bottom // of the stack of scopes Debug.Assert(_scopeIndexHighMark <= CurrentScopeIndex, "_scopeIndexHighMark <= CurrentScopeIndex"); int i = 0; for (i = CurrentScopeIndex; i >= _scopeIndexHighMark; i--) { if (_staticContext.GetScopeByIndex(i).TryLookup(key, out scopeEntry)) { if (!ignoreGroupScope && CurrentScopeRegionFlags.IsInGroupScope && scopeEntry.VarKind == SourceVarKind.GroupInput) { return false; } scopeIndex = i; return true; } } return false; } /// /// returns the appropriate expression from a given scope entry. /// /// /// ///private DbExpression GetExpressionFromScopeEntry(ScopeEntry scopeEntry, int scopeIndex) { DbExpression innerExpr = null; innerExpr = scopeEntry.Expression; // // if it inside a group aggregate function, the 'base' expression is relative to // the groupVar, represented in the scope entry as AggregateExpression // if (CurrentScopeRegionFlags.IsInsideGroupAggregate) { if (_aggregateAstNodes.Count > 0) { _aggregateAstNodes[0].ScopeIndex = Math.Max(_aggregateAstNodes[0].ScopeIndex, scopeIndex); } SourceScopeEntry sse = scopeEntry as SourceScopeEntry; if (null != sse) { if (sse.GroupVarExpression != null) { return sse.AggregateExpression; } else { return innerExpr; } } DummyGroupVarScopeEntry dgv = scopeEntry as DummyGroupVarScopeEntry; if (null != dgv) { return dgv.AggregateExpression; } } Debug.Assert(null != innerExpr, "null != innerExpr"); return innerExpr; } /// /// resolve identifier or dotidentifier ast expression /// /// /// ////// ////// /// /// DbExpression internal DbExpression ResolveIdentifier(string[] names, ErrorContext errCtx) { Debug.Assert(CurrentScopeIndex >= _scopeIndexHighMark, "CurrentScopeIndex >= _scopeIndexHighMark FAILED"); Debug.Assert(_scopeRegionFlags.Count == _scopeRegionSavepoints.Count, "_scopeRegionFlags.Count == _scopeRegionSavepoints.Count FAILED"); TypeUsage definingType = null; DbExpression innerExpr = null; ScopeEntry scopeEntry; KeyValuePairvarInfo; int scopeIndex = -1; int suffixIndex = 0; // // try base type in scope first // if (names.Length > 1 && TryScopeLookup(TypeResolver.GetFullName(names), out scopeEntry, out scopeIndex)) { // // Sets correlation flag // SetScopeRegionCorrelationFlag(scopeIndex); return GetExpressionFromScopeEntry(scopeEntry, scopeIndex); } else if (TryScopeLookup(names[0], out scopeEntry, out scopeIndex)) { // // check for invalid left correlation // if (scopeEntry.Kind == ScopeEntryKind.JoinSourceVar && !CurrentScopeRegionFlags.IsInsideJoinOnPredicate) { throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidJoinLeftCorrelation); } // // Sets correlation flag // SetScopeRegionCorrelationFlag(scopeIndex); // // trim resolved prefix // names = TypeResolver.TrimNamesPrefix(names, 1); // // get inner expression from the scope entry // also verifies if a nested group aggregate is being referenced inside // innerExpr = GetExpressionFromScopeEntry(scopeEntry, scopeIndex); // // assume defining type as the expression type // definingType = innerExpr.ResultType; } // // else if (_variables.TryGetValue(names[0], out varInfo)) { // // Can the identifier be resolved as a variable? // names = TypeResolver.TrimNamesPrefix(names, 1); innerExpr = CmdTree.CreateVariableReferenceExpression(varInfo.Key, varInfo.Value); definingType = innerExpr.ResultType; } else { // // Try resolve as entity container/entity set // if (TryResolveAsEntitySet(names, errCtx, out suffixIndex, out innerExpr)) { //return innerExpr; definingType = innerExpr.ResultType; } else { // // try base type from metadata // int matchCount = 0; definingType = TypeResolver.ResolveBaseType(names, out suffixIndex, out matchCount); if (matchCount > 1) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.AmbiguousName(TypeResolver.GetFullName(names))); } } } if (null != definingType) { return ResolveIdentifierChain(names, suffixIndex, definingType, innerExpr, errCtx); } return null; } /// /// Resolves Identifier name chain relative to a base expression /// /// /// /// /// ////// ////// /// /// internal DbExpression ResolveIdentifierChain(string[] names, int suffixIndex, DbExpression innerExpr, ErrorContext errCtx) { return ResolveIdentifierChain(names, suffixIndex, innerExpr.ResultType, innerExpr, errCtx); } /// /// Resolve identifier chain of names /// /// /// /// /// /// ///private DbExpression ResolveIdentifierChain(string[] names, int suffixIndex, TypeUsage definingType, DbExpression innerExpr, ErrorContext errCtx) { Debug.Assert(null != names, "null != names"); Debug.Assert(null != definingType, "null != definingType"); DbExpression convertedExpr = innerExpr; if (names.Length > 0) { TypeUsage baseType = definingType; for (int i = suffixIndex; i < names.Length; i++) { convertedExpr = ResolveIdentifierElement(baseType, convertedExpr, names[i], errCtx); baseType = convertedExpr.ResultType; } } return convertedExpr; } /// /// resolve part of identifier /// /// /// /// /// ////// ////// /// /// internal DbExpression ResolveIdentifierElement(TypeUsage definingType, DbExpression innerExpr, string name, ErrorContext errCtx) { DbExpression converted = null; if (TryResolveAsProperty(name, (null == innerExpr ? definingType : innerExpr.ResultType), innerExpr, errCtx, out converted)) { return converted; } if (TryResolveAsRef(name, (null == innerExpr ? definingType : innerExpr.ResultType), innerExpr, errCtx, out converted)) { return converted; } throw CqlErrorHelper.ReportIdentifierElementError(errCtx, name, definingType); } /// /// Try resolve as EntitySet /// /// /// /// ///private bool TryResolveAsEntitySet(string[] names, ErrorContext errCtx, out int suffixIndex, out DbExpression convExpr) { Debug.Assert(names.Length > 0, "(names.Length > 0) assertion failed"); convExpr = null; suffixIndex = 0; EntityContainer entityContainer = null; // first see if there a default EC if (names.Length == 1) { entityContainer = TypeResolver.Perspective.GetDefaultContainer(); suffixIndex = 0; } else { if (TypeResolver.Perspective.TryGetEntityContainer(names[0], true /*ignoreCase*/, out entityContainer)) { suffixIndex = 1; } else { return false; } } if (null != entityContainer) { EntitySetBase entitySet = null; if (TypeResolver.Perspective.TryGetExtent(entityContainer, names[suffixIndex], true /*ignoreCase*/, out entitySet)) { convExpr = CmdTree.CreateScanExpression(entitySet); suffixIndex++; return true; } else if (names.Length > 1) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.EntitySetIsDoesNotBelongToEntityContainer(names[1], names[0])); } } if (names.Length == 1 && TypeResolver.Perspective.TryGetEntityContainer(names[0], true /*ignoreCase*/, out entityContainer)) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.MissingEntitySetName(names[0])); } return false; } /// /// Try resolve name as property /// /// /// /// /// /// ///private bool TryResolveAsProperty(string name, TypeUsage definingType, DbExpression innerExpr, ErrorContext errCtx, out DbExpression convExpr) { convExpr = null; if (Helper.IsStructuralType(definingType.EdmType)) { EdmMember member = null; if (TypeResolver.Perspective.TryGetMember((StructuralType)definingType.EdmType, name, true, out member)) { if (null != member) { Debug.Assert(name.Equals(member.Name, StringComparison.OrdinalIgnoreCase), "name.Equals(member.Name,StringComparison.OrdinalIgnoreCase)"); if (null == innerExpr) { // // if we dont have an instance, then it means it is a static member that is not supported in EDM // throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.StaticMembersAreNotSupported(TypeHelpers.GetFullName(definingType.EdmType), name)); } convExpr = CmdTree.CreatePropertyExpression(name, true /* case insenstive */, innerExpr); return true; } } } return false; } /// /// try resolve name as ref /// /// /// /// /// /// ///private bool TryResolveAsRef(string name, TypeUsage definingType, DbExpression innerExpr, ErrorContext errCtx, out DbExpression convExpr) { convExpr = null; if (TypeSemantics.IsReferenceType(definingType)) { convExpr = CmdTree.CreateDerefExpression(innerExpr); TypeUsage dereferencedType = convExpr.ResultType; if (TryResolveAsProperty(name, convExpr.ResultType, convExpr, errCtx, out convExpr)) { return true; } throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.InvalidDeRefProperty(name, TypeHelpers.GetFullName(dereferencedType), TypeHelpers.GetFullName(definingType))); } return false; } /// /// Resolve given name as Type /// /// /// ////// ////// /// /// TypeUsage internal TypeUsage ResolveNameAsType(string[] names, Expr astTypeExpression) { int matchCount = 0; ErrorContext errCtx = astTypeExpression.ErrCtx; TypeUsage typeUsage = TypeResolver.ResolveNameAsType(names, names.Length, out matchCount); // // if null, means there is no type with given name in the current typespace // if (null == typeUsage) { CqlErrorHelper.ReportTypeResolutionError(names, astTypeExpression, this); } // // if match count is greater then 1, means there are more then one match and is therefore ambiguous // if (matchCount > 1) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.AmbiguousTypeName(TypeResolver.GetFullName(names))); } // // check for parametrized types // MethodExpr methodExpr = astTypeExpression as MethodExpr; if (null != methodExpr) { typeUsage = HandleParametrizedType(typeUsage, methodExpr); } return typeUsage; } ////// Handles parametrized types such as Decimal, Decimal(p) and Decimal(p,s) /// /// typeusage of the resolved type name /// ast node with representing additional type parameters to be validated ///private TypeUsage HandleParametrizedType(TypeUsage typeUsage, MethodExpr methodExpr) { // // The only type in EDM that we support arguments is EDM.Decimal as of Sep 2007. // PrimitiveType primitiveType = typeUsage.EdmType as PrimitiveType; if (null == primitiveType || primitiveType.PrimitiveTypeKind != PrimitiveTypeKind.Decimal) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.TypeDoesNotSupportParameters(primitiveType.FullName)); } Debug.Assert(methodExpr.Args[0] is Literal, "first type argument must be a literal node"); Debug.Assert((methodExpr.Args.Count == 2) ? methodExpr.Args[1] is Literal : true, "second type argument must be a literal node"); // // Get valid Precision for given type from provider // byte precision = GetValidDecimalFacetValue(primitiveType, (Literal)methodExpr.Args[0], DbProviderManifest.PrecisionFacetName); // // Get valid Scale for given type from provider // byte scale = 0; if (2 == methodExpr.Args.Count) { scale = GetValidDecimalFacetValue(primitiveType, (Literal)methodExpr.Args[1], DbProviderManifest.ScaleFacetName); } // // Ensure P >= S // if (precision < scale) { Debug.Assert(2 == methodExpr.Args.Count, "decimal with precision and scale must have 2 arguments"); throw EntityUtil.EntitySqlError(methodExpr.Args[1].ErrCtx, System.Data.Entity.Strings.DecimalPrecisionMustBeGreaterThanScale(primitiveType.FullName)); } // // finally create TypeUsage with given precision and scale // return TypeUsage.CreateDecimalTypeUsage(primitiveType, precision, scale); } /// /// validates and returns the appropriate facet for decimal type /// /// /// /// ///private byte GetValidDecimalFacetValue(PrimitiveType primitiveType, Literal typeArg, string FacetName) { FacetDescription facetDescription = Helper.GetFacet(primitiveType.ProviderManifest.GetFacetDescriptions(primitiveType), FacetName); if (null == facetDescription) { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeDoesNotSupportPrecisionOrScale(primitiveType.FullName, FacetName)); } byte typeArgValue = 0; if (Byte.TryParse(typeArg.OriginalValue, out typeArgValue)) { if (typeArgValue > facetDescription.MaxValue) { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeSpecExceedsMax(FacetName)); } if (typeArgValue < facetDescription.MinValue) { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeSpecBellowMin(FacetName)); } } else { throw EntityUtil.EntitySqlError(typeArg.ErrCtx, System.Data.Entity.Strings.TypeSpecIsNotValid); } return (byte)typeArgValue; } /// /// Handles expressions dotted id( List{e} ) that can be resolved to Static Methods, Type Constructors, Ordinary Functions and Aggregate Functions. /// this function ensures that one of the 3 posibilities must be resolved, otherwise an exception will be raised. /// Also, it ensures that the name is not ambiguous. /// /// /// /// /// ///internal void ResolveNameAsStaticMethodOrFunction(MethodExpr methodExpr, out TypeUsage constructorType, out TypeUsage staticMethodType, out IList functionType) { DotExpr dotExpr = methodExpr.MethodPrefixExpr; int matchCount = 0; int typeMatches = 0; constructorType = null; staticMethodType = null; functionType = null; // // Try Resolving as Type Constructor // constructorType = TypeResolver.ResolveNameAsType(dotExpr.Names, dotExpr.Names.Length, out matchCount); if (0 < matchCount) { typeMatches++; // // ensure type has contructor. one of: Entity, ComplexType or RelationType // if (!TypeSemantics.IsStructuralType(constructorType)) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidCtorUseOnType(TypeHelpers.GetFullName(constructorType))); } if (1 < matchCount) { dotExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericTypeCtor; throw EntityUtil.EntitySqlError(dotExpr.ErrCtx, System.Data.Entity.Strings.MultipleMatchesForName( System.Data.Entity.Strings.LocalizedType, dotExpr.FullName)); } } // // Try Resolving as EdmFunction // List foundNamespaces = null; functionType = TypeResolver.ResolveNameAsFunction(dotExpr.Names, true /* ignore case */, out matchCount, out foundNamespaces); if (0 < matchCount) { typeMatches++; if (1 < matchCount) { methodExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericFunctionCall; CqlErrorHelper.ReportAmbiguousFunctionError(methodExpr, foundNamespaces); } } #if SUPPORT_STATIC_METHODS // // Try Resolving as Static Method // staticMethodType = null; if (dotExpr.Names.Length > 1) { staticMethodType = TypeResolver.ResolveNameAsType(dotExpr.Names, dotExpr.Names.Length - 1, out matchCount); if (0 < matchCount) { typeMatches++; if (1 < matchCount) { dotExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxMethod; throw EntityUtil.QueryError(dotExpr.ErrCtx, System.Data.Entity.Strings.MultipleMatchesForName( System.Data.Entity.Strings.LocalizedType, dotExpr.FullName)); } } } #endif // // check if it is ambiguous. if the given method prefix name can be found as multiple types, then throw // if (1 < typeMatches) { throw EntityUtil.EntitySqlError(dotExpr.Identifier.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionMethodCtorName(dotExpr.FullName)); } // // Check if lookup was a miss // if (0 == typeMatches) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.CannotResolveNameToFunction(dotExpr.FullName)); } } /// /// Creates new instance of a given Type (IEntity, ComplexType or RelationType) /// Validates and infer argument types /// /// /// /// /// ////// ////// /// internal DbExpression CreateInstanceOfType(TypeUsage type, List args, List relshipExprList, MethodExpr methodExpr) { DbExpression newInstance = null; int idx = 0; int argCount = args.Count; methodExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericTypeCtor; // // abstract types cannot be instanciated // if (type.EdmType.Abstract) { throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.CannotInstantiateAbstractType(type.EdmType.FullName)); } // // ensure DISTINCT/ALL was not used on type constructors // if (methodExpr.DistinctKind != DistinctKind.None) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInCtor); } // // find overloads by searching members in order of its definition // each member will be considered as a formal argument type in the order of its definition // if (TypeSemantics.IsComplexType(type) || TypeSemantics.IsEntityType(type) || TypeSemantics.IsRelationshipType(type)) { StructuralType stype = (StructuralType)type.EdmType; foreach (EdmMember member in TypeHelpers.GetAllStructuralMembers(stype)) { TypeUsage memberModelTypeUsage = Helper.GetModelTypeUsage(member); Debug.Assert(memberModelTypeUsage.EdmType.DataSpace == DataSpace.CSpace, "member space must be CSpace"); // // ensure given arguments are not less than 'formal' constructor arguments // if (argCount <= idx) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.NumberOfTypeCtorIsLessThenFormalSpec(member.Name)); } // // if given argument is an untyped null, infer type the ctor formal argument list type // if (TypeSemantics.IsNullType(args[idx].ResultType)) { if (Helper.IsEdmProperty(member) && !((EdmProperty)member).Nullable) { throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, System.Data.Entity.Strings.InvalidNullLiteralForNonNullableMember( member.Name, TypeHelpers.GetFullName(stype))); } args[idx] = CmdTree.CreateNullExpression(memberModelTypeUsage); } // // if not, given argument is already typed, then ensure it is promotable to formal ctor argument type // bool isPromotable = TypeSemantics.IsPromotableTo(args[idx].ResultType, memberModelTypeUsage); if (ParserOptions.CompilationMode.RestrictedViewGenerationMode == _parserOptions.ParserCompilationMode || ParserOptions.CompilationMode.UserViewGenerationMode == _parserOptions.ParserCompilationMode ) { if (!isPromotable && !TypeSemantics.IsPromotableTo(memberModelTypeUsage, args[idx].ResultType)) { throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, System.Data.Entity.Strings.InvalidCtorArgumentType( args[idx].ResultType.Identity, member.Name, memberModelTypeUsage.Identity)); } if (Helper.IsPrimitiveType(memberModelTypeUsage.EdmType) && !TypeSemantics.IsSubTypeOf(args[idx].ResultType, memberModelTypeUsage)) { args[idx] = _commandTree.CreateCastExpression(args[idx], memberModelTypeUsage); } } else { if (!isPromotable) { throw EntityUtil.EntitySqlError(methodExpr.Args[idx].ErrCtx, System.Data.Entity.Strings.InvalidCtorArgumentType( args[idx].ResultType.Identity, member.Name, memberModelTypeUsage.Identity)); } } idx++; } } // // the type does not support type constructors // else { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidCtorUseOnType( TypeHelpers.GetFullName(type))); } // // ensure all given arguments and all 'formal' ctor arguments were considered and properly checked // if (idx != argCount) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxTypeCtorWithType(TypeHelpers.GetFullName(type)); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.NumberOfTypeCtorIsMoreThenFormalSpec( TypeHelpers.GetFullName(type))); } // // finally, create expression // if (relshipExprList != null && relshipExprList.Count > 0) { EntityType entityType = (EntityType)type.EdmType; newInstance = CmdTree.CreateNewEntityWithRelationshipsExpression(entityType, args, relshipExprList); } else { newInstance = CmdTree.CreateNewInstanceExpression(TypeHelpers.GetReadOnlyType(type), args); } Debug.Assert(null != newInstance, "null != newInstance"); return newInstance; } /// /// Creates DbFunctionExpression from metadata and arg list. validates overloads /// /// /// /// ////// ////// /// DbFunctionExpression internal DbExpression CreateFunction(IListfunctionTypeList, List args, MethodExpr methodExpr) { Debug.Assert(functionTypeList.Count > 0, "functionList.Count > 0"); DbExpression functionExpression = null; methodExpr.ErrCtx.ErrorContextInfo = EntityRes.CtxGenericFunctionCall; bool isAmbiguous = false; // // if DISTINCT was used, ensure function type and argument types are valid // if (methodExpr.DistinctKind != DistinctKind.None) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxFunction(functionTypeList[0].Name); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.InvalidDistinctArgumentInNonAggFunction); } // // collect argument types from argument expression list // List argTypes = new List (args.Count); for (int i = 0; i < args.Count; i++) { argTypes.Add(args[i].ResultType); } // // Find function overload match for given argument types // EdmFunction functionType = TypeResolver.ResolveFunctionOverloads(functionTypeList, argTypes, false /* isGroupAggregateFunction */, out isAmbiguous); // // if there is more then one overload that matches given arguments, throw // if (isAmbiguous) { methodExpr.ErrCtx.ErrorContextInfo = System.Data.Entity.Strings.CtxFunction(functionTypeList[0].Name); methodExpr.ErrCtx.UseContextInfoAsResourceIdentifier = false; throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, Strings.AmbiguousFunctionArguments); } // // if null, means no overload matched // if (null == functionType) { CqlErrorHelper.ReportFunctionOverloadError(methodExpr, functionTypeList[0], argTypes); } // // fixup untyped nulls in given arguments from formal method argument types // for (int i = 0; i < args.Count; i++) { if (TypeSemantics.IsNullType(args[i].ResultType)) { args[i] = CmdTree.CreateNullExpression(functionType.Parameters[i].TypeUsage); } } // // Finally, create expression // functionExpression = CmdTree.CreateFunctionExpression(functionType, args); Debug.Assert(null != functionExpression, "null != functionExpression"); return functionExpression; } /// /// Creates a static method expression /// /// /// /// ////// ////// /// MethodExpression internal static DbExpression CreateStaticMethod(TypeUsage definingType, Listargs, MethodExpr methodExpr) { return CreateMethod(definingType, args, methodExpr, null, true); } /// /// creates instance method expression /// /// /// /// ////// ////// /// InstanceMethodExpression or StaticMethodExpression internal static DbExpression CreateInstanceMethod(DbExpression instance, Listargs, MethodExpr methodExpr) { return CreateMethod(instance.ResultType, args, methodExpr, instance, false); } /// /// Creates a method expression. /// /// /// /// /// /// ///private static DbExpression CreateMethod(TypeUsage definingType, List args, MethodExpr methodExpr, DbExpression instance, bool isStatic) { #if METHOD_EXPRESSION Expression methodExpression = null; methodExpr.ErrCtx.ContextInfo = System.Data.Entity.Strings.CtxMethodTerm(methodExpr.MethodName); // // ensure definiting type is allowed for method calls // if (!TypeResolver.IsValidTypeForMethodCall(definingType)) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.DefiningTypeDoesNotSupportMethodCalls); } // // check if distinct/ALL was incorrectly used on methods // if (methodExpr.DistinctKind != DistinctKind.None) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.InvalidDistinctArgumentInMethod); } // // create list of arg types // List argTypes = new List (); foreach (Expression argExpr in args) { argTypes.Add(argExpr.ResultType); } // // Find function overload match for given args // bool isAmbiguous = false; MethodMetadata methodMetadata = TypeResolver.ResolveMethodOverloads(definingType, methodExpr.MethodName, argTypes, isStatic, out isAmbiguous); // // check if more than one overload match was found // if (isAmbiguous) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.AmbiguousFunctionArguments); } // // check if there is a match // if (null == methodMetadata) { throw EntityUtil.QueryError(methodExpr.ErrCtx, System.Data.Entity.Strings.NoMethodOverloadMatch(methodExpr.MethodName, definingType.FullName)); } // // fixup untyped nulls in given arguments from formal method argument types // for (int i = 0 ; i < args.Count ; i++) { if (TypeResolver.IsNull(args[i].ResultType)) { args[i] = CmdTree.CreateNullExpression(methodMetadata.Parameters[i].Type); } } // // Finally, create expression // #if CALCULATED_MEMBERS ClrCalculatedMethod clrCalculatedMethod = methodMetadata as ClrCalculatedMethod; if (null != clrCalculatedMethod) { if (!isStatic) { args.Insert(0, instance); } methodExpression = clrCalculatedMethod.GetExpression(CmdTree, args); } else { #endif if (isStatic) { methodExpression = CmdTree.CreateStaticMethodExpression(methodMetadata, args); } else { methodExpression = CmdTree.CreateInstanceMethodExpression(methodMetadata, instance, args); } #if CALCULATED_MEMBERS } #endif Debug.Assert(null != methodExpression); return methodExpression; #endif throw EntityUtil.EntitySqlError(methodExpr.ErrCtx, System.Data.Entity.Strings.MethodInvocationNotSupported); } /// /// Ensure and typifies nulls if present /// ////// ////// Pair of Expressions with NullTypes inferred to the proper type internal PairEnsureTypedNulls(DbExpression leftExpr, DbExpression rightExpr, ErrorContext errCtx, Func formatMessage) { DbExpression newLeftExpr = leftExpr; DbExpression newRightExpr = rightExpr; UntypedNullExpression untypedLeftExpr = leftExpr as UntypedNullExpression; UntypedNullExpression untypedRightExpr = rightExpr as UntypedNullExpression; if (null != untypedLeftExpr) { if (null != untypedRightExpr || null == rightExpr) { throw EntityUtil.EntitySqlError(errCtx, formatMessage()); } else { newLeftExpr = CmdTree.CreateNullExpression(rightExpr.ResultType); } } else if (null != untypedRightExpr) { newRightExpr = CmdTree.CreateNullExpression(leftExpr.ResultType); } return new Pair (newLeftExpr, newRightExpr); } /// /// returns if an expresison is an untyped null expression /// /// ///internal static bool IsNullExpression(DbExpression expression) { return expression is UntypedNullExpression; } /// /// Set current source scope kind /// /// internal void SetCurrentScopeKind(ScopeEntryKind scopeKind) { CurrentScopeRegionFlags.ScopeEntryKind = scopeKind; } ////// Get current source scope kind /// internal ScopeEntryKind CurrentScopeKind { get { return CurrentScopeRegionFlags.ScopeEntryKind; } } ////// Generates internal name to be used in cqt /// /// ///internal string GenerateInternalName(string hint) { // string concat is faster than String.Join and much faster than String.Format return "_##" + hint + unchecked(_namegenCounter++).ToString(CultureInfo.InvariantCulture); } private static DbExpressionKind[] joinMap = { DbExpressionKind.CrossJoin, DbExpressionKind.InnerJoin, DbExpressionKind.LeftOuterJoin, DbExpressionKind.FullOuterJoin }; /// /// maps ast jointype to cqt join type /// /// ///internal static DbExpressionKind MapJoinKind(JoinKind joinKind) { Debug.Assert(joinKind != JoinKind.RightOuter, "joinKind != JoinKind.RightOuter"); return joinMap[(int)joinKind]; } private static DbExpressionKind[] applyMap = { DbExpressionKind.CrossApply, DbExpressionKind.OuterApply }; /// /// maps ast applytype to cqt apply type /// /// ///internal static DbExpressionKind MapApplyKind(ApplyKind applyKind) { return applyMap[(int)applyKind]; } /// /// returns current scope region savepoint /// internal SavePoint CurrentScopeRegionSavePoint { get { Debug.Assert(_scopeRegionSavepoints.Count > 0, "_scopeRegionSavepoints.Count > 0"); return _scopeRegionSavepoints[0]; } } ////// propagates new ebinding up the tree for the vars in scope /// /// internal void FixupSourceVarBindings(DbVariableReferenceExpression newSourceVar) { Debug.Assert(CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex, "CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex"); for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { SourceScopeEntry scopeEntry = scope.Value as SourceScopeEntry; if (null != scopeEntry) { scopeEntry.SetNewSourceBinding(newSourceVar); } } } } /// /// fixup new eb /// /// internal void FixupNamedSourceVarBindings(DbVariableReferenceExpression newSourceVar) { Debug.Assert(CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex, "CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex"); for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { if (scope.Value.Kind == CurrentScopeKind && !scope.Value.IsHidden) { SourceScopeEntry scopeEntry = scope.Value as SourceScopeEntry; if (null != scopeEntry && TreePathTagger.IsChildNode(CurrentScopeRegionFlags.PathTagger.Tag, scopeEntry.VarTag)) { scopeEntry.AddBindingPrefix(newSourceVar.VariableName); scopeEntry.SetNewSourceBinding(newSourceVar); } } } } } internal void MarkGroupInputVars() { for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePair scope in _staticContext.GetScopeByIndex(i)) { if (scope.Value.VarKind != SourceVarKind.GroupAggregate && scope.Value.VarKind != SourceVarKind.GroupKey) { scope.Value.VarKind = SourceVarKind.GroupInput; } } } } /// /// Fixes up source vars and adds groupvar in case the var is referenced in an group aggregate fucntion /// /// /// internal void FixupGroupSourceVarBindings(DbVariableReferenceExpression newSourceVar, DbVariableReferenceExpression newGroupVar) { InternalFixupGroupSourceVarBindings(newSourceVar, newGroupVar); } ////// undo group var /// /// internal void UndoFixupGroupSourceVarBindings(DbVariableReferenceExpression originalSourceVar) { InternalFixupGroupSourceVarBindings(originalSourceVar, null); } private void InternalFixupGroupSourceVarBindings(DbVariableReferenceExpression newSourceVar, DbVariableReferenceExpression newGroupVar) { Debug.Assert(CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex, "CurrentScopeRegionSavePoint.ScopeIndex + 1 <= CurrentScopeIndex"); for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { if (scope.Value.Kind == CurrentScopeKind && !scope.Value.IsHidden) { SourceScopeEntry scopeEntry = scope.Value as SourceScopeEntry; if (null != scopeEntry) { scopeEntry.SetNewSourceBinding(newSourceVar); scopeEntry.GroupVarExpression = newGroupVar; } } } } } /// /// Sets Current Scope Source Var Kind /// /// internal void SetCurrentScopeVarKind(FromClauseItemKind fromClauseItemKind) { if (fromClauseItemKind == FromClauseItemKind.JoinFromClause) { SetCurrentScopeKind(ScopeEntryKind.JoinSourceVar); } else if (fromClauseItemKind == FromClauseItemKind.ApplyFromClause) { SetCurrentScopeKind(ScopeEntryKind.ApplySourceVar); } else { SetCurrentScopeKind(ScopeEntryKind.SourceVar); } } ////// resets all source vars to default SourceVar Kind /// internal void ResetCurrentScopeVarKind() { for (int i = CurrentScopeRegionSavePoint.ScopeIndex + 1; i <= CurrentScopeIndex; i++) { foreach (KeyValuePairscope in _staticContext.GetScopeByIndex(i)) { scope.Value.Kind = ScopeEntryKind.SourceVar; } } SetCurrentScopeKind(ScopeEntryKind.SourceVar); } /// /// Infers and Creates a new Alias name based on expr information /// /// ///private string CreateNewAlias(DbExpression expr) { DbScanExpression extent = expr as DbScanExpression; if (null != extent) { return extent.Target.Name; } DbPropertyExpression property = expr as DbPropertyExpression; if (null != property) { return property.Property.Name; } DbVariableReferenceExpression varRef = expr as DbVariableReferenceExpression; if (null != varRef) { return varRef.VariableName; } return GenerateInternalName(String.Empty); } /// /// infers alias name from astNode information and converted expression. /// /// /// ///internal string InferAliasName(AliasExpr aliasExpr, DbExpression convertedExpression) { if (aliasExpr.HasAlias) { return aliasExpr.AliasIdentifier.Name; } Identifier id = aliasExpr.Expr as Identifier; if (null != id) { return id.Name; } DotExpr dotExpr = aliasExpr.Expr as DotExpr; if (null != dotExpr && dotExpr.IsDottedIdentifier) { return dotExpr.Names[dotExpr.Length - 1]; } return CreateNewAlias(convertedExpression); } /// /// represents scope region disposer helper /// internal sealed class ScopeRegionDisposer : IDisposable { private SemanticResolver _semanticResolver; internal ScopeRegionDisposer(SemanticResolver semanticResolver) { _semanticResolver = semanticResolver; } public void Dispose() { _semanticResolver.LeaveScopeRegion(); } } ////// adds a scope entry to the scope /// /// /// internal void AddToScope(string key, ScopeEntry scopeEntry) { _staticContext.Add(key, scopeEntry); } ////// Removes an entry from scope /// /// internal void RemoveFromScope(string key) { _staticContext.RemoveFromScope(key); } ////// enters a scope region /// ///internal ScopeRegionDisposer EnterScopeRegion() { Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); // // push new savepoint // _scopeRegionSavepoints.Insert(0, CreateSavePoint()); // // push new scope // _staticContext.EnterScope(); // // push new scoperegion flags // _scopeRegionFlags.Insert(0, new ScopeRegionFlags()); Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); // // return disposer // return new ScopeRegionDisposer(this); } /// /// Leaves a scope region /// internal void LeaveScopeRegion() { Debug.Assert(_scopeRegionSavepoints.Count > 0, "_scopeRegionSavepoints.Count > 0"); Debug.Assert(_scopeRegionFlags.Count > 0, "_scopeRegionFlags.Count > 0"); Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); // // roll back scopes to the ScopeRegion savepoint // RollbackToSavepoint(CurrentScopeRegionSavePoint); // // pop top ScopeRegion savepoint // _scopeRegionSavepoints.RemoveAt(0); // // cleanup all aggregates related to this scope region // foreach (MethodExpr me in CurrentScopeRegionFlags.GroupAggregatesInfo.Keys) { me.ResetAggregateInfo(); } // // pop top ScopeRegion flags // _scopeRegionFlags.RemoveAt(0); // // restore scope view to the previously save view // SetScopeView(CurrentScopeRegionFlags.ScopeViewKind); Debug.Assert(_scopeRegionSavepoints.Count == _scopeRegionFlags.Count, "_scopeRegionSavepoints.Count == _scopeRegionFlags.Count"); } ////// Creates a scope save point /// ///internal SavePoint CreateSavePoint() { return new SavePoint(CurrentScopeIndex); } /// /// rolls back the scope to a give savepoint /// /// internal void RollbackToSavepoint(SavePoint sp) { _staticContext.RollbackToSavepoint(sp); } ////// returns current scope region flags /// internal ScopeRegionFlags CurrentScopeRegionFlags { get { return _scopeRegionFlags[0]; } } ////// scope disposer /// internal sealed class ScopeDisposer : IDisposable { private SemanticResolver _semanticResolver; internal ScopeDisposer(SemanticResolver semanticResolver) { _semanticResolver = semanticResolver; } public void Dispose() { _semanticResolver.LeaveScope(); } } ////// entry scope /// ///internal ScopeDisposer EnterScope() { _staticContext.EnterScope(); return new ScopeDisposer(this); } /// /// leave scope /// internal void LeaveScope() { _staticContext.LeaveScope(); } ////// ensures the expression is typed /// /// /// ////// internal static void EnsureIsNotUntypedNull(DbExpression expression, ErrorContext errCtx) { if (expression is UntypedNullExpression) { throw EntityUtil.EntitySqlError(errCtx, Strings.ExpressionCannotBeNull); } } ////// /// checks if a name is in the current scope /// /// ///internal bool IsInCurrentScope(string key) { return _staticContext.IsInCurrentScope(key); } /// /// adds a source binding /// /// internal void AddSourceBinding(DbExpressionBinding sourceBinding) { _staticContext.AddSourceBinding(sourceBinding, this.CurrentScopeRegionFlags.ScopeEntryKind, this.CurrentScopeRegionFlags.PathTagger.Tag); } ////// adds dummy group key to be consumed during aggregate search pahse /// /// /// /// internal void AddDummyGroupKeyToScope(string groupKey, DbExpression varBasedExpression, DbExpression groupVarBasedExpression) { _staticContext.AddGroupDummyVar(groupKey, varBasedExpression, groupVarBasedExpression); } ////// Replaces dummy key added during the aggregate search phase by the real group key /// /// /// internal void ReplaceGroupVarInScope(string groupVarName, DbVariableReferenceExpression groupSourceBinding) { _staticContext.ReplaceGroupVarInScope(groupVarName, groupSourceBinding); } ////// Adds group aggregate vars to scope, including nested aggregate /// /// /// internal void AddGroupAggregateToScope(string aggregateName, DbVariableReferenceExpression sourceVar) { _staticContext.AddAggregateToScope(aggregateName, sourceVar); } ////// Validates that the specified parameters have valid, non-duplicated names /// /// The set of input parameter names and types ///A valid dictionary that maps parameter names to types using the current StringComparer private DictionaryValidateParameters(Dictionary paramDefs) { Dictionary retParams = new Dictionary (_stringComparer); if (paramDefs != null) { foreach (KeyValuePair paramDef in paramDefs) { if (retParams.ContainsKey(paramDef.Key)) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.MultipleDefinitionsOfParameter(paramDef.Key)); } if (!ValidateParameterType(paramDef.Value)) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.InvalidParameterType(paramDef.Key)); } retParams.Add(paramDef.Key, paramDef.Value); } } return retParams; } /// /// Validates that the specified variables have valid, non-duplicated names /// /// The set of input parameter names and types ///A valid dictionary that maps variable names to types using the current StringComparer private Dictionary> ValidateVariables(Dictionary varDefs) { Dictionary > retVars = new Dictionary >(_stringComparer); if (varDefs != null) { foreach (KeyValuePair varDef in varDefs) { if (retVars.ContainsKey(varDef.Key)) { throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.MultipleDefinitionsOfVariable(varDef.Key)); } retVars.Add(varDef.Key, new KeyValuePair (varDef.Key, varDef.Value)); } } return retVars; } private static bool ValidateParameterType(TypeUsage paramType) { return (paramType != null && paramType.EdmType != null && (TypeSemantics.IsPrimitiveType(paramType) || paramType.EdmType is EnumType)); } internal static void EnsureValidTypeForNullExpression(TypeUsage type, ErrorContext errCtx) { if (TypeSemantics.IsCollectionType(type)) { throw EntityUtil.EntitySqlError(errCtx, Strings.NullLiteralCannotBePromotedToCollectionOfNulls); } } /// /// Adds the group aggregate information to the innermost correlated scope. if the expression is not correlated /// then add to the innermost scope. /// /// /// /// /// internal void AddGroupAggregateInfoToScopeRegion(MethodExpr astNode, string aggregateName, DbAggregate aggregateExpression, int groupVarScopeIndex) { int scopeRegionIndex = 0; bool bFound = false; // if scope index is NonCorrelatedScope, it means the inner aggregate expression is not correlated and it is // just as constant expression if (groupVarScopeIndex == AggregateAstNodeInfo.NonCorrelatedScope) { bFound = true; scopeRegionIndex = 0; } else { // inner expression is correlated and we need to find the exact group var scope for (scopeRegionIndex = 0; scopeRegionIndex < _scopeRegionSavepoints.Count; scopeRegionIndex++) { if (_scopeRegionSavepoints[scopeRegionIndex].ContainsScope(groupVarScopeIndex)) { bFound = true; break; } } } if (bFound) { Debug.Assert(_scopeRegionFlags.Count > 0, "must have a scope region (_scopeRegionFlags.Count > 0)"); _scopeRegionFlags[scopeRegionIndex].AddGroupAggregateInfo(astNode, new GroupAggregateInfo(aggregateName, aggregateExpression)); } else { throw EntityUtil.EntitySqlError(Strings.GroupVarNotFoundInScope); } } ////// pushes aggregate ast node /// /// internal void PushAggregateAstNode(MethodExpr astNode) { _aggregateAstNodes.Insert(0, new AggregateAstNodeInfo(astNode)); } ////// removes aggregate ast node from the top of the stack /// ///internal AggregateAstNodeInfo PopAggregateAstNode() { Debug.Assert(_aggregateAstNodes.Count > 0, "_aggregateAstNodeInfo.Count must be greater than zero to pop"); AggregateAstNodeInfo result = _aggregateAstNodes[0]; _aggregateAstNodes.RemoveAt(0); return result; } /// /// returns true if any of the ScopeRegions from the closest to the outermost has a group scope /// ///internal bool IsInAnyGroupScope() { for (int i = 0; i < _scopeRegionFlags.Count; i++) { if (_scopeRegionFlags[i].IsInGroupScope) { return true; } } return false; } /// /// resets correlation flag in current scope region /// internal void ResetScopeRegionCorrelationFlag() { CurrentScopeRegionFlags.WasResolutionCorrelated = false; } ////// sets the correlation flag based on the input var scope index /// /// internal void SetScopeRegionCorrelationFlag(int scopeIndex) { Debug.Assert(_scopeRegionFlags.Count == _scopeRegionSavepoints.Count, "_scopeRegionFlags.Count == _scopeRegionSavepoints.Count pre-condition FAILED"); for (int i = 0; i < _scopeRegionFlags.Count; i++) { if (scopeIndex > _scopeRegionSavepoints[i].ScopeIndex) { _scopeRegionFlags[i].WasResolutionCorrelated = true; return; } } } ////// ensures limit expression (used in TOP and LIMIT) have valid pre-conditions /// NOTE that in M3.2 these preconditions are exactly the same for TOP and LIMIT. /// /// /// /// internal void EnsureValidLimitExpression(ErrorContext errCtx, DbExpression expr, string subclauseName) { // // ensure resolved expression have the right type // if (!TypeSemantics.IsPromotableTo(expr.ResultType, TypeResolver.Int64Type)) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeCompatibleWithEdm64(subclauseName, expr.ResultType.EdmType.FullName)); } // // if it is a literal, make sure it has the correct value // DbConstantExpression constantExpr = expr as DbConstantExpression; if (null != constantExpr && System.Convert.ToInt64(constantExpr.Value, CultureInfo.InvariantCulture) < 0) { throw EntityUtil.EntitySqlError(errCtx, System.Data.Entity.Strings.PlaceholderExpressionMustBeGreaterThanOrEqualToZero(subclauseName)); } } ////// Ensures Projection is valid for Distinct modifier /// /// /// internal static void ValidateDistinctProjection(SelectClause selectClause, TypeUsage projectionType) { TypeUsage elemType = TypeHelpers.GetElementTypeUsage(projectionType); if (!TypeHelpers.IsValidDistinctOpType(elemType)) { ErrorContext errCtx = selectClause.Items[0].Expr.ErrCtx; if (TypeSemantics.IsRowType(elemType)) { RowType rowType = elemType.EdmType as RowType; Debug.Assert(selectClause.Items.Count == rowType.Members.Count); for (int i = 0; i < rowType.Members.Count; i++) { if (!TypeSemantics.IsEqualComparable(rowType.Members[i].TypeUsage)) { errCtx = selectClause.Items[i].Expr.ErrCtx; break; } } } throw EntityUtil.EntitySqlError(errCtx, Strings.SelectDistinctMustBeEqualComparable); } } ////// Ensures that the result of a query expression is valid. /// /// /// internal static void ValidateQueryResultType(TypeUsage resultType, ErrorContext errCtx) { if (Helper.IsCollectionType(resultType.EdmType)) { ValidateQueryResultType(((CollectionType)resultType.EdmType).TypeUsage, errCtx); } else if (Helper.IsRowType(resultType.EdmType)) { foreach (EdmProperty property in ((RowType)resultType.EdmType).Properties) { ValidateQueryResultType(property.TypeUsage, errCtx); } } else if (Helper.IsAssociationType(resultType.EdmType)) { throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidQueryResultType(resultType.Identity)); } } } ////// represents a method ast node during aggregate resolution /// internal sealed class AggregateAstNodeInfo { internal const int NonCorrelatedScope = Int32.MinValue; private MethodExpr _methodExpr; private int _scopeIndex; internal AggregateAstNodeInfo( MethodExpr methodAstNode ) { _methodExpr = methodAstNode; _scopeIndex = NonCorrelatedScope; } internal void AssertMethodExprEquivalent(MethodExpr other) { Debug.Assert(_methodExpr == other, "method ast node from the top of the stack must be the same"); } internal int ScopeIndex { get { return _scopeIndex; } set { Debug.Assert(value >= 0); _scopeIndex = value; } } } ////// represents resolved group aggregate information /// internal sealed class GroupAggregateInfo { private DbAggregate _aggregateExpression = null; private string _aggregateName = null; internal GroupAggregateInfo( string aggregateName, DbAggregate aggregateExpression ) { _aggregateName = aggregateName; _aggregateExpression = aggregateExpression; } internal DbAggregate AggregateExpression { get { return _aggregateExpression; } } internal string AggregateName { get { return _aggregateName; } } } /// /// represents set of flags for a ScopeRegion /// internal sealed class ScopeRegionFlags { /// /// used to indicate when JoinSourceVars are visible within the scope of join sub-expresion tree resolution. /// Join right sub-expressions must not refer to join left sub-expressions (recursively). Of course, by definition /// ON predicate is allowed to refer to any previously defined join sources. General left correlation /// is allowed though. for instance: /// Select ... From A JOIN B JOIN A.x -> invalid /// Select ... From A JOIN B JOIN C ON A.x == C.x -> valid /// Select ... From A JOIN B, C JOIN A.x ... -> valid /// private bool _isInsideJoinOnPredicate = false; internal bool IsInsideJoinOnPredicate { get { return _isInsideJoinOnPredicate; } set { _isInsideJoinOnPredicate = value; } } /// /// indicates if input scope has group scope semantics /// private bool _isInGroupScope = false; internal bool IsInGroupScope { get { return _isInGroupScope; } set { _isInGroupScope = value; } } ////// indicates expression being resolved is a candidate for group aggregate and /// should reference groupVar instead of the sourceVar /// private bool _isInsideGroupAggregate = false; internal bool IsInsideGroupAggregate { get { return _isInsideGroupAggregate; } set { _isInsideGroupAggregate = value; } } ////// tracks group aggregate nesting calls /// private int _groupAggregateNestingCount = 0; internal int GroupAggregateNestingCount { get { return _groupAggregateNestingCount; } } ////// resets group aggregate function nesting call /// internal void ResetGroupAggregateNestingCount() { _groupAggregateNestingCount = 0; } internal void IncrementGroupAggregateNestingCount() { _groupAggregateNestingCount++; } internal void DecrementGroupAggregateNestingCount() { _groupAggregateNestingCount--; } private Dictionary_groupAggregatesInfo = new Dictionary (); internal void AddGroupAggregateInfo( MethodExpr astMethodNode, GroupAggregateInfo groupAggrInfo ) { _groupAggregatesInfo.Add(astMethodNode, groupAggrInfo); } internal Dictionary GroupAggregatesInfo { get { return _groupAggregatesInfo; } } /// /// keep group aggregates names /// private HashSet_groupAggregateNames = new HashSet (); internal bool ContainsGroupAggregate( string groupAggregateName ) { return _groupAggregateNames.Contains(groupAggregateName); } /// /// adds group aggregate name to scope region flags /// /// internal void AddGroupAggregateToScopeFlags( string groupAggregateName ) { Debug.Assert(!_groupAggregateNames.Contains(groupAggregateName),"!_groupAggregateNames.ContainsKey(groupAggregateName)"); _groupAggregateNames.Add(groupAggregateName); } ////// indicates if a nested group aggregate was referred by any sub-expression within /// a give group scope /// private bool _wasNestedGroupAggregateReferredByInnerExpressions = false; internal bool WasNestedGroupAggregateReferredByInnerExpressions { get { return _wasNestedGroupAggregateReferredByInnerExpressions; } set { _wasNestedGroupAggregateReferredByInnerExpressions = value; } } private SemanticResolver.ScopeViewKind _scopeViewKind = SemanticResolver.ScopeViewKind.All; internal SemanticResolver.ScopeViewKind ScopeViewKind { get { return _scopeViewKind; } set { _scopeViewKind = value; } } ////// indicates if current scope region holds an implicit group /// internal bool IsImplicitGroup { get { return _isImplicitGroup; } set { _isImplicitGroup = value; } } private bool _isImplicitGroup = false; ////// indicates the kind of the current scope entries /// internal ScopeEntryKind ScopeEntryKind { get { return _scopeEntryKind; } set { _scopeEntryKind = value; } } private ScopeEntryKind _scopeEntryKind = ScopeEntryKind.SourceVar; ////// defines if a given expression resolution is correlated /// internal bool WasResolutionCorrelated { get { return _wasResolutionCorrelated; } set { _wasResolutionCorrelated = value; } } private bool _wasResolutionCorrelated = false; ////// Represents the path tagger in the current input scope /// private TreePathTagger _treePathTagger = new TreePathTagger(); internal TreePathTagger PathTagger { get { return _treePathTagger; } } } ////// represents a pair of types to avoid uncessary enumerations to split kvp elements /// ////// internal sealed class Pair { internal Pair( L left, R right ) { Left = left; Right = right; } internal L Left; internal R Right; } /// /// represents a pair of types to avoid uncessary enumerations to split kvp lists /// ////// internal sealed class PairOfLists /* : IEnumerable > */ { private List _leftValues; internal List Left { get { return _leftValues; } } private List _rightValues; internal List Right { get { return _rightValues; } } internal PairOfLists() { _leftValues = new List (); _rightValues = new List (); } internal PairOfLists( List leftValues, List rightValues ) { _leftValues = leftValues; _rightValues = rightValues; } internal int Count { get { return _leftValues.Count; } } internal void Add( L left, R right ) { _leftValues.Add(left); _rightValues.Add(right); } internal Pair this[int index] { set { Left[index] = value.Left; Right[index] = value.Right; } } #if EXTRA_ENTITYSQL_PARSER_DEBUGGING #region GetEnumerator() public IEnumerator > GetEnumerator() { return new PairEnumerator(this); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region Enumerator internal struct PairEnumerator : IEnumerator > { PairOfLists _pair; int _pos; internal PairEnumerator( PairOfLists pair ) { _pair = pair; _pos = -1; } public Pair Current { get { return new Pair (_pair._leftValues[_pos], _pair._rightValues[_pos]); } } public bool MoveNext() { _pos++; if (_pos >= _pair.Count) { return false; } return true; } public void Dispose() { } public void Reset() { } object System.Collections.IEnumerator.Current { get { return null; } } } #endregion #endif } /// /// Helper class to enable tree path tagging in complex join/apply expressions /// Consider Moving to a bitmap or dictionary if needed. /// internal class TreePathTagger { private System.Text.StringBuilder _sb; ////// defauls constructor /// internal TreePathTagger() { _sb = new System.Text.StringBuilder(8); } ////// Marks a left visit in the path /// internal void VisitLeftNode() { _sb.Append('-'); } ////// Marks a right visit in the path /// internal void VisitRightNode() { _sb.Append('+'); } ////// leaves a path /// internal void LeaveNode() { _sb.Remove(_sb.Length - 1, 1); } ////// returns the current path tag /// internal string Tag { get { return _sb.ToString(); } } ////// Validates if a given path tag is a child of another path tag. /// /// ch /// ///static internal bool IsChildNode( string parentNodePrefix, string childNodePrefix ) { if (String.IsNullOrEmpty(childNodePrefix)) { return true; } return (childNodePrefix.Length > parentNodePrefix.Length && childNodePrefix.StartsWith(parentNodePrefix, StringComparison.Ordinal)); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SqlLiftIndependentRowExpressions.cs
- BindStream.cs
- UnsafeNativeMethods.cs
- SqlParameter.cs
- BindingExpression.cs
- DocumentReferenceCollection.cs
- AnimationException.cs
- XmlMembersMapping.cs
- GroupDescription.cs
- XmlSerializerAssemblyAttribute.cs
- PageCatalogPart.cs
- FactoryMaker.cs
- ConfigurationProperty.cs
- NativeCompoundFileAPIs.cs
- IfAction.cs
- XmlSchemaRedefine.cs
- sqlcontext.cs
- FileUtil.cs
- PublisherMembershipCondition.cs
- SmtpTransport.cs
- WorkflowServiceNamespace.cs
- GeneralTransform3DTo2D.cs
- Stylus.cs
- XmlRootAttribute.cs
- BaseTemplateBuildProvider.cs
- InternalResources.cs
- UriWriter.cs
- SmtpClient.cs
- IdentityElement.cs
- UrlMappingCollection.cs
- ConstraintConverter.cs
- ClientConfigurationSystem.cs
- URLString.cs
- RegexWorker.cs
- FileIOPermission.cs
- DbConnectionPoolCounters.cs
- ImageSource.cs
- FrameSecurityDescriptor.cs
- xmlNames.cs
- TextServicesCompartmentEventSink.cs
- ViewBox.cs
- ExtensionSimplifierMarkupObject.cs
- UnsafeNativeMethods.cs
- AssociationSet.cs
- PopupEventArgs.cs
- CopyAction.cs
- AppliedDeviceFiltersDialog.cs
- XmlText.cs
- XsltLoader.cs
- FilterQueryOptionExpression.cs
- PasswordTextNavigator.cs
- SystemWebCachingSectionGroup.cs
- TextParaClient.cs
- TabPanel.cs
- BitmapDownload.cs
- SoapAttributeOverrides.cs
- XmlSchemaComplexContentRestriction.cs
- EnumMemberAttribute.cs
- SqlRowUpdatingEvent.cs
- FrameworkContentElement.cs
- PresentationTraceSources.cs
- SqlProviderManifest.cs
- DefaultProxySection.cs
- RegistrationServices.cs
- ToggleProviderWrapper.cs
- DataGridState.cs
- ArgumentOutOfRangeException.cs
- UdpConstants.cs
- ResourcePermissionBase.cs
- Transform3DGroup.cs
- OrderedDictionaryStateHelper.cs
- IncrementalReadDecoders.cs
- HtmlInputRadioButton.cs
- SettingsAttributes.cs
- TypeFieldSchema.cs
- WmpBitmapEncoder.cs
- DataGridSortCommandEventArgs.cs
- PixelFormatConverter.cs
- CacheRequest.cs
- DataGridViewButtonCell.cs
- QueryableFilterRepeater.cs
- ItemList.cs
- _Events.cs
- ExpressionPrefixAttribute.cs
- SimpleTableProvider.cs
- ViewCellSlot.cs
- hebrewshape.cs
- ParameterBuilder.cs
- TypeHelper.cs
- DummyDataSource.cs
- AttachedPropertyBrowsableForTypeAttribute.cs
- SoapAttributeAttribute.cs
- LayoutEngine.cs
- MetaModel.cs
- WSIdentityFaultException.cs
- ScaleTransform3D.cs
- GreenMethods.cs
- parserscommon.cs
- Action.cs
- SecurityKeyIdentifier.cs