PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/referencesource/System.Data.Linq/SqlClient/Query/SqlColumnizer.cs

http://github.com/mono/mono
C# | 185 lines | 161 code | 19 blank | 5 comment | 23 complexity | f7765ebc6a9d8604cd13a2aa1081db92 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using System.Reflection;
  5. using System.Data.Linq;
  6. using System.Data.Linq.Mapping;
  7. using System.Data.Linq.Provider;
  8. using System.Linq;
  9. using System.Data.Linq.SqlClient;
  10. using System.Diagnostics.CodeAnalysis;
  11. using System.Diagnostics;
  12. namespace System.Data.Linq.SqlClient {
  13. // partions select expressions and common subexpressions into scalar and non-scalar pieces by
  14. // wrapping scalar pieces floating column nodes.
  15. internal class SqlColumnizer {
  16. ColumnNominator nominator;
  17. ColumnDeclarer declarer;
  18. internal SqlColumnizer() {
  19. this.nominator = new ColumnNominator();
  20. this.declarer = new ColumnDeclarer();
  21. }
  22. internal SqlExpression ColumnizeSelection(SqlExpression selection) {
  23. return this.declarer.Declare(selection, this.nominator.Nominate(selection));
  24. }
  25. internal static bool CanBeColumn(SqlExpression expression) {
  26. return ColumnNominator.CanBeColumn(expression);
  27. }
  28. class ColumnDeclarer : SqlVisitor {
  29. HashSet<SqlExpression> candidates;
  30. internal ColumnDeclarer() {
  31. }
  32. internal SqlExpression Declare(SqlExpression expression, HashSet<SqlExpression> candidates) {
  33. this.candidates = candidates;
  34. return (SqlExpression)this.Visit(expression);
  35. }
  36. internal override SqlNode Visit(SqlNode node) {
  37. SqlExpression expr = node as SqlExpression;
  38. if (expr != null) {
  39. if (this.candidates.Contains(expr)) {
  40. if (expr.NodeType == SqlNodeType.Column ||
  41. expr.NodeType == SqlNodeType.ColumnRef) {
  42. return expr;
  43. }
  44. else {
  45. return new SqlColumn(expr.ClrType, expr.SqlType, null, null, expr, expr.SourceExpression);
  46. }
  47. }
  48. }
  49. return base.Visit(node);
  50. }
  51. }
  52. class ColumnNominator : SqlVisitor {
  53. bool isBlocked;
  54. HashSet<SqlExpression> candidates;
  55. internal HashSet<SqlExpression> Nominate(SqlExpression expression) {
  56. this.candidates = new HashSet<SqlExpression>();
  57. this.isBlocked = false;
  58. this.Visit(expression);
  59. return this.candidates;
  60. }
  61. internal override SqlNode Visit(SqlNode node) {
  62. SqlExpression expression = node as SqlExpression;
  63. if (expression != null) {
  64. bool saveIsBlocked = this.isBlocked;
  65. this.isBlocked = false;
  66. if (CanRecurseColumnize(expression)) {
  67. base.Visit(expression);
  68. }
  69. if (!this.isBlocked) {
  70. if (CanBeColumn(expression)) {
  71. this.candidates.Add(expression);
  72. }
  73. else {
  74. this.isBlocked = true;
  75. }
  76. }
  77. this.isBlocked |= saveIsBlocked;
  78. }
  79. return node;
  80. }
  81. internal override SqlExpression VisitSimpleCase(SqlSimpleCase c) {
  82. c.Expression = this.VisitExpression(c.Expression);
  83. for (int i = 0, n = c.Whens.Count; i < n; i++) {
  84. // Don't walk down the match side. This can't be a column.
  85. c.Whens[i].Value = this.VisitExpression(c.Whens[i].Value);
  86. }
  87. return c;
  88. }
  89. internal override SqlExpression VisitTypeCase(SqlTypeCase tc) {
  90. tc.Discriminator = this.VisitExpression(tc.Discriminator);
  91. for (int i = 0, n = tc.Whens.Count; i < n; i++) {
  92. // Don't walk down the match side. This can't be a column.
  93. tc.Whens[i].TypeBinding = this.VisitExpression(tc.Whens[i].TypeBinding);
  94. }
  95. return tc;
  96. }
  97. internal override SqlExpression VisitClientCase(SqlClientCase c) {
  98. c.Expression = this.VisitExpression(c.Expression);
  99. for (int i = 0, n = c.Whens.Count; i < n; i++) {
  100. // Don't walk down the match side. This can't be a column.
  101. c.Whens[i].Value = this.VisitExpression(c.Whens[i].Value);
  102. }
  103. return c;
  104. }
  105. private static bool CanRecurseColumnize(SqlExpression expr) {
  106. switch (expr.NodeType) {
  107. case SqlNodeType.AliasRef:
  108. case SqlNodeType.ColumnRef:
  109. case SqlNodeType.Column:
  110. case SqlNodeType.Multiset:
  111. case SqlNodeType.Element:
  112. case SqlNodeType.ScalarSubSelect:
  113. case SqlNodeType.Exists:
  114. case SqlNodeType.ClientQuery:
  115. case SqlNodeType.SharedExpressionRef:
  116. case SqlNodeType.Link:
  117. case SqlNodeType.Nop:
  118. case SqlNodeType.Value:
  119. case SqlNodeType.Select:
  120. return false;
  121. default:
  122. return true;
  123. }
  124. }
  125. [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.")]
  126. private static bool IsClientOnly(SqlExpression expr) {
  127. switch (expr.NodeType) {
  128. case SqlNodeType.ClientCase:
  129. case SqlNodeType.TypeCase:
  130. case SqlNodeType.ClientArray:
  131. case SqlNodeType.Grouping:
  132. case SqlNodeType.DiscriminatedType:
  133. case SqlNodeType.SharedExpression:
  134. case SqlNodeType.SimpleExpression:
  135. case SqlNodeType.AliasRef:
  136. case SqlNodeType.Multiset:
  137. case SqlNodeType.Element:
  138. case SqlNodeType.ClientQuery:
  139. case SqlNodeType.SharedExpressionRef:
  140. case SqlNodeType.Link:
  141. case SqlNodeType.Nop:
  142. return true;
  143. case SqlNodeType.OuterJoinedValue:
  144. return IsClientOnly(((SqlUnary)expr).Operand);
  145. default:
  146. return false;
  147. }
  148. }
  149. internal static bool CanBeColumn(SqlExpression expression) {
  150. if (!IsClientOnly(expression)
  151. && expression.NodeType != SqlNodeType.Column
  152. && expression.SqlType.CanBeColumn) {
  153. switch (expression.NodeType) {
  154. case SqlNodeType.MethodCall:
  155. case SqlNodeType.Member:
  156. case SqlNodeType.New:
  157. return PostBindDotNetConverter.CanConvert(expression);
  158. default:
  159. return true;
  160. }
  161. }
  162. return false;
  163. }
  164. }
  165. }
  166. }