2 using System
.Collections
.Generic
;
3 using System
.Data
.Linq
;
4 using System
.Diagnostics
.CodeAnalysis
;
6 namespace System
.Data
.Linq
.SqlClient
{
9 /// Compare two trees for value equality. Implemented as a parallel visitor.
11 internal class SqlComparer
{
13 internal SqlComparer() {
16 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification
= "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
17 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification
= "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
18 [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification
= "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
19 [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.")]
20 internal static bool AreEqual(SqlNode node1
, SqlNode node2
) {
23 if (node1
== null || node2
== null)
26 if (node1
.NodeType
== SqlNodeType
.SimpleCase
)
27 node1
= UnwrapTrivialCaseExpression((SqlSimpleCase
)node1
);
29 if (node2
.NodeType
== SqlNodeType
.SimpleCase
)
30 node2
= UnwrapTrivialCaseExpression((SqlSimpleCase
)node2
);
32 if (node1
.NodeType
!= node2
.NodeType
) {
33 // allow expression sets to compare against single expressions
34 if (node1
.NodeType
== SqlNodeType
.ExprSet
) {
35 SqlExprSet eset
= (SqlExprSet
)node1
;
36 for (int i
= 0, n
= eset
.Expressions
.Count
; i
< n
; i
++) {
37 if (AreEqual(eset
.Expressions
[i
], node2
))
41 else if (node2
.NodeType
== SqlNodeType
.ExprSet
) {
42 SqlExprSet eset
= (SqlExprSet
)node2
;
43 for (int i
= 0, n
= eset
.Expressions
.Count
; i
< n
; i
++) {
44 if (AreEqual(node1
, eset
.Expressions
[i
]))
50 if (node1
.Equals(node2
))
53 switch (node1
.NodeType
) {
55 case SqlNodeType
.Not2V
:
56 case SqlNodeType
.Negate
:
57 case SqlNodeType
.BitNot
:
58 case SqlNodeType
.IsNull
:
59 case SqlNodeType
.IsNotNull
:
60 case SqlNodeType
.Count
:
65 case SqlNodeType
.Stddev
:
66 case SqlNodeType
.ValueOf
:
67 case SqlNodeType
.OuterJoinedValue
:
68 case SqlNodeType
.ClrLength
:
69 return AreEqual(((SqlUnary
)node1
).Operand
, ((SqlUnary
)node2
).Operand
);
75 case SqlNodeType
.BitAnd
:
76 case SqlNodeType
.BitOr
:
77 case SqlNodeType
.BitXor
:
86 case SqlNodeType
.EQ2V
:
87 case SqlNodeType
.NE2V
:
88 case SqlNodeType
.Concat
:
89 SqlBinary firstNode
= (SqlBinary
)node1
;
90 SqlBinary secondNode
= (SqlBinary
)node2
;
91 return AreEqual(firstNode
.Left
, secondNode
.Left
)
92 && AreEqual(firstNode
.Right
, secondNode
.Right
);
93 case SqlNodeType
.Convert
:
94 case SqlNodeType
.Treat
: {
95 SqlUnary sun1
= (SqlUnary
)node1
;
96 SqlUnary sun2
= (SqlUnary
)node2
;
97 return sun1
.ClrType
== sun2
.ClrType
&& sun1
.SqlType
== sun2
.SqlType
&& AreEqual(sun1
.Operand
, sun2
.Operand
);
99 case SqlNodeType
.Between
: {
100 SqlBetween b1
= (SqlBetween
)node1
;
101 SqlBetween b2
= (SqlBetween
)node1
;
102 return AreEqual(b1
.Expression
, b2
.Expression
) &&
103 AreEqual(b1
.Start
, b2
.Start
) &&
104 AreEqual(b1
.End
, b2
.End
);
106 case SqlNodeType
.Parameter
:
107 return node1
== node2
;
108 case SqlNodeType
.Alias
:
109 return AreEqual(((SqlAlias
)node1
).Node
, ((SqlAlias
)node2
).Node
);
110 case SqlNodeType
.AliasRef
:
111 return AreEqual(((SqlAliasRef
)node1
).Alias
, ((SqlAliasRef
)node2
).Alias
);
112 case SqlNodeType
.Column
:
113 SqlColumn col1
= (SqlColumn
)node1
;
114 SqlColumn col2
= (SqlColumn
)node2
;
115 return col1
== col2
|| (col1
.Expression
!= null && col2
.Expression
!= null && AreEqual(col1
.Expression
, col2
.Expression
));
116 case SqlNodeType
.Table
:
117 return ((SqlTable
)node1
).MetaTable
== ((SqlTable
)node2
).MetaTable
;
118 case SqlNodeType
.Member
:
119 return (((SqlMember
)node1
).Member
== ((SqlMember
)node2
).Member
) &&
120 AreEqual(((SqlMember
)node1
).Expression
, ((SqlMember
)node2
).Expression
);
121 case SqlNodeType
.ColumnRef
:
122 SqlColumnRef cref1
= (SqlColumnRef
)node1
;
123 SqlColumnRef cref2
= (SqlColumnRef
)node2
;
124 return GetBaseColumn(cref1
) == GetBaseColumn(cref2
);
125 case SqlNodeType
.Value
:
126 return Object
.Equals(((SqlValue
)node1
).Value
, ((SqlValue
)node2
).Value
);
127 case SqlNodeType
.TypeCase
: {
128 SqlTypeCase c1
= (SqlTypeCase
)node1
;
129 SqlTypeCase c2
= (SqlTypeCase
)node2
;
130 if (!AreEqual(c1
.Discriminator
, c2
.Discriminator
)) {
133 if (c1
.Whens
.Count
!= c2
.Whens
.Count
) {
136 for (int i
= 0, c
= c1
.Whens
.Count
; i
< c
; ++i
) {
137 if (!AreEqual(c1
.Whens
[i
].Match
, c2
.Whens
[i
].Match
)) {
140 if (!AreEqual(c1
.Whens
[i
].TypeBinding
, c2
.Whens
[i
].TypeBinding
)) {
147 case SqlNodeType
.SearchedCase
: {
148 SqlSearchedCase c1
= (SqlSearchedCase
)node1
;
149 SqlSearchedCase c2
= (SqlSearchedCase
)node2
;
150 if (c1
.Whens
.Count
!= c2
.Whens
.Count
)
152 for (int i
= 0, n
= c1
.Whens
.Count
; i
< n
; i
++) {
153 if (!AreEqual(c1
.Whens
[i
].Match
, c2
.Whens
[i
].Match
) ||
154 !AreEqual(c1
.Whens
[i
].Value
, c2
.Whens
[i
].Value
))
157 return AreEqual(c1
.Else
, c2
.Else
);
159 case SqlNodeType
.ClientCase
: {
160 SqlClientCase c1
= (SqlClientCase
)node1
;
161 SqlClientCase c2
= (SqlClientCase
)node2
;
162 if (c1
.Whens
.Count
!= c2
.Whens
.Count
)
164 for (int i
= 0, n
= c1
.Whens
.Count
; i
< n
; i
++) {
165 if (!AreEqual(c1
.Whens
[i
].Match
, c2
.Whens
[i
].Match
) ||
166 !AreEqual(c1
.Whens
[i
].Value
, c2
.Whens
[i
].Value
))
171 case SqlNodeType
.DiscriminatedType
: {
172 SqlDiscriminatedType dt1
= (SqlDiscriminatedType
)node1
;
173 SqlDiscriminatedType dt2
= (SqlDiscriminatedType
)node2
;
174 return AreEqual(dt1
.Discriminator
, dt2
.Discriminator
);
176 case SqlNodeType
.SimpleCase
: {
177 SqlSimpleCase c1
= (SqlSimpleCase
)node1
;
178 SqlSimpleCase c2
= (SqlSimpleCase
)node2
;
179 if (c1
.Whens
.Count
!= c2
.Whens
.Count
)
181 for (int i
= 0, n
= c1
.Whens
.Count
; i
< n
; i
++) {
182 if (!AreEqual(c1
.Whens
[i
].Match
, c2
.Whens
[i
].Match
) ||
183 !AreEqual(c1
.Whens
[i
].Value
, c2
.Whens
[i
].Value
))
188 case SqlNodeType
.Like
: {
189 SqlLike like1
= (SqlLike
)node1
;
190 SqlLike like2
= (SqlLike
)node2
;
191 return AreEqual(like1
.Expression
, like2
.Expression
) &&
192 AreEqual(like1
.Pattern
, like2
.Pattern
) &&
193 AreEqual(like1
.Escape
, like2
.Escape
);
195 case SqlNodeType
.Variable
: {
196 SqlVariable v1
= (SqlVariable
)node1
;
197 SqlVariable v2
= (SqlVariable
)node2
;
198 return v1
.Name
== v2
.Name
;
200 case SqlNodeType
.FunctionCall
: {
201 SqlFunctionCall f1
= (SqlFunctionCall
)node1
;
202 SqlFunctionCall f2
= (SqlFunctionCall
)node2
;
203 if (f1
.Name
!= f2
.Name
)
205 if (f1
.Arguments
.Count
!= f2
.Arguments
.Count
)
207 for (int i
= 0, n
= f1
.Arguments
.Count
; i
< n
; i
++) {
208 if (!AreEqual(f1
.Arguments
[i
], f2
.Arguments
[i
]))
213 case SqlNodeType
.Link
: {
214 SqlLink l1
= (SqlLink
)node1
;
215 SqlLink l2
= (SqlLink
)node2
;
216 if (!MetaPosition
.AreSameMember(l1
.Member
.Member
, l2
.Member
.Member
)) {
219 if (!AreEqual(l1
.Expansion
, l2
.Expansion
)) {
222 if (l1
.KeyExpressions
.Count
!= l2
.KeyExpressions
.Count
) {
225 for (int i
= 0, c
= l1
.KeyExpressions
.Count
; i
< c
; ++i
) {
226 if (!AreEqual(l1
.KeyExpressions
[i
], l2
.KeyExpressions
[i
])) {
232 case SqlNodeType
.ExprSet
:
233 SqlExprSet es1
= (SqlExprSet
)node1
;
234 SqlExprSet es2
= (SqlExprSet
)node2
;
235 if (es1
.Expressions
.Count
!= es2
.Expressions
.Count
)
237 for(int i
= 0, n
= es1
.Expressions
.Count
; i
< n
; i
++) {
238 if (!AreEqual(es1
.Expressions
[i
], es2
.Expressions
[i
]))
242 case SqlNodeType
.OptionalValue
:
243 SqlOptionalValue ov1
= (SqlOptionalValue
)node1
;
244 SqlOptionalValue ov2
= (SqlOptionalValue
)node2
;
245 return AreEqual(ov1
.Value
, ov2
.Value
);
246 case SqlNodeType
.Row
:
247 case SqlNodeType
.UserQuery
:
248 case SqlNodeType
.StoredProcedureCall
:
249 case SqlNodeType
.UserRow
:
250 case SqlNodeType
.UserColumn
:
251 case SqlNodeType
.Multiset
:
252 case SqlNodeType
.ScalarSubSelect
:
253 case SqlNodeType
.Element
:
254 case SqlNodeType
.Exists
:
255 case SqlNodeType
.Join
:
256 case SqlNodeType
.Select
:
257 case SqlNodeType
.New
:
258 case SqlNodeType
.ClientQuery
:
259 case SqlNodeType
.ClientArray
:
260 case SqlNodeType
.Insert
:
261 case SqlNodeType
.Update
:
262 case SqlNodeType
.Delete
:
263 case SqlNodeType
.MemberAssign
:
264 case SqlNodeType
.Assign
:
265 case SqlNodeType
.Block
:
266 case SqlNodeType
.Union
:
267 case SqlNodeType
.DoNotVisit
:
268 case SqlNodeType
.MethodCall
:
269 case SqlNodeType
.Nop
:
275 private static SqlColumn
GetBaseColumn(SqlColumnRef cref
) {
276 while (cref
!= null && cref
.Column
.Expression
!= null) {
277 SqlColumnRef cr
= cref
.Column
.Expression
as SqlColumnRef
;
289 private static SqlExpression
UnwrapTrivialCaseExpression(SqlSimpleCase sc
) {
290 if (sc
.Whens
.Count
!= 1) {
293 if (!SqlComparer
.AreEqual(sc
.Expression
, sc
.Whens
[0].Match
)) {
296 SqlExpression result
= sc
.Whens
[0].Value
;
297 if (result
.NodeType
== SqlNodeType
.SimpleCase
) {
298 return UnwrapTrivialCaseExpression((SqlSimpleCase
)result
);