/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/Sugar/Implementation/ExpressionOptimizer.cs

https://github.com/ccflo/mono · C# · 237 lines · 184 code · 10 blank · 43 comment · 61 complexity · 50ea2abaec8e0636626efd86efd15b86 MD5 · raw file

  1. #region MIT license
  2. //
  3. // MIT license
  4. //
  5. // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. //
  25. #endregion
  26. using System;
  27. using System.Linq;
  28. using System.Linq.Expressions;
  29. using DbLinq.Data.Linq.Sugar;
  30. using DbLinq.Data.Linq.Sugar.ExpressionMutator;
  31. using DbLinq.Data.Linq.Sugar.Expressions;
  32. namespace DbLinq.Data.Linq.Sugar.Implementation
  33. {
  34. /// <summary>
  35. /// Optimizes expressions (such as constant chains)
  36. /// </summary>
  37. internal class ExpressionOptimizer : IExpressionOptimizer
  38. {
  39. public virtual Expression Optimize(Expression expression, BuilderContext builderContext)
  40. {
  41. return expression.Recurse(e => Analyze(e, builderContext));
  42. }
  43. protected Expression Analyze(Expression expression, BuilderContext builderContext)
  44. {
  45. // small optimization
  46. if (expression is ConstantExpression)
  47. return expression;
  48. expression = AnalyzeNull(expression, builderContext);
  49. expression = AnalyzeNot(expression, builderContext);
  50. expression = AnalyzeBinaryBoolean(expression, builderContext);
  51. // constant optimization at last, because the previous optimizations may generate constant expressions
  52. expression = AnalyzeConstant(expression, builderContext);
  53. return expression;
  54. }
  55. private Expression AnalyzeBinaryBoolean(Expression expression, BuilderContext builderContext)
  56. {
  57. if (expression.Type != typeof(bool))
  58. return expression;
  59. var bin = expression as BinaryExpression;
  60. if (bin == null)
  61. return expression;
  62. bool canOptimizeLeft = bin.Left.NodeType == ExpressionType.Constant && bin.Left.Type == typeof(bool);
  63. bool canOptimizeRight = bin.Right.NodeType == ExpressionType.Constant && bin.Right.Type == typeof(bool);
  64. if (canOptimizeLeft && canOptimizeRight)
  65. return Expression.Constant(expression.Evaluate());
  66. if (canOptimizeLeft || canOptimizeRight)
  67. switch (expression.NodeType)
  68. {
  69. case ExpressionType.AndAlso:
  70. if (canOptimizeLeft)
  71. if ((bool)bin.Left.Evaluate())
  72. return bin.Right; // (TRUE and X) == X
  73. else
  74. return bin.Left; // (FALSE and X) == FALSE
  75. if (canOptimizeRight)
  76. if ((bool)bin.Right.Evaluate())
  77. return bin.Left; // (X and TRUE) == X
  78. else
  79. return bin.Right; // (X and FALSE) == FALSE
  80. break;
  81. case ExpressionType.OrElse:
  82. if (canOptimizeLeft)
  83. if ((bool)bin.Left.Evaluate())
  84. return bin.Left; // (TRUE or X) == TRUE
  85. else
  86. return bin.Right; // (FALSE or X) == X
  87. if (canOptimizeRight)
  88. if ((bool)bin.Right.Evaluate())
  89. return bin.Right; // (X or TRUE) == TRUE
  90. else
  91. return bin.Left; // (X or FALSE) == X
  92. break;
  93. case ExpressionType.Equal:
  94. // TODO: this optimization should work for Unary Expression Too
  95. // this actually produce errors becouse of string based Sql generation
  96. canOptimizeLeft = canOptimizeLeft && bin.Right is BinaryExpression;
  97. if (canOptimizeLeft)
  98. if ((bool)bin.Left.Evaluate())
  99. return bin.Right; // (TRUE == X) == X
  100. else
  101. return Expression.Not(bin.Right); // (FALSE == X) == not X
  102. canOptimizeRight = canOptimizeRight && bin.Left is BinaryExpression;
  103. // TODO: this optimization should work for Unary Expression Too
  104. // this actually produce errors becouse of string based Sql generation
  105. if (canOptimizeRight)
  106. if ((bool)bin.Right.Evaluate())
  107. return bin.Left; // (X == TRUE) == X
  108. else
  109. return Expression.Not(bin.Left); // (X == FALSE) == not X
  110. break;
  111. case ExpressionType.NotEqual:
  112. canOptimizeLeft = canOptimizeLeft && bin.Right is BinaryExpression;
  113. // TODO: this optimization should work for Unary Expression Too
  114. // this actually produce errors becouse of string based Sql generation
  115. if (canOptimizeLeft)
  116. if ((bool)bin.Left.Evaluate())
  117. return Expression.Not(bin.Right); // (TRUE != X) == not X
  118. else
  119. return bin.Right; // (FALSE != X) == X
  120. canOptimizeRight = canOptimizeRight && bin.Left is BinaryExpression;
  121. // TODO: this optimization should work for Unary Expression Too
  122. // this actually produce errors becouse of string based Sql generation
  123. if (canOptimizeRight)
  124. if ((bool)bin.Right.Evaluate())
  125. return Expression.Not(bin.Left); // (X != TRUE) == not X
  126. else
  127. return bin.Left; // (X != FALSE) == X
  128. break;
  129. }
  130. return expression;
  131. }
  132. protected virtual Expression AnalyzeConstant(Expression expression, BuilderContext builderContext)
  133. {
  134. // we try to find a non-constant operand, and if we do, we won't change this expression
  135. foreach (var operand in expression.GetOperands())
  136. {
  137. if (!(operand is ConstantExpression))
  138. return expression;
  139. }
  140. if (expression.NodeType == ExpressionType.Parameter)
  141. return expression;
  142. if (expression.NodeType == (ExpressionType)SpecialExpressionType.Like)
  143. return expression;
  144. // SETuse
  145. // If the value of the first SpecialExpressionType change this 999 should change too
  146. if ((short)expression.NodeType > 999)
  147. return expression;
  148. // now, we just simply return a constant with new value
  149. try
  150. {
  151. var optimizedExpression = Expression.Constant(expression.Evaluate());
  152. // sometimes, optimizing an expression changes its type, and we just can't allow this.
  153. if (optimizedExpression.Type == expression.Type)
  154. return optimizedExpression;
  155. }
  156. // if we fail to evaluate the expression, then just return it
  157. catch (ArgumentException)
  158. {
  159. return expression;
  160. }
  161. return expression;
  162. }
  163. protected virtual Expression AnalyzeNot(Expression expression, BuilderContext builderContext)
  164. {
  165. if (expression.NodeType == ExpressionType.Not)
  166. {
  167. var notExpression = expression as UnaryExpression;
  168. var subExpression = notExpression.Operand;
  169. var subOperands = subExpression.GetOperands().ToList();
  170. switch (subExpression.NodeType)
  171. {
  172. case ExpressionType.Equal:
  173. return Expression.NotEqual(subOperands[0], subOperands[1]);
  174. case ExpressionType.GreaterThan:
  175. return Expression.LessThanOrEqual(subOperands[0], subOperands[1]);
  176. case ExpressionType.GreaterThanOrEqual:
  177. return Expression.LessThan(subOperands[0], subOperands[1]);
  178. case ExpressionType.LessThan:
  179. return Expression.GreaterThanOrEqual(subOperands[0], subOperands[1]);
  180. case ExpressionType.LessThanOrEqual:
  181. return Expression.GreaterThan(subOperands[0], subOperands[1]);
  182. case ExpressionType.Not:
  183. return subOperands[0]; // not not x -> x :)
  184. case ExpressionType.NotEqual:
  185. return Expression.Equal(subOperands[0], subOperands[1]);
  186. case (ExpressionType)SpecialExpressionType.IsNotNull: // is this dirty work?
  187. return new SpecialExpression(SpecialExpressionType.IsNull, subOperands);
  188. case (ExpressionType)SpecialExpressionType.IsNull:
  189. return new SpecialExpression(SpecialExpressionType.IsNotNull, subOperands);
  190. }
  191. }
  192. return expression;
  193. }
  194. protected virtual Expression AnalyzeNull(Expression expression, BuilderContext builderContext)
  195. {
  196. // this first test only to speed up things a little
  197. if (expression.NodeType == ExpressionType.Equal || expression.NodeType == ExpressionType.NotEqual)
  198. {
  199. var operands = expression.GetOperands().ToList();
  200. var nullComparison = GetNullComparison(expression.NodeType, operands[0], operands[1]);
  201. if (nullComparison == null)
  202. nullComparison = GetNullComparison(expression.NodeType, operands[1], operands[0]);
  203. if (nullComparison != null)
  204. return nullComparison;
  205. return expression;
  206. }
  207. return expression;
  208. }
  209. protected virtual Expression GetNullComparison(ExpressionType nodeType, Expression columnExpression, Expression nullExpression)
  210. {
  211. if (columnExpression is ColumnExpression || columnExpression is InputParameterExpression)
  212. {
  213. if (nullExpression is ConstantExpression && ((ConstantExpression)nullExpression).Value == null)
  214. {
  215. switch (nodeType)
  216. {
  217. case ExpressionType.Equal:
  218. return new SpecialExpression(SpecialExpressionType.IsNull, columnExpression);
  219. case ExpressionType.NotEqual:
  220. return new SpecialExpression(SpecialExpressionType.IsNotNull, columnExpression);
  221. }
  222. }
  223. }
  224. return null;
  225. }
  226. }
  227. }