e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
613 lines
28 KiB
C#
613 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Linq.Expressions;
|
|
using System.Data.Linq;
|
|
|
|
namespace System.Data.Linq.SqlClient {
|
|
using System.Data.Linq.Mapping;
|
|
using System.Data.Linq.Provider;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Diagnostics;
|
|
|
|
/// <summary>
|
|
/// Factory class produces SqlNodes. Smarts about type system mappings should go
|
|
/// here and not in the individual SqlNodes.
|
|
/// </summary>
|
|
internal class SqlFactory {
|
|
private TypeSystemProvider typeProvider;
|
|
private MetaModel model;
|
|
|
|
internal TypeSystemProvider TypeProvider {
|
|
get { return typeProvider; }
|
|
}
|
|
|
|
internal SqlFactory(TypeSystemProvider typeProvider, MetaModel model) {
|
|
this.typeProvider = typeProvider;
|
|
this.model = model;
|
|
}
|
|
|
|
#region Expression Operators
|
|
|
|
internal SqlExpression ConvertTo(Type clrType, ProviderType sqlType, SqlExpression expr) {
|
|
return UnaryConvert(clrType, sqlType, expr, expr.SourceExpression);
|
|
}
|
|
|
|
internal SqlExpression ConvertTo(Type clrType, SqlExpression expr) {
|
|
//
|
|
// In SQL Server 2008, the new TIME data type cannot be converted to BIGINT, or FLOAT,
|
|
// or a bunch of other SQL types.
|
|
//
|
|
if (clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
|
clrType = clrType.GetGenericArguments()[0];
|
|
|
|
bool isClrTimeSpanType = clrType == typeof(TimeSpan);
|
|
|
|
if (IsSqlTimeType(expr))
|
|
{
|
|
if (isClrTimeSpanType) {
|
|
// no conversion necessary
|
|
return expr;
|
|
} else {
|
|
expr = ConvertToDateTime(expr);
|
|
}
|
|
}
|
|
|
|
return UnaryConvert(clrType, typeProvider.From(clrType), expr, expr.SourceExpression);
|
|
}
|
|
|
|
internal SqlExpression ConvertToBigint(SqlExpression expr) {
|
|
return ConvertTo(typeof(long), expr);
|
|
}
|
|
|
|
internal SqlExpression ConvertToInt(SqlExpression expr) {
|
|
return ConvertTo(typeof(int), expr);
|
|
}
|
|
|
|
internal SqlExpression ConvertToDouble(SqlExpression expr) {
|
|
return ConvertTo(typeof(double), expr);
|
|
}
|
|
|
|
// If the argument expression has SqlDbType Time, inject a conversion to Double, else return
|
|
// the expression unchanged.
|
|
//
|
|
internal SqlExpression ConvertTimeToDouble(SqlExpression exp) {
|
|
return IsSqlTimeType(exp) ? ConvertToDouble(exp) : exp;
|
|
}
|
|
|
|
internal SqlExpression ConvertToBool(SqlExpression expr) {
|
|
return ConvertTo(typeof(bool), expr);
|
|
}
|
|
|
|
internal SqlExpression ConvertToDateTime(SqlExpression expr) {
|
|
return UnaryConvert(typeof(DateTime), typeProvider.From(typeof(DateTime)), expr, expr.SourceExpression);
|
|
}
|
|
|
|
internal SqlExpression AndAccumulate(SqlExpression left, SqlExpression right) {
|
|
if (left == null) {
|
|
return right;
|
|
}
|
|
else if (right == null) {
|
|
return left;
|
|
}
|
|
else {
|
|
return Binary(SqlNodeType.And, left, right);
|
|
}
|
|
}
|
|
|
|
internal SqlExpression OrAccumulate(SqlExpression left, SqlExpression right) {
|
|
if (left == null) {
|
|
return right;
|
|
}
|
|
else if (right == null) {
|
|
return left;
|
|
}
|
|
else {
|
|
return Binary(SqlNodeType.Or, left, right);
|
|
}
|
|
}
|
|
|
|
internal SqlExpression Concat(params SqlExpression[] expressions) {
|
|
SqlExpression result = expressions[expressions.Length - 1];
|
|
for (int i = expressions.Length - 2; i >= 0; i--) {
|
|
result = Binary(SqlNodeType.Concat, expressions[i], result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal SqlExpression Add(params SqlExpression[] expressions) {
|
|
SqlExpression sum = expressions[expressions.Length - 1];
|
|
for (int i = expressions.Length - 2; i >= 0; i--) {
|
|
sum = Binary(SqlNodeType.Add, expressions[i], sum);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
internal SqlExpression Subtract(SqlExpression first, SqlExpression second) {
|
|
return Binary(SqlNodeType.Sub, first, second);
|
|
}
|
|
|
|
internal SqlExpression Multiply(params SqlExpression[] expressions) {
|
|
SqlExpression result = expressions[expressions.Length - 1];
|
|
for (int i = expressions.Length - 2; i >= 0; i--) {
|
|
result = Binary(SqlNodeType.Mul, expressions[i], result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal SqlExpression Divide(SqlExpression first, SqlExpression second) {
|
|
return Binary(SqlNodeType.Div, first, second);
|
|
}
|
|
|
|
internal SqlExpression Add(SqlExpression expr, int second) {
|
|
return Binary(SqlNodeType.Add, expr, ValueFromObject(second, false, expr.SourceExpression));
|
|
}
|
|
|
|
internal SqlExpression Subtract(SqlExpression expr, int second) {
|
|
return Binary(SqlNodeType.Sub, expr, ValueFromObject(second, false, expr.SourceExpression));
|
|
}
|
|
|
|
internal SqlExpression Multiply(SqlExpression expr, long second) {
|
|
return Binary(SqlNodeType.Mul, expr, ValueFromObject(second, false, expr.SourceExpression));
|
|
}
|
|
|
|
internal SqlExpression Divide(SqlExpression expr, long second) {
|
|
return Binary(SqlNodeType.Div, expr, ValueFromObject(second, false, expr.SourceExpression));
|
|
}
|
|
|
|
internal SqlExpression Mod(SqlExpression expr, long second) {
|
|
return Binary(SqlNodeType.Mod, expr, ValueFromObject(second, false, expr.SourceExpression));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Non-internal string length. This should only be used when translating an explicit call by the
|
|
/// user to String.Length.
|
|
/// </summary>
|
|
internal SqlExpression LEN(SqlExpression expr) {
|
|
return FunctionCall(typeof(int), "LEN", new SqlExpression[] { expr }, expr.SourceExpression);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This represents the SQL DATALENGTH function, which is the raw number of bytes in the argument. In the
|
|
/// case of string types it will count trailing spaces, but doesn't understand unicode.
|
|
/// </summary>
|
|
internal SqlExpression DATALENGTH(SqlExpression expr) {
|
|
return FunctionCall(typeof(int), "DATALENGTH", new SqlExpression[] { expr }, expr.SourceExpression);
|
|
}
|
|
|
|
/// <summary>
|
|
/// A unary function that uses DATALENGTH, dividing by two if the string is unicode. This is the internal
|
|
/// form of String.Length that should always be used.
|
|
/// </summary>
|
|
internal SqlExpression CLRLENGTH(SqlExpression expr) {
|
|
return Unary(SqlNodeType.ClrLength, expr);
|
|
}
|
|
|
|
internal SqlExpression DATEPART(string partName, SqlExpression expr) {
|
|
return FunctionCall(
|
|
typeof(int),
|
|
"DATEPART",
|
|
new SqlExpression[] {
|
|
new SqlVariable(typeof(void), null, partName, expr.SourceExpression),
|
|
expr
|
|
},
|
|
expr.SourceExpression
|
|
);
|
|
}
|
|
|
|
internal SqlExpression DATEADD(string partName, SqlExpression value, SqlExpression expr) {
|
|
return DATEADD(partName, value, expr, expr.SourceExpression, false);
|
|
}
|
|
|
|
internal SqlExpression DATEADD(string partName, SqlExpression value, SqlExpression expr, Expression sourceExpression, bool asNullable) {
|
|
Type returnType = asNullable ? typeof(DateTime?) : typeof(DateTime);
|
|
|
|
return FunctionCall(
|
|
returnType,
|
|
"DATEADD",
|
|
new SqlExpression[] {
|
|
new SqlVariable(typeof(void), null, partName, sourceExpression),
|
|
value,
|
|
expr },
|
|
sourceExpression
|
|
);
|
|
}
|
|
|
|
internal SqlExpression DATETIMEOFFSETADD(string partName, SqlExpression value, SqlExpression expr) {
|
|
return DATETIMEOFFSETADD(partName, value, expr, expr.SourceExpression, false);
|
|
}
|
|
|
|
internal SqlExpression DATETIMEOFFSETADD(string partName, SqlExpression value, SqlExpression expr, Expression sourceExpression, bool asNullable) {
|
|
Type returnType = asNullable ? typeof(DateTimeOffset?) : typeof(DateTimeOffset);
|
|
|
|
return FunctionCall(
|
|
returnType,
|
|
"DATEADD",
|
|
new SqlExpression[] {
|
|
new SqlVariable(typeof(void), null, partName, sourceExpression),
|
|
value,
|
|
expr },
|
|
sourceExpression
|
|
);
|
|
}
|
|
|
|
#endregion
|
|
|
|
internal SqlExpression AddTimeSpan(SqlExpression dateTime, SqlExpression timeSpan) {
|
|
return AddTimeSpan(dateTime, timeSpan, false);
|
|
}
|
|
|
|
internal SqlExpression AddTimeSpan(SqlExpression dateTime, SqlExpression timeSpan, bool asNullable) {
|
|
Debug.Assert(IsSqlHighPrecisionDateTimeType(timeSpan));
|
|
|
|
SqlExpression ns = DATEPART("NANOSECOND", timeSpan);
|
|
SqlExpression ms = DATEPART("MILLISECOND", timeSpan);
|
|
SqlExpression ss = DATEPART("SECOND", timeSpan);
|
|
SqlExpression mi = DATEPART("MINUTE", timeSpan);
|
|
SqlExpression hh = DATEPART("HOUR", timeSpan);
|
|
|
|
SqlExpression result = dateTime;
|
|
if (IsSqlHighPrecisionDateTimeType(dateTime)) {
|
|
result = DATEADD("NANOSECOND", ns, result, dateTime.SourceExpression, asNullable);
|
|
} else {
|
|
result = DATEADD("MILLISECOND", ms, result, dateTime.SourceExpression, asNullable);
|
|
}
|
|
result = DATEADD("SECOND", ss, result, dateTime.SourceExpression, asNullable);
|
|
result = DATEADD("MINUTE", mi, result, dateTime.SourceExpression, asNullable);
|
|
result = DATEADD("HOUR", hh, result, dateTime.SourceExpression, asNullable);
|
|
|
|
if (IsSqlDateTimeOffsetType(dateTime))
|
|
return ConvertTo(typeof(DateTimeOffset), result);
|
|
|
|
return result;
|
|
}
|
|
|
|
internal static bool IsSqlDateTimeType(SqlExpression exp) {
|
|
SqlDbType sqlDbType = ((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType;
|
|
return (sqlDbType == SqlDbType.DateTime || sqlDbType == SqlDbType.SmallDateTime);
|
|
}
|
|
|
|
internal static bool IsSqlDateType(SqlExpression exp) {
|
|
return (((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType == SqlDbType.Date);
|
|
}
|
|
|
|
internal static bool IsSqlTimeType(SqlExpression exp) {
|
|
return (((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType == SqlDbType.Time);
|
|
}
|
|
|
|
internal static bool IsSqlDateTimeOffsetType(SqlExpression exp) {
|
|
return (((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType == SqlDbType.DateTimeOffset);
|
|
}
|
|
|
|
internal static bool IsSqlHighPrecisionDateTimeType(SqlExpression exp) {
|
|
SqlDbType sqlDbType = ((SqlTypeSystem.SqlType)(exp.SqlType)).SqlDbType;
|
|
return (sqlDbType == SqlDbType.Time || sqlDbType == SqlDbType.DateTime2 || sqlDbType == SqlDbType.DateTimeOffset);
|
|
}
|
|
|
|
internal SqlExpression Value(Type clrType, ProviderType sqlType, object value, bool isClientSpecified, Expression sourceExpression) {
|
|
if (typeof(Type).IsAssignableFrom(clrType) && value != null) {
|
|
MetaType typeOf = this.model.GetMetaType((Type)value);
|
|
return StaticType(typeOf, sourceExpression);
|
|
}
|
|
return new SqlValue(clrType, sqlType, value, isClientSpecified, sourceExpression);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return a node representing typeof(typeOf)
|
|
/// </summary>
|
|
internal SqlExpression StaticType(MetaType typeOf, Expression sourceExpression) {
|
|
if (typeOf==null)
|
|
throw Error.ArgumentNull("typeOf");
|
|
if(typeOf.InheritanceCode==null) {
|
|
// If no inheritance is involved, then there's no discriminator to
|
|
// make a discriminated type. In this case, just make a literal type.
|
|
return new SqlValue(typeof(Type), this.typeProvider.From(typeof(Type)), typeOf.Type, false, sourceExpression);
|
|
}
|
|
Type type = typeOf.InheritanceCode.GetType();
|
|
SqlValue match = new SqlValue(type, this.typeProvider.From(type), typeOf.InheritanceCode, true, sourceExpression);
|
|
return this.DiscriminatedType(match, typeOf);
|
|
}
|
|
|
|
internal SqlExpression DiscriminatedType(SqlExpression discriminator, MetaType targetType) {
|
|
return new SqlDiscriminatedType(typeProvider.From(typeof(Type)), discriminator, targetType, discriminator.SourceExpression);
|
|
}
|
|
|
|
internal SqlTable Table(MetaTable table, MetaType rowType, Expression sourceExpression) {
|
|
return new SqlTable(table, rowType, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), sourceExpression);
|
|
}
|
|
|
|
internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression) {
|
|
return Unary(nodeType, expression, expression.SourceExpression);
|
|
}
|
|
|
|
internal SqlRowNumber RowNumber(List<SqlOrderExpression> orderBy, Expression sourceExpression) {
|
|
return new SqlRowNumber(typeof(long), typeProvider.From(typeof(long)), orderBy, sourceExpression);
|
|
}
|
|
|
|
internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression, Expression sourceExpression) {
|
|
return Unary(nodeType, expression, null, sourceExpression);
|
|
}
|
|
|
|
internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression, MethodInfo method, Expression sourceExpression) {
|
|
Type clrType = null;
|
|
ProviderType sqlType = null;
|
|
|
|
if (nodeType == SqlNodeType.Count) {
|
|
clrType = typeof(int);
|
|
sqlType = typeProvider.From(typeof(int));
|
|
}
|
|
else if (nodeType == SqlNodeType.LongCount) {
|
|
clrType = typeof(long);
|
|
sqlType = typeProvider.From(typeof(long));
|
|
}
|
|
else if (nodeType == SqlNodeType.ClrLength) {
|
|
clrType = typeof(int);
|
|
sqlType = typeProvider.From(typeof(int));
|
|
}
|
|
else {
|
|
if (nodeType.IsPredicateUnaryOperator()) {
|
|
// DevDiv 201730 - Do not ignore nullability of bool type
|
|
clrType = expression.ClrType.Equals(typeof(bool?)) ? typeof(bool?) : typeof(bool);
|
|
}
|
|
else {
|
|
clrType = expression.ClrType;
|
|
}
|
|
sqlType = typeProvider.PredictTypeForUnary(nodeType, expression.SqlType);
|
|
}
|
|
|
|
return new SqlUnary(nodeType, clrType, sqlType, expression, method, sourceExpression);
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlUnary UnaryConvert(Type targetClrType, ProviderType targetSqlType, SqlExpression expression, Expression sourceExpression) {
|
|
System.Diagnostics.Debug.Assert(!targetSqlType.IsRuntimeOnlyType, "Attempted coversion to a runtime type: from = " + expression.SqlType.ToQueryString() + "; to = " + targetSqlType.ToQueryString() + "; source = " + sourceExpression.ToString());
|
|
return new SqlUnary(SqlNodeType.Convert, targetClrType, targetSqlType, expression, null, sourceExpression);
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlUnary UnaryValueOf(SqlExpression expression, Expression sourceExpression) {
|
|
Type valueType = TypeSystem.GetNonNullableType(expression.ClrType);
|
|
return new SqlUnary(SqlNodeType.ValueOf, valueType, expression.SqlType, expression, null, sourceExpression);
|
|
}
|
|
|
|
internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right) {
|
|
return Binary(nodeType, left, right, null, null);
|
|
}
|
|
|
|
internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, MethodInfo method) {
|
|
return Binary(nodeType, left, right, method, null);
|
|
}
|
|
|
|
internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, Type clrType) {
|
|
return Binary(nodeType, left, right, null, clrType);
|
|
}
|
|
|
|
internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right, MethodInfo method, Type clrType) {
|
|
ProviderType sqlType = null;
|
|
if (nodeType.IsPredicateBinaryOperator()) {
|
|
if (clrType == null) {
|
|
clrType = typeof(bool);
|
|
}
|
|
sqlType = typeProvider.From(clrType);
|
|
}
|
|
else {
|
|
ProviderType resultType = this.typeProvider.PredictTypeForBinary(nodeType, left.SqlType, right.SqlType);
|
|
if (resultType == right.SqlType) {
|
|
if (clrType == null) {
|
|
clrType = right.ClrType;
|
|
}
|
|
sqlType = right.SqlType;
|
|
}
|
|
else if (resultType == left.SqlType) {
|
|
if (clrType == null) {
|
|
clrType = left.ClrType;
|
|
}
|
|
sqlType = left.SqlType;
|
|
}
|
|
else {
|
|
sqlType = resultType;
|
|
if (clrType == null) {
|
|
clrType = resultType.GetClosestRuntimeType();
|
|
}
|
|
}
|
|
}
|
|
return new SqlBinary(nodeType, clrType, sqlType, left, right, method);
|
|
}
|
|
|
|
internal SqlBetween Between(SqlExpression expr, SqlExpression start, SqlExpression end, Expression source) {
|
|
return new SqlBetween(typeof(bool), typeProvider.From(typeof(bool)), expr, start, end, source);
|
|
}
|
|
|
|
internal SqlIn In(SqlExpression expr, IEnumerable<SqlExpression> values, Expression source) {
|
|
return new SqlIn(typeof(bool), typeProvider.From(typeof(bool)), expr, values, source);
|
|
}
|
|
|
|
internal SqlLike Like(SqlExpression expr, SqlExpression pattern, SqlExpression escape, Expression source) {
|
|
SqlLike like = new SqlLike(typeof(bool), typeProvider.From(typeof(bool)), expr, pattern, escape, source);
|
|
return like;
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlSearchedCase SearchedCase(SqlWhen[] whens, SqlExpression @else, Expression sourceExpression) {
|
|
return new SqlSearchedCase(whens[0].Value.ClrType, whens, @else, sourceExpression);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct either a SqlClientCase or a SqlSimpleCase depending on whether the individual cases
|
|
/// are client-aided or not.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlExpression Case(Type clrType, SqlExpression discriminator, List<SqlExpression> matches, List<SqlExpression> values, Expression sourceExpression) {
|
|
if (values.Count == 0) {
|
|
throw Error.EmptyCaseNotSupported();
|
|
}
|
|
bool anyClient = false;
|
|
foreach (SqlExpression value in values) {
|
|
anyClient |= value.IsClientAidedExpression();
|
|
}
|
|
if (anyClient) {
|
|
List<SqlClientWhen> whens = new List<SqlClientWhen>();
|
|
for (int i = 0, c = matches.Count; i < c; ++i) {
|
|
whens.Add(new SqlClientWhen(matches[i], values[i]));
|
|
}
|
|
return new SqlClientCase(clrType, discriminator, whens, sourceExpression);
|
|
}
|
|
else {
|
|
List<SqlWhen> whens = new List<SqlWhen>();
|
|
for (int i = 0, c = matches.Count; i < c; ++i) {
|
|
whens.Add(new SqlWhen(matches[i], values[i]));
|
|
}
|
|
return new SqlSimpleCase(clrType, discriminator, whens, sourceExpression);
|
|
}
|
|
}
|
|
|
|
internal SqlExpression Parameter(object value, Expression source) {
|
|
System.Diagnostics.Debug.Assert(value != null);
|
|
Type type = value.GetType();
|
|
return Value(type, this.typeProvider.From(value), value, true, source);
|
|
}
|
|
|
|
internal SqlExpression ValueFromObject(object value, Expression sourceExpression) {
|
|
return ValueFromObject(value, false, sourceExpression);
|
|
}
|
|
|
|
internal SqlExpression ValueFromObject(object value, bool isClientSpecified, Expression sourceExpression) {
|
|
if (value == null) {
|
|
System.Diagnostics.Debug.Assert(false);
|
|
throw Error.ArgumentNull("value");
|
|
}
|
|
Type clrType = value.GetType();
|
|
return ValueFromObject(value, clrType, isClientSpecified, sourceExpression);
|
|
}
|
|
|
|
// Override allowing the CLR type of the value to be specified explicitly.
|
|
internal SqlExpression ValueFromObject(object value, Type clrType, bool isClientSpecified, Expression sourceExpression) {
|
|
if (clrType == null) {
|
|
throw Error.ArgumentNull("clrType");
|
|
}
|
|
ProviderType sqlType = (value == null) ? this.typeProvider.From(clrType) : this.typeProvider.From(value);
|
|
return Value(clrType, sqlType, value, isClientSpecified, sourceExpression);
|
|
}
|
|
|
|
public SqlExpression TypedLiteralNull(Type type, Expression sourceExpression) {
|
|
return ValueFromObject(null, type, false, sourceExpression);
|
|
}
|
|
|
|
internal SqlMember Member(SqlExpression expr, MetaDataMember member) {
|
|
return new SqlMember(member.Type, this.Default(member), expr, member.Member);
|
|
}
|
|
|
|
internal SqlMember Member(SqlExpression expr, MemberInfo member) {
|
|
Type clrType = TypeSystem.GetMemberType(member);
|
|
MetaType metaType = this.model.GetMetaType(member.DeclaringType);
|
|
MetaDataMember metaDataMember = metaType.GetDataMember(member);
|
|
if (metaType != null && metaDataMember != null) {
|
|
return new SqlMember(clrType, this.Default(metaDataMember), expr, member);
|
|
} else {
|
|
return new SqlMember(clrType, this.Default(clrType), expr, member);
|
|
}
|
|
}
|
|
|
|
internal SqlExpression TypeCase(Type clrType, MetaType rowType, SqlExpression discriminator, IEnumerable<SqlTypeCaseWhen> whens, Expression sourceExpression) {
|
|
return new SqlTypeCase(clrType, typeProvider.From(clrType), rowType, discriminator, whens, sourceExpression);
|
|
}
|
|
|
|
internal SqlNew New(MetaType type, ConstructorInfo cons, IEnumerable<SqlExpression> args, IEnumerable<MemberInfo> argMembers, IEnumerable<SqlMemberAssign> bindings, Expression sourceExpression) {
|
|
SqlNew tb = new SqlNew(type, typeProvider.From(type.Type), cons, args, argMembers, bindings, sourceExpression);
|
|
return tb;
|
|
}
|
|
|
|
internal SqlMethodCall MethodCall(MethodInfo method, SqlExpression obj, SqlExpression[] args, Expression sourceExpression) {
|
|
return new SqlMethodCall(method.ReturnType, this.Default(method.ReturnType), method, obj, args, sourceExpression);
|
|
}
|
|
|
|
internal SqlMethodCall MethodCall(Type returnType, MethodInfo method, SqlExpression obj, SqlExpression[] args, Expression sourceExpression) {
|
|
return new SqlMethodCall(returnType, this.Default(returnType), method, obj, args, sourceExpression);
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlExprSet ExprSet(SqlExpression[] exprs, Expression sourceExpression) {
|
|
return new SqlExprSet(exprs[0].ClrType, exprs, sourceExpression);
|
|
}
|
|
|
|
internal SqlSubSelect SubSelect(SqlNodeType nt, SqlSelect select) {
|
|
return this.SubSelect(nt, select, null);
|
|
}
|
|
internal SqlSubSelect SubSelect(SqlNodeType nt, SqlSelect select, Type clrType) {
|
|
ProviderType sqlType = null;
|
|
switch (nt) {
|
|
case SqlNodeType.ScalarSubSelect:
|
|
case SqlNodeType.Element:
|
|
clrType = select.Selection.ClrType;
|
|
sqlType = select.Selection.SqlType;
|
|
break;
|
|
case SqlNodeType.Multiset:
|
|
if (clrType == null) {
|
|
clrType = typeof(List<>).MakeGenericType(select.Selection.ClrType);
|
|
}
|
|
sqlType = typeProvider.GetApplicationType((int)ConverterSpecialTypes.Table);
|
|
break;
|
|
case SqlNodeType.Exists:
|
|
clrType = typeof(bool);
|
|
sqlType = typeProvider.From(typeof(bool));
|
|
break;
|
|
}
|
|
return new SqlSubSelect(nt, clrType, sqlType, select);
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlDoNotVisitExpression DoNotVisitExpression(SqlExpression expr) {
|
|
return new SqlDoNotVisitExpression(expr);
|
|
}
|
|
|
|
internal SqlFunctionCall FunctionCall(Type clrType, string name, IEnumerable<SqlExpression> args, Expression source) {
|
|
return new SqlFunctionCall(clrType, Default(clrType), name, args, source);
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
|
|
internal SqlFunctionCall FunctionCall(Type clrType, ProviderType sqlType, string name, IEnumerable<SqlExpression> args, Expression source) {
|
|
return new SqlFunctionCall(clrType, sqlType, name, args, source);
|
|
}
|
|
|
|
internal SqlTableValuedFunctionCall TableValuedFunctionCall(MetaType rowType, Type clrType, string name, IEnumerable<SqlExpression> args, Expression source) {
|
|
return new SqlTableValuedFunctionCall(rowType, clrType, Default(clrType), name, args, source);
|
|
}
|
|
|
|
internal ProviderType Default(Type clrType) {
|
|
return typeProvider.From(clrType);
|
|
}
|
|
|
|
internal ProviderType Default(MetaDataMember member) {
|
|
if(member == null)
|
|
throw Error.ArgumentNull("member");
|
|
|
|
if (member.DbType != null) {
|
|
return this.typeProvider.Parse(member.DbType);
|
|
}
|
|
else {
|
|
return this.typeProvider.From(member.Type);
|
|
}
|
|
}
|
|
|
|
internal SqlJoin MakeJoin(SqlJoinType joinType, SqlSource location, SqlAlias alias, SqlExpression condition, Expression source) {
|
|
// 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
|
|
if (joinType == SqlJoinType.LeftOuter) {
|
|
SqlSelect sel = alias.Node as SqlSelect;
|
|
if (sel != null && sel.Selection != null && sel.Selection.NodeType != SqlNodeType.OptionalValue) {
|
|
// replace selection w/ optional + outer-joined-value
|
|
sel.Selection = new SqlOptionalValue(
|
|
new SqlColumn(
|
|
"test",
|
|
this.Unary(SqlNodeType.OuterJoinedValue,
|
|
this.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, source))
|
|
),
|
|
sel.Selection
|
|
);
|
|
}
|
|
}
|
|
return new SqlJoin(joinType, location, alias, condition, source);
|
|
}
|
|
}
|
|
}
|