/mcs/class/referencesource/System.Data.Linq/SqlClient/Query/QueryConverter.cs
C# | 2887 lines | 2342 code | 301 blank | 244 comment | 771 complexity | 74b75f6e2c6ad9ddcfb9453582b373da MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- using System;
- using System.Globalization;
- using System.Collections;
- using System.Collections.Generic;
- using System.Data;
- using System.Reflection;
- using System.Text;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Data.Linq;
- using System.Data.Linq.Mapping;
- using System.Data.Linq.Provider;
- using System.Collections.ObjectModel;
- using System.Diagnostics.CodeAnalysis;
- namespace System.Data.Linq.SqlClient {
- /// <summary>
- /// These are application types used to represent types used during intermediate
- /// stages of the query building process.
- /// </summary>
- enum ConverterSpecialTypes {
- Row,
- Table
- }
- [Flags]
- internal enum ConverterStrategy {
- Default = 0x0,
- SkipWithRowNumber = 0x1,
- CanUseScopeIdentity = 0x2,
- CanUseOuterApply = 0x4,
- CanUseRowStatus = 0x8,
- CanUseJoinOn = 0x10, // Whether or not to use ON clause of JOIN.
- CanOutputFromInsert = 0x20
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
- internal class QueryConverter {
- IDataServices services;
- Translator translator;
- SqlFactory sql;
- TypeSystemProvider typeProvider;
- bool outerNode;
- Dictionary<ParameterExpression, SqlExpression> map;
- Dictionary<ParameterExpression, Expression> exprMap;
- Dictionary<ParameterExpression, SqlNode> dupMap;
- Dictionary<SqlNode, GroupInfo> gmap;
- Expression dominatingExpression;
- bool allowDeferred;
- ConverterStrategy converterStrategy = ConverterStrategy.Default;
- class GroupInfo {
- internal SqlSelect SelectWithGroup;
- internal SqlExpression ElementOnGroupSource;
- }
- internal ConverterStrategy ConverterStrategy {
- get { return converterStrategy; }
- set { converterStrategy = value; }
- }
- private bool UseConverterStrategy(ConverterStrategy strategy) {
- return (this.converterStrategy & strategy) == strategy;
- }
- internal QueryConverter(IDataServices services, TypeSystemProvider typeProvider, Translator translator, SqlFactory sql) {
- if (services == null) {
- throw Error.ArgumentNull("services");
- }
- if (sql == null) {
- throw Error.ArgumentNull("sql");
- }
- if (translator == null) {
- throw Error.ArgumentNull("translator");
- }
- if (typeProvider == null) {
- throw Error.ArgumentNull("typeProvider");
- }
- this.services = services;
- this.translator = translator;
- this.sql = sql;
- this.typeProvider = typeProvider;
- this.map = new Dictionary<ParameterExpression, SqlExpression>();
- this.exprMap = new Dictionary<ParameterExpression, Expression>();
- this.dupMap = new Dictionary<ParameterExpression, SqlNode>();
- this.gmap = new Dictionary<SqlNode, GroupInfo>();
- this.allowDeferred = true;
- }
- /// <summary>
- /// Convert inner expression from C# expression to basic SQL Query.
- /// </summary>
- /// <param name="node">The expression to convert.</param>
- /// <returns>The converted SQL query.</returns>
- internal SqlNode ConvertOuter(Expression node) {
- this.dominatingExpression = node;
- this.outerNode = true;
- SqlNode retNode;
- if (typeof(ITable).IsAssignableFrom(node.Type)) {
- retNode = this.VisitSequence(node);
- }
- else {
- retNode = this.VisitInner(node);
- }
- if (retNode.NodeType == SqlNodeType.MethodCall) {
- // if a tree consists of a single method call expression only, that method
- // must be either a mapped stored procedure or a mapped function
- throw Error.InvalidMethodExecution(((SqlMethodCall)retNode).Method.Name);
- }
- // if after conversion the node is an expression, we must
- // wrap it in a select
- SqlExpression sqlExpression = retNode as SqlExpression;
- if (sqlExpression != null) {
- retNode = new SqlSelect(sqlExpression, null, this.dominatingExpression);
- }
- retNode = new SqlIncludeScope(retNode, this.dominatingExpression);
- return retNode;
- }
- internal SqlNode Visit(Expression node) {
- bool tempOuterNode = this.outerNode;
- this.outerNode = false;
- SqlNode result = this.VisitInner(node);
- this.outerNode = tempOuterNode;
- return result;
- }
- /// <summary>
- /// Convert inner expression from C# expression to basic SQL Query.
- /// </summary>
- /// <param name="node">The expression to convert.</param>
- /// <param name="dominantExpression">Current dominating expression, used for producing meaningful exception text.</param>
- /// <returns>The converted SQL query.</returns>
- internal SqlNode ConvertInner(Expression node, Expression dominantExpression) {
- this.dominatingExpression = dominantExpression;
- bool tempOuterNode = this.outerNode;
- this.outerNode = false;
- SqlNode result = this.VisitInner(node);
- this.outerNode = tempOuterNode;
- return result;
- }
- [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
- [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
- private SqlNode VisitInner(Expression node) {
- if (node == null) return null;
- Expression save = this.dominatingExpression;
- this.dominatingExpression = ChooseBestDominatingExpression(this.dominatingExpression, node);
- try {
- switch (node.NodeType) {
- case ExpressionType.New:
- return this.VisitNew((NewExpression)node);
- case ExpressionType.MemberInit:
- return this.VisitMemberInit((MemberInitExpression)node);
- case ExpressionType.Negate:
- case ExpressionType.NegateChecked:
- case ExpressionType.Not:
- return this.VisitUnary((UnaryExpression)node);
- case ExpressionType.UnaryPlus:
- if (node.Type == typeof(TimeSpan))
- return this.VisitUnary((UnaryExpression)node);
- throw Error.UnrecognizedExpressionNode(node.NodeType);
- case ExpressionType.Add:
- case ExpressionType.AddChecked:
- case ExpressionType.Subtract:
- case ExpressionType.SubtractChecked:
- case ExpressionType.Multiply:
- case ExpressionType.MultiplyChecked:
- case ExpressionType.Divide:
- case ExpressionType.Modulo:
- case ExpressionType.And:
- case ExpressionType.AndAlso:
- case ExpressionType.Or:
- case ExpressionType.OrElse:
- case ExpressionType.Power:
- case ExpressionType.LessThan:
- case ExpressionType.LessThanOrEqual:
- case ExpressionType.GreaterThan:
- case ExpressionType.GreaterThanOrEqual:
- case ExpressionType.Equal:
- case ExpressionType.NotEqual:
- case ExpressionType.Coalesce:
- case ExpressionType.ExclusiveOr:
- return this.VisitBinary((BinaryExpression)node);
- case ExpressionType.ArrayIndex:
- return this.VisitArrayIndex((BinaryExpression)node);
- case ExpressionType.TypeIs:
- return this.VisitTypeBinary((TypeBinaryExpression)node);
- case ExpressionType.Convert:
- case ExpressionType.ConvertChecked:
- return this.VisitCast((UnaryExpression)node);
- case ExpressionType.TypeAs:
- return this.VisitAs((UnaryExpression)node);
- case ExpressionType.Conditional:
- return this.VisitConditional((ConditionalExpression)node);
- case ExpressionType.Constant:
- return this.VisitConstant((ConstantExpression)node);
- case ExpressionType.Parameter:
- return this.VisitParameter((ParameterExpression)node);
- case ExpressionType.MemberAccess:
- return this.VisitMemberAccess((MemberExpression)node);
- case ExpressionType.Call:
- return this.VisitMethodCall((MethodCallExpression)node);
- case ExpressionType.ArrayLength:
- return this.VisitArrayLength((UnaryExpression)node);
- case ExpressionType.NewArrayInit:
- return this.VisitNewArrayInit((NewArrayExpression)node);
- case ExpressionType.ListInit:
- return this.VisitListInit((ListInitExpression)node);
- case ExpressionType.Quote:
- return this.Visit(((UnaryExpression)node).Operand);
- case ExpressionType.Invoke:
- return this.VisitInvocation((InvocationExpression)node);
- case ExpressionType.Lambda:
- return this.VisitLambda((LambdaExpression)node);
- case ExpressionType.RightShift:
- case ExpressionType.LeftShift:
- throw Error.UnsupportedNodeType(node.NodeType);
- case (ExpressionType)InternalExpressionType.Known:
- return ((KnownExpression)node).Node;
- case (ExpressionType)InternalExpressionType.LinkedTable:
- return this.VisitLinkedTable((LinkedTableExpression)node);
- default:
- throw Error.UnrecognizedExpressionNode(node.NodeType);
- }
- }
- finally {
- this.dominatingExpression = save;
- }
- }
- /// <summary>
- /// Heuristic which chooses the best Expression root to use for displaying user messages
- /// and exception text.
- /// </summary>
- private static Expression ChooseBestDominatingExpression(Expression last, Expression next) {
- if (last == null) {
- return next;
- }
- else if (next == null) {
- return last;
- }
- else {
- if (next is MethodCallExpression) {
- return next;
- }
- if (last is MethodCallExpression) {
- return last;
- }
- }
- return next;
- }
- private SqlSelect LockSelect(SqlSelect sel) {
- if (sel.Selection.NodeType != SqlNodeType.AliasRef ||
- sel.Where != null ||
- sel.OrderBy.Count > 0 ||
- sel.GroupBy.Count > 0 ||
- sel.Having != null ||
- sel.Top != null ||
- sel.OrderingType != SqlOrderingType.Default ||
- sel.IsDistinct) {
- SqlAlias alias = new SqlAlias(sel);
- SqlAliasRef aref = new SqlAliasRef(alias);
- return new SqlSelect(aref, alias, this.dominatingExpression);
- }
- return sel;
- }
- private SqlSelect VisitSequence(Expression exp) {
- return this.CoerceToSequence(this.Visit(exp));
- }
- private SqlSelect CoerceToSequence(SqlNode node) {
- SqlSelect select = node as SqlSelect;
- if (select == null) {
- if (node.NodeType == SqlNodeType.Value) {
- SqlValue sv = (SqlValue)node;
- // Check for ITables.
- ITable t = sv.Value as ITable;
- if (t != null) {
- return this.CoerceToSequence(this.TranslateConstantTable(t, null));
- }
- // Check for IQueryable.
- IQueryable query = sv.Value as IQueryable;
- if (query != null) {
- Expression fex = Funcletizer.Funcletize(query.Expression);
- // IQueryables that return self-referencing Constant expressions cause infinite recursion
- if (fex.NodeType != ExpressionType.Constant ||
- ((ConstantExpression)fex).Value != query) {
- return this.VisitSequence(fex);
- }
- throw Error.IQueryableCannotReturnSelfReferencingConstantExpression();
- }
- throw Error.CapturedValuesCannotBeSequences();
- }
- else if (node.NodeType == SqlNodeType.Multiset || node.NodeType == SqlNodeType.Element) {
- return ((SqlSubSelect)node).Select;
- }
- else if (node.NodeType == SqlNodeType.ClientArray) {
- throw Error.ConstructedArraysNotSupported();
- }
- else if (node.NodeType == SqlNodeType.ClientParameter) {
- throw Error.ParametersCannotBeSequences();
- }
- // this needs to be a sequence expression!
- SqlExpression sqlExpr = (SqlExpression)node;
- SqlAlias sa = new SqlAlias(sqlExpr);
- SqlAliasRef aref = new SqlAliasRef(sa);
- return new SqlSelect(aref, sa, this.dominatingExpression);
- }
- return select;
- }
- //
- // Recursive call to VisitInvocation.
- [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- private SqlNode VisitInvocation(InvocationExpression invoke) {
- LambdaExpression lambda =
- (invoke.Expression.NodeType == ExpressionType.Quote)
- ? (LambdaExpression)((UnaryExpression)invoke.Expression).Operand
- : (invoke.Expression as LambdaExpression);
- if (lambda != null) {
- // just map arg values into lambda's parameters and evaluate lambda's body
- for (int i = 0, n = invoke.Arguments.Count; i < n; i++) {
- this.exprMap[lambda.Parameters[i]] = invoke.Arguments[i];
- }
- return this.VisitInner(lambda.Body);
- }
- else {
- // check for compiled query invocation
- SqlExpression expr = this.VisitExpression(invoke.Expression);
- if (expr.NodeType == SqlNodeType.Value) {
- SqlValue value = (SqlValue)expr;
- Delegate d = value.Value as Delegate;
- if (d != null) {
- CompiledQuery cq = d.Target as CompiledQuery;
- if (cq != null) {
- return this.VisitInvocation(Expression.Invoke(cq.Expression, invoke.Arguments));
- } else if (invoke.Arguments.Count == 0) {
- object invokeResult;
- try {
- invokeResult = d.DynamicInvoke(null);
- } catch (System.Reflection.TargetInvocationException e) {
- throw e.InnerException;
- }
- return this.sql.ValueFromObject(invokeResult, invoke.Type, true, this.dominatingExpression);
- }
- }
- }
- SqlExpression [] args = new SqlExpression[invoke.Arguments.Count];
- for(int i = 0; i<args.Length; ++i) {
- args[i] = (SqlExpression)this.Visit(invoke.Arguments[i]);
- }
- var sca = new SqlClientArray(typeof(object[]), this.typeProvider.From(typeof(object[])), args, this.dominatingExpression);
- return sql.MethodCall(invoke.Type, typeof(Delegate).GetMethod("DynamicInvoke"), expr, new SqlExpression[] {sca}, this.dominatingExpression);
- }
- }
- // inline lambda expressions w/o invocation are parameterized queries
- private SqlNode VisitLambda(LambdaExpression lambda) {
- // turn lambda parameters into client parameters
- for (int i = 0, n = lambda.Parameters.Count; i < n; i++) {
- ParameterExpression p = lambda.Parameters[i];
- if (p.Type == typeof(Type)) {
- throw Error.BadParameterType(p.Type);
- }
- // construct accessor for parameter
- ParameterExpression pa = Expression.Parameter(typeof(object[]), "args");
- LambdaExpression accessor =
- Expression.Lambda(
- typeof(Func<,>).MakeGenericType(typeof(object[]), p.Type),
- Expression.Convert(
- #pragma warning disable 618 // Disable the 'obsolete' warning
- Expression.ArrayIndex(pa, Expression.Constant(i)),
- p.Type
- ),
- #pragma warning restore 618
- pa
- );
- SqlClientParameter cp = new SqlClientParameter(p.Type, this.typeProvider.From(p.Type), accessor, this.dominatingExpression);
- // map references to lambda's parameter to client parameter node
- this.dupMap[p] = cp;
- }
- // call this so we don't erase 'outerNode' setting
- return this.VisitInner(lambda.Body);
- }
- private SqlExpression VisitExpression(Expression exp) {
- SqlNode result = this.Visit(exp);
- if (result == null) return null;
- SqlExpression x = result as SqlExpression;
- if (x != null) return x;
- SqlSelect select = result as SqlSelect;
- if (select != null) {
- SqlSubSelect ms = sql.SubSelect(SqlNodeType.Multiset, select, exp.Type);
- return ms;
- }
- throw Error.UnrecognizedExpressionNode(result);
- }
- private SqlSelect VisitSelect(Expression sequence, LambdaExpression selector) {
- SqlSelect source = this.VisitSequence(sequence);
- SqlAlias alias = new SqlAlias(source);
- SqlAliasRef aref = new SqlAliasRef(alias);
- this.map[selector.Parameters[0]] = aref;
- SqlNode project = this.Visit(selector.Body);
- SqlSelect pselect = project as SqlSelect;
- if (pselect != null) {
- return new SqlSelect(sql.SubSelect(SqlNodeType.Multiset, pselect, selector.Body.Type), alias, this.dominatingExpression);
- }
- else if ((project.NodeType == SqlNodeType.Element || project.NodeType == SqlNodeType.ScalarSubSelect) &&
- (this.converterStrategy & ConverterStrategy.CanUseOuterApply) != 0) {
- SqlSubSelect sub = (SqlSubSelect)project;
- SqlSelect inner = sub.Select;
- SqlAlias innerAlias = new SqlAlias(inner);
- SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
- if (project.NodeType == SqlNodeType.Element) {
- inner.Selection = new SqlOptionalValue(
- new SqlColumn(
- "test",
- sql.Unary(
- SqlNodeType.OuterJoinedValue,
- sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression)
- )
- ),
- sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection)
- );
- }
- else {
- inner.Selection = sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection);
- }
- SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, alias, innerAlias, null, this.dominatingExpression);
- return new SqlSelect(innerRef, join, this.dominatingExpression);
- }
- else {
- SqlExpression expr = project as SqlExpression;
- if (expr != null) {
- return new SqlSelect(expr, alias, this.dominatingExpression);
- }
- else {
- throw Error.BadProjectionInSelect();
- }
- }
- }
- private SqlSelect VisitSelectMany(Expression sequence, LambdaExpression colSelector, LambdaExpression resultSelector) {
- SqlSelect seqSelect = this.VisitSequence(sequence);
- SqlAlias seqAlias = new SqlAlias(seqSelect);
- SqlAliasRef seqRef = new SqlAliasRef(seqAlias);
- this.map[colSelector.Parameters[0]] = seqRef;
- SqlNode colSelectorNode = this.VisitSequence(colSelector.Body);
- SqlAlias selAlias = new SqlAlias(colSelectorNode);
- SqlAliasRef selRef = new SqlAliasRef(selAlias);
- SqlJoin join = new SqlJoin(SqlJoinType.CrossApply, seqAlias, selAlias, null, this.dominatingExpression);
- SqlExpression projection = selRef;
- if (resultSelector != null) {
- this.map[resultSelector.Parameters[0]] = seqRef;
- this.map[resultSelector.Parameters[1]] = selRef;
- projection = this.VisitExpression(resultSelector.Body);
- }
- return new SqlSelect(projection, join, this.dominatingExpression);
- }
- private SqlSelect VisitJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) {
- SqlSelect outerSelect = this.VisitSequence(outerSequence);
- SqlSelect innerSelect = this.VisitSequence(innerSequence);
- SqlAlias outerAlias = new SqlAlias(outerSelect);
- SqlAliasRef outerRef = new SqlAliasRef(outerAlias);
- SqlAlias innerAlias = new SqlAlias(innerSelect);
- SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
- this.map[outerKeySelector.Parameters[0]] = outerRef;
- SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body);
- this.map[innerKeySelector.Parameters[0]] = innerRef;
- SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body);
- this.map[resultSelector.Parameters[0]] = outerRef;
- this.map[resultSelector.Parameters[1]] = innerRef;
- SqlExpression result = this.VisitExpression(resultSelector.Body);
- SqlExpression condition = sql.Binary(SqlNodeType.EQ, outerKey, innerKey);
- SqlSelect select = null;
- if ((this.converterStrategy & ConverterStrategy.CanUseJoinOn) != 0) {
- SqlJoin join = new SqlJoin(SqlJoinType.Inner, outerAlias, innerAlias, condition, this.dominatingExpression);
- select = new SqlSelect(result, join, this.dominatingExpression);
- } else {
- SqlJoin join = new SqlJoin(SqlJoinType.Cross, outerAlias, innerAlias, null, this.dominatingExpression);
- select = new SqlSelect(result, join, this.dominatingExpression);
- select.Where = condition;
- }
- return select;
- }
- private SqlSelect VisitGroupJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) {
- SqlSelect outerSelect = this.VisitSequence(outerSequence);
- SqlSelect innerSelect = this.VisitSequence(innerSequence);
- SqlAlias outerAlias = new SqlAlias(outerSelect);
- SqlAliasRef outerRef = new SqlAliasRef(outerAlias);
- SqlAlias innerAlias = new SqlAlias(innerSelect);
- SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
- this.map[outerKeySelector.Parameters[0]] = outerRef;
- SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body);
- this.map[innerKeySelector.Parameters[0]] = innerRef;
- SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body);
- // make multiset
- SqlExpression pred = sql.Binary(SqlNodeType.EQ, outerKey, innerKey);
- SqlSelect select = new SqlSelect(innerRef, innerAlias, this.dominatingExpression);
- select.Where = pred;
- SqlSubSelect subquery = sql.SubSelect(SqlNodeType.Multiset, select);
- // make outer ref & multiset for result-selector params
- this.map[resultSelector.Parameters[0]] = outerRef;
- this.dupMap[resultSelector.Parameters[1]] = subquery;
- SqlExpression result = this.VisitExpression(resultSelector.Body);
- return new SqlSelect(result, outerAlias, this.dominatingExpression);
- }
- private SqlSelect VisitDefaultIfEmpty(Expression sequence) {
- SqlSelect select = this.VisitSequence(sequence);
- SqlAlias alias = new SqlAlias(select);
- SqlAliasRef aliasRef = new SqlAliasRef(alias);
- SqlExpression opt = new SqlOptionalValue(
- new SqlColumn(
- "test",
- sql.Unary(SqlNodeType.OuterJoinedValue,
- sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression)
- )
- ),
- sql.Unary(SqlNodeType.OuterJoinedValue, aliasRef)
- );
- SqlSelect optSelect = new SqlSelect(opt, alias, this.dominatingExpression);
- alias = new SqlAlias(optSelect);
- aliasRef = new SqlAliasRef(alias);
- SqlExpression litNull = sql.TypedLiteralNull(typeof(string), this.dominatingExpression);
- SqlSelect selNull = new SqlSelect(litNull, null, this.dominatingExpression);
- SqlAlias aliasNull = new SqlAlias(selNull);
- SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, aliasNull, alias, null, this.dominatingExpression);
- return new SqlSelect(aliasRef, join, this.dominatingExpression);
- }
- /// <summary>
- /// Rewrite seq.OfType<T> as seq.Select(s=>s as T).Where(p=>p!=null).
- /// </summary>
- private SqlSelect VisitOfType(Expression sequence, Type ofType) {
- SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
- SqlAliasRef aref = (SqlAliasRef)select.Selection;
- select.Selection = new SqlUnary(SqlNodeType.Treat, ofType, typeProvider.From(ofType), aref, this.dominatingExpression);
- select = this.LockSelect(select);
- aref = (SqlAliasRef)select.Selection;
- // Append the 'is' operator into the WHERE clause.
- select.Where = sql.AndAccumulate(select.Where,
- sql.Unary(SqlNodeType.IsNotNull, aref, this.dominatingExpression)
- );
- return select;
- }
- /// <summary>
- /// Rewrite seq.Cast<T> as seq.Select(s=>(T)s).
- /// </summary>
- private SqlNode VisitSequenceCast(Expression sequence, Type type) {
- Type sourceType = TypeSystem.GetElementType(sequence.Type);
- ParameterExpression p = Expression.Parameter(sourceType, "pc");
- return this.Visit(Expression.Call(
- typeof(Enumerable), "Select",
- new Type[] {
- sourceType, // TSource element type.
- type, // TResult element type.
- },
- sequence,
- Expression.Lambda(
- Expression.Convert(p, type),
- new ParameterExpression[] { p }
- ))
- );
- }
- /// <summary>
- /// This is the 'is' operator.
- /// </summary>
- private SqlNode VisitTypeBinary(TypeBinaryExpression b) {
- SqlExpression expr = this.VisitExpression(b.Expression);
- SqlExpression result = null;
- switch (b.NodeType) {
- case ExpressionType.TypeIs:
- Type ofType = b.TypeOperand;
- result = sql.Unary(SqlNodeType.IsNotNull, new SqlUnary(SqlNodeType.Treat, ofType, typeProvider.From(ofType), expr, this.dominatingExpression), this.dominatingExpression);
- break;
- default:
- throw Error.TypeBinaryOperatorNotRecognized();
- }
- return result;
- }
- private SqlSelect VisitWhere(Expression sequence, LambdaExpression predicate) {
- SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
- this.map[predicate.Parameters[0]] = (SqlAliasRef)select.Selection;
- select.Where = this.VisitExpression(predicate.Body);
- return select;
- }
- private SqlNode VisitAs(UnaryExpression a) {
- SqlNode node = this.Visit(a.Operand);
- SqlExpression expr = node as SqlExpression;
- if (expr != null) {
- return new SqlUnary(SqlNodeType.Treat, a.Type, typeProvider.From(a.Type), expr, a);
- }
- SqlSelect select = node as SqlSelect;
- if (select != null) {
- SqlSubSelect ms = sql.SubSelect(SqlNodeType.Multiset, select);
- return new SqlUnary(SqlNodeType.Treat, a.Type, typeProvider.From(a.Type), ms, a);
- }
- throw Error.DidNotExpectAs(a);
- }
- private SqlNode VisitArrayLength(UnaryExpression c) {
- SqlExpression exp = this.VisitExpression(c.Operand);
- if (exp.SqlType.IsString || exp.SqlType.IsChar) {
- return sql.CLRLENGTH(exp);
- }
- else {
- return sql.DATALENGTH(exp);
- }
- }
- private SqlNode VisitArrayIndex(BinaryExpression b) {
- SqlExpression array = this.VisitExpression(b.Left);
- SqlExpression index = this.VisitExpression(b.Right);
- if (array.NodeType == SqlNodeType.ClientParameter
- && index.NodeType == SqlNodeType.Value) {
- SqlClientParameter cpArray = (SqlClientParameter)array;
- SqlValue vIndex = (SqlValue)index;
- return new SqlClientParameter(
- b.Type, sql.TypeProvider.From(b.Type),
- Expression.Lambda(
- #pragma warning disable 618 // Disable the 'obsolete' warning
- Expression.ArrayIndex(cpArray.Accessor.Body, Expression.Constant(vIndex.Value, vIndex.ClrType)),
- #pragma warning restore 618
- cpArray.Accessor.Parameters.ToArray()
- ),
- this.dominatingExpression
- );
- }
- throw Error.UnrecognizedExpressionNode(b.NodeType);
- }
- private SqlNode VisitCast(UnaryExpression c) {
- if (c.Method != null) {
- SqlExpression exp = this.VisitExpression(c.Operand);
- return sql.MethodCall(c.Type, c.Method, null, new SqlExpression[] { exp }, dominatingExpression);
- }
- return this.VisitChangeType(c.Operand, c.Type);
- }
- private SqlNode VisitChangeType(Expression expression, Type type) {
- SqlExpression expr = this.VisitExpression(expression);
- return this.ChangeType(expr, type);
- }
- private SqlNode ConvertDateToDateTime2(SqlExpression expr) {
- SqlExpression datetime2 = new SqlVariable(expr.ClrType, expr.SqlType, "DATETIME2", expr.SourceExpression);
- return sql.FunctionCall(typeof(DateTime), "CONVERT", new SqlExpression[2] { datetime2, expr }, expr.SourceExpression);
- }
- private SqlNode ChangeType(SqlExpression expr, Type type) {
- if (type == typeof(object)) {
- return expr; // Boxing conversion?
- }
- else if (expr.NodeType == SqlNodeType.Value && ((SqlValue)expr).Value == null) {
- return sql.TypedLiteralNull(type, expr.SourceExpression);
- }
- else if (expr.NodeType == SqlNodeType.ClientParameter) {
- SqlClientParameter cp = (SqlClientParameter)expr;
- return new SqlClientParameter(
- type, sql.TypeProvider.From(type),
- Expression.Lambda(Expression.Convert(cp.Accessor.Body, type), cp.Accessor.Parameters.ToArray()),
- cp.SourceExpression
- );
- }
- ConversionMethod cm = ChooseConversionMethod(expr.ClrType, type);
- switch (cm) {
- case ConversionMethod.Convert:
- return sql.UnaryConvert(type, typeProvider.From(type), expr, expr.SourceExpression);
- case ConversionMethod.Lift:
- if (SqlFactory.IsSqlDateType(expr)) {
- expr = (SqlExpression) ConvertDateToDateTime2(expr);
- }
- return new SqlLift(type, expr, this.dominatingExpression);
- case ConversionMethod.Ignore:
- if (SqlFactory.IsSqlDateType(expr)) {
- return ConvertDateToDateTime2(expr);
- }
- return expr;
- case ConversionMethod.Treat:
- return new SqlUnary(SqlNodeType.Treat, type, typeProvider.From(type), expr, expr.SourceExpression);
- default:
- throw Error.UnhandledExpressionType(cm);
- }
- }
- enum ConversionMethod {
- Treat,
- Ignore,
- Convert,
- Lift
- }
- private ConversionMethod ChooseConversionMethod(Type fromType, Type toType) {
- Type nnFromType = TypeSystem.GetNonNullableType(fromType);
- Type nnToType = TypeSystem.GetNonNullableType(toType);
- if (fromType != toType && nnFromType == nnToType) {
- return ConversionMethod.Lift;
- }
- else if (TypeSystem.IsSequenceType(nnFromType) || TypeSystem.IsSequenceType(nnToType)) {
- return ConversionMethod.Ignore;
- }
- ProviderType sfromType = typeProvider.From(nnFromType);
- ProviderType stoType = typeProvider.From(nnToType);
- bool isRuntimeOnly1 = sfromType.IsRuntimeOnlyType;
- bool isRuntimeOnly2 = stoType.IsRuntimeOnlyType;
- if (isRuntimeOnly1 || isRuntimeOnly2) {
- return ConversionMethod.Treat;
- }
- if (nnFromType == nnToType // same non-nullable .NET types
- || (sfromType.IsString && sfromType.Equals(stoType)) // same SQL string types
- || (nnFromType.IsEnum || nnToType.IsEnum) // any .NET enum type
- ) {
- return ConversionMethod.Ignore;
- }
- else {
- return ConversionMethod.Convert;
- }
- }
- /// <summary>
- /// Convert ITable into SqlNodes. If the hierarchy involves inheritance then
- /// a type case is built. Abstractly, a type case is a CASE where each WHEN is a possible
- /// a typebinding that may be instantianted.
- /// </summary>
- private SqlNode TranslateConstantTable(ITable table, SqlLink link) {
- if (table.Context != this.services.Context) {
- throw Error.WrongDataContext();
- }
- MetaTable metaTable = this.services.Model.GetTable(table.ElementType);
- return this.translator.BuildDefaultQuery(metaTable.RowType, this.allowDeferred, link, this.dominatingExpression);
- }
- private SqlNode VisitLinkedTable(LinkedTableExpression linkedTable) {
- return TranslateConstantTable(linkedTable.Table, linkedTable.Link);
- }
- private SqlNode VisitConstant(ConstantExpression cons) {
- // A value constant or null.
- Type type = cons.Type;
- if (cons.Value == null) {
- return sql.TypedLiteralNull(type, this.dominatingExpression);
- }
- if (type == typeof(object)) {
- type = cons.Value.GetType();
- }
- return sql.ValueFromObject(cons.Value, type, true, this.dominatingExpression);
- }
- private SqlExpression VisitConditional(ConditionalExpression cond) {
- List<SqlWhen> whens = new List<SqlWhen>(1);
- whens.Add(new SqlWhen(this.VisitExpression(cond.Test), this.VisitExpression(cond.IfTrue)));
- SqlExpression @else = this.VisitExpression(cond.IfFalse);
- // combine search cases found in the else clause into a single seach case
- while (@else.NodeType == SqlNodeType.SearchedCase) {
- SqlSearchedCase sc = (SqlSearchedCase)@else;
- whens.AddRange(sc.Whens);
- @else = sc.Else;
- }
- return sql.SearchedCase(whens.ToArray(), @else, this.dominatingExpression);
- }
- private SqlExpression VisitNew(NewExpression qn) {
- if (TypeSystem.IsNullableType(qn.Type) && qn.Arguments.Count == 1 &&
- TypeSystem.GetNonNullableType(qn.Type) == qn.Arguments[0].Type) {
- return this.VisitCast(Expression.Convert(qn.Arguments[0], qn.Type)) as SqlExpression;
- }
- else if (qn.Type == typeof(decimal) && qn.Arguments.Count == 1) {
- return this.VisitCast(Expression.Convert(qn.Arguments[0], typeof(decimal))) as SqlExpression;
- }
- MetaType mt = this.services.Model.GetMetaType(qn.Type);
- if (mt.IsEntity) {
- throw Error.CannotMaterializeEntityType(qn.Type);
- }
- SqlExpression[] args = null;
- if (qn.Arguments.Count > 0) {
- args = new SqlExpression[qn.Arguments.Count];
- for (int i = 0, n = qn.Arguments.Count; i < n; i++) {
- args[i] = this.VisitExpression(qn.Arguments[i]);
- }
- }
- SqlNew tb = sql.New(mt, qn.Constructor, args, PropertyOrFieldOf(qn.Members), null, this.dominatingExpression);
- return tb;
- }
- private SqlExpression VisitMemberInit(MemberInitExpression init) {
- MetaType mt = this.services.Model.GetMetaType(init.Type);
- if (mt.IsEntity) {
- throw Error.CannotMaterializeEntityType(init.Type);
- }
- SqlExpression[] args = null;
- NewExpression qn = init.NewExpression;
- if (qn.Type == typeof(decimal) && qn.Arguments.Count == 1) {
- return this.VisitCast(Expression.Convert(qn.Arguments[0], typeof(decimal))) as SqlExpression;
- }
- if (qn.Arguments.Count > 0) {
- args = new SqlExpression[qn.Arguments.Count];
- for (int i = 0, n = args.Length; i < n; i++) {
- args[i] = this.VisitExpression(qn.Arguments[i]);
- }
- }
- int cBindings = init.Bindings.Count;
- SqlMemberAssign[] members = new SqlMemberAssign[cBindings];
- int[] ordinal = new int[members.Length];
- for (int i = 0; i < cBindings; i++) {
- MemberAssignment mb = init.Bindings[i] as MemberAssignment;
- if (mb != null) {
- SqlExpression expr = this.VisitExpression(mb.Expression);
- SqlMemberAssign sma = new SqlMemberAssign(mb.Member, expr);
- members[i] = sma;
- ordinal[i] = mt.GetDataMember(mb.Member).Ordinal;
- }
- else {
- throw Error.UnhandledBindingType(init.Bindings[i].BindingType);
- }
- }
- // put members in type's declaration order
- Array.Sort(ordinal, members, 0, members.Length);
- SqlNew tb = sql.New(mt, qn.Constructor, args, PropertyOrFieldOf(qn.Members), members, this.dominatingExpression);
- return tb;
- }
- private static IEnumerable<MemberInfo> PropertyOrFieldOf(IEnumerable<MemberInfo> members) {
- if (members == null) {
- return null;
- }
- List<MemberInfo> result = new List<MemberInfo>();
- foreach (MemberInfo mi in members) {
- switch (mi.MemberType) {
- case MemberTypes.Method: {
- foreach (PropertyInfo pi in mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
- MethodInfo method = mi as MethodInfo;
- if (pi.CanRead && pi.GetGetMethod() == method) {
- result.Add(pi);
- break;
- }
- }
- break;
- }
- case MemberTypes.Field:
- case MemberTypes.Property: {
- result.Add(mi);
- break;
- }
- default: {
- throw Error.CouldNotConvertToPropertyOrField(mi);
- }
- }
- }
- return result;
- }
- private SqlSelect VisitDistinct(Expression sequence) {
- SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
- select.IsDistinct = true;
- select.OrderingType = SqlOrderingType.Blocked;
- return select;
- }
- private SqlSelect VisitTake(Expression sequence, Expression count) {
- // verify that count >= 0
- SqlExpression takeExp = this.VisitExpression(count);
- if (takeExp.NodeType == SqlNodeType.Value) {
- SqlValue constTakeCount = (SqlValue)takeExp;
- if (typeof(int).IsAssignableFrom(constTakeCount.Value.GetType()) && ((int)constTakeCount.Value) < 0) {
- throw Error.ArgumentOutOfRange("takeCount");
- }
- }
- MethodCallExpression mce = sequence as MethodCallExpression;
- if (mce != null && IsSequenceOperatorCall(mce) && mce.Method.Name == "Skip" && mce.Arguments.Count == 2) {
- SqlExpression skipExp = this.VisitExpression(mce.Arguments[1]);
- // verify that count >= 0
- if (skipExp.NodeType == SqlNodeType.Value) {
- SqlValue constSkipCount = (SqlValue)skipExp;
- if (typeof(int).IsAssignableFrom(constSkipCount.Value.GetType()) && ((int)constSkipCount.Value) < 0) {
- throw Error.ArgumentOutOfRange("skipCount");
- }
- }
- SqlSelect select = this.VisitSequence(mce.Arguments[0]);
- return this.GenerateSkipTake(select, skipExp, takeExp);
- }
- else {
- SqlSelect select = this.VisitSequence(sequence);
- return this.GenerateSkipTake(select, null, takeExp);
- }
- }
- /// <summary>
- /// In order for elements of a sequence to be skipped, they must have identity
- /// that can be compared. This excludes elements that are sequences and elements
- /// that contain sequences.
- /// </summary>
- private bool CanSkipOnSelection(SqlExpression selection) {
- // we can skip over groupings (since we can compare them by key)
- if (IsGrouping(selection.ClrType)) {
- return true;
- }
- // we can skip over entities (since we can compare them by primary key)
- MetaTable table = this.services.Model.GetTable(selection.ClrType);
- if (table != null) {
- return true;
- }
- // sequences that are not primitives are not skippable
- if (TypeSystem.IsSequenceType(selection.ClrType) && !selection.SqlType.CanBeColumn) {
- return false;
- }
- switch (selection.NodeType) {
- case SqlNodeType.AliasRef: {
- SqlNode node = ((SqlAliasRef)selection).Alias.Node;
- SqlSelect select = node as SqlSelect;
- if (select != null) {
- return CanSkipOnSelection(select.Selection);
- }
- SqlUnion union = node as SqlUnion;
- if (union != null) {
- bool left = default(bool);
- bool right = default(bool);
- SqlSelect selectLeft = union.Left as SqlSelect;
- if (selectLeft != null) {
- left = CanSkipOnSelection(selectLeft.Selection);
- }
- SqlSelect selectRight = union.Right as SqlSelect;
- if (selectRight != null) {
- right = CanSkipOnSelection(selectRight.Selection);
- }
- return left && right;
- }
- SqlExpression expr = (SqlExpression)node;
- return CanSkipOnSelection(expr);
- }
- case SqlNodeType.New:
- SqlNew sn = (SqlNew)selection;
- // check each member of the projection for sequences
- foreach (SqlMemberAssign ma in sn.Members) {
- if (!CanSkipOnSelection(ma.Expression))
- return false;
- }
- if (sn.ArgMembers != null) {
- for (int i = 0, n = sn.ArgMembers.Count; i < n; ++i) {
- if (!CanSkipOnSelection(sn.Args[i])) {
- return false;
- }
- }
- }
- break;
- }
- return true;
- }
- /// <summary>
- /// SQL2000:
- /// SELECT *
- /// FROM sequence
- /// WHERE NOT EXISTS (
- /// SELECT TOP count *
- /// FROM sequence)
- ///
- /// SQL2005: SELECT *
- /// FROM (SELECT sequence.*,
- /// ROW_NUMBER() OVER (ORDER BY order) AS ROW_NUMBER
- /// FROM sequence)
- /// WHERE ROW_NUMBER > count
- /// </summary>
- /// <param name="sequence">Sequence containing elements to skip</param>
- /// <param name="count">Number of elements to skip</param>
- /// <returns>SELECT node</returns>
- private SqlSelect VisitSkip(Expression sequence, Expression skipCount) {
- SqlExpression skipExp = this.VisitExpression(skipCount);
- // verify that count >= 0
- if (skipExp.NodeType == SqlNodeType.Value) {
- SqlValue constSkipCount = (SqlValue)skipExp;
- if (typeof(int).IsAssignableFrom(constSkipCount.Value.GetType()) && ((int)constSkipCount.Value) < 0) {
- throw Error.ArgumentOutOfRange("skipCount");
- }
- }
- SqlSelect select = this.VisitSequence(sequence);
- return this.GenerateSkipTake(select, skipExp, null);
- }
- private SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) {
- SqlSelect select = this.LockSelect(sequence);
- // no skip?
- if (skipExp == null) {
- if (takeExp != null) {
- select.Top = takeExp;
- }
- return select;
- }
- SqlAlias alias = new SqlAlias(select);
- SqlAliasRef aref = new SqlAliasRef(alias);
- if (this.UseConverterStrategy(ConverterStrategy.SkipWithRowNumber)) {
- // use ROW_NUMBER() (preferred)
- SqlColumn rowNumber = new SqlColumn("ROW_NUMBER", sql.RowNumber(new List<SqlOrderExpression>(), this.dominatingExpression));
- SqlColumnRef rowNumberRef = new SqlColumnRef(rowNumber);
- select.Row.Columns.Add(rowNumber);
- SqlSelect final = new SqlSelect(aref, alias, this.dominatingExpression);
- if (takeExp != null) {
- // use BETWEEN for skip+take combo (much faster)
- final.Where = sql.Between(
- rowNumberRef,
- sql.Add(skipExp, 1),
- sql.Binary(Sql…
Large files files are truncated, but you can click here to view the full file