Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Common / EntitySql / SemanticResolver.cs / 4 / 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 Dictionary Parameters
{
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;
KeyValuePair varInfo;
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(IList functionTypeList,
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, List args, MethodExpr methodExpr)
{
return CreateMethod(definingType, args, methodExpr, null, true);
}
///
/// creates instance method expression
///
///
///
///
///
///
///
///
/// InstanceMethodExpression or StaticMethodExpression
internal static DbExpression CreateInstanceMethod(DbExpression instance, List args, 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 Pair EnsureTypedNulls(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 (KeyValuePair scope 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 (KeyValuePair scope 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 (KeyValuePair scope 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 (KeyValuePair scope 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 Dictionary ValidateParameters(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 Dictionary Parameters
{
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;
KeyValuePair varInfo;
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(IList functionTypeList,
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, List args, MethodExpr methodExpr)
{
return CreateMethod(definingType, args, methodExpr, null, true);
}
///
/// creates instance method expression
///
///
///
///
///
///
///
///
/// InstanceMethodExpression or StaticMethodExpression
internal static DbExpression CreateInstanceMethod(DbExpression instance, List args, 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 Pair EnsureTypedNulls(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 (KeyValuePair scope 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 (KeyValuePair scope 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 (KeyValuePair scope 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 (KeyValuePair scope 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 Dictionary ValidateParameters(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
- CompleteWizardStep.cs
- ApplicationSettingsBase.cs
- ControlCodeDomSerializer.cs
- CodeMemberField.cs
- UndoManager.cs
- TransformerConfigurationWizardBase.cs
- RowToParametersTransformer.cs
- XPathPatternParser.cs
- dsa.cs
- RtfControls.cs
- ConstraintManager.cs
- AccessDataSourceView.cs
- PublisherIdentityPermission.cs
- AutomationTextAttribute.cs
- TextParagraphView.cs
- RedistVersionInfo.cs
- odbcmetadatacolumnnames.cs
- FileDialogPermission.cs
- MaskPropertyEditor.cs
- StringComparer.cs
- ZoneMembershipCondition.cs
- XmlWriterTraceListener.cs
- KeySpline.cs
- DrawingContextDrawingContextWalker.cs
- RawStylusInputCustomDataList.cs
- XsltLibrary.cs
- ReadingWritingEntityEventArgs.cs
- QilIterator.cs
- CryptoProvider.cs
- Stylesheet.cs
- _AutoWebProxyScriptHelper.cs
- WorkItem.cs
- InternalControlCollection.cs
- HyperLink.cs
- IriParsingElement.cs
- SplineKeyFrames.cs
- ClientEventManager.cs
- KeySplineConverter.cs
- HttpGetProtocolImporter.cs
- EntityWrapper.cs
- SrgsItemList.cs
- AppSettingsReader.cs
- QilInvokeLateBound.cs
- MD5.cs
- AsymmetricSignatureFormatter.cs
- CallSite.cs
- xmlglyphRunInfo.cs
- KeyedHashAlgorithm.cs
- PagerSettings.cs
- DataRow.cs
- FormsAuthenticationModule.cs
- HashJoinQueryOperatorEnumerator.cs
- ScrollBar.cs
- StatusBarDrawItemEvent.cs
- _ScatterGatherBuffers.cs
- HttpListener.cs
- OraclePermissionAttribute.cs
- ScrollBar.cs
- ComAdminInterfaces.cs
- Int16AnimationUsingKeyFrames.cs
- ExpressionBuilderCollection.cs
- LayoutEvent.cs
- ContainerUtilities.cs
- COM2PictureConverter.cs
- DbInsertCommandTree.cs
- QuaternionAnimationBase.cs
- recordstate.cs
- TreeNodeMouseHoverEvent.cs
- DataViewListener.cs
- DataGridLength.cs
- RtType.cs
- InteropEnvironment.cs
- BulletedList.cs
- DataPagerCommandEventArgs.cs
- IndentedWriter.cs
- DetailsViewRowCollection.cs
- BindingsCollection.cs
- _NestedMultipleAsyncResult.cs
- JoinCqlBlock.cs
- TokenBasedSet.cs
- RsaKeyIdentifierClause.cs
- TextParagraphProperties.cs
- PartitionResolver.cs
- ViewCellRelation.cs
- DrawItemEvent.cs
- ResolveCriteriaApril2005.cs
- PlacementWorkspace.cs
- COMException.cs
- ProfileSettingsCollection.cs
- EditorBrowsableAttribute.cs
- PathGradientBrush.cs
- ColumnHeaderConverter.cs
- CompilerTypeWithParams.cs
- SQLDouble.cs
- TypeDescriptionProvider.cs
- CommandConverter.cs
- RoutedPropertyChangedEventArgs.cs
- CharacterString.cs
- ServerIdentity.cs
- MetadataPropertyAttribute.cs