2 using System.Globalization;
3 using System.Collections;
4 using System.Collections.Generic;
6 using System.Reflection;
9 using System.Linq.Expressions;
10 using System.Data.Linq;
11 using System.Data.Linq.Mapping;
12 using System.Data.Linq.Provider;
13 using System.Collections.ObjectModel;
14 using System.Diagnostics.CodeAnalysis;
16 namespace System.Data.Linq.SqlClient {
19 /// These are application types used to represent types used during intermediate
20 /// stages of the query building process.
22 enum ConverterSpecialTypes {
28 internal enum ConverterStrategy {
30 SkipWithRowNumber = 0x1,
31 CanUseScopeIdentity = 0x2,
32 CanUseOuterApply = 0x4,
33 CanUseRowStatus = 0x8,
34 CanUseJoinOn = 0x10, // Whether or not to use ON clause of JOIN.
35 CanOutputFromInsert = 0x20
38 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
39 internal class QueryConverter {
40 IDataServices services;
41 Translator translator;
43 TypeSystemProvider typeProvider;
45 Dictionary<ParameterExpression, SqlExpression> map;
46 Dictionary<ParameterExpression, Expression> exprMap;
47 Dictionary<ParameterExpression, SqlNode> dupMap;
48 Dictionary<SqlNode, GroupInfo> gmap;
49 Expression dominatingExpression;
51 ConverterStrategy converterStrategy = ConverterStrategy.Default;
54 internal SqlSelect SelectWithGroup;
55 internal SqlExpression ElementOnGroupSource;
58 internal ConverterStrategy ConverterStrategy {
59 get { return converterStrategy; }
60 set { converterStrategy = value; }
63 private bool UseConverterStrategy(ConverterStrategy strategy) {
64 return (this.converterStrategy & strategy) == strategy;
67 internal QueryConverter(IDataServices services, TypeSystemProvider typeProvider, Translator translator, SqlFactory sql) {
68 if (services == null) {
69 throw Error.ArgumentNull("services");
72 throw Error.ArgumentNull("sql");
74 if (translator == null) {
75 throw Error.ArgumentNull("translator");
77 if (typeProvider == null) {
78 throw Error.ArgumentNull("typeProvider");
80 this.services = services;
81 this.translator = translator;
83 this.typeProvider = typeProvider;
84 this.map = new Dictionary<ParameterExpression, SqlExpression>();
85 this.exprMap = new Dictionary<ParameterExpression, Expression>();
86 this.dupMap = new Dictionary<ParameterExpression, SqlNode>();
87 this.gmap = new Dictionary<SqlNode, GroupInfo>();
88 this.allowDeferred = true;
92 /// Convert inner expression from C# expression to basic SQL Query.
94 /// <param name="node">The expression to convert.</param>
95 /// <returns>The converted SQL query.</returns>
96 internal SqlNode ConvertOuter(Expression node) {
97 this.dominatingExpression = node;
98 this.outerNode = true;
100 if (typeof(ITable).IsAssignableFrom(node.Type)) {
101 retNode = this.VisitSequence(node);
104 retNode = this.VisitInner(node);
107 if (retNode.NodeType == SqlNodeType.MethodCall) {
108 // if a tree consists of a single method call expression only, that method
109 // must be either a mapped stored procedure or a mapped function
110 throw Error.InvalidMethodExecution(((SqlMethodCall)retNode).Method.Name);
113 // if after conversion the node is an expression, we must
114 // wrap it in a select
115 SqlExpression sqlExpression = retNode as SqlExpression;
116 if (sqlExpression != null) {
117 retNode = new SqlSelect(sqlExpression, null, this.dominatingExpression);
119 retNode = new SqlIncludeScope(retNode, this.dominatingExpression);
123 internal SqlNode Visit(Expression node) {
124 bool tempOuterNode = this.outerNode;
125 this.outerNode = false;
126 SqlNode result = this.VisitInner(node);
127 this.outerNode = tempOuterNode;
132 /// Convert inner expression from C# expression to basic SQL Query.
134 /// <param name="node">The expression to convert.</param>
135 /// <param name="dominantExpression">Current dominating expression, used for producing meaningful exception text.</param>
136 /// <returns>The converted SQL query.</returns>
137 internal SqlNode ConvertInner(Expression node, Expression dominantExpression) {
138 this.dominatingExpression = dominantExpression;
139 bool tempOuterNode = this.outerNode;
140 this.outerNode = false;
141 SqlNode result = this.VisitInner(node);
142 this.outerNode = tempOuterNode;
146 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
147 [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.")]
148 private SqlNode VisitInner(Expression node) {
149 if (node == null) return null;
150 Expression save = this.dominatingExpression;
151 this.dominatingExpression = ChooseBestDominatingExpression(this.dominatingExpression, node);
154 switch (node.NodeType) {
155 case ExpressionType.New:
156 return this.VisitNew((NewExpression)node);
157 case ExpressionType.MemberInit:
158 return this.VisitMemberInit((MemberInitExpression)node);
159 case ExpressionType.Negate:
160 case ExpressionType.NegateChecked:
161 case ExpressionType.Not:
162 return this.VisitUnary((UnaryExpression)node);
163 case ExpressionType.UnaryPlus:
164 if (node.Type == typeof(TimeSpan))
165 return this.VisitUnary((UnaryExpression)node);
166 throw Error.UnrecognizedExpressionNode(node.NodeType);
167 case ExpressionType.Add:
168 case ExpressionType.AddChecked:
169 case ExpressionType.Subtract:
170 case ExpressionType.SubtractChecked:
171 case ExpressionType.Multiply:
172 case ExpressionType.MultiplyChecked:
173 case ExpressionType.Divide:
174 case ExpressionType.Modulo:
175 case ExpressionType.And:
176 case ExpressionType.AndAlso:
177 case ExpressionType.Or:
178 case ExpressionType.OrElse:
179 case ExpressionType.Power:
180 case ExpressionType.LessThan:
181 case ExpressionType.LessThanOrEqual:
182 case ExpressionType.GreaterThan:
183 case ExpressionType.GreaterThanOrEqual:
184 case ExpressionType.Equal:
185 case ExpressionType.NotEqual:
186 case ExpressionType.Coalesce:
187 case ExpressionType.ExclusiveOr:
188 return this.VisitBinary((BinaryExpression)node);
189 case ExpressionType.ArrayIndex:
190 return this.VisitArrayIndex((BinaryExpression)node);
191 case ExpressionType.TypeIs:
192 return this.VisitTypeBinary((TypeBinaryExpression)node);
193 case ExpressionType.Convert:
194 case ExpressionType.ConvertChecked:
195 return this.VisitCast((UnaryExpression)node);
196 case ExpressionType.TypeAs:
197 return this.VisitAs((UnaryExpression)node);
198 case ExpressionType.Conditional:
199 return this.VisitConditional((ConditionalExpression)node);
200 case ExpressionType.Constant:
201 return this.VisitConstant((ConstantExpression)node);
202 case ExpressionType.Parameter:
203 return this.VisitParameter((ParameterExpression)node);
204 case ExpressionType.MemberAccess:
205 return this.VisitMemberAccess((MemberExpression)node);
206 case ExpressionType.Call:
207 return this.VisitMethodCall((MethodCallExpression)node);
208 case ExpressionType.ArrayLength:
209 return this.VisitArrayLength((UnaryExpression)node);
210 case ExpressionType.NewArrayInit:
211 return this.VisitNewArrayInit((NewArrayExpression)node);
212 case ExpressionType.ListInit:
213 return this.VisitListInit((ListInitExpression)node);
214 case ExpressionType.Quote:
215 return this.Visit(((UnaryExpression)node).Operand);
216 case ExpressionType.Invoke:
217 return this.VisitInvocation((InvocationExpression)node);
218 case ExpressionType.Lambda:
219 return this.VisitLambda((LambdaExpression)node);
220 case ExpressionType.RightShift:
221 case ExpressionType.LeftShift:
222 throw Error.UnsupportedNodeType(node.NodeType);
223 case (ExpressionType)InternalExpressionType.Known:
224 return ((KnownExpression)node).Node;
225 case (ExpressionType)InternalExpressionType.LinkedTable:
226 return this.VisitLinkedTable((LinkedTableExpression)node);
228 throw Error.UnrecognizedExpressionNode(node.NodeType);
232 this.dominatingExpression = save;
237 /// Heuristic which chooses the best Expression root to use for displaying user messages
238 /// and exception text.
240 private static Expression ChooseBestDominatingExpression(Expression last, Expression next) {
244 else if (next == null) {
248 if (next is MethodCallExpression) {
251 if (last is MethodCallExpression) {
258 private SqlSelect LockSelect(SqlSelect sel) {
259 if (sel.Selection.NodeType != SqlNodeType.AliasRef ||
261 sel.OrderBy.Count > 0 ||
262 sel.GroupBy.Count > 0 ||
263 sel.Having != null ||
265 sel.OrderingType != SqlOrderingType.Default ||
267 SqlAlias alias = new SqlAlias(sel);
268 SqlAliasRef aref = new SqlAliasRef(alias);
269 return new SqlSelect(aref, alias, this.dominatingExpression);
274 private SqlSelect VisitSequence(Expression exp) {
275 return this.CoerceToSequence(this.Visit(exp));
278 private SqlSelect CoerceToSequence(SqlNode node) {
279 SqlSelect select = node as SqlSelect;
280 if (select == null) {
281 if (node.NodeType == SqlNodeType.Value) {
282 SqlValue sv = (SqlValue)node;
283 // Check for ITables.
284 ITable t = sv.Value as ITable;
286 return this.CoerceToSequence(this.TranslateConstantTable(t, null));
288 // Check for IQueryable.
289 IQueryable query = sv.Value as IQueryable;
291 Expression fex = Funcletizer.Funcletize(query.Expression);
292 // IQueryables that return self-referencing Constant expressions cause infinite recursion
293 if (fex.NodeType != ExpressionType.Constant ||
294 ((ConstantExpression)fex).Value != query) {
295 return this.VisitSequence(fex);
297 throw Error.IQueryableCannotReturnSelfReferencingConstantExpression();
299 throw Error.CapturedValuesCannotBeSequences();
301 else if (node.NodeType == SqlNodeType.Multiset || node.NodeType == SqlNodeType.Element) {
302 return ((SqlSubSelect)node).Select;
304 else if (node.NodeType == SqlNodeType.ClientArray) {
305 throw Error.ConstructedArraysNotSupported();
307 else if (node.NodeType == SqlNodeType.ClientParameter) {
308 throw Error.ParametersCannotBeSequences();
310 // this needs to be a sequence expression!
311 SqlExpression sqlExpr = (SqlExpression)node;
312 SqlAlias sa = new SqlAlias(sqlExpr);
313 SqlAliasRef aref = new SqlAliasRef(sa);
314 return new SqlSelect(aref, sa, this.dominatingExpression);
320 // Recursive call to VisitInvocation.
321 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
322 private SqlNode VisitInvocation(InvocationExpression invoke) {
323 LambdaExpression lambda =
324 (invoke.Expression.NodeType == ExpressionType.Quote)
325 ? (LambdaExpression)((UnaryExpression)invoke.Expression).Operand
326 : (invoke.Expression as LambdaExpression);
327 if (lambda != null) {
328 // just map arg values into lambda's parameters and evaluate lambda's body
329 for (int i = 0, n = invoke.Arguments.Count; i < n; i++) {
330 this.exprMap[lambda.Parameters[i]] = invoke.Arguments[i];
332 return this.VisitInner(lambda.Body);
335 // check for compiled query invocation
336 SqlExpression expr = this.VisitExpression(invoke.Expression);
337 if (expr.NodeType == SqlNodeType.Value) {
338 SqlValue value = (SqlValue)expr;
339 Delegate d = value.Value as Delegate;
341 CompiledQuery cq = d.Target as CompiledQuery;
343 return this.VisitInvocation(Expression.Invoke(cq.Expression, invoke.Arguments));
344 } else if (invoke.Arguments.Count == 0) {
347 invokeResult = d.DynamicInvoke(null);
348 } catch (System.Reflection.TargetInvocationException e) {
349 throw e.InnerException;
351 return this.sql.ValueFromObject(invokeResult, invoke.Type, true, this.dominatingExpression);
355 SqlExpression [] args = new SqlExpression[invoke.Arguments.Count];
356 for(int i = 0; i<args.Length; ++i) {
357 args[i] = (SqlExpression)this.Visit(invoke.Arguments[i]);
359 var sca = new SqlClientArray(typeof(object[]), this.typeProvider.From(typeof(object[])), args, this.dominatingExpression);
360 return sql.MethodCall(invoke.Type, typeof(Delegate).GetMethod("DynamicInvoke"), expr, new SqlExpression[] {sca}, this.dominatingExpression);
364 // inline lambda expressions w/o invocation are parameterized queries
365 private SqlNode VisitLambda(LambdaExpression lambda) {
367 // turn lambda parameters into client parameters
368 for (int i = 0, n = lambda.Parameters.Count; i < n; i++) {
369 ParameterExpression p = lambda.Parameters[i];
371 if (p.Type == typeof(Type)) {
372 throw Error.BadParameterType(p.Type);
375 // construct accessor for parameter
376 ParameterExpression pa = Expression.Parameter(typeof(object[]), "args");
377 LambdaExpression accessor =
379 typeof(Func<,>).MakeGenericType(typeof(object[]), p.Type),
381 #pragma warning disable 618 // Disable the 'obsolete' warning
382 Expression.ArrayIndex(pa, Expression.Constant(i)),
385 #pragma warning restore 618
389 SqlClientParameter cp = new SqlClientParameter(p.Type, this.typeProvider.From(p.Type), accessor, this.dominatingExpression);
391 // map references to lambda's parameter to client parameter node
395 // call this so we don't erase 'outerNode' setting
396 return this.VisitInner(lambda.Body);
399 private SqlExpression VisitExpression(Expression exp) {
400 SqlNode result = this.Visit(exp);
401 if (result == null) return null;
402 SqlExpression x = result as SqlExpression;
403 if (x != null) return x;
404 SqlSelect select = result as SqlSelect;
405 if (select != null) {
406 SqlSubSelect ms = sql.SubSelect(SqlNodeType.Multiset, select, exp.Type);
409 throw Error.UnrecognizedExpressionNode(result);
412 private SqlSelect VisitSelect(Expression sequence, LambdaExpression selector) {
413 SqlSelect source = this.VisitSequence(sequence);
414 SqlAlias alias = new SqlAlias(source);
415 SqlAliasRef aref = new SqlAliasRef(alias);
417 this.map[selector.Parameters[0]] = aref;
418 SqlNode project = this.Visit(selector.Body);
420 SqlSelect pselect = project as SqlSelect;
421 if (pselect != null) {
422 return new SqlSelect(sql.SubSelect(SqlNodeType.Multiset, pselect, selector.Body.Type), alias, this.dominatingExpression);
424 else if ((project.NodeType == SqlNodeType.Element || project.NodeType == SqlNodeType.ScalarSubSelect) &&
425 (this.converterStrategy & ConverterStrategy.CanUseOuterApply) != 0) {
426 SqlSubSelect sub = (SqlSubSelect)project;
427 SqlSelect inner = sub.Select;
428 SqlAlias innerAlias = new SqlAlias(inner);
429 SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
430 if (project.NodeType == SqlNodeType.Element) {
431 inner.Selection = new SqlOptionalValue(
435 SqlNodeType.OuterJoinedValue,
436 sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression)
439 sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection)
443 inner.Selection = sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection);
445 SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, alias, innerAlias, null, this.dominatingExpression);
446 return new SqlSelect(innerRef, join, this.dominatingExpression);
449 SqlExpression expr = project as SqlExpression;
451 return new SqlSelect(expr, alias, this.dominatingExpression);
454 throw Error.BadProjectionInSelect();
459 private SqlSelect VisitSelectMany(Expression sequence, LambdaExpression colSelector, LambdaExpression resultSelector) {
460 SqlSelect seqSelect = this.VisitSequence(sequence);
461 SqlAlias seqAlias = new SqlAlias(seqSelect);
462 SqlAliasRef seqRef = new SqlAliasRef(seqAlias);
464 this.map[colSelector.Parameters[0]] = seqRef;
466 SqlNode colSelectorNode = this.VisitSequence(colSelector.Body);
467 SqlAlias selAlias = new SqlAlias(colSelectorNode);
468 SqlAliasRef selRef = new SqlAliasRef(selAlias);
469 SqlJoin join = new SqlJoin(SqlJoinType.CrossApply, seqAlias, selAlias, null, this.dominatingExpression);
471 SqlExpression projection = selRef;
473 if (resultSelector != null) {
474 this.map[resultSelector.Parameters[0]] = seqRef;
475 this.map[resultSelector.Parameters[1]] = selRef;
476 projection = this.VisitExpression(resultSelector.Body);
479 return new SqlSelect(projection, join, this.dominatingExpression);
482 private SqlSelect VisitJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) {
483 SqlSelect outerSelect = this.VisitSequence(outerSequence);
484 SqlSelect innerSelect = this.VisitSequence(innerSequence);
486 SqlAlias outerAlias = new SqlAlias(outerSelect);
487 SqlAliasRef outerRef = new SqlAliasRef(outerAlias);
488 SqlAlias innerAlias = new SqlAlias(innerSelect);
489 SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
491 this.map[outerKeySelector.Parameters[0]] = outerRef;
492 SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body);
494 this.map[innerKeySelector.Parameters[0]] = innerRef;
495 SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body);
497 this.map[resultSelector.Parameters[0]] = outerRef;
498 this.map[resultSelector.Parameters[1]] = innerRef;
499 SqlExpression result = this.VisitExpression(resultSelector.Body);
501 SqlExpression condition = sql.Binary(SqlNodeType.EQ, outerKey, innerKey);
502 SqlSelect select = null;
503 if ((this.converterStrategy & ConverterStrategy.CanUseJoinOn) != 0) {
504 SqlJoin join = new SqlJoin(SqlJoinType.Inner, outerAlias, innerAlias, condition, this.dominatingExpression);
505 select = new SqlSelect(result, join, this.dominatingExpression);
507 SqlJoin join = new SqlJoin(SqlJoinType.Cross, outerAlias, innerAlias, null, this.dominatingExpression);
508 select = new SqlSelect(result, join, this.dominatingExpression);
509 select.Where = condition;
514 private SqlSelect VisitGroupJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) {
515 SqlSelect outerSelect = this.VisitSequence(outerSequence);
516 SqlSelect innerSelect = this.VisitSequence(innerSequence);
518 SqlAlias outerAlias = new SqlAlias(outerSelect);
519 SqlAliasRef outerRef = new SqlAliasRef(outerAlias);
520 SqlAlias innerAlias = new SqlAlias(innerSelect);
521 SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
523 this.map[outerKeySelector.Parameters[0]] = outerRef;
524 SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body);
526 this.map[innerKeySelector.Parameters[0]] = innerRef;
527 SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body);
530 SqlExpression pred = sql.Binary(SqlNodeType.EQ, outerKey, innerKey);
531 SqlSelect select = new SqlSelect(innerRef, innerAlias, this.dominatingExpression);
533 SqlSubSelect subquery = sql.SubSelect(SqlNodeType.Multiset, select);
535 // make outer ref & multiset for result-selector params
536 this.map[resultSelector.Parameters[0]] = outerRef;
537 this.dupMap[resultSelector.Parameters[1]] = subquery;
538 SqlExpression result = this.VisitExpression(resultSelector.Body);
540 return new SqlSelect(result, outerAlias, this.dominatingExpression);
543 private SqlSelect VisitDefaultIfEmpty(Expression sequence) {
544 SqlSelect select = this.VisitSequence(sequence);
545 SqlAlias alias = new SqlAlias(select);
546 SqlAliasRef aliasRef = new SqlAliasRef(alias);
548 SqlExpression opt = new SqlOptionalValue(
551 sql.Unary(SqlNodeType.OuterJoinedValue,
552 sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression)
555 sql.Unary(SqlNodeType.OuterJoinedValue, aliasRef)
557 SqlSelect optSelect = new SqlSelect(opt, alias, this.dominatingExpression);
559 alias = new SqlAlias(optSelect);
560 aliasRef = new SqlAliasRef(alias);
562 SqlExpression litNull = sql.TypedLiteralNull(typeof(string), this.dominatingExpression);
563 SqlSelect selNull = new SqlSelect(litNull, null, this.dominatingExpression);
564 SqlAlias aliasNull = new SqlAlias(selNull);
566 SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, aliasNull, alias, null, this.dominatingExpression);
568 return new SqlSelect(aliasRef, join, this.dominatingExpression);
572 /// Rewrite seq.OfType<T> as seq.Select(s=>s as T).Where(p=>p!=null).
574 private SqlSelect VisitOfType(Expression sequence, Type ofType) {
575 SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
576 SqlAliasRef aref = (SqlAliasRef)select.Selection;
578 select.Selection = new SqlUnary(SqlNodeType.Treat, ofType, typeProvider.From(ofType), aref, this.dominatingExpression);
580 select = this.LockSelect(select);
581 aref = (SqlAliasRef)select.Selection;
583 // Append the 'is' operator into the WHERE clause.
584 select.Where = sql.AndAccumulate(select.Where,
585 sql.Unary(SqlNodeType.IsNotNull, aref, this.dominatingExpression)
592 /// Rewrite seq.Cast<T> as seq.Select(s=>(T)s).
594 private SqlNode VisitSequenceCast(Expression sequence, Type type) {
595 Type sourceType = TypeSystem.GetElementType(sequence.Type);
596 ParameterExpression p = Expression.Parameter(sourceType, "pc");
597 return this.Visit(Expression.Call(
598 typeof(Enumerable), "Select",
600 sourceType, // TSource element type.
601 type, // TResult element type.
605 Expression.Convert(p, type),
606 new ParameterExpression[] { p }
612 /// This is the 'is' operator.
614 private SqlNode VisitTypeBinary(TypeBinaryExpression b) {
615 SqlExpression expr = this.VisitExpression(b.Expression);
616 SqlExpression result = null;
617 switch (b.NodeType) {
618 case ExpressionType.TypeIs:
619 Type ofType = b.TypeOperand;
620 result = sql.Unary(SqlNodeType.IsNotNull, new SqlUnary(SqlNodeType.Treat, ofType, typeProvider.From(ofType), expr, this.dominatingExpression), this.dominatingExpression);
623 throw Error.TypeBinaryOperatorNotRecognized();
627 private SqlSelect VisitWhere(Expression sequence, LambdaExpression predicate) {
628 SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
630 this.map[predicate.Parameters[0]] = (SqlAliasRef)select.Selection;
632 select.Where = this.VisitExpression(predicate.Body);
636 private SqlNode VisitAs(UnaryExpression a) {
637 SqlNode node = this.Visit(a.Operand);
638 SqlExpression expr = node as SqlExpression;
640 return new SqlUnary(SqlNodeType.Treat, a.Type, typeProvider.From(a.Type), expr, a);
642 SqlSelect select = node as SqlSelect;
643 if (select != null) {
644 SqlSubSelect ms = sql.SubSelect(SqlNodeType.Multiset, select);
645 return new SqlUnary(SqlNodeType.Treat, a.Type, typeProvider.From(a.Type), ms, a);
647 throw Error.DidNotExpectAs(a);
650 private SqlNode VisitArrayLength(UnaryExpression c) {
651 SqlExpression exp = this.VisitExpression(c.Operand);
653 if (exp.SqlType.IsString || exp.SqlType.IsChar) {
654 return sql.CLRLENGTH(exp);
657 return sql.DATALENGTH(exp);
661 private SqlNode VisitArrayIndex(BinaryExpression b) {
662 SqlExpression array = this.VisitExpression(b.Left);
663 SqlExpression index = this.VisitExpression(b.Right);
665 if (array.NodeType == SqlNodeType.ClientParameter
666 && index.NodeType == SqlNodeType.Value) {
667 SqlClientParameter cpArray = (SqlClientParameter)array;
668 SqlValue vIndex = (SqlValue)index;
669 return new SqlClientParameter(
670 b.Type, sql.TypeProvider.From(b.Type),
672 #pragma warning disable 618 // Disable the 'obsolete' warning
673 Expression.ArrayIndex(cpArray.Accessor.Body, Expression.Constant(vIndex.Value, vIndex.ClrType)),
674 #pragma warning restore 618
675 cpArray.Accessor.Parameters.ToArray()
678 this.dominatingExpression
682 throw Error.UnrecognizedExpressionNode(b.NodeType);
685 private SqlNode VisitCast(UnaryExpression c) {
686 if (c.Method != null) {
687 SqlExpression exp = this.VisitExpression(c.Operand);
688 return sql.MethodCall(c.Type, c.Method, null, new SqlExpression[] { exp }, dominatingExpression);
690 return this.VisitChangeType(c.Operand, c.Type);
693 private SqlNode VisitChangeType(Expression expression, Type type) {
694 SqlExpression expr = this.VisitExpression(expression);
695 return this.ChangeType(expr, type);
698 private SqlNode ConvertDateToDateTime2(SqlExpression expr) {
699 SqlExpression datetime2 = new SqlVariable(expr.ClrType, expr.SqlType, "DATETIME2", expr.SourceExpression);
700 return sql.FunctionCall(typeof(DateTime), "CONVERT", new SqlExpression[2] { datetime2, expr }, expr.SourceExpression);
703 private SqlNode ChangeType(SqlExpression expr, Type type) {
704 if (type == typeof(object)) {
705 return expr; // Boxing conversion?
707 else if (expr.NodeType == SqlNodeType.Value && ((SqlValue)expr).Value == null) {
708 return sql.TypedLiteralNull(type, expr.SourceExpression);
710 else if (expr.NodeType == SqlNodeType.ClientParameter) {
711 SqlClientParameter cp = (SqlClientParameter)expr;
712 return new SqlClientParameter(
713 type, sql.TypeProvider.From(type),
714 Expression.Lambda(Expression.Convert(cp.Accessor.Body, type), cp.Accessor.Parameters.ToArray()),
719 ConversionMethod cm = ChooseConversionMethod(expr.ClrType, type);
721 case ConversionMethod.Convert:
722 return sql.UnaryConvert(type, typeProvider.From(type), expr, expr.SourceExpression);
723 case ConversionMethod.Lift:
724 if (SqlFactory.IsSqlDateType(expr)) {
725 expr = (SqlExpression) ConvertDateToDateTime2(expr);
727 return new SqlLift(type, expr, this.dominatingExpression);
728 case ConversionMethod.Ignore:
729 if (SqlFactory.IsSqlDateType(expr)) {
730 return ConvertDateToDateTime2(expr);
733 case ConversionMethod.Treat:
734 return new SqlUnary(SqlNodeType.Treat, type, typeProvider.From(type), expr, expr.SourceExpression);
736 throw Error.UnhandledExpressionType(cm);
740 enum ConversionMethod {
747 private ConversionMethod ChooseConversionMethod(Type fromType, Type toType) {
748 Type nnFromType = TypeSystem.GetNonNullableType(fromType);
749 Type nnToType = TypeSystem.GetNonNullableType(toType);
751 if (fromType != toType && nnFromType == nnToType) {
752 return ConversionMethod.Lift;
754 else if (TypeSystem.IsSequenceType(nnFromType) || TypeSystem.IsSequenceType(nnToType)) {
755 return ConversionMethod.Ignore;
758 ProviderType sfromType = typeProvider.From(nnFromType);
759 ProviderType stoType = typeProvider.From(nnToType);
761 bool isRuntimeOnly1 = sfromType.IsRuntimeOnlyType;
762 bool isRuntimeOnly2 = stoType.IsRuntimeOnlyType;
764 if (isRuntimeOnly1 || isRuntimeOnly2) {
765 return ConversionMethod.Treat;
768 if (nnFromType == nnToType // same non-nullable .NET types
769 || (sfromType.IsString && sfromType.Equals(stoType)) // same SQL string types
770 || (nnFromType.IsEnum || nnToType.IsEnum) // any .NET enum type
772 return ConversionMethod.Ignore;
775 return ConversionMethod.Convert;
780 /// Convert ITable into SqlNodes. If the hierarchy involves inheritance then
781 /// a type case is built. Abstractly, a type case is a CASE where each WHEN is a possible
782 /// a typebinding that may be instantianted.
784 private SqlNode TranslateConstantTable(ITable table, SqlLink link) {
785 if (table.Context != this.services.Context) {
786 throw Error.WrongDataContext();
788 MetaTable metaTable = this.services.Model.GetTable(table.ElementType);
789 return this.translator.BuildDefaultQuery(metaTable.RowType, this.allowDeferred, link, this.dominatingExpression);
792 private SqlNode VisitLinkedTable(LinkedTableExpression linkedTable) {
793 return TranslateConstantTable(linkedTable.Table, linkedTable.Link);
796 private SqlNode VisitConstant(ConstantExpression cons) {
797 // A value constant or null.
798 Type type = cons.Type;
799 if (cons.Value == null) {
800 return sql.TypedLiteralNull(type, this.dominatingExpression);
802 if (type == typeof(object)) {
803 type = cons.Value.GetType();
805 return sql.ValueFromObject(cons.Value, type, true, this.dominatingExpression);
808 private SqlExpression VisitConditional(ConditionalExpression cond) {
809 List<SqlWhen> whens = new List<SqlWhen>(1);
810 whens.Add(new SqlWhen(this.VisitExpression(cond.Test), this.VisitExpression(cond.IfTrue)));
811 SqlExpression @else = this.VisitExpression(cond.IfFalse);
812 // combine search cases found in the else clause into a single seach case
813 while (@else.NodeType == SqlNodeType.SearchedCase) {
814 SqlSearchedCase sc = (SqlSearchedCase)@else;
815 whens.AddRange(sc.Whens);
818 return sql.SearchedCase(whens.ToArray(), @else, this.dominatingExpression);
821 private SqlExpression VisitNew(NewExpression qn) {
822 if (TypeSystem.IsNullableType(qn.Type) && qn.Arguments.Count == 1 &&
823 TypeSystem.GetNonNullableType(qn.Type) == qn.Arguments[0].Type) {
824 return this.VisitCast(Expression.Convert(qn.Arguments[0], qn.Type)) as SqlExpression;
826 else if (qn.Type == typeof(decimal) && qn.Arguments.Count == 1) {
827 return this.VisitCast(Expression.Convert(qn.Arguments[0], typeof(decimal))) as SqlExpression;
830 MetaType mt = this.services.Model.GetMetaType(qn.Type);
832 throw Error.CannotMaterializeEntityType(qn.Type);
836 SqlExpression[] args = null;
838 if (qn.Arguments.Count > 0) {
839 args = new SqlExpression[qn.Arguments.Count];
840 for (int i = 0, n = qn.Arguments.Count; i < n; i++) {
841 args[i] = this.VisitExpression(qn.Arguments[i]);
845 SqlNew tb = sql.New(mt, qn.Constructor, args, PropertyOrFieldOf(qn.Members), null, this.dominatingExpression);
849 private SqlExpression VisitMemberInit(MemberInitExpression init) {
850 MetaType mt = this.services.Model.GetMetaType(init.Type);
852 throw Error.CannotMaterializeEntityType(init.Type);
854 SqlExpression[] args = null;
856 NewExpression qn = init.NewExpression;
858 if (qn.Type == typeof(decimal) && qn.Arguments.Count == 1) {
859 return this.VisitCast(Expression.Convert(qn.Arguments[0], typeof(decimal))) as SqlExpression;
862 if (qn.Arguments.Count > 0) {
863 args = new SqlExpression[qn.Arguments.Count];
864 for (int i = 0, n = args.Length; i < n; i++) {
865 args[i] = this.VisitExpression(qn.Arguments[i]);
869 int cBindings = init.Bindings.Count;
870 SqlMemberAssign[] members = new SqlMemberAssign[cBindings];
871 int[] ordinal = new int[members.Length];
872 for (int i = 0; i < cBindings; i++) {
873 MemberAssignment mb = init.Bindings[i] as MemberAssignment;
875 SqlExpression expr = this.VisitExpression(mb.Expression);
876 SqlMemberAssign sma = new SqlMemberAssign(mb.Member, expr);
878 ordinal[i] = mt.GetDataMember(mb.Member).Ordinal;
881 throw Error.UnhandledBindingType(init.Bindings[i].BindingType);
884 // put members in type's declaration order
885 Array.Sort(ordinal, members, 0, members.Length);
887 SqlNew tb = sql.New(mt, qn.Constructor, args, PropertyOrFieldOf(qn.Members), members, this.dominatingExpression);
891 private static IEnumerable<MemberInfo> PropertyOrFieldOf(IEnumerable<MemberInfo> members) {
892 if (members == null) {
895 List<MemberInfo> result = new List<MemberInfo>();
896 foreach (MemberInfo mi in members) {
897 switch (mi.MemberType) {
898 case MemberTypes.Method: {
899 foreach (PropertyInfo pi in mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
900 MethodInfo method = mi as MethodInfo;
901 if (pi.CanRead && pi.GetGetMethod() == method) {
908 case MemberTypes.Field:
909 case MemberTypes.Property: {
914 throw Error.CouldNotConvertToPropertyOrField(mi);
921 private SqlSelect VisitDistinct(Expression sequence) {
922 SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
923 select.IsDistinct = true;
924 select.OrderingType = SqlOrderingType.Blocked;
928 private SqlSelect VisitTake(Expression sequence, Expression count) {
929 // verify that count >= 0
930 SqlExpression takeExp = this.VisitExpression(count);
931 if (takeExp.NodeType == SqlNodeType.Value) {
932 SqlValue constTakeCount = (SqlValue)takeExp;
933 if (typeof(int).IsAssignableFrom(constTakeCount.Value.GetType()) && ((int)constTakeCount.Value) < 0) {
934 throw Error.ArgumentOutOfRange("takeCount");
938 MethodCallExpression mce = sequence as MethodCallExpression;
939 if (mce != null && IsSequenceOperatorCall(mce) && mce.Method.Name == "Skip" && mce.Arguments.Count == 2) {
940 SqlExpression skipExp = this.VisitExpression(mce.Arguments[1]);
942 // verify that count >= 0
943 if (skipExp.NodeType == SqlNodeType.Value) {
944 SqlValue constSkipCount = (SqlValue)skipExp;
945 if (typeof(int).IsAssignableFrom(constSkipCount.Value.GetType()) && ((int)constSkipCount.Value) < 0) {
946 throw Error.ArgumentOutOfRange("skipCount");
950 SqlSelect select = this.VisitSequence(mce.Arguments[0]);
951 return this.GenerateSkipTake(select, skipExp, takeExp);
954 SqlSelect select = this.VisitSequence(sequence);
955 return this.GenerateSkipTake(select, null, takeExp);
960 /// In order for elements of a sequence to be skipped, they must have identity
961 /// that can be compared. This excludes elements that are sequences and elements
962 /// that contain sequences.
964 private bool CanSkipOnSelection(SqlExpression selection) {
965 // we can skip over groupings (since we can compare them by key)
966 if (IsGrouping(selection.ClrType)) {
969 // we can skip over entities (since we can compare them by primary key)
970 MetaTable table = this.services.Model.GetTable(selection.ClrType);
974 // sequences that are not primitives are not skippable
975 if (TypeSystem.IsSequenceType(selection.ClrType) && !selection.SqlType.CanBeColumn) {
978 switch (selection.NodeType) {
979 case SqlNodeType.AliasRef: {
980 SqlNode node = ((SqlAliasRef)selection).Alias.Node;
981 SqlSelect select = node as SqlSelect;
982 if (select != null) {
983 return CanSkipOnSelection(select.Selection);
985 SqlUnion union = node as SqlUnion;
987 bool left = default(bool);
988 bool right = default(bool);
990 SqlSelect selectLeft = union.Left as SqlSelect;
991 if (selectLeft != null) {
992 left = CanSkipOnSelection(selectLeft.Selection);
995 SqlSelect selectRight = union.Right as SqlSelect;
996 if (selectRight != null) {
997 right = CanSkipOnSelection(selectRight.Selection);
1000 return left && right;
1002 SqlExpression expr = (SqlExpression)node;
1003 return CanSkipOnSelection(expr);
1005 case SqlNodeType.New:
1006 SqlNew sn = (SqlNew)selection;
1007 // check each member of the projection for sequences
1008 foreach (SqlMemberAssign ma in sn.Members) {
1009 if (!CanSkipOnSelection(ma.Expression))
1012 if (sn.ArgMembers != null) {
1013 for (int i = 0, n = sn.ArgMembers.Count; i < n; ++i) {
1014 if (!CanSkipOnSelection(sn.Args[i])) {
1028 /// WHERE NOT EXISTS (
1029 /// SELECT TOP count *
1032 /// SQL2005: SELECT *
1033 /// FROM (SELECT sequence.*,
1034 /// ROW_NUMBER() OVER (ORDER BY order) AS ROW_NUMBER
1036 /// WHERE ROW_NUMBER > count
1038 /// <param name="sequence">Sequence containing elements to skip</param>
1039 /// <param name="count">Number of elements to skip</param>
1040 /// <returns>SELECT node</returns>
1041 private SqlSelect VisitSkip(Expression sequence, Expression skipCount) {
1042 SqlExpression skipExp = this.VisitExpression(skipCount);
1044 // verify that count >= 0
1045 if (skipExp.NodeType == SqlNodeType.Value) {
1046 SqlValue constSkipCount = (SqlValue)skipExp;
1047 if (typeof(int).IsAssignableFrom(constSkipCount.Value.GetType()) && ((int)constSkipCount.Value) < 0) {
1048 throw Error.ArgumentOutOfRange("skipCount");
1052 SqlSelect select = this.VisitSequence(sequence);
1053 return this.GenerateSkipTake(select, skipExp, null);
1056 private SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) {
1057 SqlSelect select = this.LockSelect(sequence);
1060 if (skipExp == null) {
1061 if (takeExp != null) {
1062 select.Top = takeExp;
1067 SqlAlias alias = new SqlAlias(select);
1068 SqlAliasRef aref = new SqlAliasRef(alias);
1070 if (this.UseConverterStrategy(ConverterStrategy.SkipWithRowNumber)) {
1071 // use ROW_NUMBER() (preferred)
1072 SqlColumn rowNumber = new SqlColumn("ROW_NUMBER", sql.RowNumber(new List<SqlOrderExpression>(), this.dominatingExpression));
1073 SqlColumnRef rowNumberRef = new SqlColumnRef(rowNumber);
1075 select.Row.Columns.Add(rowNumber);
1077 SqlSelect final = new SqlSelect(aref, alias, this.dominatingExpression);
1079 if (takeExp != null) {
1080 // use BETWEEN for skip+take combo (much faster)
1081 final.Where = sql.Between(
1083 sql.Add(skipExp, 1),
1084 sql.Binary(SqlNodeType.Add, (SqlExpression)SqlDuplicator.Copy(skipExp), takeExp),
1085 this.dominatingExpression
1089 final.Where = sql.Binary(SqlNodeType.GT, rowNumberRef, skipExp);
1095 // Ensure that the sequence contains elements that can be skipped
1096 if (!CanSkipOnSelection(select.Selection)) {
1097 throw Error.SkipNotSupportedForSequenceTypes();
1104 // - Projections that contain all PK columns
1106 // .. where there sequence can be traced back to a:
1107 // - Single-table query
1111 // - Union, where union.All == false
1113 // Not supported: joins
1115 // Sequence should also be ordered, but we can't test for it at this
1116 // point in processing, and we won't know that we need to test it, later.
1118 SingleTableQueryVisitor stqv = new SingleTableQueryVisitor();
1120 if (!stqv.IsValid) {
1121 throw Error.SkipRequiresSingleTableQueryWithPKs();
1124 SqlSelect dupsel = (SqlSelect)SqlDuplicator.Copy(select);
1125 dupsel.Top = skipExp;
1127 SqlAlias dupAlias = new SqlAlias(dupsel);
1128 SqlAliasRef dupRef = new SqlAliasRef(dupAlias);
1130 SqlSelect eqsel = new SqlSelect(dupRef, dupAlias, this.dominatingExpression);
1131 eqsel.Where = sql.Binary(SqlNodeType.EQ2V, aref, dupRef);
1132 SqlSubSelect ss = sql.SubSelect(SqlNodeType.Exists, eqsel);
1134 SqlSelect final = new SqlSelect(aref, alias, this.dominatingExpression);
1135 final.Where = sql.Unary(SqlNodeType.Not, ss, this.dominatingExpression);
1136 final.Top = takeExp;
1142 private SqlNode VisitParameter(ParameterExpression p) {
1143 SqlExpression sqlExpr;
1144 if (this.map.TryGetValue(p, out sqlExpr))
1147 if (this.exprMap.TryGetValue(p, out expr))
1148 return this.Visit(expr);
1150 if (this.dupMap.TryGetValue(p, out nodeToDup)) {
1151 SqlDuplicator duplicator = new SqlDuplicator(true);
1152 return duplicator.Duplicate(nodeToDup);
1154 throw Error.ParameterNotInScope(p.Name);
1158 /// Translate a call to a table valued function expression into a sql select.
1160 private SqlNode TranslateTableValuedFunction(MethodCallExpression mce, MetaFunction function) {
1161 // translate method call into sql function call
1162 List<SqlExpression> sqlParams = GetFunctionParameters(mce, function);
1163 SqlTableValuedFunctionCall functionCall = sql.TableValuedFunctionCall(function.ResultRowTypes[0].InheritanceRoot, mce.Method.ReturnType, function.MappedName, sqlParams, mce);
1165 SqlAlias alias = new SqlAlias(functionCall);
1166 SqlAliasRef aref = new SqlAliasRef(alias);
1168 // Build default projection
1169 SqlExpression projection = this.translator.BuildProjection(aref, function.ResultRowTypes[0].InheritanceRoot, this.allowDeferred, null, mce);
1171 SqlSelect select = new SqlSelect(projection, alias, mce);
1176 /// Translate a call to a stored procedure
1178 private SqlNode TranslateStoredProcedureCall(MethodCallExpression mce, MetaFunction function) {
1179 if (!this.outerNode) {
1180 throw Error.SprocsCannotBeComposed();
1183 // translate method call into sql function call
1184 List<SqlExpression> sqlParams = GetFunctionParameters(mce, function);
1186 SqlStoredProcedureCall spc = new SqlStoredProcedureCall(function, null, sqlParams, mce);
1188 Type returnType = mce.Method.ReturnType;
1189 if (returnType.IsGenericType &&
1190 (returnType.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
1191 returnType.GetGenericTypeDefinition() == typeof(ISingleResult<>))) {
1193 // Since this is a single rowset returning sproc, we use the one
1194 // and only root metatype.
1195 MetaType rowType = function.ResultRowTypes[0].InheritanceRoot;
1197 SqlUserRow rowExp = new SqlUserRow(rowType, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), spc, mce);
1198 spc.Projection = this.translator.BuildProjection(rowExp, rowType, this.allowDeferred, null, mce);
1201 typeof(IMultipleResults).IsAssignableFrom(returnType)
1202 || returnType == typeof(int)
1203 || returnType == typeof(int?)
1205 throw Error.InvalidReturnFromSproc(returnType);
1212 /// Create a list of sql parameters for the specified method call expression,
1213 /// taking into account any explicit typing applied to the parameters via the
1214 /// Parameter attribute.
1216 private List<SqlExpression> GetFunctionParameters(MethodCallExpression mce, MetaFunction function) {
1217 List<SqlExpression> sqlParams = new List<SqlExpression>(mce.Arguments.Count);
1219 // create sql parameters for each method parameter
1220 for (int i = 0, n = mce.Arguments.Count; i < n; i++) {
1221 SqlExpression newParamExpression = this.VisitExpression(mce.Arguments[i]);
1223 // If the parameter explicitly specifies a type in metadata,
1224 // use it as the provider type.
1225 MetaParameter currMetaParam = function.Parameters[i];
1226 if (!string.IsNullOrEmpty(currMetaParam.DbType)) {
1227 SqlSimpleTypeExpression typeExpression = newParamExpression as SqlSimpleTypeExpression;
1228 if (typeExpression != null) {
1229 // determine provider type, and update the parameter expression
1230 ProviderType providerType = typeProvider.Parse(currMetaParam.DbType);
1231 typeExpression.SetSqlType(providerType);
1234 sqlParams.Add(newParamExpression);
1240 private SqlUserQuery VisitUserQuery(string query, Expression[] arguments, Type resultType) {
1241 SqlExpression[] args = new SqlExpression[arguments.Length];
1242 for (int i = 0, n = args.Length; i < n; i++) {
1243 args[i] = this.VisitExpression(arguments[i]);
1245 SqlUserQuery suq = new SqlUserQuery(query, null, args, this.dominatingExpression);
1246 if (resultType != typeof(void)) {
1247 Type elementType = TypeSystem.GetElementType(resultType);
1248 MetaType mType = this.services.Model.GetMetaType(elementType);
1250 // if the element type is a simple type (int, bool, etc.) we create
1251 // a single column binding
1252 if (TypeSystem.IsSimpleType(elementType)) {
1253 SqlUserColumn col = new SqlUserColumn(elementType, typeProvider.From(elementType), suq, "", false, this.dominatingExpression);
1254 suq.Columns.Add(col);
1255 suq.Projection = col;
1258 // ... otherwise we generate a default projection
1259 SqlUserRow rowExp = new SqlUserRow(mType.InheritanceRoot, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), suq, this.dominatingExpression);
1260 suq.Projection = this.translator.BuildProjection(rowExp, mType, this.allowDeferred, null, this.dominatingExpression);
1266 private SqlNode VisitUnary(UnaryExpression u) {
1267 SqlExpression exp = this.VisitExpression(u.Operand);
1269 if (u.Method != null) {
1270 return sql.MethodCall(u.Type, u.Method, null, new SqlExpression[] { exp }, dominatingExpression);
1273 SqlExpression result = null;
1274 switch (u.NodeType) {
1275 case ExpressionType.Negate:
1276 case ExpressionType.NegateChecked:
1277 result = sql.Unary(SqlNodeType.Negate, exp, this.dominatingExpression);
1279 case ExpressionType.Not:
1280 if (u.Operand.Type == typeof(bool) || u.Operand.Type == typeof(bool?)) {
1281 result = sql.Unary(SqlNodeType.Not, exp, this.dominatingExpression);
1284 result = sql.Unary(SqlNodeType.BitNot, exp, this.dominatingExpression);
1287 case ExpressionType.TypeAs:
1288 result = sql.Unary(SqlNodeType.Treat, exp, this.dominatingExpression);
1295 [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.")]
1296 private SqlNode VisitBinary(BinaryExpression b) {
1297 SqlExpression left = this.VisitExpression(b.Left);
1298 SqlExpression right = this.VisitExpression(b.Right);
1300 if (b.Method != null) {
1301 return sql.MethodCall(b.Type, b.Method, null, new SqlExpression[] { left, right }, dominatingExpression);
1304 SqlExpression result = null;
1306 switch (b.NodeType) {
1307 case ExpressionType.Add:
1308 case ExpressionType.AddChecked:
1309 result = sql.Binary(SqlNodeType.Add, left, right, b.Type);
1311 case ExpressionType.Subtract:
1312 case ExpressionType.SubtractChecked:
1313 result = sql.Binary(SqlNodeType.Sub, left, right, b.Type);
1315 case ExpressionType.Multiply:
1316 case ExpressionType.MultiplyChecked:
1317 result = sql.Binary(SqlNodeType.Mul, left, right, b.Type);
1319 case ExpressionType.Divide:
1320 result = sql.Binary(SqlNodeType.Div, left, right, b.Type);
1322 case ExpressionType.Modulo:
1323 result = sql.Binary(SqlNodeType.Mod, left, right, b.Type);
1325 case ExpressionType.And:
1326 if (b.Left.Type == typeof(bool) || b.Left.Type == typeof(bool?)) {
1327 result = sql.Binary(SqlNodeType.And, left, right, b.Type);
1330 result = sql.Binary(SqlNodeType.BitAnd, left, right, b.Type);
1333 case ExpressionType.AndAlso:
1334 result = sql.Binary(SqlNodeType.And, left, right, b.Type);
1336 case ExpressionType.Or:
1337 if (b.Left.Type == typeof(bool) || b.Left.Type == typeof(bool?)) {
1338 result = sql.Binary(SqlNodeType.Or, left, right, b.Type);
1341 result = sql.Binary(SqlNodeType.BitOr, left, right, b.Type);
1344 case ExpressionType.OrElse:
1345 result = sql.Binary(SqlNodeType.Or, left, right, b.Type);
1347 case ExpressionType.LessThan:
1348 result = sql.Binary(SqlNodeType.LT, left, right, b.Type);
1350 case ExpressionType.LessThanOrEqual:
1351 result = sql.Binary(SqlNodeType.LE, left, right, b.Type);
1353 case ExpressionType.GreaterThan:
1354 result = sql.Binary(SqlNodeType.GT, left, right, b.Type);
1356 case ExpressionType.GreaterThanOrEqual:
1357 result = sql.Binary(SqlNodeType.GE, left, right, b.Type);
1359 case ExpressionType.Equal:
1360 result = sql.Binary(SqlNodeType.EQ, left, right, b.Type);
1362 case ExpressionType.NotEqual:
1363 result = sql.Binary(SqlNodeType.NE, left, right, b.Type);
1365 case ExpressionType.ExclusiveOr:
1366 result = sql.Binary(SqlNodeType.BitXor, left, right, b.Type);
1368 case ExpressionType.Coalesce:
1369 result = this.MakeCoalesce(left, right, b.Type);
1372 throw Error.BinaryOperatorNotRecognized(b.NodeType);
1377 private SqlExpression MakeCoalesce(SqlExpression left, SqlExpression right, Type resultType) {
1378 CompensateForLowerPrecedenceOfDateType(ref left, ref right); // DevDiv 176874
1379 if (TypeSystem.IsSimpleType(resultType)) {
1380 return sql.Binary(SqlNodeType.Coalesce, left, right, resultType);
1383 List<SqlWhen> whens = new List<SqlWhen>(1);
1384 whens.Add(new SqlWhen(sql.Unary(SqlNodeType.IsNull, left, left.SourceExpression), right));
1385 SqlDuplicator dup = new SqlDuplicator(true);
1386 return sql.SearchedCase(whens.ToArray(), (SqlExpression)dup.Duplicate(left), this.dominatingExpression);
1390 // The result *type* of a COALESCE function call is that of the operand with the highest precedence.
1391 // However, the SQL DATE type has a lower precedence than DATETIME or SMALLDATETIME, despite having
1392 // a hihger range. The following logic compensates for that discrepancy.
1394 private void CompensateForLowerPrecedenceOfDateType(ref SqlExpression left, ref SqlExpression right) {
1395 if (SqlFactory.IsSqlDateType(left) && SqlFactory.IsSqlDateTimeType(right)) {
1396 right = (SqlExpression)ConvertDateToDateTime2(right);
1398 else if (SqlFactory.IsSqlDateType(right) && SqlFactory.IsSqlDateTimeType(left)) {
1399 left = (SqlExpression)ConvertDateToDateTime2(left);
1403 private SqlNode VisitConcat(Expression source1, Expression source2) {
1404 SqlSelect left = this.VisitSequence(source1);
1405 SqlSelect right = this.VisitSequence(source2);
1406 SqlUnion union = new SqlUnion(left, right, true);
1407 SqlAlias alias = new SqlAlias(union);
1408 SqlAliasRef aref = new SqlAliasRef(alias);
1409 SqlSelect result = new SqlSelect(aref, alias, this.dominatingExpression);
1410 result.OrderingType = SqlOrderingType.Blocked;
1414 private SqlNode VisitUnion(Expression source1, Expression source2) {
1415 SqlSelect left = this.VisitSequence(source1);
1416 SqlSelect right = this.VisitSequence(source2);
1417 SqlUnion union = new SqlUnion(left, right, false);
1418 SqlAlias alias = new SqlAlias(union);
1419 SqlAliasRef aref = new SqlAliasRef(alias);
1420 SqlSelect result = new SqlSelect(aref, alias, this.dominatingExpression);
1421 result.OrderingType = SqlOrderingType.Blocked;
1425 private SqlNode VisitIntersect(Expression source1, Expression source2) {
1426 Type type = TypeSystem.GetElementType(source1.Type);
1427 if (IsGrouping(type)) {
1428 throw Error.IntersectNotSupportedForHierarchicalTypes();
1431 SqlSelect select1 = this.LockSelect(this.VisitSequence(source1));
1432 SqlSelect select2 = this.VisitSequence(source2);
1434 SqlAlias alias1 = new SqlAlias(select1);
1435 SqlAliasRef aref1 = new SqlAliasRef(alias1);
1437 SqlAlias alias2 = new SqlAlias(select2);
1438 SqlAliasRef aref2 = new SqlAliasRef(alias2);
1440 SqlExpression any = this.GenerateQuantifier(alias2, sql.Binary(SqlNodeType.EQ2V, aref1, aref2), true);
1442 SqlSelect result = new SqlSelect(aref1, alias1, select1.SourceExpression);
1444 result.IsDistinct = true;
1445 result.OrderingType = SqlOrderingType.Blocked;
1449 private SqlNode VisitExcept(Expression source1, Expression source2) {
1450 Type type = TypeSystem.GetElementType(source1.Type);
1451 if (IsGrouping(type)) {
1452 throw Error.ExceptNotSupportedForHierarchicalTypes();
1455 SqlSelect select1 = this.LockSelect(this.VisitSequence(source1));
1456 SqlSelect select2 = this.VisitSequence(source2);
1458 SqlAlias alias1 = new SqlAlias(select1);
1459 SqlAliasRef aref1 = new SqlAliasRef(alias1);
1461 SqlAlias alias2 = new SqlAlias(select2);
1462 SqlAliasRef aref2 = new SqlAliasRef(alias2);
1464 SqlExpression any = this.GenerateQuantifier(alias2, sql.Binary(SqlNodeType.EQ2V, aref1, aref2), true);
1466 SqlSelect result = new SqlSelect(aref1, alias1, select1.SourceExpression);
1467 result.Where = sql.Unary(SqlNodeType.Not, any);
1468 result.IsDistinct = true;
1469 result.OrderingType = SqlOrderingType.Blocked;
1474 /// Returns true if the type is an IGrouping.
1476 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
1477 private bool IsGrouping(Type t) {
1478 if (t.IsGenericType &&
1479 t.GetGenericTypeDefinition() == typeof(IGrouping<,>))
1484 private SqlSelect VisitOrderBy(Expression sequence, LambdaExpression expression, SqlOrderType orderType) {
1485 if (IsGrouping(expression.Body.Type)) {
1486 throw Error.GroupingNotSupportedAsOrderCriterion();
1488 if (!this.typeProvider.From(expression.Body.Type).IsOrderable) {
1489 throw Error.TypeCannotBeOrdered(expression.Body.Type);
1492 SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
1494 if (select.Selection.NodeType != SqlNodeType.AliasRef || select.OrderBy.Count > 0) {
1495 SqlAlias alias = new SqlAlias(select);
1496 SqlAliasRef aref = new SqlAliasRef(alias);
1497 select = new SqlSelect(aref, alias, this.dominatingExpression);
1500 this.map[expression.Parameters[0]] = (SqlAliasRef)select.Selection;
1501 SqlExpression expr = this.VisitExpression(expression.Body);
1503 select.OrderBy.Add(new SqlOrderExpression(orderType, expr));
1507 private SqlSelect VisitThenBy(Expression sequence, LambdaExpression expression, SqlOrderType orderType) {
1508 if (IsGrouping(expression.Body.Type)) {
1509 throw Error.GroupingNotSupportedAsOrderCriterion();
1511 if (!this.typeProvider.From(expression.Body.Type).IsOrderable) {
1512 throw Error.TypeCannotBeOrdered(expression.Body.Type);
1515 SqlSelect select = this.VisitSequence(sequence);
1516 System.Diagnostics.Debug.Assert(select.Selection.NodeType == SqlNodeType.AliasRef);
1518 this.map[expression.Parameters[0]] = (SqlAliasRef)select.Selection;
1519 SqlExpression expr = this.VisitExpression(expression.Body);
1521 select.OrderBy.Add(new SqlOrderExpression(orderType, expr));
1525 private SqlNode VisitGroupBy(Expression sequence, LambdaExpression keyLambda, LambdaExpression elemLambda, LambdaExpression resultSelector) {
1526 // Convert seq.Group(elem, key) into
1528 // SELECT s.key, MULTISET(select s2.elem from seq AS s2 where s.key == s2.key)
1531 // where key and elem can be either simple scalars or object constructions
1533 SqlSelect seq = this.VisitSequence(sequence);
1534 seq = this.LockSelect(seq);
1535 SqlAlias seqAlias = new SqlAlias(seq);
1536 SqlAliasRef seqAliasRef = new SqlAliasRef(seqAlias);
1538 // evaluate the key expression relative to original sequence
1539 this.map[keyLambda.Parameters[0]] = seqAliasRef;
1540 SqlExpression keyExpr = this.VisitExpression(keyLambda.Body);
1542 // make a duplicate of the original sequence to use as a foundation of our group multiset
1543 SqlDuplicator sd = new SqlDuplicator();
1544 SqlSelect selDup = (SqlSelect)sd.Duplicate(seq);
1546 // rebind key in relative to the duplicate sequence
1547 SqlAlias selDupAlias = new SqlAlias(selDup);
1548 SqlAliasRef selDupRef = new SqlAliasRef(selDupAlias);
1549 this.map[keyLambda.Parameters[0]] = selDupRef;
1550 SqlExpression keyDup = this.VisitExpression(keyLambda.Body);
1552 SqlExpression elemExpr = null;
1553 SqlExpression elemOnGroupSource = null;
1554 if (elemLambda != null) {
1555 // evaluate element expression relative to the duplicate sequence
1556 this.map[elemLambda.Parameters[0]] = selDupRef;
1557 elemExpr = this.VisitExpression(elemLambda.Body);
1559 // evaluate element expression relative to original sequence
1560 this.map[elemLambda.Parameters[0]] = seqAliasRef;
1561 elemOnGroupSource = this.VisitExpression(elemLambda.Body);
1564 // no elem expression supplied, so just use an alias ref to the duplicate sequence.
1565 // this will resolve to whatever was being produced by the sequence
1566 elemExpr = selDupRef;
1567 elemOnGroupSource = seqAliasRef;
1570 // Make a sub expression out of the key. This will allow a single definition of the
1571 // expression to be shared at multiple points in the tree (via SqlSharedExpressionRef's)
1572 SqlSharedExpression keySubExpr = new SqlSharedExpression(keyExpr);
1573 keyExpr = new SqlSharedExpressionRef(keySubExpr);
1575 // construct the select clause that picks out the elements (this may be redundant...)
1576 SqlSelect selElem = new SqlSelect(elemExpr, selDupAlias, this.dominatingExpression);
1577 selElem.Where = sql.Binary(SqlNodeType.EQ2V, keyExpr, keyDup);
1579 // Finally, make the MULTISET node. this will be used as part of the final select
1580 SqlSubSelect ss = sql.SubSelect(SqlNodeType.Multiset, selElem);
1582 // add a layer to the original sequence before applying the actual group-by clause
1583 SqlSelect gsel = new SqlSelect(new SqlSharedExpressionRef(keySubExpr), seqAlias, this.dominatingExpression);
1584 gsel.GroupBy.Add(keySubExpr);
1585 SqlAlias gselAlias = new SqlAlias(gsel);
1587 SqlSelect result = null;
1588 if (resultSelector != null) {
1589 // Create final select to include construction of group multiset
1590 // select new Grouping { Key = key, Group = Multiset(select elem from seq where match) } from ...
1591 Type elementType = typeof(IGrouping<,>).MakeGenericType(keyExpr.ClrType, elemExpr.ClrType);
1593 SqlExpression keyGroup = new SqlGrouping(elementType, this.typeProvider.From(elementType), keyExpr, ss, this.dominatingExpression);
1594 SqlSelect keyGroupSel = new SqlSelect(keyGroup, gselAlias, this.dominatingExpression);
1595 SqlAlias kgAlias = new SqlAlias(keyGroupSel);
1596 SqlAliasRef kgAliasRef = new SqlAliasRef(kgAlias);
1598 this.map[resultSelector.Parameters[0]] = sql.Member(kgAliasRef, elementType.GetProperty("Key"));
1599 this.map[resultSelector.Parameters[1]] = kgAliasRef;
1601 // remember the select that has the actual group (for optimizing aggregates later)
1602 this.gmap[kgAliasRef] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource };
1604 SqlExpression resultExpr = this.VisitExpression(resultSelector.Body);
1605 result = new SqlSelect(resultExpr, kgAlias, this.dominatingExpression);
1607 // remember the select that has the actual group (for optimizing aggregates later)
1608 this.gmap[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource };
1611 // Create final select to include construction of group multiset
1612 // select new Grouping { Key = key, Group = Multiset(select elem from seq where match) } from ...
1613 Type elementType = typeof(IGrouping<,>).MakeGenericType(keyExpr.ClrType, elemExpr.ClrType);
1615 SqlExpression resultExpr = new SqlGrouping(elementType, this.typeProvider.From(elementType), keyExpr, ss, this.dominatingExpression);
1616 result = new SqlSelect(resultExpr, gselAlias, this.dominatingExpression);
1618 // remember the select that has the actual group (for optimizing aggregates later)
1619 this.gmap[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource };
1625 [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.")]
1626 private SqlNode VisitAggregate(Expression sequence, LambdaExpression lambda, SqlNodeType aggType, Type returnType) {
1627 // Convert seq.Agg(exp) into
1629 // 1) SELECT Agg(exp) FROM seq
1630 // 2) SELECT Agg1 FROM (SELECT Agg(exp) as Agg1 FROM group-seq GROUP BY ...)
1631 // 3) SCALAR(SELECT Agg(exp) FROM seq)
1633 bool isCount = aggType == SqlNodeType.Count || aggType == SqlNodeType.LongCount;
1635 SqlNode source = this.Visit(sequence);
1636 SqlSelect select = this.CoerceToSequence(source);
1637 SqlAlias alias = new SqlAlias(select);
1638 SqlAliasRef aref = new SqlAliasRef(alias);
1640 // If the sequence is of the form x.Select(expr).Agg() and the lambda for the aggregate is null,
1641 // or is a no-op parameter expression (like u=>u), clone the group by selection lambda
1642 // expression, and use for the aggregate.
1643 // Final form should be x.Agg(expr)
1644 MethodCallExpression mce = sequence as MethodCallExpression;
1645 if (!outerNode && !isCount && (lambda == null || (lambda.Parameters.Count == 1 && lambda.Parameters[0] == lambda.Body)) &&
1646 (mce != null) && IsSequenceOperatorCall(mce, "Select") && select.From is SqlAlias) {
1647 LambdaExpression selectionLambda = GetLambda(mce.Arguments[1]);
1649 lambda = Expression.Lambda(selectionLambda.Type, selectionLambda.Body, selectionLambda.Parameters);
1651 alias = (SqlAlias)select.From;
1652 aref = new SqlAliasRef(alias);
1655 if (lambda != null && !TypeSystem.IsSimpleType(lambda.Body.Type)) {
1656 throw Error.CannotAggregateType(lambda.Body.Type);
1659 //Empty parameter aggregates are not allowed on anonymous types
1660 //i.e. db.Customers.Select(c=>new{c.Age}).Max() instead it should be
1661 // db.Customers.Select(c=>new{c.Age}).Max(c=>c.Age)
1662 if (select.Selection.SqlType.IsRuntimeOnlyType && !IsGrouping(sequence.Type) && !isCount && lambda == null) {
1663 throw Error.NonCountAggregateFunctionsAreNotValidOnProjections(aggType);
1666 this.map[lambda.Parameters[0]] = aref;
1668 if (this.outerNode) {
1669 // If this aggregate is basically the last/outer-most operator of the query
1671 // produce SELECT Agg(exp) FROM seq
1673 SqlExpression exp = (lambda != null) ? this.VisitExpression(lambda.Body) : null;
1674 SqlExpression where = null;
1675 if (isCount && exp != null) {
1679 else if (exp == null && !isCount) {
1683 // in case this contains another aggregate
1684 exp = new SqlSimpleExpression(exp);
1686 SqlSelect sel = new SqlSelect(
1687 this.GetAggregate(aggType, returnType, exp),
1689 this.dominatingExpression
1692 sel.OrderingType = SqlOrderingType.Never;
1695 else if (!isCount || lambda == null) {
1696 // Look to optimize aggregate by pushing its evaluation down to the select node that has the
1697 // actual group-by operator.
1699 // Produce: SELECT Agg1 FROM (SELECT Agg(exp) as Agg1 FROM seq GROUP BY ...)
1701 GroupInfo info = this.FindGroupInfo(source);
1703 SqlExpression exp = null;
1704 if (lambda != null) {
1705 // evaluate expression relative to the group-by select node
1706 this.map[lambda.Parameters[0]] = (SqlExpression)SqlDuplicator.Copy(info.ElementOnGroupSource);
1707 exp = this.VisitExpression(lambda.Body);
1708 } else if (!isCount) {
1709 // support aggregates w/o an explicit selector specified
1710 exp = info.ElementOnGroupSource;
1713 // in case this contains another aggregate
1714 exp = new SqlSimpleExpression(exp);
1716 SqlExpression agg = this.GetAggregate(aggType, returnType, exp);
1717 SqlColumn c = new SqlColumn(agg.ClrType, agg.SqlType, null, null, agg, this.dominatingExpression);
1718 info.SelectWithGroup.Row.Columns.Add(c);
1719 return new SqlColumnRef(c);
1722 // Otherwise, if we cannot optimize then fall back to generating a nested aggregate in a correlated sub query
1724 // SCALAR(SELECT Agg(exp) FROM seq)
1726 SqlExpression exp = (lambda != null) ? this.VisitExpression(lambda.Body) : null;
1728 // in case this contains another aggregate
1729 exp = new SqlSimpleExpression(exp);
1731 SqlSelect sel = new SqlSelect(
1732 this.GetAggregate(aggType, returnType, isCount ? null : (lambda == null) ? aref : exp),
1734 this.dominatingExpression
1736 sel.Where = isCount ? exp : null;
1737 return sql.SubSelect(SqlNodeType.ScalarSubSelect, sel);
1741 private GroupInfo FindGroupInfo(SqlNode source) {
1742 GroupInfo info = null;
1743 this.gmap.TryGetValue(source, out info);
1747 SqlAlias alias = source as SqlAlias;
1748 if (alias != null) {
1749 SqlSelect select = alias.Node as SqlSelect;
1750 if (select != null) {
1751 return this.FindGroupInfo(select.Selection);
1753 // it might be an expression (not yet fully resolved)
1754 source = alias.Node;
1756 SqlExpression expr = source as SqlExpression;
1758 switch (expr.NodeType) {
1759 case SqlNodeType.AliasRef:
1760 return this.FindGroupInfo(((SqlAliasRef)expr).Alias);
1761 case SqlNodeType.Member:
1762 return this.FindGroupInfo(((SqlMember)expr).Expression);
1764 this.gmap.TryGetValue(expr, out info);
1771 private SqlExpression GetAggregate(SqlNodeType aggType, Type clrType, SqlExpression exp) {
1772 ProviderType sqlType = this.typeProvider.From(clrType);
1773 return new SqlUnary(aggType, clrType, sqlType, exp, this.dominatingExpression);
1776 private SqlNode VisitContains(Expression sequence, Expression value) {
1777 Type elemType = TypeSystem.GetElementType(sequence.Type);
1778 SqlNode seqNode = this.Visit(sequence);
1779 if (seqNode.NodeType == SqlNodeType.ClientArray) {
1780 SqlClientArray array = (SqlClientArray)seqNode;
1781 return this.GenerateInExpression(this.VisitExpression(value), array.Expressions);
1783 else if (seqNode.NodeType == SqlNodeType.Value) {
1784 IEnumerable values = ((SqlValue)seqNode).Value as IEnumerable;
1785 IQueryable query = values as IQueryable;
1786 if (query == null) {
1787 SqlExpression expr = this.VisitExpression(value);
1788 List<SqlExpression> list = values.OfType<object>().Select(v => sql.ValueFromObject(v, elemType, true, this.dominatingExpression)).ToList();
1789 return this.GenerateInExpression(expr, list);
1791 seqNode = this.Visit(query.Expression);
1793 ParameterExpression p = Expression.Parameter(value.Type, "p");
1794 LambdaExpression lambda = Expression.Lambda(Expression.Equal(p, value), p);
1795 return this.VisitQuantifier(this.CoerceToSequence(seqNode), lambda, true);
1798 private SqlExpression GenerateInExpression(SqlExpression expr, List<SqlExpression> list) {
1799 if (list.Count == 0) {
1800 return sql.ValueFromObject(false, this.dominatingExpression);
1802 else if (list[0].SqlType.CanBeColumn) {
1803 return sql.In(expr, list, this.dominatingExpression);
1806 SqlExpression pred = sql.Binary(SqlNodeType.EQ, expr, list[0]);
1807 for (int i = 1, n = list.Count; i < n; i++) {
1808 pred = sql.Binary(SqlNodeType.Or, pred, sql.Binary(SqlNodeType.EQ, (SqlExpression)SqlDuplicator.Copy(expr), list[i]));
1814 private SqlNode VisitQuantifier(Expression sequence, LambdaExpression lambda, bool isAny) {
1815 return this.VisitQuantifier(this.VisitSequence(sequence), lambda, isAny);
1818 private SqlNode VisitQuantifier(SqlSelect select, LambdaExpression lambda, bool isAny) {
1819 SqlAlias alias = new SqlAlias(select);
1820 SqlAliasRef aref = new SqlAliasRef(alias);
1821 if (lambda != null) {
1822 this.map[lambda.Parameters[0]] = aref;
1824 SqlExpression cond = lambda != null ? this.VisitExpression(lambda.Body) : null;
1825 return this.GenerateQuantifier(alias, cond, isAny);
1828 private SqlExpression GenerateQuantifier(SqlAlias alias, SqlExpression cond, bool isAny) {
1829 SqlAliasRef aref = new SqlAliasRef(alias);
1831 SqlSelect sel = new SqlSelect(aref, alias, this.dominatingExpression);
1833 sel.OrderingType = SqlOrderingType.Never;
1834 SqlSubSelect exists = sql.SubSelect(SqlNodeType.Exists, sel);
1838 SqlSelect sel = new SqlSelect(aref, alias, this.dominatingExpression);
1839 SqlSubSelect ss = sql.SubSelect(SqlNodeType.Exists, sel);
1840 sel.Where = sql.Unary(SqlNodeType.Not2V, cond, this.dominatingExpression);
1841 return sql.Unary(SqlNodeType.Not, ss, this.dominatingExpression);
1845 private void CheckContext(SqlExpression expr) {
1846 // try to catch use of incorrect context if we can
1847 SqlValue value = expr as SqlValue;
1848 if (value != null) {
1849 DataContext dc = value.Value as DataContext;
1851 if (dc != this.services.Context) {
1852 throw Error.WrongDataContext();
1858 private SqlNode VisitMemberAccess(MemberExpression ma) {
1859 Type memberType = TypeSystem.GetMemberType(ma.Member);
1860 if (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(Table<>)) {
1861 Type rowType = memberType.GetGenericArguments()[0];
1862 CheckContext(this.VisitExpression(ma.Expression));
1863 ITable table = this.services.Context.GetTable(rowType);
1865 return this.Visit(Expression.Constant(table));
1867 if (ma.Member.Name == "Count" && TypeSystem.IsSequenceType(ma.Expression.Type)) {
1868 return this.VisitAggregate(ma.Expression, null, SqlNodeType.Count, typeof(int));
1871 return sql.Member(VisitExpression(ma.Expression), ma.Member);
1874 [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.")]
1875 private SqlNode VisitMethodCall(MethodCallExpression mc) {
1876 Type declType = mc.Method.DeclaringType;
1877 if (mc.Method.IsStatic) {
1878 if (this.IsSequenceOperatorCall(mc)) {
1879 return this.VisitSequenceOperatorCall(mc);
1881 else if (IsDataManipulationCall(mc)) {
1882 return this.VisitDataManipulationCall(mc);
1884 // why is this handled here and not in SqlMethodCallConverter?
1885 else if (declType == typeof(DBConvert) || declType == typeof(Convert)) {
1886 if (mc.Method.Name == "ChangeType") {
1888 if (mc.Arguments.Count == 2) {
1889 object value = GetValue(mc.Arguments[1], "ChangeType");
1890 if (value != null && typeof(Type).IsAssignableFrom(value.GetType())) {
1891 sn = this.VisitChangeType(mc.Arguments[0], (Type)value);
1895 throw Error.MethodFormHasNoSupportConversionToSql(mc.Method.Name, mc.Method);
1901 else if (typeof(DataContext).IsAssignableFrom(mc.Method.DeclaringType)) {
1902 switch (mc.Method.Name) {
1904 // calls to GetTable<T> can be translated directly as table references
1905 if (mc.Method.IsGenericMethod) {
1906 Type[] typeArgs = mc.Method.GetGenericArguments();
1907 if (typeArgs.Length == 1 && mc.Method.GetParameters().Length == 0) {
1908 CheckContext(this.VisitExpression(mc.Object));
1909 ITable table = this.services.Context.GetTable(typeArgs[0]);
1910 if (table != null) {
1911 return this.Visit(Expression.Constant(table));
1917 case "ExecuteCommand":
1918 case "ExecuteQuery":
1919 return this.VisitUserQuery((string)GetValue(mc.Arguments[0], mc.Method.Name), GetArray(mc.Arguments[1]), mc.Type);
1922 if (this.IsMappedFunctionCall(mc)) {
1923 return this.VisitMappedFunctionCall(mc);
1927 mc.Method.DeclaringType != typeof(string)
1928 && mc.Method.Name == "Contains"
1929 && !mc.Method.IsStatic
1930 && typeof(IList).IsAssignableFrom(mc.Method.DeclaringType)
1931 && mc.Type == typeof(bool)
1932 && mc.Arguments.Count == 1
1933 && TypeSystem.GetElementType(mc.Method.DeclaringType).IsAssignableFrom(mc.Arguments[0].Type)
1935 return this.VisitContains(mc.Object, mc.Arguments[0]);
1938 // default: create sql method call node instead
1939 SqlExpression obj = VisitExpression(mc.Object);
1940 SqlExpression[] args = new SqlExpression[mc.Arguments.Count];
1941 for (int i = 0, n = args.Length; i < n; i++) {
1942 args[i] = VisitExpression(mc.Arguments[i]);
1944 return sql.MethodCall(mc.Method, obj, args, dominatingExpression);
1947 private object GetValue(Expression expression, string operation) {
1948 SqlExpression exp = this.VisitExpression(expression);
1949 if (exp.NodeType == SqlNodeType.Value) {
1950 return ((SqlValue)exp).Value;
1952 throw Error.NonConstantExpressionsNotSupportedFor(operation);
1955 private static Expression[] GetArray(Expression array) {
1956 NewArrayExpression n = array as NewArrayExpression;
1958 return n.Expressions.ToArray();
1960 ConstantExpression c = array as ConstantExpression;
1962 object[] obs = c.Value as object[];
1964 Type elemType = TypeSystem.GetElementType(c.Type);
1965 return obs.Select(o => Expression.Constant(o, elemType)).ToArray();
1968 return new Expression[] { };
1971 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
1972 private Expression RemoveQuotes(Expression expression) {
1973 while (expression.NodeType == ExpressionType.Quote) {
1974 expression = ((UnaryExpression)expression).Operand;
1979 private bool IsLambda(Expression expression) {
1980 return this.RemoveQuotes(expression).NodeType == ExpressionType.Lambda;
1983 private LambdaExpression GetLambda(Expression expression) {
1984 return this.RemoveQuotes(expression) as LambdaExpression;
1987 private bool IsMappedFunctionCall(MethodCallExpression mc) {
1988 MetaFunction function = services.Model.GetFunction(mc.Method);
1989 return function != null;
1992 private SqlNode VisitMappedFunctionCall(MethodCallExpression mc) {
1993 // See if the method maps to a user defined function
1994 MetaFunction function = services.Model.GetFunction(mc.Method);
1995 System.Diagnostics.Debug.Assert(function != null);
1996 CheckContext(this.VisitExpression(mc.Object));
1997 if (!function.IsComposable) {
1998 return this.TranslateStoredProcedureCall(mc, function);
2000 else if (function.ResultRowTypes.Count > 0) {
2001 return this.TranslateTableValuedFunction(mc, function);
2004 ProviderType sqlType = function.ReturnParameter != null && !string.IsNullOrEmpty(function.ReturnParameter.DbType)
2005 ? this.typeProvider.Parse(function.ReturnParameter.DbType)
2006 : this.typeProvider.From(mc.Method.ReturnType);
2007 List<SqlExpression> sqlParams = this.GetFunctionParameters(mc, function);
2008 return sql.FunctionCall(mc.Method.ReturnType, sqlType, function.MappedName, sqlParams, mc);
2012 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
2013 private bool IsSequenceOperatorCall(MethodCallExpression mc) {
2014 Type declType = mc.Method.DeclaringType;
2015 if (declType == typeof(System.Linq.Enumerable) ||
2016 declType == typeof(System.Linq.Queryable)) {
2022 private bool IsSequenceOperatorCall(MethodCallExpression mc, string methodName) {
2023 if (IsSequenceOperatorCall(mc) && mc.Method.Name == methodName) {
2029 [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", 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.")]
2030 [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.")]
2031 private SqlNode VisitSequenceOperatorCall(MethodCallExpression mc) {
2032 Type declType = mc.Method.DeclaringType;
2033 bool isSupportedSequenceOperator = false;
2034 if (IsSequenceOperatorCall(mc)) {
2035 switch (mc.Method.Name) {
2037 isSupportedSequenceOperator = true;
2038 if (mc.Arguments.Count == 2 &&
2039 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2040 return this.VisitSelect(mc.Arguments[0], this.GetLambda(mc.Arguments[1]));
2044 isSupportedSequenceOperator = true;
2045 if (mc.Arguments.Count == 2 &&
2046 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2047 return this.VisitSelectMany(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), null);
2049 else if (mc.Arguments.Count == 3 &&
2050 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1 &&
2051 this.IsLambda(mc.Arguments[2]) && this.GetLambda(mc.Arguments[2]).Parameters.Count == 2) {
2052 return this.VisitSelectMany(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), this.GetLambda(mc.Arguments[2]));
2056 isSupportedSequenceOperator = true;
2057 if (mc.Arguments.Count == 5 &&
2058 this.IsLambda(mc.Arguments[2]) && this.GetLambda(mc.Arguments[2]).Parameters.Count == 1 &&
2059 this.IsLambda(mc.Arguments[3]) && this.GetLambda(mc.Arguments[3]).Parameters.Count == 1 &&
2060 this.IsLambda(mc.Arguments[4]) && this.GetLambda(mc.Arguments[4]).Parameters.Count == 2) {
2061 return this.VisitJoin(mc.Arguments[0], mc.Arguments[1], this.GetLambda(mc.Arguments[2]), this.GetLambda(mc.Arguments[3]), this.GetLambda(mc.Arguments[4]));
2065 isSupportedSequenceOperator = true;
2066 if (mc.Arguments.Count == 5 &&
2067 this.IsLambda(mc.Arguments[2]) && this.GetLambda(mc.Arguments[2]).Parameters.Count == 1 &&
2068 this.IsLambda(mc.Arguments[3]) && this.GetLambda(mc.Arguments[3]).Parameters.Count == 1 &&
2069 this.IsLambda(mc.Arguments[4]) && this.GetLambda(mc.Arguments[4]).Parameters.Count == 2) {
2070 return this.VisitGroupJoin(mc.Arguments[0], mc.Arguments[1], this.GetLambda(mc.Arguments[2]), this.GetLambda(mc.Arguments[3]), this.GetLambda(mc.Arguments[4]));
2073 case "DefaultIfEmpty":
2074 isSupportedSequenceOperator = true;
2075 if (mc.Arguments.Count == 1) {
2076 return this.VisitDefaultIfEmpty(mc.Arguments[0]);
2080 isSupportedSequenceOperator = true;
2081 if (mc.Arguments.Count == 1) {
2082 Type ofType = mc.Method.GetGenericArguments()[0];
2083 return this.VisitOfType(mc.Arguments[0], ofType);
2087 isSupportedSequenceOperator = true;
2088 if (mc.Arguments.Count == 1) {
2089 Type type = mc.Method.GetGenericArguments()[0];
2090 return this.VisitSequenceCast(mc.Arguments[0], type);
2094 isSupportedSequenceOperator = true;
2095 if (mc.Arguments.Count == 2 &&
2096 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2097 return this.VisitWhere(mc.Arguments[0], this.GetLambda(mc.Arguments[1]));
2101 case "FirstOrDefault":
2102 isSupportedSequenceOperator = true;
2103 if (mc.Arguments.Count == 1) {
2104 return this.VisitFirst(mc.Arguments[0], null, true);
2106 else if (mc.Arguments.Count == 2 &&
2107 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2108 return this.VisitFirst(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), true);
2112 case "SingleOrDefault":
2113 isSupportedSequenceOperator = true;
2114 if (mc.Arguments.Count == 1) {
2115 return this.VisitFirst(mc.Arguments[0], null, false);
2117 else if (mc.Arguments.Count == 2 &&
2118 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2119 return this.VisitFirst(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), false);
2123 isSupportedSequenceOperator = true;
2124 if (mc.Arguments.Count == 1) {
2125 return this.VisitDistinct(mc.Arguments[0]);
2129 isSupportedSequenceOperator = true;
2130 if (mc.Arguments.Count == 2) {
2131 return this.VisitConcat(mc.Arguments[0], mc.Arguments[1]);
2135 isSupportedSequenceOperator = true;
2136 if (mc.Arguments.Count == 2) {
2137 return this.VisitUnion(mc.Arguments[0], mc.Arguments[1]);
2141 isSupportedSequenceOperator = true;
2142 if (mc.Arguments.Count == 2) {
2143 return this.VisitIntersect(mc.Arguments[0], mc.Arguments[1]);
2147 isSupportedSequenceOperator = true;
2148 if (mc.Arguments.Count == 2) {
2149 return this.VisitExcept(mc.Arguments[0], mc.Arguments[1]);
2153 isSupportedSequenceOperator = true;
2154 if (mc.Arguments.Count == 1) {
2155 return this.VisitQuantifier(mc.Arguments[0], null, true);
2157 else if (mc.Arguments.Count == 2 &&
2158 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2159 return this.VisitQuantifier(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), true);
2163 isSupportedSequenceOperator = true;
2164 if (mc.Arguments.Count == 2 &&
2165 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2166 return this.VisitQuantifier(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), false);
2170 isSupportedSequenceOperator = true;
2171 if (mc.Arguments.Count == 1) {
2172 return this.VisitAggregate(mc.Arguments[0], null, SqlNodeType.Count, mc.Type);
2174 else if (mc.Arguments.Count == 2 &&
2175 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2176 return this.VisitAggregate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlNodeType.Count, mc.Type);
2180 isSupportedSequenceOperator = true;
2181 if (mc.Arguments.Count == 1) {
2182 return this.VisitAggregate(mc.Arguments[0], null, SqlNodeType.LongCount, mc.Type);
2184 else if (mc.Arguments.Count == 2 &&
2185 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2186 return this.VisitAggregate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlNodeType.LongCount, mc.Type);
2190 isSupportedSequenceOperator = true;
2191 if (mc.Arguments.Count == 1) {
2192 return this.VisitAggregate(mc.Arguments[0], null, SqlNodeType.Sum, mc.Type);
2194 else if (mc.Arguments.Count == 2 &&
2195 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2196 return this.VisitAggregate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlNodeType.Sum, mc.Type);
2200 isSupportedSequenceOperator = true;
2201 if (mc.Arguments.Count == 1) {
2202 return this.VisitAggregate(mc.Arguments[0], null, SqlNodeType.Min, mc.Type);
2204 else if (mc.Arguments.Count == 2 &&
2205 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2206 return this.VisitAggregate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlNodeType.Min, mc.Type);
2210 isSupportedSequenceOperator = true;
2211 if (mc.Arguments.Count == 1) {
2212 return this.VisitAggregate(mc.Arguments[0], null, SqlNodeType.Max, mc.Type);
2214 else if (mc.Arguments.Count == 2 &&
2215 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2216 return this.VisitAggregate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlNodeType.Max, mc.Type);
2220 isSupportedSequenceOperator = true;
2221 if (mc.Arguments.Count == 1) {
2222 return this.VisitAggregate(mc.Arguments[0], null, SqlNodeType.Avg, mc.Type);
2224 else if (mc.Arguments.Count == 2 &&
2225 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2226 return this.VisitAggregate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlNodeType.Avg, mc.Type);
2230 isSupportedSequenceOperator = true;
2231 if (mc.Arguments.Count == 2 &&
2232 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2233 return this.VisitGroupBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), null, null);
2235 else if (mc.Arguments.Count == 3 &&
2236 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1 &&
2237 this.IsLambda(mc.Arguments[2]) && this.GetLambda(mc.Arguments[2]).Parameters.Count == 1) {
2238 return this.VisitGroupBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), this.GetLambda(mc.Arguments[2]), null);
2240 else if (mc.Arguments.Count == 3 &&
2241 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1 &&
2242 this.IsLambda(mc.Arguments[2]) && this.GetLambda(mc.Arguments[2]).Parameters.Count == 2) {
2243 return this.VisitGroupBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), null, this.GetLambda(mc.Arguments[2]));
2245 else if (mc.Arguments.Count == 4 &&
2246 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1 &&
2247 this.IsLambda(mc.Arguments[2]) && this.GetLambda(mc.Arguments[2]).Parameters.Count == 1 &&
2248 this.IsLambda(mc.Arguments[3]) && this.GetLambda(mc.Arguments[3]).Parameters.Count == 2) {
2249 return this.VisitGroupBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), this.GetLambda(mc.Arguments[2]), this.GetLambda(mc.Arguments[3]));
2253 isSupportedSequenceOperator = true;
2254 if (mc.Arguments.Count == 2 &&
2255 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2256 return this.VisitOrderBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlOrderType.Ascending);
2259 case "OrderByDescending":
2260 isSupportedSequenceOperator = true;
2261 if (mc.Arguments.Count == 2 &&
2262 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2263 return this.VisitOrderBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlOrderType.Descending);
2267 isSupportedSequenceOperator = true;
2268 if (mc.Arguments.Count == 2 &&
2269 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2270 return this.VisitThenBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlOrderType.Ascending);
2273 case "ThenByDescending":
2274 isSupportedSequenceOperator = true;
2275 if (mc.Arguments.Count == 2 &&
2276 this.IsLambda(mc.Arguments[1]) && this.GetLambda(mc.Arguments[1]).Parameters.Count == 1) {
2277 return this.VisitThenBy(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), SqlOrderType.Descending);
2281 isSupportedSequenceOperator = true;
2282 if (mc.Arguments.Count == 2) {
2283 return this.VisitTake(mc.Arguments[0], mc.Arguments[1]);
2287 isSupportedSequenceOperator = true;
2288 if (mc.Arguments.Count == 2) {
2289 return this.VisitSkip(mc.Arguments[0], mc.Arguments[1]);
2293 isSupportedSequenceOperator = true;
2294 if (mc.Arguments.Count == 2) {
2295 return this.VisitContains(mc.Arguments[0], mc.Arguments[1]);
2299 case "AsEnumerable":
2301 isSupportedSequenceOperator = true;
2302 if (mc.Arguments.Count == 1) {
2303 return this.Visit(mc.Arguments[0]);
2307 // If the operator is supported, but the particular overload is not,
2308 // give an appropriate error message
2309 if (isSupportedSequenceOperator) {
2310 throw Error.QueryOperatorOverloadNotSupported(mc.Method.Name);
2312 throw Error.QueryOperatorNotSupported(mc.Method.Name);
2315 throw Error.InvalidSequenceOperatorCall(declType);
2319 private static bool IsDataManipulationCall(MethodCallExpression mc) {
2320 return mc.Method.IsStatic && mc.Method.DeclaringType == typeof(DataManipulation);
2323 private SqlNode VisitDataManipulationCall(MethodCallExpression mc) {
2324 if (IsDataManipulationCall(mc)) {
2325 bool isSupportedDML = false;
2326 switch (mc.Method.Name) {
2328 isSupportedDML = true;
2329 if (mc.Arguments.Count == 2) {
2330 return this.VisitInsert(mc.Arguments[0], this.GetLambda(mc.Arguments[1]));
2332 else if (mc.Arguments.Count == 1) {
2333 return this.VisitInsert(mc.Arguments[0], null);
2337 isSupportedDML = true;
2338 if (mc.Arguments.Count == 3) {
2339 return this.VisitUpdate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), this.GetLambda(mc.Arguments[2]));
2341 else if (mc.Arguments.Count == 2) {
2342 if (mc.Method.GetGenericArguments().Length == 1) {
2343 return this.VisitUpdate(mc.Arguments[0], this.GetLambda(mc.Arguments[1]), null);
2346 return this.VisitUpdate(mc.Arguments[0], null, this.GetLambda(mc.Arguments[1]));
2349 else if (mc.Arguments.Count == 1) {
2350 return this.VisitUpdate(mc.Arguments[0], null, null);
2354 isSupportedDML = true;
2355 if (mc.Arguments.Count == 2) {
2356 return this.VisitDelete(mc.Arguments[0], this.GetLambda(mc.Arguments[1]));
2358 else if (mc.Arguments.Count == 1) {
2359 return this.VisitDelete(mc.Arguments[0], null);
2363 if (isSupportedDML) {
2364 throw Error.QueryOperatorOverloadNotSupported(mc.Method.Name);
2366 throw Error.QueryOperatorNotSupported(mc.Method.Name);
2368 throw Error.InvalidSequenceOperatorCall(mc.Method.Name);
2371 private SqlNode VisitFirst(Expression sequence, LambdaExpression lambda, bool isFirst) {
2372 SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
2373 if (lambda != null) {
2374 this.map[lambda.Parameters[0]] = (SqlAliasRef)select.Selection;
2375 select.Where = this.VisitExpression(lambda.Body);
2378 select.Top = this.sql.ValueFromObject(1, false, this.dominatingExpression);
2380 if (this.outerNode) {
2383 SqlNodeType subType = (this.typeProvider.From(select.Selection.ClrType).CanBeColumn) ? SqlNodeType.ScalarSubSelect : SqlNodeType.Element;
2384 SqlSubSelect elem = sql.SubSelect(subType, select, sequence.Type);
2388 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", 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.")]
2389 private SqlStatement VisitInsert(Expression item, LambdaExpression resultSelector) {
2391 throw Error.ArgumentNull("item");
2393 this.dominatingExpression = item;
2395 MetaTable metaTable = this.services.Model.GetTable(item.Type);
2396 Expression source = this.services.Context.GetTable(metaTable.RowType.Type).Expression;
2398 MetaType itemMetaType = null;
2399 SqlNew sqlItem = null;
2401 // construct insert assignments from 'item' info
2402 ConstantExpression conItem = item as ConstantExpression;
2403 if (conItem == null) {
2404 throw Error.InsertItemMustBeConstant();
2406 if (conItem.Value == null) {
2407 throw Error.ArgumentNull("item");
2409 // construct insert based on constant value
2410 List<SqlMemberAssign> bindings = new List<SqlMemberAssign>();
2411 itemMetaType = metaTable.RowType.GetInheritanceType(conItem.Value.GetType());
2412 SqlExpression sqlExprItem = sql.ValueFromObject(conItem.Value, true, source);
2413 foreach (MetaDataMember mm in itemMetaType.PersistentDataMembers) {
2414 if (!mm.IsAssociation && !mm.IsDbGenerated && !mm.IsVersion) {
2415 bindings.Add(new SqlMemberAssign(mm.Member, sql.Member(sqlExprItem, mm.Member)));
2418 ConstructorInfo cons = itemMetaType.Type.GetConstructor(Type.EmptyTypes);
2419 System.Diagnostics.Debug.Assert(cons != null);
2420 sqlItem = sql.New(itemMetaType, cons, null, null, bindings, item);
2422 SqlTable tab = sql.Table(metaTable, metaTable.RowType, this.dominatingExpression);
2423 SqlInsert sin = new SqlInsert(tab, sqlItem, item);
2425 if (resultSelector == null) {
2429 MetaDataMember id = itemMetaType.DBGeneratedIdentityMember;
2430 bool isDbGenOnly = false;
2432 isDbGenOnly = this.IsDbGeneratedKeyProjectionOnly(resultSelector.Body, id);
2433 if (id.Type == typeof(Guid) && (this.converterStrategy & ConverterStrategy.CanOutputFromInsert) != 0) {
2434 sin.OutputKey = new SqlColumn(id.Type, sql.Default(id), id.Name, id, null, this.dominatingExpression);
2436 sin.OutputToLocal = true;
2441 SqlSelect result = null;
2442 SqlSelect preResult = null;
2443 SqlAlias tableAlias = new SqlAlias(tab);
2444 SqlAliasRef tableAliasRef = new SqlAliasRef(tableAlias);
2445 System.Diagnostics.Debug.Assert(resultSelector.Parameters.Count == 1);
2446 this.map.Add(resultSelector.Parameters[0], tableAliasRef);
2447 SqlExpression projection = this.VisitExpression(resultSelector.Body);
2449 // build select to return result
2450 SqlExpression pred = null;
2454 sql.Member(tableAliasRef, id.Member),
2455 this.GetIdentityExpression(id, sin.OutputKey != null)
2459 SqlExpression itemExpression = this.VisitExpression(item);
2460 pred = sql.Binary(SqlNodeType.EQ2V, tableAliasRef, itemExpression);
2462 result = new SqlSelect(projection, tableAlias, resultSelector);
2463 result.Where = pred;
2465 // Since we're only projecting back a single generated key, we can
2466 // optimize the query to a simple selection (e.g. SELECT @@IDENTITY)
2467 // rather than selecting back from the table.
2468 if (id != null && isDbGenOnly) {
2469 if (sin.OutputKey == null) {
2470 SqlExpression exp = this.GetIdentityExpression(id, false);
2471 if (exp.ClrType != id.Type) {
2472 ProviderType sqlType = sql.Default(id);
2473 exp = sql.ConvertTo(id.Type, sqlType, exp);
2475 // The result selector passed in was bound to the table -
2476 // we need to rebind to the single result as an array projection
2477 ParameterExpression p = Expression.Parameter(id.Type, "p");
2478 Expression[] init = new Expression[1] { Expression.Convert(p, typeof(object)) };
2479 NewArrayExpression arrExp = Expression.NewArrayInit(typeof(object), init);
2480 LambdaExpression rs = Expression.Lambda(arrExp, p);
2481 this.map.Add(p, exp);
2482 SqlExpression proj = this.VisitExpression(rs.Body);
2483 preResult = new SqlSelect(proj, null, rs);
2486 // case handled in formatter automatically
2488 result.DoNotOutput = true;
2491 // combine insert & result into block
2492 SqlBlock block = new SqlBlock(this.dominatingExpression);
2493 block.Statements.Add(sin);
2494 if (preResult != null) {
2495 block.Statements.Add(preResult);
2497 block.Statements.Add(result);
2502 private bool IsDbGeneratedKeyProjectionOnly(Expression projection, MetaDataMember keyMember) {
2503 NewArrayExpression array = projection as NewArrayExpression;
2504 if (array != null && array.Expressions.Count == 1) {
2505 Expression exp = array.Expressions[0];
2506 while (exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked) {
2507 exp = ((UnaryExpression)exp).Operand;
2509 MemberExpression mex = exp as MemberExpression;
2510 if (mex != null && mex.Member == keyMember.Member) {
2517 private SqlExpression GetIdentityExpression(MetaDataMember id, bool isOutputFromInsert) {
2518 if (isOutputFromInsert) {
2519 return new SqlVariable(id.Type, sql.Default(id), "@id", this.dominatingExpression);
2522 ProviderType sqlType = sql.Default(id);
2523 if (!IsLegalIdentityType(sqlType.GetClosestRuntimeType())) {
2524 throw Error.InvalidDbGeneratedType(sqlType.ToQueryString());
2526 if ((this.converterStrategy & ConverterStrategy.CanUseScopeIdentity) != 0) {
2527 return new SqlVariable(typeof(decimal), typeProvider.From(typeof(decimal)), "SCOPE_IDENTITY()", this.dominatingExpression);
2530 return new SqlVariable(typeof(decimal), typeProvider.From(typeof(decimal)), "@@IDENTITY", this.dominatingExpression);
2535 private static bool IsLegalIdentityType(Type type) {
2536 switch (Type.GetTypeCode(type)) {
2537 case TypeCode.SByte:
2538 case TypeCode.Int16:
2539 case TypeCode.Int32:
2540 case TypeCode.Int64:
2541 case TypeCode.Decimal:
2547 private SqlExpression GetRowCountExpression() {
2548 if ((this.converterStrategy & ConverterStrategy.CanUseRowStatus) != 0) {
2549 return new SqlVariable(typeof(decimal), typeProvider.From(typeof(decimal)), "@@ROWCOUNT", this.dominatingExpression);
2552 return new SqlVariable(typeof(decimal), typeProvider.From(typeof(decimal)), "@ROWCOUNT", this.dominatingExpression);
2556 private SqlStatement VisitUpdate(Expression item, LambdaExpression check, LambdaExpression resultSelector) {
2558 throw Error.ArgumentNull("item");
2560 MetaTable metaTable = this.services.Model.GetTable(item.Type);
2561 Expression source = this.services.Context.GetTable(metaTable.RowType.Type).Expression;
2562 Type rowType = metaTable.RowType.Type;
2564 bool saveAllowDeferred = this.allowDeferred;
2565 this.allowDeferred = false;
2568 Expression seq = source;
2569 // construct identity predicate based on supplied item
2570 ParameterExpression p = Expression.Parameter(rowType, "p");
2571 LambdaExpression idPredicate = Expression.Lambda(Expression.Equal(p, item), p);
2573 // combine predicate and check expression into single find predicate
2574 LambdaExpression findPredicate = idPredicate;
2575 if (check != null) {
2576 findPredicate = Expression.Lambda(Expression.And(Expression.Invoke(findPredicate, p), Expression.Invoke(check, p)), p);
2578 seq = Expression.Call(typeof(Enumerable), "Where", new Type[] { rowType }, seq, findPredicate);
2580 // source 'query' is based on table + find predicate
2581 SqlSelect ss = new RetypeCheckClause().VisitSelect(this.VisitSequence(seq));
2583 // construct update assignments from 'item' info
2584 List<SqlAssign> assignments = new List<SqlAssign>();
2585 ConstantExpression conItem = item as ConstantExpression;
2586 if (conItem == null) {
2587 throw Error.UpdateItemMustBeConstant();
2589 if (conItem.Value == null) {
2590 throw Error.ArgumentNull("item");
2592 // get changes from data services to construct update command
2593 Type entityType = conItem.Value.GetType();
2594 MetaType metaType = this.services.Model.GetMetaType(entityType);
2595 ITable table = this.services.Context.GetTable(metaType.InheritanceRoot.Type);
2596 foreach (ModifiedMemberInfo mmi in table.GetModifiedMembers(conItem.Value)) {
2597 MetaDataMember mdm = metaType.GetDataMember(mmi.Member);
2600 sql.Member(ss.Selection, mmi.Member),
2601 new SqlValue(mdm.Type, this.typeProvider.From(mdm.Type), mmi.CurrentValue, true, source),
2606 SqlUpdate upd = new SqlUpdate(ss, assignments, source);
2608 if (resultSelector == null) {
2612 SqlSelect select = null;
2614 // build select to return result
2616 seq = Expression.Call(typeof(Enumerable), "Where", new Type[] { rowType }, seq, idPredicate);
2617 seq = Expression.Call(typeof(Enumerable), "Select", new Type[] { rowType, resultSelector.Body.Type }, seq, resultSelector);
2618 select = this.VisitSequence(seq);
2619 select.Where = sql.AndAccumulate(
2620 sql.Binary(SqlNodeType.GT, this.GetRowCountExpression(), sql.ValueFromObject(0, false, this.dominatingExpression)),
2624 // combine update & select into statement block
2625 SqlBlock block = new SqlBlock(source);
2626 block.Statements.Add(upd);
2627 block.Statements.Add(select);
2631 this.allowDeferred = saveAllowDeferred;
2635 private SqlStatement VisitDelete(Expression item, LambdaExpression check) {
2637 throw Error.ArgumentNull("item");
2640 bool saveAllowDeferred = this.allowDeferred;
2641 this.allowDeferred = false;
2644 MetaTable metaTable = this.services.Model.GetTable(item.Type);
2645 Expression source = this.services.Context.GetTable(metaTable.RowType.Type).Expression;
2646 Type rowType = metaTable.RowType.Type;
2648 // construct identity predicate based on supplied item
2649 ParameterExpression p = Expression.Parameter(rowType, "p");
2650 LambdaExpression idPredicate = Expression.Lambda(Expression.Equal(p, item), p);
2652 // combine predicate and check expression into single find predicate
2653 LambdaExpression findPredicate = idPredicate;
2654 if (check != null) {
2655 findPredicate = Expression.Lambda(Expression.And(Expression.Invoke(findPredicate, p), Expression.Invoke(check, p)), p);
2657 Expression seq = Expression.Call(typeof(Enumerable), "Where", new Type[] { rowType }, source, findPredicate);
2658 SqlSelect ss = new RetypeCheckClause().VisitSelect(this.VisitSequence(seq));
2659 this.allowDeferred = saveAllowDeferred;
2661 SqlDelete sd = new SqlDelete(ss, source);
2665 this.allowDeferred = saveAllowDeferred;
2668 private class RetypeCheckClause : SqlVisitor {
2669 internal override SqlExpression VisitMethodCall(SqlMethodCall mc) {
2670 if (mc.Arguments.Count==2 && mc.Method.Name=="op_Equality") {
2671 var r = mc.Arguments[1];
2672 if (r.NodeType == SqlNodeType.Value) {
2673 var v = (SqlValue)r;
2674 v.SetSqlType(mc.Arguments[0].SqlType);
2677 return base.VisitMethodCall(mc);
2682 private SqlExpression VisitNewArrayInit(NewArrayExpression arr) {
2683 SqlExpression[] exprs = new SqlExpression[arr.Expressions.Count];
2684 for (int i = 0, n = exprs.Length; i < n; i++) {
2685 exprs[i] = this.VisitExpression(arr.Expressions[i]);
2687 return new SqlClientArray(arr.Type, this.typeProvider.From(arr.Type), exprs, this.dominatingExpression);
2690 private SqlExpression VisitListInit(ListInitExpression list) {
2691 if (null != list.NewExpression.Constructor && 0 != list.NewExpression.Arguments.Count) {
2692 // Throw existing exception for unrecognized expressions if list
2693 // init does not use a default constructor.
2694 throw Error.UnrecognizedExpressionNode(list.NodeType);
2696 SqlExpression[] exprs = new SqlExpression[list.Initializers.Count];
2697 for (int i = 0, n = exprs.Length; i < n; i++) {
2698 if (1 != list.Initializers[i].Arguments.Count) {
2699 // Throw existing exception for unrecognized expressions if element
2700 // init is not adding a single element.
2701 throw Error.UnrecognizedExpressionNode(list.NodeType);
2703 exprs[i] = this.VisitExpression(list.Initializers[i].Arguments.Single());
2705 return new SqlClientArray(list.Type, this.typeProvider.From(list.Type), exprs, this.dominatingExpression);
2709 class SingleTableQueryVisitor : SqlVisitor {
2710 public bool IsValid;
2712 List<MemberInfo> IdentityMembers;
2714 void AddIdentityMembers(IEnumerable<MemberInfo> members) {
2715 System.Diagnostics.Debug.Assert(this.IdentityMembers == null, "We already have a set of keys -- why are we adding more?");
2716 this.IdentityMembers = new List<MemberInfo>(members);
2719 internal SingleTableQueryVisitor(): base() {
2720 this.IsValid = true;
2723 internal override SqlNode Visit(SqlNode node) {
2724 // recurse until we know we're invalid
2725 if (this.IsValid && node != null) {
2726 return base.Visit(node);
2732 internal override SqlTable VisitTable(SqlTable tab) {
2733 // if we're distinct, we don't care about joins
2734 if (this.IsDistinct) {
2738 if (this.IdentityMembers != null) {
2739 this.IsValid = false;
2741 this.AddIdentityMembers(tab.MetaTable.RowType.IdentityMembers.Select(m => m.Member));
2747 internal override SqlSource VisitSource(SqlSource source) {
2748 return base.VisitSource(source);
2751 internal override SqlSelect VisitSelect(SqlSelect select) {
2752 if (select.IsDistinct) {
2753 this.IsDistinct = true;
2754 // get all members from selection
2755 this.AddIdentityMembers(select.Selection.ClrType.GetProperties());
2765 // We're not distinct, but let's check our sources...
2766 select.From = (SqlSource)base.Visit(select.From);
2768 if (this.IdentityMembers == null || this.IdentityMembers.Count == 0) {
2769 throw Error.SkipRequiresSingleTableQueryWithPKs();
2772 switch (select.Selection.NodeType) {
2773 case SqlNodeType.Column:
2774 case SqlNodeType.ColumnRef:
2775 case SqlNodeType.Member: {
2776 // we've got a bare member/column node, eg "select c.CustomerId"
2777 // find out if it refers to the table's PK, of which there must be only 1
2778 if (this.IdentityMembers.Count == 1) {
2779 MemberInfo column = this.IdentityMembers[0];
2780 this.IsValid &= IsColumnMatch(column, select.Selection);
2783 this.IsValid = false;
2788 case SqlNodeType.New:
2789 case SqlNodeType.AliasRef: {
2790 select.Selection = this.VisitExpression(select.Selection);
2793 case SqlNodeType.Treat:
2794 case SqlNodeType.TypeCase: {
2798 this.IsValid = false;
2813 internal override SqlExpression VisitNew(SqlNew sox) {
2814 // check the args for the PKs
2815 foreach (MemberInfo column in this.IdentityMembers) {
2816 // assume we're invalid unless we find a matching argument which is
2817 // a bare column/columnRef to the PK
2818 bool isMatch = false;
2820 // find a matching arg
2821 foreach (SqlExpression expr in sox.Args) {
2822 isMatch = IsColumnMatch(column, expr);
2830 foreach (SqlMemberAssign ma in sox.Members) {
2831 SqlExpression expr = ma.Expression;
2833 isMatch = IsColumnMatch(column, expr);
2841 this.IsValid &= isMatch;
2842 if (!this.IsValid) {
2850 internal override SqlNode VisitUnion(SqlUnion su) {
2851 // we don't want to descend inward
2853 // just check that it's not a UNION ALL
2855 this.IsValid = false;
2858 // UNIONs are distinct
2859 this.IsDistinct = true;
2861 // get all members from selection
2862 this.AddIdentityMembers(su.GetClrType().GetProperties());
2866 private static bool IsColumnMatch(MemberInfo column, SqlExpression expr) {
2867 MemberInfo memberInfo = null;
2869 switch (expr.NodeType) {
2870 case SqlNodeType.Column: {
2871 memberInfo = ((SqlColumn)expr).MetaMember.Member;
2874 case SqlNodeType.ColumnRef: {
2875 memberInfo = (((SqlColumnRef)expr).Column).MetaMember.Member;
2878 case SqlNodeType.Member: {
2879 memberInfo = ((SqlMember)expr).Member;
2884 return (memberInfo != null && memberInfo == column);