SqlMethodTransformer.cs 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Data.Linq;
  5. namespace System.Data.Linq.SqlClient {
  6. /// <summary>
  7. /// After retyping and conversions take place, some functions need to be changed into more suitable calls.
  8. /// Example: LEN -> DATALENGTH for long text types.
  9. /// </summary>
  10. internal class SqlMethodTransformer : SqlVisitor {
  11. protected SqlFactory sql;
  12. internal SqlMethodTransformer(SqlFactory sql) {
  13. this.sql = sql;
  14. }
  15. internal override SqlExpression VisitFunctionCall(SqlFunctionCall fc) {
  16. // process the arguments
  17. SqlExpression result = base.VisitFunctionCall(fc);
  18. if (result is SqlFunctionCall) {
  19. SqlFunctionCall resultFunctionCall = (SqlFunctionCall)result;
  20. if (resultFunctionCall.Name == "LEN") {
  21. SqlExpression expr = resultFunctionCall.Arguments[0];
  22. if (expr.SqlType.IsLargeType && !expr.SqlType.SupportsLength) {
  23. result = sql.DATALENGTH(expr);
  24. if (expr.SqlType.IsUnicodeType) {
  25. result = sql.ConvertToInt(sql.Divide(result, sql.ValueFromObject(2, expr.SourceExpression)));
  26. }
  27. }
  28. }
  29. // If the return type of the sql function is not compatible with
  30. // the expected CLR type of the function, inject a conversion. This
  31. // step must be performed AFTER SqlRetyper has run.
  32. Type clrType = resultFunctionCall.SqlType.GetClosestRuntimeType();
  33. bool skipConversion = SqlMethodTransformer.SkipConversionForDateAdd(resultFunctionCall.Name,
  34. resultFunctionCall.ClrType,
  35. clrType);
  36. if ((resultFunctionCall.ClrType != clrType) && !skipConversion) {
  37. result = sql.ConvertTo(resultFunctionCall.ClrType, resultFunctionCall);
  38. }
  39. }
  40. return result;
  41. }
  42. internal override SqlExpression VisitUnaryOperator(SqlUnary fc) {
  43. // process the arguments
  44. SqlExpression result = base.VisitUnaryOperator(fc);
  45. if (result is SqlUnary) {
  46. SqlUnary unary = (SqlUnary)result;
  47. switch (unary.NodeType) {
  48. case SqlNodeType.ClrLength:
  49. SqlExpression expr = unary.Operand;
  50. result = sql.DATALENGTH(expr);
  51. if (expr.SqlType.IsUnicodeType) {
  52. result = sql.Divide(result, sql.ValueFromObject(2, expr.SourceExpression));
  53. }
  54. result = sql.ConvertToInt(result);
  55. break;
  56. default:
  57. break;
  58. }
  59. }
  60. return result;
  61. }
  62. // We don't inject a conversion for DATEADD if doing so will downgrade the result to
  63. // a less precise type.
  64. //
  65. private static bool SkipConversionForDateAdd(string functionName, Type expected, Type actual) {
  66. if (string.Compare(functionName, "DATEADD", StringComparison.OrdinalIgnoreCase) != 0)
  67. return false;
  68. return (expected == typeof(DateTime) && actual == typeof(DateTimeOffset));
  69. }
  70. }
  71. }