You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
internal abstract class DbFormatter {
|
||||
internal abstract string Format(SqlNode node, bool isDebug);
|
||||
internal abstract string Format(SqlNode node);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Data.Linq;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
// SQL Client extensions to ExpressionType
|
||||
internal enum InternalExpressionType {
|
||||
Known = 2000,
|
||||
LinkedTable = 2001
|
||||
}
|
||||
|
||||
abstract internal class InternalExpression : Expression {
|
||||
#pragma warning disable 618 // Disable the 'obsolete' warning.
|
||||
internal InternalExpression(InternalExpressionType nt, Type type)
|
||||
: base ((ExpressionType)nt, type) {
|
||||
}
|
||||
#pragma warning restore 618
|
||||
internal static KnownExpression Known(SqlExpression expr) {
|
||||
return new KnownExpression(expr, expr.ClrType);
|
||||
}
|
||||
internal static KnownExpression Known(SqlNode node, Type type) {
|
||||
return new KnownExpression(node, type);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class KnownExpression : InternalExpression {
|
||||
SqlNode node;
|
||||
internal KnownExpression(SqlNode node, Type type)
|
||||
: base(InternalExpressionType.Known, type) {
|
||||
this.node = node;
|
||||
}
|
||||
internal SqlNode Node {
|
||||
get { return this.node; }
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LinkedTableExpression : InternalExpression {
|
||||
private SqlLink link;
|
||||
private ITable table;
|
||||
internal LinkedTableExpression(SqlLink link, ITable table, Type type)
|
||||
: base(InternalExpressionType.LinkedTable, type) {
|
||||
this.link = link;
|
||||
this.table = table;
|
||||
}
|
||||
internal SqlLink Link {
|
||||
get {return this.link;}
|
||||
}
|
||||
internal ITable Table {
|
||||
get {return this.table;}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
internal interface IConnectionManager {
|
||||
DbConnection UseConnection(IConnectionUser user);
|
||||
void ReleaseConnection(IConnectionUser user);
|
||||
}
|
||||
|
||||
internal interface IConnectionUser {
|
||||
void CompleteUse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Data.Linq.Mapping;
|
||||
using System.Data.Linq.Provider;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
/// <summary>
|
||||
/// This class defines the rules for inheritance behaviors. The rules:
|
||||
///
|
||||
/// (1) The same field may not be mapped to different database columns.
|
||||
/// The DistinguishedMemberName and AreSameMember methods describe what 'same' means between two MemberInfos.
|
||||
/// (2) Discriminators held in fixed-length fields in the database don't need
|
||||
/// to be manually padded in inheritance mapping [InheritanceMapping(Code='x')].
|
||||
///
|
||||
/// </summary>
|
||||
static class InheritanceRules {
|
||||
/// <summary>
|
||||
/// Creates a name that is the same when the member should be considered 'same'
|
||||
/// for the purposes of the inheritance feature.
|
||||
/// </summary>
|
||||
internal static object DistinguishedMemberName(MemberInfo mi) {
|
||||
PropertyInfo pi = mi as PropertyInfo;
|
||||
FieldInfo fi = mi as FieldInfo;
|
||||
if (fi != null) {
|
||||
// Human readable variant:
|
||||
// return "fi:" + mi.Name + ":" + mi.DeclaringType;
|
||||
return new MetaPosition(mi);
|
||||
}
|
||||
else if (pi != null) {
|
||||
MethodInfo meth = null;
|
||||
|
||||
if (pi.CanRead) {
|
||||
meth = pi.GetGetMethod();
|
||||
}
|
||||
if (meth == null && pi.CanWrite) {
|
||||
meth = pi.GetSetMethod();
|
||||
}
|
||||
bool isVirtual = meth != null && meth.IsVirtual;
|
||||
|
||||
// Human readable variant:
|
||||
// return "pi:" + mi.Name + ":" + (isVirtual ? "virtual" : mi.DeclaringType.ToString());
|
||||
|
||||
if (isVirtual) {
|
||||
return mi.Name;
|
||||
} else {
|
||||
return new MetaPosition(mi);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw Error.ArgumentOutOfRange("mi");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two MemberInfos for 'same-ness'.
|
||||
/// </summary>
|
||||
internal static bool AreSameMember(MemberInfo mi1, MemberInfo mi2) {
|
||||
return DistinguishedMemberName(mi1).Equals(DistinguishedMemberName(mi2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The representation of a inheritance code when mapped to a specific provider type.
|
||||
/// </summary>
|
||||
internal static object InheritanceCodeForClientCompare(object rawCode, System.Data.Linq.SqlClient.ProviderType providerType) {
|
||||
// If its a fixed-size string type in the store then pad it with spaces so that
|
||||
// comparing the string on the client agrees with the value returnd on the store.
|
||||
if (providerType.IsFixedSize && rawCode.GetType()==typeof(string)) {
|
||||
string s = (string) rawCode;
|
||||
if (providerType.Size.HasValue && s.Length!=providerType.Size) {
|
||||
s = s.PadRight(providerType.Size.Value).Substring(0,providerType.Size.Value);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return rawCode;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
/// <summary>
|
||||
/// Annotation about a particular SqlNode.
|
||||
/// </summary>
|
||||
internal abstract class SqlNodeAnnotation {
|
||||
string message;
|
||||
internal SqlNodeAnnotation(string message) {
|
||||
this.message = message;
|
||||
}
|
||||
internal string Message {
|
||||
get {return this.message;}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
/// <summary>
|
||||
/// Associate annotations with SqlNodes.
|
||||
/// </summary>
|
||||
internal class SqlNodeAnnotations {
|
||||
Dictionary<SqlNode, List<SqlNodeAnnotation>> annotationMap = new Dictionary<SqlNode, List<SqlNodeAnnotation>>();
|
||||
Dictionary<Type, string> uniqueTypes = new Dictionary<Type, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Add an annotation to the given node.
|
||||
/// </summary>
|
||||
internal void Add(SqlNode node, SqlNodeAnnotation annotation) {
|
||||
List<SqlNodeAnnotation> list = null;
|
||||
|
||||
if (!this.annotationMap.TryGetValue(node, out list)) {
|
||||
list = new List<SqlNodeAnnotation>();
|
||||
this.annotationMap[node]=list;
|
||||
}
|
||||
|
||||
uniqueTypes[annotation.GetType()] = String.Empty;
|
||||
|
||||
list.Add(annotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the annotations for the given node. Null if none.
|
||||
/// </summary>
|
||||
internal List<SqlNodeAnnotation> Get(SqlNode node) {
|
||||
List<SqlNodeAnnotation> list = null;
|
||||
this.annotationMap.TryGetValue(node, out list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given node has annotations.
|
||||
/// </summary>
|
||||
internal bool NodeIsAnnotated(SqlNode node) {
|
||||
if (node == null)
|
||||
return false;
|
||||
return this.annotationMap.ContainsKey(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether there's at least one annotation of the given type.
|
||||
/// </summary>
|
||||
internal bool HasAnnotationType(Type type) {
|
||||
return this.uniqueTypes.ContainsKey(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
internal static class SqlNodeTypeOperators {
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given unary operator node type returns a value that
|
||||
/// is predicate.
|
||||
/// </summary>
|
||||
[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.")]
|
||||
internal static bool IsPredicateUnaryOperator(this SqlNodeType nodeType) {
|
||||
switch (nodeType) {
|
||||
case SqlNodeType.Not:
|
||||
case SqlNodeType.Not2V:
|
||||
case SqlNodeType.IsNull:
|
||||
case SqlNodeType.IsNotNull:
|
||||
return true;
|
||||
case SqlNodeType.Negate:
|
||||
case SqlNodeType.BitNot:
|
||||
case SqlNodeType.Count:
|
||||
case SqlNodeType.LongCount:
|
||||
case SqlNodeType.Max:
|
||||
case SqlNodeType.Min:
|
||||
case SqlNodeType.Sum:
|
||||
case SqlNodeType.Avg:
|
||||
case SqlNodeType.Stddev:
|
||||
case SqlNodeType.Convert:
|
||||
case SqlNodeType.ValueOf:
|
||||
case SqlNodeType.OuterJoinedValue:
|
||||
case SqlNodeType.ClrLength:
|
||||
return false;
|
||||
default:
|
||||
throw Error.UnexpectedNode(nodeType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given unary operator expects a predicate as input.
|
||||
/// </summary>
|
||||
[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.")]
|
||||
internal static bool IsUnaryOperatorExpectingPredicateOperand(this SqlNodeType nodeType) {
|
||||
switch (nodeType) {
|
||||
case SqlNodeType.Not:
|
||||
case SqlNodeType.Not2V:
|
||||
return true;
|
||||
case SqlNodeType.IsNull:
|
||||
case SqlNodeType.IsNotNull:
|
||||
case SqlNodeType.Negate:
|
||||
case SqlNodeType.BitNot:
|
||||
case SqlNodeType.Count:
|
||||
case SqlNodeType.LongCount:
|
||||
case SqlNodeType.Max:
|
||||
case SqlNodeType.Min:
|
||||
case SqlNodeType.Sum:
|
||||
case SqlNodeType.Avg:
|
||||
case SqlNodeType.Stddev:
|
||||
case SqlNodeType.Convert:
|
||||
case SqlNodeType.ValueOf:
|
||||
case SqlNodeType.OuterJoinedValue:
|
||||
case SqlNodeType.ClrLength:
|
||||
return false;
|
||||
default:
|
||||
throw Error.UnexpectedNode(nodeType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given binary operator node type returns a value that
|
||||
/// is a predicate boolean.
|
||||
/// </summary>
|
||||
[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.")]
|
||||
internal static bool IsPredicateBinaryOperator(this SqlNodeType nodeType) {
|
||||
switch (nodeType) {
|
||||
case SqlNodeType.GE:
|
||||
case SqlNodeType.GT:
|
||||
case SqlNodeType.LE:
|
||||
case SqlNodeType.LT:
|
||||
case SqlNodeType.EQ:
|
||||
case SqlNodeType.NE:
|
||||
case SqlNodeType.EQ2V:
|
||||
case SqlNodeType.NE2V:
|
||||
case SqlNodeType.And:
|
||||
case SqlNodeType.Or:
|
||||
return true;
|
||||
case SqlNodeType.Add:
|
||||
case SqlNodeType.Sub:
|
||||
case SqlNodeType.Mul:
|
||||
case SqlNodeType.Div:
|
||||
case SqlNodeType.Mod:
|
||||
case SqlNodeType.BitAnd:
|
||||
case SqlNodeType.BitOr:
|
||||
case SqlNodeType.BitXor:
|
||||
case SqlNodeType.Concat:
|
||||
case SqlNodeType.Coalesce:
|
||||
return false;
|
||||
default:
|
||||
throw Error.UnexpectedNode(nodeType);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether this operator is a binary comparison operator (i.e. >, =>, ==, etc)
|
||||
/// </summary>
|
||||
[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.")]
|
||||
internal static bool IsComparisonOperator(this SqlNodeType nodeType)
|
||||
{
|
||||
switch (nodeType)
|
||||
{
|
||||
case SqlNodeType.GE:
|
||||
case SqlNodeType.GT:
|
||||
case SqlNodeType.LE:
|
||||
case SqlNodeType.LT:
|
||||
case SqlNodeType.EQ:
|
||||
case SqlNodeType.NE:
|
||||
case SqlNodeType.EQ2V:
|
||||
case SqlNodeType.NE2V:
|
||||
return true;
|
||||
case SqlNodeType.And:
|
||||
case SqlNodeType.Or:
|
||||
case SqlNodeType.Add:
|
||||
case SqlNodeType.Sub:
|
||||
case SqlNodeType.Mul:
|
||||
case SqlNodeType.Div:
|
||||
case SqlNodeType.Mod:
|
||||
case SqlNodeType.BitAnd:
|
||||
case SqlNodeType.BitOr:
|
||||
case SqlNodeType.BitXor:
|
||||
case SqlNodeType.Concat:
|
||||
case SqlNodeType.Coalesce:
|
||||
return false;
|
||||
default:
|
||||
throw Error.UnexpectedNode(nodeType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given binary operator node type returns a value that
|
||||
/// is a predicate boolean.
|
||||
/// </summary>
|
||||
[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.")]
|
||||
internal static bool IsBinaryOperatorExpectingPredicateOperands(this SqlNodeType nodeType) {
|
||||
switch (nodeType) {
|
||||
case SqlNodeType.And:
|
||||
case SqlNodeType.Or:
|
||||
return true;
|
||||
case SqlNodeType.EQ:
|
||||
case SqlNodeType.EQ2V:
|
||||
case SqlNodeType.GE:
|
||||
case SqlNodeType.GT:
|
||||
case SqlNodeType.LE:
|
||||
case SqlNodeType.LT:
|
||||
case SqlNodeType.NE:
|
||||
case SqlNodeType.NE2V:
|
||||
case SqlNodeType.Add:
|
||||
case SqlNodeType.Sub:
|
||||
case SqlNodeType.Mul:
|
||||
case SqlNodeType.Div:
|
||||
case SqlNodeType.Mod:
|
||||
case SqlNodeType.BitAnd:
|
||||
case SqlNodeType.BitOr:
|
||||
case SqlNodeType.BitXor:
|
||||
case SqlNodeType.Concat:
|
||||
case SqlNodeType.Coalesce:
|
||||
return false;
|
||||
default:
|
||||
throw Error.UnexpectedNode(nodeType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given node requires support on the client for evaluation.
|
||||
/// For example, LINK nodes may be delay-executed only when the user requests the result.
|
||||
/// </summary>
|
||||
internal static bool IsClientAidedExpression(this SqlExpression expr) {
|
||||
switch (expr.NodeType) {
|
||||
case SqlNodeType.Link:
|
||||
case SqlNodeType.Element:
|
||||
case SqlNodeType.Multiset:
|
||||
case SqlNodeType.ClientQuery:
|
||||
case SqlNodeType.TypeCase:
|
||||
case SqlNodeType.New:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
/// <summary>
|
||||
/// Annotation which indicates that the given node will cause a compatibility problem
|
||||
/// for the indicated set of providers.
|
||||
/// </summary>
|
||||
internal class SqlServerCompatibilityAnnotation : SqlNodeAnnotation {
|
||||
SqlProvider.ProviderMode[] providers;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="message">The compatibility message.</param>
|
||||
/// <param name="providers">The set of providers this compatibility issue applies to.</param>
|
||||
internal SqlServerCompatibilityAnnotation(string message, params SqlProvider.ProviderMode[] providers)
|
||||
: base(message) {
|
||||
this.providers = providers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this annotation applies to the specified provider.
|
||||
/// </summary>
|
||||
internal bool AppliesTo(SqlProvider.ProviderMode provider) {
|
||||
foreach (SqlProvider.ProviderMode p in providers) {
|
||||
if (p == provider) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
/// <summary>
|
||||
/// Methods for checking whethe a query was compatible with the
|
||||
/// server it will be sent to.
|
||||
/// </summary>
|
||||
static internal class SqlServerCompatibilityCheck {
|
||||
|
||||
/// <summary>
|
||||
/// Private visitor class checks each node for compatibility annotations.
|
||||
/// </summary>
|
||||
private class Visitor : SqlVisitor {
|
||||
|
||||
private SqlProvider.ProviderMode provider;
|
||||
internal SqlNodeAnnotations annotations;
|
||||
|
||||
internal Visitor(SqlProvider.ProviderMode provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The reasons why this query is not 2K compatible.
|
||||
/// </summary>
|
||||
internal Collection<string> reasons = new Collection<string>();
|
||||
|
||||
internal override SqlNode Visit(SqlNode node) {
|
||||
if (annotations.NodeIsAnnotated(node)) {
|
||||
foreach (SqlNodeAnnotation annotation in annotations.Get(node)) {
|
||||
SqlServerCompatibilityAnnotation ssca = annotation as SqlServerCompatibilityAnnotation;
|
||||
if (ssca != null && ssca.AppliesTo(provider)) {
|
||||
reasons.Add(annotation.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return base.Visit(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given node is supported on the given server.
|
||||
/// </summary>
|
||||
internal static void ThrowIfUnsupported(SqlNode node, SqlNodeAnnotations annotations, SqlProvider.ProviderMode provider) {
|
||||
// Check to see whether there's at least one SqlServerCompatibilityAnnotation.
|
||||
if (annotations.HasAnnotationType(typeof(SqlServerCompatibilityAnnotation))) {
|
||||
Visitor visitor = new Visitor(provider);
|
||||
visitor.annotations = annotations;
|
||||
visitor.Visit(node);
|
||||
|
||||
// If any messages were recorded, then throw an exception.
|
||||
if (visitor.reasons.Count > 0) {
|
||||
throw Error.ExpressionNotSupportedForSqlServerVersion(visitor.reasons);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,302 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
internal static class TypeSystem {
|
||||
|
||||
internal static bool IsSequenceType(Type seqType) {
|
||||
return seqType != typeof(string)
|
||||
&& seqType != typeof(byte[])
|
||||
&& seqType != typeof(char[])
|
||||
&& FindIEnumerable(seqType) != null;
|
||||
}
|
||||
internal static bool HasIEnumerable(Type seqType) {
|
||||
return FindIEnumerable(seqType) != null;
|
||||
}
|
||||
private static Type FindIEnumerable(Type seqType) {
|
||||
if (seqType == null || seqType == typeof(string))
|
||||
return null;
|
||||
if (seqType.IsArray)
|
||||
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
|
||||
if (seqType.IsGenericType) {
|
||||
foreach (Type arg in seqType.GetGenericArguments()) {
|
||||
Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
|
||||
if (ienum.IsAssignableFrom(seqType)) {
|
||||
return ienum;
|
||||
}
|
||||
}
|
||||
}
|
||||
Type[] ifaces = seqType.GetInterfaces();
|
||||
if (ifaces != null && ifaces.Length > 0) {
|
||||
foreach (Type iface in ifaces) {
|
||||
Type ienum = FindIEnumerable(iface);
|
||||
if (ienum != null) return ienum;
|
||||
}
|
||||
}
|
||||
if (seqType.BaseType != null && seqType.BaseType != typeof(object)) {
|
||||
return FindIEnumerable(seqType.BaseType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
internal static Type GetFlatSequenceType(Type elementType) {
|
||||
Type ienum = FindIEnumerable(elementType);
|
||||
if (ienum != null) return ienum;
|
||||
return typeof(IEnumerable<>).MakeGenericType(elementType);
|
||||
}
|
||||
internal static Type GetSequenceType(Type elementType) {
|
||||
return typeof(IEnumerable<>).MakeGenericType(elementType);
|
||||
}
|
||||
internal static Type GetElementType(Type seqType) {
|
||||
Type ienum = FindIEnumerable(seqType);
|
||||
if (ienum == null) return seqType;
|
||||
return ienum.GetGenericArguments()[0];
|
||||
}
|
||||
internal static bool IsNullableType(Type type) {
|
||||
return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
}
|
||||
internal static bool IsNullAssignable(Type type) {
|
||||
return !type.IsValueType || IsNullableType(type);
|
||||
}
|
||||
internal static Type GetNonNullableType(Type type) {
|
||||
if (IsNullableType(type)) {
|
||||
return type.GetGenericArguments()[0];
|
||||
}
|
||||
return type;
|
||||
}
|
||||
internal static Type GetMemberType(MemberInfo mi) {
|
||||
FieldInfo fi = mi as FieldInfo;
|
||||
if (fi != null) return fi.FieldType;
|
||||
PropertyInfo pi = mi as PropertyInfo;
|
||||
if (pi != null) return pi.PropertyType;
|
||||
EventInfo ei = mi as EventInfo;
|
||||
if (ei != null) return ei.EventHandlerType;
|
||||
return null;
|
||||
}
|
||||
internal static IEnumerable<FieldInfo> GetAllFields(Type type, BindingFlags flags) {
|
||||
Dictionary<MetaPosition, FieldInfo> seen = new Dictionary<MetaPosition, FieldInfo>();
|
||||
Type currentType = type;
|
||||
do {
|
||||
foreach (FieldInfo fi in currentType.GetFields(flags)) {
|
||||
if (fi.IsPrivate || type == currentType) {
|
||||
MetaPosition mp = new MetaPosition(fi);
|
||||
seen[mp] = fi;
|
||||
}
|
||||
}
|
||||
currentType = currentType.BaseType;
|
||||
} while (currentType != null);
|
||||
return seen.Values;
|
||||
}
|
||||
internal static IEnumerable<PropertyInfo> GetAllProperties(Type type, BindingFlags flags) {
|
||||
Dictionary<MetaPosition, PropertyInfo> seen = new Dictionary<MetaPosition, PropertyInfo>();
|
||||
Type currentType = type;
|
||||
do {
|
||||
foreach (PropertyInfo pi in currentType.GetProperties(flags)) {
|
||||
if (type == currentType || IsPrivate(pi)) {
|
||||
MetaPosition mp = new MetaPosition(pi);
|
||||
seen[mp] = pi;
|
||||
}
|
||||
}
|
||||
currentType = currentType.BaseType;
|
||||
} while (currentType != null);
|
||||
return seen.Values;
|
||||
}
|
||||
|
||||
private static bool IsPrivate(PropertyInfo pi) {
|
||||
MethodInfo mi = pi.GetGetMethod() ?? pi.GetSetMethod();
|
||||
if (mi != null) {
|
||||
return mi.IsPrivate;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ILookup<string, MethodInfo> _sequenceMethods;
|
||||
internal static MethodInfo FindSequenceMethod(string name, Type[] args, params Type[] typeArgs) {
|
||||
if (_sequenceMethods == null) {
|
||||
_sequenceMethods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).ToLookup(m => m.Name);
|
||||
}
|
||||
MethodInfo mi = _sequenceMethods[name].FirstOrDefault(m => ArgsMatchExact(m, args, typeArgs));
|
||||
if (mi == null)
|
||||
return null;
|
||||
if (typeArgs != null)
|
||||
return mi.MakeGenericMethod(typeArgs);
|
||||
return mi;
|
||||
}
|
||||
internal static MethodInfo FindSequenceMethod(string name, IEnumerable sequence) {
|
||||
return FindSequenceMethod(name, new Type[] {sequence.GetType()}, new Type[] {GetElementType(sequence.GetType())});
|
||||
}
|
||||
|
||||
private static ILookup<string, MethodInfo> _queryMethods;
|
||||
internal static MethodInfo FindQueryableMethod(string name, Type[] args, params Type[] typeArgs) {
|
||||
if (_queryMethods == null) {
|
||||
_queryMethods = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).ToLookup(m => m.Name);
|
||||
}
|
||||
MethodInfo mi = _queryMethods[name].FirstOrDefault(m => ArgsMatchExact(m, args, typeArgs));
|
||||
if (mi == null)
|
||||
throw Error.NoMethodInTypeMatchingArguments(typeof(Queryable));
|
||||
if (typeArgs != null)
|
||||
return mi.MakeGenericMethod(typeArgs);
|
||||
return mi;
|
||||
}
|
||||
|
||||
internal static MethodInfo FindStaticMethod(Type type, string name, Type[] args, params Type[] typeArgs) {
|
||||
MethodInfo mi = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.FirstOrDefault(m => m.Name == name && ArgsMatchExact(m, args, typeArgs));
|
||||
if (mi == null)
|
||||
throw Error.NoMethodInTypeMatchingArguments(type);
|
||||
if (typeArgs != null)
|
||||
return mi.MakeGenericMethod(typeArgs);
|
||||
return mi;
|
||||
}
|
||||
|
||||
private static bool ArgsMatchExact(MethodInfo m, Type[] argTypes, Type[] typeArgs) {
|
||||
ParameterInfo[] mParams = m.GetParameters();
|
||||
if (mParams.Length != argTypes.Length)
|
||||
return false;
|
||||
if (!m.IsGenericMethodDefinition && m.IsGenericMethod && m.ContainsGenericParameters) {
|
||||
m = m.GetGenericMethodDefinition();
|
||||
}
|
||||
if (m.IsGenericMethodDefinition) {
|
||||
if (typeArgs == null || typeArgs.Length == 0)
|
||||
return false;
|
||||
if (m.GetGenericArguments().Length != typeArgs.Length)
|
||||
return false;
|
||||
m = m.MakeGenericMethod(typeArgs);
|
||||
mParams = m.GetParameters();
|
||||
}
|
||||
else if (typeArgs != null && typeArgs.Length > 0) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, n = argTypes.Length; i < n; i++) {
|
||||
Type parameterType = mParams[i].ParameterType;
|
||||
if (parameterType == null)
|
||||
return false;
|
||||
Type argType = argTypes[i];
|
||||
if (!parameterType.IsAssignableFrom(argType))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the type is one of the built in simple types.
|
||||
/// </summary>
|
||||
internal static bool IsSimpleType(Type type)
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
type = type.GetGenericArguments()[0];
|
||||
|
||||
if (type.IsEnum)
|
||||
return true;
|
||||
|
||||
if (type == typeof(Guid))
|
||||
return true;
|
||||
|
||||
TypeCode tc = Type.GetTypeCode(type);
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
case TypeCode.Decimal:
|
||||
case TypeCode.Char:
|
||||
case TypeCode.String:
|
||||
case TypeCode.Boolean:
|
||||
case TypeCode.DateTime:
|
||||
return true;
|
||||
case TypeCode.Object:
|
||||
return (typeof(TimeSpan) == type) || (typeof(DateTimeOffset) == type);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hashable MetaDataToken+Assembly. This type uniquely describes a metadata element
|
||||
/// like a MemberInfo. MetaDataToken by itself is not sufficient because its only
|
||||
/// unique within a single assembly.
|
||||
/// </summary>
|
||||
internal struct MetaPosition : IEqualityComparer<MetaPosition>, IEqualityComparer {
|
||||
private int metadataToken;
|
||||
private Assembly assembly;
|
||||
internal MetaPosition(MemberInfo mi)
|
||||
: this(mi.DeclaringType.Assembly, mi.MetadataToken) {
|
||||
}
|
||||
private MetaPosition(Assembly assembly, int metadataToken) {
|
||||
this.assembly = assembly;
|
||||
this.metadataToken = metadataToken;
|
||||
}
|
||||
|
||||
// Equality is implemented here according to the advice in
|
||||
// CLR via C# 2ed, J. Richter, p 146. In particular, ValueType.Equals
|
||||
// should not be called for perf reasons.
|
||||
|
||||
#region Object Members
|
||||
public override bool Equals(object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
if (obj.GetType() != this.GetType())
|
||||
return false;
|
||||
|
||||
return AreEqual(this, (MetaPosition)obj);
|
||||
}
|
||||
public override int GetHashCode() {
|
||||
return metadataToken;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEqualityComparer<MetaPosition> Members
|
||||
public bool Equals(MetaPosition x, MetaPosition y) {
|
||||
return AreEqual(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(MetaPosition obj) {
|
||||
return obj.metadataToken;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEqualityComparer Members
|
||||
bool IEqualityComparer.Equals(object x, object y) {
|
||||
return this.Equals((MetaPosition)x, (MetaPosition)y);
|
||||
}
|
||||
int IEqualityComparer.GetHashCode(object obj) {
|
||||
return this.GetHashCode((MetaPosition) obj);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static bool AreEqual(MetaPosition x, MetaPosition y) {
|
||||
return (x.metadataToken == y.metadataToken)
|
||||
&& (x.assembly == y.assembly);
|
||||
}
|
||||
|
||||
// Since MetaPositions are immutable, we overload the equality operator
|
||||
// to test for value equality, rather than reference equality
|
||||
public static bool operator==(MetaPosition x, MetaPosition y) {
|
||||
return AreEqual(x, y);
|
||||
}
|
||||
public static bool operator !=(MetaPosition x, MetaPosition y) {
|
||||
return !AreEqual(x, y);
|
||||
}
|
||||
|
||||
internal static bool AreSameMember(MemberInfo x, MemberInfo y) {
|
||||
if (x.MetadataToken != y.MetadataToken || x.DeclaringType.Assembly != y.DeclaringType.Assembly) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
/// <summary>
|
||||
/// Abstracts the provider side type system. Encapsulates:
|
||||
/// - Mapping from runtime types to provider types.
|
||||
/// - Parsing type strings in the provider's language.
|
||||
/// - Handling application defined (opaque) types.
|
||||
/// - Type coercion precedence rules.
|
||||
/// - Type family organization.
|
||||
/// </summary>
|
||||
internal abstract class TypeSystemProvider {
|
||||
|
||||
internal abstract ProviderType PredictTypeForUnary(SqlNodeType unaryOp, ProviderType operandType);
|
||||
|
||||
internal abstract ProviderType PredictTypeForBinary(SqlNodeType binaryOp, ProviderType leftType, ProviderType rightType);
|
||||
|
||||
/// <summary>
|
||||
/// Return the provider type corresponding to the given clr type.
|
||||
/// </summary>
|
||||
internal abstract ProviderType From(Type runtimeType);
|
||||
|
||||
/// <summary>
|
||||
/// Return the provider type corresponding to the given object instance.
|
||||
/// </summary>
|
||||
internal abstract ProviderType From(object o);
|
||||
|
||||
/// <summary>
|
||||
/// Return the provider type corresponding to the given clr type and size.
|
||||
/// </summary>
|
||||
internal abstract ProviderType From(Type type, int? size);
|
||||
|
||||
/// <summary>
|
||||
/// Return a type by parsing a string. The format of the string is
|
||||
/// provider specific.
|
||||
/// </summary>
|
||||
internal abstract ProviderType Parse(string text);
|
||||
|
||||
/// <summary>
|
||||
/// Return a type understood only by the application.
|
||||
/// Each call with the same index will return the same ProviderType.
|
||||
/// </summary>
|
||||
internal abstract ProviderType GetApplicationType(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most precise type in the family of the type given.
|
||||
/// A family is a group types that serve similar functions. For example,
|
||||
/// in SQL SmallInt and Int are part of one family.
|
||||
/// </summary>
|
||||
internal abstract ProviderType MostPreciseTypeInFamily(ProviderType type);
|
||||
|
||||
/// <summary>
|
||||
/// For LOB data types that have large type equivalents, this function returns the equivalent large
|
||||
/// data type. If the type is not an LOB or cannot be converted, the function returns the current type.
|
||||
/// For example SqlServer defines the 'Image' LOB type, whose large type equivalent is VarBinary(MAX).
|
||||
/// </summary>
|
||||
internal abstract ProviderType GetBestLargeType(ProviderType type);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a type that can be used to hold values for both the current
|
||||
/// type and the specified type without data loss.
|
||||
/// </summary>
|
||||
internal abstract ProviderType GetBestType(ProviderType typeA, ProviderType typeB);
|
||||
|
||||
internal abstract ProviderType ReturnTypeOfFunction(SqlFunctionCall functionCall);
|
||||
|
||||
/// <summary>
|
||||
/// Get a type that can hold the same information but belongs to a different type family.
|
||||
/// For example, to represent a SQL NChar as an integer type, we need to use the type int.
|
||||
/// (SQL smallint would not be able to contain characters with unicode >32768)
|
||||
/// </summary>
|
||||
/// <param name="toType">Type of the target type family</param>
|
||||
/// <returns>Smallest type of target type family that can hold equivalent information</returns>
|
||||
internal abstract ProviderType ChangeTypeFamilyTo(ProviderType type, ProviderType typeWithFamily);
|
||||
|
||||
internal abstract void InitializeParameter(ProviderType type, System.Data.Common.DbParameter parameter, object value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags control the format of string returned by ToQueryString().
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum QueryFormatOptions {
|
||||
None = 0,
|
||||
SuppressSize = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An abstract type exposed by the TypeSystemProvider.
|
||||
/// </summary>
|
||||
internal abstract class ProviderType {
|
||||
|
||||
/// <summary>
|
||||
/// True if this type is a Unicode type (eg, ntext, etc).
|
||||
/// </summary>
|
||||
internal abstract bool IsUnicodeType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// For a unicode type, return it's non-unicode equivalent.
|
||||
/// </summary>
|
||||
internal abstract ProviderType GetNonUnicodeEquivalent();
|
||||
|
||||
/// <summary>
|
||||
/// True if this type has only a CLR representation and no provider representation.
|
||||
/// </summary>
|
||||
internal abstract bool IsRuntimeOnlyType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this type is an application defined type.
|
||||
/// </summary>
|
||||
internal abstract bool IsApplicationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether this is the given application type.
|
||||
/// </summary>
|
||||
internal abstract bool IsApplicationTypeOf(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CLR type which most closely corresponds to this provider type.
|
||||
/// </summary>
|
||||
internal abstract Type GetClosestRuntimeType();
|
||||
|
||||
/// <summary>
|
||||
/// Compare implicit type coercion precedence.
|
||||
/// -1 means there is an implicit conversion from this->type.
|
||||
/// 0 means there is a two way implicit conversion from this->type
|
||||
/// 1 means there is an implicit conversion from type->this.
|
||||
/// </summary>
|
||||
internal abstract int ComparePrecedenceTo(ProviderType type);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two types are in the same type family.
|
||||
/// A family is a group types that serve similar functions. For example,
|
||||
/// in SQL SmallInt and Int are part of one family.
|
||||
/// </summary>
|
||||
internal abstract bool IsSameTypeFamily(ProviderType type);
|
||||
|
||||
/// <summary>
|
||||
/// Used to indicate if the type supports comparison in provider.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if type supports comparison in provider.</returns>
|
||||
internal abstract bool SupportsComparison { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to indicate if the types supports Length function (LEN in T-SQL).
|
||||
/// </summary>
|
||||
/// <returns>Returns true if type supports use of length function on the type.</returns>
|
||||
internal abstract bool SupportsLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given values will be equal to eachother for this type.
|
||||
/// </summary>
|
||||
internal abstract bool AreValuesEqual(object o1, object o2);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this type is a LOB (large object) type, or an equivalent type.
|
||||
/// For example, on SqlServer, Image and VarChar(MAX) among others are considered large types.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal abstract bool IsLargeType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert this type into a string that can be used in a query.
|
||||
/// </summary>
|
||||
internal abstract string ToQueryString();
|
||||
|
||||
/// <summary>
|
||||
/// Convert this type into a string that can be used in a query.
|
||||
/// </summary>
|
||||
internal abstract string ToQueryString(QueryFormatOptions formatOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this type is fixed size or not.
|
||||
/// </summary>
|
||||
internal abstract bool IsFixedSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type has a size or is large.
|
||||
/// </summary>
|
||||
internal abstract bool HasSizeOrIsLarge { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The size of this type.
|
||||
/// </summary>
|
||||
internal abstract int? Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type can be ordered.
|
||||
/// </summary>
|
||||
internal abstract bool IsOrderable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type can be grouped.
|
||||
/// </summary>
|
||||
internal abstract bool IsGroupable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type can appear in a column
|
||||
/// </summary>
|
||||
internal abstract bool CanBeColumn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type can appear as a parameter
|
||||
/// </summary>
|
||||
internal abstract bool CanBeParameter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type is a single character type.
|
||||
/// </summary>
|
||||
internal abstract bool IsChar { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type is a multi-character type.
|
||||
/// </summary>
|
||||
internal abstract bool IsString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the type is a number.
|
||||
/// </summary>
|
||||
internal abstract bool IsNumeric { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the type uses precision and scale. For example, returns true
|
||||
/// for SqlDBTypes Decimal, Float and Real.
|
||||
/// </summary>
|
||||
internal abstract bool HasPrecisionAndScale { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if it is safe to suppress size specifications for
|
||||
/// the operand of a cast/convert. For example, when casting to string,
|
||||
/// all these types have length less than the default sized used by SqlServer,
|
||||
/// so the length specification can be omitted without fear of truncation.
|
||||
/// </summary>
|
||||
internal abstract bool CanSuppressSizeForConversionToString{ get; }
|
||||
|
||||
public static bool operator ==(ProviderType typeA, ProviderType typeB) {
|
||||
if ((object)typeA == (object)typeB)
|
||||
return true;
|
||||
if ((object)typeA != null)
|
||||
return typeA.Equals(typeB);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator != (ProviderType typeA, ProviderType typeB) {
|
||||
if ((object)typeA == (object)typeB)
|
||||
return false;
|
||||
if ((object)typeA != null)
|
||||
return !typeA.Equals(typeB);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
/// <summary>
|
||||
/// DLinq-providerbase-specific custom exception factory.
|
||||
/// </summary>
|
||||
internal partial class Error {
|
||||
/// <summary>
|
||||
/// Exception thrown when a query cannot execute against a particular SQL server version.
|
||||
/// </summary>
|
||||
static internal Exception ExpressionNotSupportedForSqlServerVersion(Collection<string> reasons) {
|
||||
StringBuilder exceptionMessage = new StringBuilder(Strings.CannotTranslateExpressionToSql);
|
||||
foreach (string reason in reasons) {
|
||||
exceptionMessage.AppendLine(reason);
|
||||
}
|
||||
return new NotSupportedException(exceptionMessage.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
/// <summary>
|
||||
/// Flags that control the optimization of SQL produced.
|
||||
/// Only optimization flags should go here because QA will be looking
|
||||
/// here to see what optimizations they need to test.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum OptimizationFlags {
|
||||
None = 0,
|
||||
SimplifyCaseStatements = 1,
|
||||
OptimizeLinkExpansions = 2,
|
||||
All = SimplifyCaseStatements | OptimizeLinkExpansions
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
#if PERFORMANCE_BUILD
|
||||
class PerfTimer {
|
||||
long startTime;
|
||||
long stopTime;
|
||||
long frequency;
|
||||
|
||||
public PerfTimer() {
|
||||
QueryPerformanceFrequency(out frequency);
|
||||
}
|
||||
|
||||
public void Start() {
|
||||
Thread.Sleep(0);
|
||||
QueryPerformanceCounter(out startTime);
|
||||
}
|
||||
|
||||
public void Stop() {
|
||||
QueryPerformanceCounter(out stopTime);
|
||||
}
|
||||
|
||||
public long Duration {
|
||||
get { return (long)( 1000000.0 * (double)(stopTime - startTime) / (double) frequency ); }
|
||||
}
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
|
||||
private static extern bool QueryPerformanceCounter(out long count);
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
|
||||
private static extern bool QueryPerformanceFrequency(out long frequency);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
internal class SqlRowNumberChecker {
|
||||
Visitor rowNumberVisitor;
|
||||
|
||||
internal SqlRowNumberChecker() {
|
||||
this.rowNumberVisitor = new Visitor();
|
||||
}
|
||||
|
||||
internal bool HasRowNumber(SqlNode node) {
|
||||
this.rowNumberVisitor.Visit(node);
|
||||
return rowNumberVisitor.HasRowNumber;
|
||||
}
|
||||
|
||||
internal bool HasRowNumber(SqlRow row) {
|
||||
foreach (SqlColumn column in row.Columns) {
|
||||
if (this.HasRowNumber(column)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal SqlColumn RowNumberColumn {
|
||||
get {
|
||||
return rowNumberVisitor.HasRowNumber ? rowNumberVisitor.CurrentColumn : null;
|
||||
}
|
||||
}
|
||||
|
||||
private class Visitor: SqlVisitor {
|
||||
bool hasRowNumber = false;
|
||||
|
||||
public bool HasRowNumber {
|
||||
get { return hasRowNumber; }
|
||||
}
|
||||
|
||||
public SqlColumn CurrentColumn { private set; get; }
|
||||
|
||||
internal override SqlRowNumber VisitRowNumber(SqlRowNumber rowNumber) {
|
||||
this.hasRowNumber = true;
|
||||
return rowNumber;
|
||||
}
|
||||
|
||||
// shortcuts
|
||||
internal override SqlExpression VisitScalarSubSelect(SqlSubSelect ss) {
|
||||
return ss;
|
||||
}
|
||||
|
||||
internal override SqlExpression VisitSubSelect(SqlSubSelect ss) {
|
||||
return ss;
|
||||
}
|
||||
|
||||
internal override SqlRow VisitRow(SqlRow row)
|
||||
{
|
||||
for (int i = 0, n = row.Columns.Count; i < n; i++) {
|
||||
row.Columns[i].Expression = this.VisitExpression(row.Columns[i].Expression);
|
||||
if (this.hasRowNumber) {
|
||||
this.CurrentColumn = row.Columns[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
internal override SqlSelect VisitSelect(SqlSelect select) {
|
||||
this.Visit(select.Row);
|
||||
this.Visit(select.Where);
|
||||
|
||||
return select;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
using System.Data.Linq.Mapping;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
internal static class Funcletizer {
|
||||
|
||||
internal static Expression Funcletize(Expression expression) {
|
||||
return new Localizer(new LocalMapper().MapLocals(expression)).Localize(expression);
|
||||
}
|
||||
|
||||
class Localizer : ExpressionVisitor {
|
||||
Dictionary<Expression, bool> locals;
|
||||
|
||||
internal Localizer(Dictionary<Expression, bool> locals) {
|
||||
this.locals = locals;
|
||||
}
|
||||
|
||||
internal Expression Localize(Expression expression) {
|
||||
return this.Visit(expression);
|
||||
}
|
||||
|
||||
internal override Expression Visit(Expression exp) {
|
||||
if (exp == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.locals.ContainsKey(exp)) {
|
||||
return MakeLocal(exp);
|
||||
}
|
||||
if (exp.NodeType == (ExpressionType)InternalExpressionType.Known) {
|
||||
return exp;
|
||||
}
|
||||
return base.Visit(exp);
|
||||
}
|
||||
|
||||
private static Expression MakeLocal(Expression e) {
|
||||
if (e.NodeType == ExpressionType.Constant) {
|
||||
return e;
|
||||
}
|
||||
else if (e.NodeType == ExpressionType.Convert || e.NodeType == ExpressionType.ConvertChecked) {
|
||||
UnaryExpression ue = (UnaryExpression)e;
|
||||
if (ue.Type == typeof(object)) {
|
||||
Expression local = MakeLocal(ue.Operand);
|
||||
return (e.NodeType == ExpressionType.Convert) ? Expression.Convert(local, e.Type) : Expression.ConvertChecked(local, e.Type);
|
||||
}
|
||||
// convert a const null
|
||||
if (ue.Operand.NodeType == ExpressionType.Constant) {
|
||||
ConstantExpression c = (ConstantExpression)ue.Operand;
|
||||
if (c.Value == null) {
|
||||
return Expression.Constant(null, ue.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Expression.Invoke(Expression.Constant(Expression.Lambda(e).Compile()));
|
||||
}
|
||||
}
|
||||
class DependenceChecker : ExpressionVisitor {
|
||||
HashSet<ParameterExpression> inScope = new HashSet<ParameterExpression>();
|
||||
bool isIndependent = true;
|
||||
|
||||
/// <summary>
|
||||
/// This method returns 'true' when the expression doesn't reference any parameters
|
||||
/// from outside the scope of the expression.
|
||||
/// </summary>
|
||||
static public bool IsIndependent(Expression expression) {
|
||||
var v = new DependenceChecker();
|
||||
v.Visit(expression);
|
||||
return v.isIndependent;
|
||||
}
|
||||
internal override Expression VisitLambda(LambdaExpression lambda) {
|
||||
foreach (var p in lambda.Parameters) {
|
||||
this.inScope.Add(p);
|
||||
}
|
||||
return base.VisitLambda(lambda);
|
||||
}
|
||||
internal override Expression VisitParameter(ParameterExpression p) {
|
||||
this.isIndependent &= this.inScope.Contains(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
class LocalMapper : ExpressionVisitor {
|
||||
bool isRemote;
|
||||
Dictionary<Expression, bool> locals;
|
||||
|
||||
internal Dictionary<Expression, bool> MapLocals(Expression expression) {
|
||||
this.locals = new Dictionary<Expression, bool>();
|
||||
this.isRemote = false;
|
||||
this.Visit(expression);
|
||||
return this.locals;
|
||||
}
|
||||
|
||||
internal override Expression Visit(Expression expression) {
|
||||
if (expression == null) {
|
||||
return null;
|
||||
}
|
||||
bool saveIsRemote = this.isRemote;
|
||||
switch (expression.NodeType) {
|
||||
case (ExpressionType)InternalExpressionType.Known:
|
||||
return expression;
|
||||
case (ExpressionType)ExpressionType.Constant:
|
||||
break;
|
||||
default:
|
||||
this.isRemote = false;
|
||||
base.Visit(expression);
|
||||
if (!this.isRemote
|
||||
&& expression.NodeType != ExpressionType.Lambda
|
||||
&& expression.NodeType != ExpressionType.Quote
|
||||
&& DependenceChecker.IsIndependent(expression)) {
|
||||
this.locals[expression] = true; // Not 'Add' because the same expression may exist in the tree twice.
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (typeof(ITable).IsAssignableFrom(expression.Type) ||
|
||||
typeof(DataContext).IsAssignableFrom(expression.Type)) {
|
||||
this.isRemote = true;
|
||||
}
|
||||
this.isRemote |= saveIsRemote;
|
||||
return expression;
|
||||
}
|
||||
internal override Expression VisitMemberAccess(MemberExpression m) {
|
||||
base.VisitMemberAccess(m);
|
||||
this.isRemote |= (m.Expression != null && typeof(ITable).IsAssignableFrom(m.Expression.Type));
|
||||
return m;
|
||||
}
|
||||
internal override Expression VisitMethodCall(MethodCallExpression m) {
|
||||
base.VisitMethodCall(m);
|
||||
this.isRemote |= m.Method.DeclaringType == typeof(System.Data.Linq.Provider.DataManipulation)
|
||||
|| Attribute.IsDefined(m.Method, typeof(FunctionAttribute));
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class ExpressionVisitor {
|
||||
internal ExpressionVisitor() {
|
||||
}
|
||||
|
||||
[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.")]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "[....]: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
|
||||
internal virtual Expression Visit(Expression exp) {
|
||||
if (exp == null)
|
||||
return exp;
|
||||
switch (exp.NodeType) {
|
||||
case ExpressionType.Negate:
|
||||
case ExpressionType.NegateChecked:
|
||||
case ExpressionType.Not:
|
||||
case ExpressionType.Convert:
|
||||
case ExpressionType.ConvertChecked:
|
||||
case ExpressionType.ArrayLength:
|
||||
case ExpressionType.Quote:
|
||||
case ExpressionType.TypeAs:
|
||||
return this.VisitUnary((UnaryExpression)exp);
|
||||
case ExpressionType.Add:
|
||||
case ExpressionType.AddChecked:
|
||||
case ExpressionType.Subtract:
|
||||
case ExpressionType.SubtractChecked:
|
||||
case ExpressionType.Multiply:
|
||||
case ExpressionType.MultiplyChecked:
|
||||
case ExpressionType.Divide:
|
||||
case ExpressionType.Modulo:
|
||||
case ExpressionType.Power:
|
||||
case ExpressionType.And:
|
||||
case ExpressionType.AndAlso:
|
||||
case ExpressionType.Or:
|
||||
case ExpressionType.OrElse:
|
||||
case ExpressionType.LessThan:
|
||||
case ExpressionType.LessThanOrEqual:
|
||||
case ExpressionType.GreaterThan:
|
||||
case ExpressionType.GreaterThanOrEqual:
|
||||
case ExpressionType.Equal:
|
||||
case ExpressionType.NotEqual:
|
||||
case ExpressionType.Coalesce:
|
||||
case ExpressionType.ArrayIndex:
|
||||
case ExpressionType.RightShift:
|
||||
case ExpressionType.LeftShift:
|
||||
case ExpressionType.ExclusiveOr:
|
||||
return this.VisitBinary((BinaryExpression)exp);
|
||||
case ExpressionType.TypeIs:
|
||||
return this.VisitTypeIs((TypeBinaryExpression)exp);
|
||||
case ExpressionType.Conditional:
|
||||
return this.VisitConditional((ConditionalExpression)exp);
|
||||
case ExpressionType.Constant:
|
||||
return this.VisitConstant((ConstantExpression)exp);
|
||||
case ExpressionType.Parameter:
|
||||
return this.VisitParameter((ParameterExpression)exp);
|
||||
case ExpressionType.MemberAccess:
|
||||
return this.VisitMemberAccess((MemberExpression)exp);
|
||||
case ExpressionType.Call:
|
||||
return this.VisitMethodCall((MethodCallExpression)exp);
|
||||
case ExpressionType.Lambda:
|
||||
return this.VisitLambda((LambdaExpression)exp);
|
||||
case ExpressionType.New:
|
||||
return this.VisitNew((NewExpression)exp);
|
||||
case ExpressionType.NewArrayInit:
|
||||
case ExpressionType.NewArrayBounds:
|
||||
return this.VisitNewArray((NewArrayExpression)exp);
|
||||
case ExpressionType.Invoke:
|
||||
return this.VisitInvocation((InvocationExpression)exp);
|
||||
case ExpressionType.MemberInit:
|
||||
return this.VisitMemberInit((MemberInitExpression)exp);
|
||||
case ExpressionType.ListInit:
|
||||
return this.VisitListInit((ListInitExpression)exp);
|
||||
case ExpressionType.UnaryPlus:
|
||||
if (exp.Type == typeof(TimeSpan))
|
||||
return this.VisitUnary((UnaryExpression)exp);
|
||||
throw Error.UnhandledExpressionType(exp.NodeType);
|
||||
default:
|
||||
throw Error.UnhandledExpressionType(exp.NodeType);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual MemberBinding VisitBinding(MemberBinding binding) {
|
||||
switch (binding.BindingType) {
|
||||
case MemberBindingType.Assignment:
|
||||
return this.VisitMemberAssignment((MemberAssignment)binding);
|
||||
case MemberBindingType.MemberBinding:
|
||||
return this.VisitMemberMemberBinding((MemberMemberBinding)binding);
|
||||
case MemberBindingType.ListBinding:
|
||||
return this.VisitMemberListBinding((MemberListBinding)binding);
|
||||
default:
|
||||
throw Error.UnhandledBindingType(binding.BindingType);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual ElementInit VisitElementInitializer(ElementInit initializer) {
|
||||
ReadOnlyCollection<Expression> arguments = this.VisitExpressionList(initializer.Arguments);
|
||||
if (arguments != initializer.Arguments) {
|
||||
return Expression.ElementInit(initializer.AddMethod, arguments);
|
||||
}
|
||||
return initializer;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitUnary(UnaryExpression u) {
|
||||
Expression operand = this.Visit(u.Operand);
|
||||
if (operand != u.Operand) {
|
||||
return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitBinary(BinaryExpression b) {
|
||||
Expression left = this.Visit(b.Left);
|
||||
Expression right = this.Visit(b.Right);
|
||||
if (left != b.Left || right != b.Right) {
|
||||
return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitTypeIs(TypeBinaryExpression b) {
|
||||
Expression expr = this.Visit(b.Expression);
|
||||
if (expr != b.Expression) {
|
||||
return Expression.TypeIs(expr, b.TypeOperand);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitConstant(ConstantExpression c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitConditional(ConditionalExpression c) {
|
||||
Expression test = this.Visit(c.Test);
|
||||
Expression ifTrue = this.Visit(c.IfTrue);
|
||||
Expression ifFalse = this.Visit(c.IfFalse);
|
||||
if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse) {
|
||||
return Expression.Condition(test, ifTrue, ifFalse);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitParameter(ParameterExpression p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitMemberAccess(MemberExpression m) {
|
||||
Expression exp = this.Visit(m.Expression);
|
||||
if (exp != m.Expression) {
|
||||
return Expression.MakeMemberAccess(exp, m.Member);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitMethodCall(MethodCallExpression m) {
|
||||
Expression obj = this.Visit(m.Object);
|
||||
IEnumerable<Expression> args = this.VisitExpressionList(m.Arguments);
|
||||
if (obj != m.Object || args != m.Arguments) {
|
||||
return Expression.Call(obj, m.Method, args);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
internal virtual ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCollection<Expression> original) {
|
||||
List<Expression> list = null;
|
||||
for (int i = 0, n = original.Count; i < n; i++) {
|
||||
Expression p = this.Visit(original[i]);
|
||||
if (list != null) {
|
||||
list.Add(p);
|
||||
}
|
||||
else if (p != original[i]) {
|
||||
list = new List<Expression>(n);
|
||||
for (int j = 0; j < i; j++) {
|
||||
list.Add(original[j]);
|
||||
}
|
||||
list.Add(p);
|
||||
}
|
||||
}
|
||||
if (list != null)
|
||||
return new ReadOnlyCollection<Expression>(list);
|
||||
return original;
|
||||
}
|
||||
|
||||
internal virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
|
||||
Expression e = this.Visit(assignment.Expression);
|
||||
if (e != assignment.Expression) {
|
||||
return Expression.Bind(assignment.Member, e);
|
||||
}
|
||||
return assignment;
|
||||
}
|
||||
|
||||
internal virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {
|
||||
IEnumerable<MemberBinding> bindings = this.VisitBindingList(binding.Bindings);
|
||||
if (bindings != binding.Bindings) {
|
||||
return Expression.MemberBind(binding.Member, bindings);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
internal virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
|
||||
IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(binding.Initializers);
|
||||
if (initializers != binding.Initializers) {
|
||||
return Expression.ListBind(binding.Member, initializers);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
internal virtual IEnumerable<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original) {
|
||||
List<MemberBinding> list = null;
|
||||
for (int i = 0, n = original.Count; i < n; i++) {
|
||||
MemberBinding b = this.VisitBinding(original[i]);
|
||||
if (list != null) {
|
||||
list.Add(b);
|
||||
}
|
||||
else if (b != original[i]) {
|
||||
list = new List<MemberBinding>(n);
|
||||
for (int j = 0; j < i; j++) {
|
||||
list.Add(original[j]);
|
||||
}
|
||||
list.Add(b);
|
||||
}
|
||||
}
|
||||
if (list != null)
|
||||
return list;
|
||||
return original;
|
||||
}
|
||||
|
||||
internal virtual IEnumerable<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original) {
|
||||
List<ElementInit> list = null;
|
||||
for (int i = 0, n = original.Count; i < n; i++) {
|
||||
ElementInit init = this.VisitElementInitializer(original[i]);
|
||||
if (list != null) {
|
||||
list.Add(init);
|
||||
}
|
||||
else if (init != original[i]) {
|
||||
list = new List<ElementInit>(n);
|
||||
for (int j = 0; j < i; j++) {
|
||||
list.Add(original[j]);
|
||||
}
|
||||
list.Add(init);
|
||||
}
|
||||
}
|
||||
if (list != null) {
|
||||
return list;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitLambda(LambdaExpression lambda) {
|
||||
Expression body = this.Visit(lambda.Body);
|
||||
if (body != lambda.Body) {
|
||||
return Expression.Lambda(lambda.Type, body, lambda.Parameters);
|
||||
}
|
||||
return lambda;
|
||||
}
|
||||
|
||||
internal virtual NewExpression VisitNew(NewExpression nex) {
|
||||
IEnumerable<Expression> args = this.VisitExpressionList(nex.Arguments);
|
||||
if (args != nex.Arguments) {
|
||||
if (nex.Members != null) {
|
||||
return Expression.New(nex.Constructor, args, nex.Members);
|
||||
}
|
||||
else {
|
||||
return Expression.New(nex.Constructor, args);
|
||||
}
|
||||
}
|
||||
return nex;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitMemberInit(MemberInitExpression init) {
|
||||
NewExpression n = this.VisitNew(init.NewExpression);
|
||||
IEnumerable<MemberBinding> bindings = this.VisitBindingList(init.Bindings);
|
||||
if (n != init.NewExpression || bindings != init.Bindings) {
|
||||
return Expression.MemberInit(n, bindings);
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitListInit(ListInitExpression init) {
|
||||
NewExpression n = this.VisitNew(init.NewExpression);
|
||||
IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(init.Initializers);
|
||||
if (n != init.NewExpression || initializers != init.Initializers) {
|
||||
return Expression.ListInit(n, initializers);
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitNewArray(NewArrayExpression na) {
|
||||
IEnumerable<Expression> exprs = this.VisitExpressionList(na.Expressions);
|
||||
if (exprs != na.Expressions) {
|
||||
if (na.NodeType == ExpressionType.NewArrayInit) {
|
||||
return Expression.NewArrayInit(na.Type.GetElementType(), exprs);
|
||||
}
|
||||
else {
|
||||
return Expression.NewArrayBounds(na.Type.GetElementType(), exprs);
|
||||
}
|
||||
}
|
||||
return na;
|
||||
}
|
||||
|
||||
internal virtual Expression VisitInvocation(InvocationExpression iv) {
|
||||
IEnumerable<Expression> args = this.VisitExpressionList(iv.Arguments);
|
||||
Expression expr = this.Visit(iv.Expression);
|
||||
if (args != iv.Arguments || expr != iv.Expression) {
|
||||
return Expression.Invoke(expr, args);
|
||||
}
|
||||
return iv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Data.Linq;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
/// <summary>
|
||||
/// Converts expressions of type NText, Text, Image to NVarChar(MAX), VarChar(MAX), VarBinary(MAX)
|
||||
/// where necessary. This can only be done on SQL2005, so we add a SqlServerCompatibilityAnnotation
|
||||
/// to the changed nodes.
|
||||
/// </summary>
|
||||
internal class LongTypeConverter {
|
||||
Visitor visitor;
|
||||
|
||||
internal LongTypeConverter(SqlFactory sql) {
|
||||
this.visitor = new Visitor(sql);
|
||||
}
|
||||
|
||||
internal SqlNode AddConversions(SqlNode node, SqlNodeAnnotations annotations) {
|
||||
visitor.Annotations = annotations;
|
||||
return visitor.Visit(node);
|
||||
}
|
||||
|
||||
class Visitor : SqlVisitor {
|
||||
SqlFactory sql;
|
||||
SqlNodeAnnotations annotations;
|
||||
|
||||
internal SqlNodeAnnotations Annotations {
|
||||
set { this.annotations = value; }
|
||||
}
|
||||
|
||||
internal Visitor(SqlFactory sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
private SqlExpression ConvertToMax(SqlExpression expr, ProviderType newType) {
|
||||
return sql.UnaryConvert(expr.ClrType, newType, expr, expr.SourceExpression);
|
||||
}
|
||||
|
||||
// returns CONVERT(VARCHAR/NVARCHAR/VARBINARY(MAX), expr) if provType is one of Text, NText or Image
|
||||
// otherwise just returns expr
|
||||
// changed is true if CONVERT(...(MAX),...) was added
|
||||
private SqlExpression ConvertToMax(SqlExpression expr, out bool changed) {
|
||||
changed = false;
|
||||
if (!expr.SqlType.IsLargeType)
|
||||
return expr;
|
||||
ProviderType newType = sql.TypeProvider.GetBestLargeType(expr.SqlType);
|
||||
changed = true;
|
||||
if (expr.SqlType != newType) {
|
||||
return ConvertToMax(expr, newType);
|
||||
}
|
||||
changed = false;
|
||||
return expr;
|
||||
}
|
||||
|
||||
private void ConvertColumnsToMax(SqlSelect select, out bool changed, out bool containsLongExpressions) {
|
||||
SqlRow row = select.Row;
|
||||
changed = false;
|
||||
containsLongExpressions = false;
|
||||
foreach (SqlColumn col in row.Columns) {
|
||||
bool columnChanged;
|
||||
containsLongExpressions = containsLongExpressions || col.SqlType.IsLargeType;
|
||||
col.Expression = ConvertToMax(col.Expression, out columnChanged);
|
||||
changed = changed || columnChanged;
|
||||
}
|
||||
}
|
||||
|
||||
internal override SqlSelect VisitSelect(SqlSelect select) {
|
||||
if (select.IsDistinct) {
|
||||
bool changed;
|
||||
bool containsLongExpressions;
|
||||
ConvertColumnsToMax(select, out changed, out containsLongExpressions);
|
||||
if (containsLongExpressions) {
|
||||
this.annotations.Add(select, new SqlServerCompatibilityAnnotation(
|
||||
Strings.TextNTextAndImageCannotOccurInDistinct(select.SourceExpression), SqlProvider.ProviderMode.Sql2000, SqlProvider.ProviderMode.SqlCE));
|
||||
}
|
||||
|
||||
}
|
||||
return base.VisitSelect(select);
|
||||
}
|
||||
|
||||
internal override SqlNode VisitUnion(SqlUnion su) {
|
||||
bool changedLeft = false;
|
||||
bool containsLongExpressionsLeft = false;
|
||||
SqlSelect left = su.Left as SqlSelect;
|
||||
if (left != null) {
|
||||
ConvertColumnsToMax(left, out changedLeft, out containsLongExpressionsLeft);
|
||||
}
|
||||
bool changedRight = false;
|
||||
bool containsLongExpressionsRight = false;
|
||||
SqlSelect right = su.Right as SqlSelect;
|
||||
if (right != null) {
|
||||
ConvertColumnsToMax(right, out changedRight, out containsLongExpressionsRight);
|
||||
}
|
||||
if (!su.All && (containsLongExpressionsLeft || containsLongExpressionsRight)) {
|
||||
// unless the UNION is 'ALL', the server will perform a DISTINCT operation,
|
||||
// which isn't valid for large types (text, ntext, image)
|
||||
this.annotations.Add(su, new SqlServerCompatibilityAnnotation(
|
||||
Strings.TextNTextAndImageCannotOccurInUnion(su.SourceExpression), SqlProvider.ProviderMode.Sql2000, SqlProvider.ProviderMode.SqlCE));
|
||||
}
|
||||
return base.VisitUnion(su);
|
||||
}
|
||||
|
||||
internal override SqlExpression VisitFunctionCall(SqlFunctionCall fc) {
|
||||
if (fc.Name == "LEN") {
|
||||
bool changed;
|
||||
fc.Arguments[0] = ConvertToMax(fc.Arguments[0],out changed);
|
||||
if (fc.Arguments[0].SqlType.IsLargeType) {
|
||||
this.annotations.Add(fc, new SqlServerCompatibilityAnnotation(
|
||||
Strings.LenOfTextOrNTextNotSupported(fc.SourceExpression), SqlProvider.ProviderMode.Sql2000));
|
||||
}
|
||||
}
|
||||
return base.VisitFunctionCall(fc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user