Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Linq / SqlClient / Query / SqlComparer.cs
blobd70c0becbe9d92b6dd50df23dac321ff25a94c18
1 using System;
2 using System.Collections.Generic;
3 using System.Data.Linq;
4 using System.Diagnostics.CodeAnalysis;
6 namespace System.Data.Linq.SqlClient {
8 /// <summary>
9 /// Compare two trees for value equality. Implemented as a parallel visitor.
10 /// </summary>
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) {
21 if (node1 == node2)
22 return true;
23 if (node1 == null || node2 == null)
24 return false;
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))
38 return true;
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]))
45 return true;
48 return false;
50 if (node1.Equals(node2))
51 return true;
53 switch (node1.NodeType) {
54 case SqlNodeType.Not:
55 case SqlNodeType.Not2V:
56 case SqlNodeType.Negate:
57 case SqlNodeType.BitNot:
58 case SqlNodeType.IsNull:
59 case SqlNodeType.IsNotNull:
60 case SqlNodeType.Count:
61 case SqlNodeType.Max:
62 case SqlNodeType.Min:
63 case SqlNodeType.Sum:
64 case SqlNodeType.Avg:
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);
70 case SqlNodeType.Add:
71 case SqlNodeType.Sub:
72 case SqlNodeType.Mul:
73 case SqlNodeType.Div:
74 case SqlNodeType.Mod:
75 case SqlNodeType.BitAnd:
76 case SqlNodeType.BitOr:
77 case SqlNodeType.BitXor:
78 case SqlNodeType.And:
79 case SqlNodeType.Or:
80 case SqlNodeType.GE:
81 case SqlNodeType.GT:
82 case SqlNodeType.LE:
83 case SqlNodeType.LT:
84 case SqlNodeType.EQ:
85 case SqlNodeType.NE:
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)) {
131 return false;
133 if (c1.Whens.Count != c2.Whens.Count) {
134 return false;
136 for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
137 if (!AreEqual(c1.Whens[i].Match, c2.Whens[i].Match)) {
138 return false;
140 if (!AreEqual(c1.Whens[i].TypeBinding, c2.Whens[i].TypeBinding)) {
141 return false;
145 return true;
147 case SqlNodeType.SearchedCase: {
148 SqlSearchedCase c1 = (SqlSearchedCase)node1;
149 SqlSearchedCase c2 = (SqlSearchedCase)node2;
150 if (c1.Whens.Count != c2.Whens.Count)
151 return false;
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))
155 return false;
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)
163 return false;
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))
167 return false;
169 return true;
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)
180 return false;
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))
184 return false;
186 return true;
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)
204 return false;
205 if (f1.Arguments.Count != f2.Arguments.Count)
206 return false;
207 for (int i = 0, n = f1.Arguments.Count; i < n; i++) {
208 if (!AreEqual(f1.Arguments[i], f2.Arguments[i]))
209 return false;
211 return true;
213 case SqlNodeType.Link: {
214 SqlLink l1 = (SqlLink)node1;
215 SqlLink l2 = (SqlLink)node2;
216 if (!MetaPosition.AreSameMember(l1.Member.Member, l2.Member.Member)) {
217 return false;
219 if (!AreEqual(l1.Expansion, l2.Expansion)) {
220 return false;
222 if (l1.KeyExpressions.Count != l2.KeyExpressions.Count) {
223 return false;
225 for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
226 if (!AreEqual(l1.KeyExpressions[i], l2.KeyExpressions[i])) {
227 return false;
230 return true;
232 case SqlNodeType.ExprSet:
233 SqlExprSet es1 = (SqlExprSet)node1;
234 SqlExprSet es2 = (SqlExprSet)node2;
235 if (es1.Expressions.Count != es2.Expressions.Count)
236 return false;
237 for(int i = 0, n = es1.Expressions.Count; i < n; i++) {
238 if (!AreEqual(es1.Expressions[i], es2.Expressions[i]))
239 return false;
241 return true;
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:
270 default:
271 return false;
275 private static SqlColumn GetBaseColumn(SqlColumnRef cref) {
276 while (cref != null && cref.Column.Expression != null) {
277 SqlColumnRef cr = cref.Column.Expression as SqlColumnRef;
278 if (cr != null) {
279 cref = cr;
280 continue;
282 else {
283 break;
286 return cref.Column;
289 private static SqlExpression UnwrapTrivialCaseExpression(SqlSimpleCase sc) {
290 if (sc.Whens.Count != 1) {
291 return sc;
293 if (!SqlComparer.AreEqual(sc.Expression, sc.Whens[0].Match)) {
294 return sc;
296 SqlExpression result = sc.Whens[0].Value;
297 if (result.NodeType == SqlNodeType.SimpleCase) {
298 return UnwrapTrivialCaseExpression((SqlSimpleCase)result);
300 return result;