1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
5 using System.Linq.Expressions;
6 using System.Data.Linq;
7 
8 namespace System.Data.Linq.SqlClient {
9     using System.Data.Linq.Mapping;
10     using System.Data.Linq.Provider;
11     using System.Diagnostics.CodeAnalysis;
12     using System.Diagnostics;
13 
14     /// <summary>
15     /// Factory class produces SqlNodes. Smarts about type system mappings should go
16     /// here and not in the individual SqlNodes.
17     /// </summary>
18     internal class SqlFactory {
19         private TypeSystemProvider typeProvider;
20         private MetaModel model;
21 
22         internal TypeSystemProvider TypeProvider {
23             get { return typeProvider; }
24         }
25 
SqlFactory(TypeSystemProvider typeProvider, MetaModel model)26         internal SqlFactory(TypeSystemProvider typeProvider, MetaModel model) {
27             this.typeProvider = typeProvider;
28             this.model = model;
29         }
30 
31         #region Expression Operators
32 
ConvertTo(Type clrType, ProviderType sqlType, SqlExpression expr)33         internal SqlExpression ConvertTo(Type clrType, ProviderType sqlType, SqlExpression expr) {
34             return UnaryConvert(clrType, sqlType, expr, expr.SourceExpression);
35         }
36 
ConvertTo(Type clrType, SqlExpression expr)37         internal SqlExpression ConvertTo(Type clrType, SqlExpression expr) {
38             //
39             // In SQL Server 2008, the new TIME data type cannot be converted to BIGINT, or FLOAT,
40             // or a bunch of other SQL types.
41             //
42             if (clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(Nullable<>))
43                 clrType = clrType.GetGenericArguments()[0];
44 
45             bool isClrTimeSpanType = clrType == typeof(TimeSpan);
46 
47             if (IsSqlTimeType(expr))
48             {
49                 if (isClrTimeSpanType) {
50                     // no conversion necessary
51                     return expr;
52                 } else {
53                     expr = ConvertToDateTime(expr);
54                 }
55             }
56 
57             return UnaryConvert(clrType, typeProvider.From(clrType), expr, expr.SourceExpression);
58         }
59 
ConvertToBigint(SqlExpression expr)60         internal SqlExpression ConvertToBigint(SqlExpression expr) {
61             return ConvertTo(typeof(long), expr);
62         }
63 
ConvertToInt(SqlExpression expr)64         internal SqlExpression ConvertToInt(SqlExpression expr) {
65             return ConvertTo(typeof(int), expr);
66         }
67 
ConvertToDouble(SqlExpression expr)68         internal SqlExpression ConvertToDouble(SqlExpression expr) {
69             return ConvertTo(typeof(double), expr);
70         }
71 
72         // If the argument expression has SqlDbType Time, inject a conversion to Double, else return
73         // the expression unchanged.
74         //
ConvertTimeToDouble(SqlExpression exp)75         internal SqlExpression ConvertTimeToDouble(SqlExpression exp) {
76             return IsSqlTimeType(exp) ? ConvertToDouble(exp) : exp;
77         }
78 
ConvertToBool(SqlExpression expr)79         internal SqlExpression ConvertToBool(SqlExpression expr) {
80             return ConvertTo(typeof(bool), expr);
81         }
82 
ConvertToDateTime(SqlExpression expr)83         internal SqlExpression ConvertToDateTime(SqlExpression expr) {
84             return UnaryConvert(typeof(DateTime), typeProvider.From(typeof(DateTime)), expr, expr.SourceExpression);
85         }
86 
AndAccumulate(SqlExpression left, SqlExpression right)87         internal SqlExpression AndAccumulate(SqlExpression left, SqlExpression right) {
88             if (left == null) {
89                 return right;
90             }
91             else if (right == null) {
92                 return left;
93             }
94             else {
95                 return Binary(SqlNodeType.And, left, right);
96             }
97         }
98 
OrAccumulate(SqlExpression left, SqlExpression right)99         internal SqlExpression OrAccumulate(SqlExpression left, SqlExpression right) {
100             if (left == null) {
101                 return right;
102             }
103             else if (right == null) {
104                 return left;
105             }
106             else {
107                 return Binary(SqlNodeType.Or, left, right);
108             }
109         }
110 
Concat(params SqlExpression[] expressions)111         internal SqlExpression Concat(params SqlExpression[] expressions) {
112             SqlExpression result = expressions[expressions.Length - 1];
113             for (int i = expressions.Length - 2; i >= 0; i--) {
114                 result = Binary(SqlNodeType.Concat, expressions[i], result);
115             }
116             return result;
117         }
118 
Add(params SqlExpression[] expressions)119         internal SqlExpression Add(params SqlExpression[] expressions) {
120             SqlExpression sum = expressions[expressions.Length - 1];
121             for (int i = expressions.Length - 2; i >= 0; i--) {
122                 sum = Binary(SqlNodeType.Add, expressions[i], sum);
123             }
124             return sum;
125         }
126 
Subtract(SqlExpression first, SqlExpression second)127         internal SqlExpression Subtract(SqlExpression first, SqlExpression second) {
128             return Binary(SqlNodeType.Sub, first, second);
129         }
130 
Multiply(params SqlExpression[] expressions)131         internal SqlExpression Multiply(params SqlExpression[] expressions) {
132             SqlExpression result = expressions[expressions.Length - 1];
133             for (int i = expressions.Length - 2; i >= 0; i--) {
134                 result = Binary(SqlNodeType.Mul, expressions[i], result);
135             }
136             return result;
137         }
138 
Divide(SqlExpression first, SqlExpression second)139         internal SqlExpression Divide(SqlExpression first, SqlExpression second) {
140             return Binary(SqlNodeType.Div, first, second);
141         }
142 
Add(SqlExpression expr, int second)143         internal SqlExpression Add(SqlExpression expr, int second) {
144             return Binary(SqlNodeType.Add, expr, ValueFromObject(second, false, expr.SourceExpression));
145         }
146 
Subtract(SqlExpression expr, int second)147         internal SqlExpression Subtract(SqlExpression expr, int second) {
148             return Binary(SqlNodeType.Sub, expr, ValueFromObject(second, false, expr.SourceExpression));
149         }
150 
Multiply(SqlExpression expr, long second)151         internal SqlExpression Multiply(SqlExpression expr, long second) {
152             return Binary(SqlNodeType.Mul, expr, ValueFromObject(second, false, expr.SourceExpression));
153         }
154 
Divide(SqlExpression expr, long second)155         internal SqlExpression Divide(SqlExpression expr, long second) {
156             return Binary(SqlNodeType.Div, expr, ValueFromObject(second, false, expr.SourceExpression));
157         }
158 
Mod(SqlExpression expr, long second)159         internal SqlExpression Mod(SqlExpression expr, long second) {
160             return Binary(SqlNodeType.Mod, expr, ValueFromObject(second, false, expr.SourceExpression));
161         }
162 
163         /// <summary>
164         /// Non-internal string length.  This should only be used when translating an explicit call by the
165         /// user to String.Length.
166         /// </summary>
LEN(SqlExpression expr)167         internal SqlExpression LEN(SqlExpression expr) {
168             return FunctionCall(typeof(int), "LEN", new SqlExpression[] { expr }, expr.SourceExpression);
169         }
170 
171         /// <summary>
172         /// This represents the SQL DATALENGTH function, which is the raw number of bytes in the argument.  In the
173         /// case of string types it will count trailing spaces, but doesn't understand unicode.
174         /// </summary>
DATALENGTH(SqlExpression expr)175         internal SqlExpression DATALENGTH(SqlExpression expr) {
176             return FunctionCall(typeof(int), "DATALENGTH", new SqlExpression[] { expr }, expr.SourceExpression);
177         }
178 
179         /// <summary>
180         /// A unary function that uses DATALENGTH, dividing by two if the string is unicode.  This is the internal
181         /// form of String.Length that should always be used.
182         /// </summary>
CLRLENGTH(SqlExpression expr)183         internal SqlExpression CLRLENGTH(SqlExpression expr) {
184             return Unary(SqlNodeType.ClrLength, expr);
185         }
186 
DATEPART(string partName, SqlExpression expr)187         internal SqlExpression DATEPART(string partName, SqlExpression expr) {
188             return FunctionCall(
189                 typeof(int),
190                 "DATEPART",
191                 new SqlExpression[] {
192                     new SqlVariable(typeof(void), null, partName, expr.SourceExpression),
193                     expr
194                 },
195                 expr.SourceExpression
196                 );
197         }
198 
DATEADD(string partName, SqlExpression value, SqlExpression expr)199         internal SqlExpression DATEADD(string partName, SqlExpression value, SqlExpression expr) {
200             return DATEADD(partName, value, expr, expr.SourceExpression, false);
201         }
202 
DATEADD(string partName, SqlExpression value, SqlExpression expr, Expression sourceExpression, bool asNullable)203         internal SqlExpression DATEADD(string partName, SqlExpression value, SqlExpression expr, Expression sourceExpression, bool asNullable) {
204             Type returnType = asNullable ? typeof(DateTime?) : typeof(DateTime);
205 
206             return FunctionCall(
207                 returnType,
208                 "DATEADD",
209                 new SqlExpression[] {
210                     new SqlVariable(typeof(void), null, partName, sourceExpression),
211                     value,
212                     expr },
213                 sourceExpression
214                 );
215         }
216 
DATETIMEOFFSETADD(string partName, SqlExpression value, SqlExpression expr)217         internal SqlExpression DATETIMEOFFSETADD(string partName, SqlExpression value, SqlExpression expr) {
218             return DATETIMEOFFSETADD(partName, value, expr, expr.SourceExpression, false);
219         }
220 
DATETIMEOFFSETADD(string partName, SqlExpression value, SqlExpression expr, Expression sourceExpression, bool asNullable)221         internal SqlExpression DATETIMEOFFSETADD(string partName, SqlExpression value, SqlExpression expr, Expression sourceExpression, bool asNullable) {
222             Type returnType = asNullable ? typeof(DateTimeOffset?) : typeof(DateTimeOffset);
223 
224             return FunctionCall(
225                 returnType,
226                 "DATEADD",
227                 new SqlExpression[] {
228                     new SqlVariable(typeof(void), null, partName, sourceExpression),
229                     value,
230                     expr },
231                 sourceExpression
232                 );
233         }
234 
235         #endregion
236 
AddTimeSpan(SqlExpression dateTime, SqlExpression timeSpan)237         internal SqlExpression AddTimeSpan(SqlExpression dateTime, SqlExpression timeSpan) {
238             return AddTimeSpan(dateTime, timeSpan, false);
239         }
240 
AddTimeSpan(SqlExpression dateTime, SqlExpression timeSpan, bool asNullable)241         internal SqlExpression AddTimeSpan(SqlExpression dateTime, SqlExpression timeSpan, bool asNullable) {
242             Debug.Assert(IsSqlHighPrecisionDateTimeType(timeSpan));
243 
244             SqlExpression ns = DATEPART("NANOSECOND", timeSpan);
245             SqlExpression ms = DATEPART("MILLISECOND", timeSpan);
246             SqlExpression ss = DATEPART("SECOND", timeSpan);
247             SqlExpression mi = DATEPART("MINUTE", timeSpan);
248             SqlExpression hh = DATEPART("HOUR", timeSpan);
249 
250             SqlExpression result = dateTime;
251             if (IsSqlHighPrecisionDateTimeType(dateTime)) {
252                 result = DATEADD("NANOSECOND", ns, result, dateTime.SourceExpression, asNullable);
253             } else {
254                 result = DATEADD("MILLISECOND", ms, result, dateTime.SourceExpression, asNullable);
255             }
256             result = DATEADD("SECOND", ss, result, dateTime.SourceExpression, asNullable);
257             result = DATEADD("MINUTE", mi, result, dateTime.SourceExpression, asNullable);
258             result = DATEADD("HOUR", hh, result, dateTime.SourceExpression, asNullable);
259 
260             if (IsSqlDateTimeOffsetType(dateTime))
261                 return ConvertTo(typeof(DateTimeOffset), result);
262 
263             return result;
264         }
265 
IsSqlDateTimeType(SqlExpression exp)266         internal static bool IsSqlDateTimeType(SqlExpression exp) {
267             SqlDbType sqlDbType = ((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType;
268             return (sqlDbType == SqlDbType.DateTime || sqlDbType == SqlDbType.SmallDateTime);
269         }
270 
IsSqlDateType(SqlExpression exp)271         internal static bool IsSqlDateType(SqlExpression exp) {
272             return (((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType == SqlDbType.Date);
273         }
274 
IsSqlTimeType(SqlExpression exp)275         internal static bool IsSqlTimeType(SqlExpression exp) {
276             return (((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType == SqlDbType.Time);
277         }
278 
IsSqlDateTimeOffsetType(SqlExpression exp)279         internal static bool IsSqlDateTimeOffsetType(SqlExpression exp) {
280             return (((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType == SqlDbType.DateTimeOffset);
281         }
282 
IsSqlHighPrecisionDateTimeType(SqlExpression exp)283         internal static bool IsSqlHighPrecisionDateTimeType(SqlExpression exp) {
284             SqlDbType sqlDbType = ((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType;
285             return (sqlDbType == SqlDbType.Time || sqlDbType == SqlDbType.DateTime2 || sqlDbType == SqlDbType.DateTimeOffset);
286         }
287 
Value(Type clrType, ProviderType sqlType, object value, bool isClientSpecified, Expression sourceExpression)288         internal SqlExpression Value(Type clrType, ProviderType sqlType, object value, bool isClientSpecified, Expression sourceExpression) {
289             if (typeof(Type).IsAssignableFrom(clrType) && value != null) {
290                 MetaType typeOf = this.model.GetMetaType((Type)value);
291                 return StaticType(typeOf, sourceExpression);
292             }
293             return new SqlValue(clrType, sqlType, value, isClientSpecified, sourceExpression);
294         }
295 
296         /// <summary>
297         /// Return a node representing typeof(typeOf)
298         /// </summary>
StaticType(MetaType typeOf, Expression sourceExpression)299         internal SqlExpression StaticType(MetaType typeOf, Expression sourceExpression) {
300             if (typeOf==null)
301                 throw Error.ArgumentNull("typeOf");
302             if(typeOf.InheritanceCode==null) {
303                 // If no inheritance is involved, then there's no discriminator to
304                 // make a discriminated type. In this case, just make a literal type.
305                 return new SqlValue(typeof(Type), this.typeProvider.From(typeof(Type)), typeOf.Type, false, sourceExpression);
306             }
307             Type type = typeOf.InheritanceCode.GetType();
308             SqlValue match = new SqlValue(type, this.typeProvider.From(type), typeOf.InheritanceCode, true, sourceExpression);
309             return this.DiscriminatedType(match, typeOf);
310         }
311 
DiscriminatedType(SqlExpression discriminator, MetaType targetType)312         internal SqlExpression DiscriminatedType(SqlExpression discriminator, MetaType targetType) {
313             return new SqlDiscriminatedType(typeProvider.From(typeof(Type)), discriminator, targetType, discriminator.SourceExpression);
314         }
315 
Table(MetaTable table, MetaType rowType, Expression sourceExpression)316         internal SqlTable Table(MetaTable table, MetaType rowType, Expression sourceExpression) {
317             return new SqlTable(table, rowType, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), sourceExpression);
318         }
319 
Unary(SqlNodeType nodeType, SqlExpression expression)320         internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression) {
321             return Unary(nodeType, expression, expression.SourceExpression);
322         }
323 
RowNumber(List<SqlOrderExpression> orderBy, Expression sourceExpression)324         internal SqlRowNumber RowNumber(List<SqlOrderExpression> orderBy, Expression sourceExpression) {
325             return new SqlRowNumber(typeof(long), typeProvider.From(typeof(long)), orderBy, sourceExpression);
326         }
327 
Unary(SqlNodeType nodeType, SqlExpression expression, Expression sourceExpression)328         internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression, Expression sourceExpression) {
329             return Unary(nodeType, expression, null, sourceExpression);
330         }
331 
Unary(SqlNodeType nodeType, SqlExpression expression, MethodInfo method, Expression sourceExpression)332         internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression, MethodInfo method, Expression sourceExpression) {
333             Type clrType = null;
334             ProviderType sqlType = null;
335 
336             if (nodeType == SqlNodeType.Count) {
337                 clrType = typeof(int);
338                 sqlType = typeProvider.From(typeof(int));
339             }
340             else if (nodeType == SqlNodeType.LongCount) {
341                 clrType = typeof(long);
342                 sqlType = typeProvider.From(typeof(long));
343             }
344             else if (nodeType == SqlNodeType.ClrLength) {
345                 clrType = typeof(int);
346                 sqlType = typeProvider.From(typeof(int));
347             }
348             else {
349                 if (nodeType.IsPredicateUnaryOperator()) {
350                     // DevDiv 201730 - Do not ignore nullability of bool type
351                     clrType = expression.ClrType.Equals(typeof(bool?)) ? typeof(bool?) : typeof(bool);
352                 }
353                 else {
354                     clrType = expression.ClrType;
355                 }
356                 sqlType = typeProvider.PredictTypeForUnary(nodeType, expression.SqlType);
357             }
358 
359             return new SqlUnary(nodeType, clrType, sqlType, expression, method, sourceExpression);
360         }
361 
362         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
UnaryConvert(Type targetClrType, ProviderType targetSqlType, SqlExpression expression, Expression sourceExpression)363         internal SqlUnary UnaryConvert(Type targetClrType, ProviderType targetSqlType, SqlExpression expression, Expression sourceExpression) {
364             System.Diagnostics.Debug.Assert(!targetSqlType.IsRuntimeOnlyType, "Attempted coversion to a runtime type: from = " + expression.SqlType.ToQueryString() + "; to = " + targetSqlType.ToQueryString() + "; source = " + sourceExpression.ToString());
365             return new SqlUnary(SqlNodeType.Convert, targetClrType, targetSqlType, expression, null, sourceExpression);
366         }
367 
368         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
UnaryValueOf(SqlExpression expression, Expression sourceExpression)369         internal SqlUnary UnaryValueOf(SqlExpression expression, Expression sourceExpression) {
370             Type valueType = TypeSystem.GetNonNullableType(expression.ClrType);
371             return new SqlUnary(SqlNodeType.ValueOf, valueType, expression.SqlType, expression, null, sourceExpression);
372         }
373 
Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right)374         internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right) {
375             return Binary(nodeType, left, right, null, null);
376         }
377 
Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, MethodInfo method)378         internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, MethodInfo method) {
379             return Binary(nodeType, left, right, method, null);
380         }
381 
Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, Type clrType)382         internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, Type clrType) {
383             return Binary(nodeType, left, right, null, clrType);
384         }
385 
Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, MethodInfo method, Type clrType)386         internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, MethodInfo method, Type clrType) {
387             ProviderType sqlType = null;
388             if (nodeType.IsPredicateBinaryOperator()) {
389                 if (clrType == null) {
390                     clrType = typeof(bool);
391                 }
392                 sqlType = typeProvider.From(clrType);
393             }
394             else {
395                 ProviderType resultType = this.typeProvider.PredictTypeForBinary(nodeType, left.SqlType, right.SqlType);
396                 if (resultType == right.SqlType) {
397                     if (clrType == null) {
398                         clrType = right.ClrType;
399                     }
400                     sqlType = right.SqlType;
401                 }
402                 else if (resultType == left.SqlType) {
403                     if (clrType == null) {
404                         clrType = left.ClrType;
405                     }
406                     sqlType = left.SqlType;
407                 }
408                 else {
409                     sqlType = resultType;
410                     if (clrType == null) {
411                         clrType = resultType.GetClosestRuntimeType();
412                     }
413                 }
414             }
415             return new SqlBinary(nodeType, clrType, sqlType, left, right, method);
416         }
417 
Between(SqlExpression expr, SqlExpression start, SqlExpression end, Expression source)418         internal SqlBetween Between(SqlExpression expr, SqlExpression start, SqlExpression end, Expression source) {
419             return new SqlBetween(typeof(bool), typeProvider.From(typeof(bool)), expr, start, end, source);
420         }
421 
In(SqlExpression expr, IEnumerable<SqlExpression> values, Expression source)422         internal SqlIn In(SqlExpression expr, IEnumerable<SqlExpression> values, Expression source) {
423             return new SqlIn(typeof(bool), typeProvider.From(typeof(bool)), expr, values, source);
424         }
425 
Like(SqlExpression expr, SqlExpression pattern, SqlExpression escape, Expression source)426         internal SqlLike Like(SqlExpression expr, SqlExpression pattern, SqlExpression escape, Expression source) {
427             SqlLike like = new SqlLike(typeof(bool), typeProvider.From(typeof(bool)), expr, pattern, escape, source);
428             return like;
429         }
430 
431         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
SearchedCase(SqlWhen[] whens, SqlExpression @else, Expression sourceExpression)432         internal SqlSearchedCase SearchedCase(SqlWhen[] whens, SqlExpression @else, Expression sourceExpression) {
433             return new SqlSearchedCase(whens[0].Value.ClrType, whens, @else, sourceExpression);
434         }
435 
436         /// <summary>
437         /// Construct either a SqlClientCase or a SqlSimpleCase depending on whether the individual cases
438         /// are client-aided or not.
439         /// </summary>
440         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
Case(Type clrType, SqlExpression discriminator, List<SqlExpression> matches, List<SqlExpression> values, Expression sourceExpression)441         internal SqlExpression Case(Type clrType, SqlExpression discriminator, List<SqlExpression> matches, List<SqlExpression> values, Expression sourceExpression) {
442             if (values.Count == 0) {
443                 throw Error.EmptyCaseNotSupported();
444             }
445             bool anyClient = false;
446             foreach (SqlExpression value in values) {
447                 anyClient |= value.IsClientAidedExpression();
448             }
449             if (anyClient) {
450                 List<SqlClientWhen> whens = new List<SqlClientWhen>();
451                 for (int i = 0, c = matches.Count; i < c; ++i) {
452                     whens.Add(new SqlClientWhen(matches[i], values[i]));
453                 }
454                 return new SqlClientCase(clrType, discriminator, whens, sourceExpression);
455             }
456             else {
457                 List<SqlWhen> whens = new List<SqlWhen>();
458                 for (int i = 0, c = matches.Count; i < c; ++i) {
459                     whens.Add(new SqlWhen(matches[i], values[i]));
460                 }
461                 return new SqlSimpleCase(clrType, discriminator, whens, sourceExpression);
462             }
463         }
464 
Parameter(object value, Expression source)465         internal SqlExpression Parameter(object value, Expression source) {
466             System.Diagnostics.Debug.Assert(value != null);
467             Type type = value.GetType();
468             return Value(type, this.typeProvider.From(value), value, true, source);
469         }
470 
ValueFromObject(object value, Expression sourceExpression)471         internal SqlExpression ValueFromObject(object value, Expression sourceExpression) {
472             return ValueFromObject(value, false, sourceExpression);
473         }
474 
ValueFromObject(object value, bool isClientSpecified, Expression sourceExpression)475         internal SqlExpression ValueFromObject(object value, bool isClientSpecified, Expression sourceExpression) {
476             if (value == null) {
477                 System.Diagnostics.Debug.Assert(false);
478                 throw Error.ArgumentNull("value");
479             }
480             Type clrType = value.GetType();
481             return ValueFromObject(value, clrType, isClientSpecified, sourceExpression);
482         }
483 
484         // Override allowing the CLR type of the value to be specified explicitly.
ValueFromObject(object value, Type clrType, bool isClientSpecified, Expression sourceExpression)485         internal SqlExpression ValueFromObject(object value, Type clrType, bool isClientSpecified, Expression sourceExpression) {
486             if (clrType == null) {
487                 throw Error.ArgumentNull("clrType");
488             }
489             ProviderType sqlType = (value == null) ? this.typeProvider.From(clrType) : this.typeProvider.From(value);
490             return Value(clrType, sqlType, value, isClientSpecified, sourceExpression);
491         }
492 
TypedLiteralNull(Type type, Expression sourceExpression)493         public SqlExpression TypedLiteralNull(Type type, Expression sourceExpression) {
494             return ValueFromObject(null, type, false, sourceExpression);
495         }
496 
Member(SqlExpression expr, MetaDataMember member)497         internal SqlMember Member(SqlExpression expr, MetaDataMember member) {
498             return new SqlMember(member.Type, this.Default(member), expr, member.Member);
499         }
500 
Member(SqlExpression expr, MemberInfo member)501         internal SqlMember Member(SqlExpression expr, MemberInfo member) {
502             Type clrType = TypeSystem.GetMemberType(member);
503             MetaType metaType = this.model.GetMetaType(member.DeclaringType);
504             MetaDataMember metaDataMember = metaType.GetDataMember(member);
505             if (metaType != null && metaDataMember != null) {
506                 return new SqlMember(clrType, this.Default(metaDataMember), expr, member);
507             } else {
508                 return new SqlMember(clrType, this.Default(clrType), expr, member);
509             }
510         }
511 
TypeCase(Type clrType, MetaType rowType, SqlExpression discriminator, IEnumerable<SqlTypeCaseWhen> whens, Expression sourceExpression)512         internal SqlExpression TypeCase(Type clrType, MetaType rowType, SqlExpression discriminator, IEnumerable<SqlTypeCaseWhen> whens, Expression sourceExpression) {
513             return new SqlTypeCase(clrType, typeProvider.From(clrType), rowType, discriminator, whens, sourceExpression);
514         }
515 
New(MetaType type, ConstructorInfo cons, IEnumerable<SqlExpression> args, IEnumerable<MemberInfo> argMembers, IEnumerable<SqlMemberAssign> bindings, Expression sourceExpression)516         internal SqlNew New(MetaType type, ConstructorInfo cons, IEnumerable<SqlExpression> args, IEnumerable<MemberInfo> argMembers, IEnumerable<SqlMemberAssign> bindings, Expression sourceExpression) {
517             SqlNew tb = new SqlNew(type, typeProvider.From(type.Type), cons, args, argMembers, bindings, sourceExpression);
518             return tb;
519         }
520 
MethodCall(MethodInfo method, SqlExpression obj, SqlExpression[] args, Expression sourceExpression)521         internal SqlMethodCall MethodCall(MethodInfo method, SqlExpression obj, SqlExpression[] args, Expression sourceExpression) {
522             return new SqlMethodCall(method.ReturnType, this.Default(method.ReturnType), method, obj, args, sourceExpression);
523         }
524 
MethodCall(Type returnType, MethodInfo method, SqlExpression obj, SqlExpression[] args, Expression sourceExpression)525         internal SqlMethodCall MethodCall(Type returnType, MethodInfo method, SqlExpression obj, SqlExpression[] args, Expression sourceExpression) {
526             return new SqlMethodCall(returnType, this.Default(returnType), method, obj, args, sourceExpression);
527         }
528 
529         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
ExprSet(SqlExpression[] exprs, Expression sourceExpression)530         internal SqlExprSet ExprSet(SqlExpression[] exprs, Expression sourceExpression) {
531             return new SqlExprSet(exprs[0].ClrType, exprs, sourceExpression);
532         }
533 
SubSelect(SqlNodeType nt, SqlSelect select)534         internal SqlSubSelect SubSelect(SqlNodeType nt, SqlSelect select) {
535             return this.SubSelect(nt, select, null);
536         }
SubSelect(SqlNodeType nt, SqlSelect select, Type clrType)537         internal SqlSubSelect SubSelect(SqlNodeType nt, SqlSelect select, Type clrType) {
538             ProviderType sqlType = null;
539             switch (nt) {
540                 case SqlNodeType.ScalarSubSelect:
541                 case SqlNodeType.Element:
542                     clrType = select.Selection.ClrType;
543                     sqlType = select.Selection.SqlType;
544                     break;
545                 case SqlNodeType.Multiset:
546                     if (clrType == null) {
547                         clrType = typeof(List<>).MakeGenericType(select.Selection.ClrType);
548                     }
549                     sqlType = typeProvider.GetApplicationType((int)ConverterSpecialTypes.Table);
550                     break;
551                 case SqlNodeType.Exists:
552                     clrType = typeof(bool);
553                     sqlType = typeProvider.From(typeof(bool));
554                     break;
555             }
556             return new SqlSubSelect(nt, clrType, sqlType, select);
557         }
558 
559         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
DoNotVisitExpression(SqlExpression expr)560         internal SqlDoNotVisitExpression DoNotVisitExpression(SqlExpression expr) {
561             return new SqlDoNotVisitExpression(expr);
562         }
563 
FunctionCall(Type clrType, string name, IEnumerable<SqlExpression> args, Expression source)564         internal SqlFunctionCall FunctionCall(Type clrType, string name, IEnumerable<SqlExpression> args, Expression source) {
565             return new SqlFunctionCall(clrType, Default(clrType), name, args, source);
566         }
567 
568         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
FunctionCall(Type clrType, ProviderType sqlType, string name, IEnumerable<SqlExpression> args, Expression source)569         internal SqlFunctionCall FunctionCall(Type clrType, ProviderType sqlType, string name, IEnumerable<SqlExpression> args, Expression source) {
570             return new SqlFunctionCall(clrType, sqlType, name, args, source);
571         }
572 
TableValuedFunctionCall(MetaType rowType, Type clrType, string name, IEnumerable<SqlExpression> args, Expression source)573         internal SqlTableValuedFunctionCall TableValuedFunctionCall(MetaType rowType, Type clrType, string name, IEnumerable<SqlExpression> args, Expression source) {
574             return new SqlTableValuedFunctionCall(rowType, clrType, Default(clrType), name, args, source);
575         }
576 
Default(Type clrType)577         internal ProviderType Default(Type clrType) {
578             return typeProvider.From(clrType);
579         }
580 
Default(MetaDataMember member)581         internal ProviderType Default(MetaDataMember member) {
582             if(member == null)
583                 throw Error.ArgumentNull("member");
584 
585             if (member.DbType != null) {
586                 return this.typeProvider.Parse(member.DbType);
587             }
588             else {
589                 return this.typeProvider.From(member.Type);
590             }
591         }
592 
MakeJoin(SqlJoinType joinType, SqlSource location, SqlAlias alias, SqlExpression condition, Expression source)593         internal SqlJoin MakeJoin(SqlJoinType joinType, SqlSource location, SqlAlias alias, SqlExpression condition, Expression source) {
594             // if the new item is on the right side of some outer join then fixup the projection to reflect that it can possibly be null
595             if (joinType == SqlJoinType.LeftOuter) {
596                 SqlSelect sel = alias.Node as SqlSelect;
597                 if (sel != null && sel.Selection != null && sel.Selection.NodeType != SqlNodeType.OptionalValue) {
598                     // replace selection w/ optional + outer-joined-value
599                     sel.Selection = new SqlOptionalValue(
600                                         new SqlColumn(
601                                             "test",
602                                             this.Unary(SqlNodeType.OuterJoinedValue,
603                                                 this.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, source))
604                                             ),
605                                         sel.Selection
606                                         );
607                 }
608             }
609             return new SqlJoin(joinType, location, alias, condition, source);
610         }
611     }
612 }
613