SqlTranslateService.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using Vit.Linq;
  5. using Vit.Linq.ExpressionTree.ComponentModel;
  6. using Vitorm.Entity;
  7. using Vitorm.Sql.SqlTranslate;
  8. using Vitorm.Sqlite.TranslateService;
  9. namespace Vitorm.Sqlite
  10. {
  11. public class SqlTranslateService : Vitorm.Sql.SqlTranslate.SqlTranslateService
  12. {
  13. public static readonly SqlTranslateService Instance = new SqlTranslateService();
  14. protected override BaseQueryTranslateService queryTranslateService { get; }
  15. protected override BaseQueryTranslateService executeUpdateTranslateService { get; }
  16. protected override BaseQueryTranslateService executeDeleteTranslateService { get; }
  17. public SqlTranslateService()
  18. {
  19. queryTranslateService = new QueryTranslateService(this);
  20. executeUpdateTranslateService = new ExecuteUpdateTranslateService(this);
  21. executeDeleteTranslateService = new ExecuteDeleteTranslateService(this);
  22. }
  23. #region EvalExpression
  24. /// <summary>
  25. /// read where or value or on
  26. /// </summary>
  27. /// <param name="arg"></param>
  28. /// <returns></returns>
  29. /// <exception cref="NotSupportedException"></exception>
  30. /// <param name="data"></param>
  31. public override string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
  32. {
  33. switch (data.nodeType)
  34. {
  35. case NodeType.MethodCall:
  36. {
  37. ExpressionNode_MethodCall methodCall = data;
  38. switch (methodCall.methodName)
  39. {
  40. // ##1 ToString
  41. case nameof(object.ToString):
  42. {
  43. return $"cast({EvalExpression(arg, methodCall.@object)} as text)";
  44. }
  45. #region ##2 String method: StartsWith EndsWith Contains
  46. case nameof(string.StartsWith): // String.StartsWith
  47. {
  48. var str = methodCall.@object;
  49. var value = methodCall.arguments[0];
  50. return $"{EvalExpression(arg, str)} like {EvalExpression(arg, value)}||'%'";
  51. }
  52. case nameof(string.EndsWith): // String.EndsWith
  53. {
  54. var str = methodCall.@object;
  55. var value = methodCall.arguments[0];
  56. return $"{EvalExpression(arg, str)} like '%'||{EvalExpression(arg, value)}";
  57. }
  58. case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
  59. {
  60. var str = methodCall.@object;
  61. var value = methodCall.arguments[0];
  62. return $"{EvalExpression(arg, str)} like '%'||{EvalExpression(arg, value)}||'%'";
  63. }
  64. #endregion
  65. }
  66. break;
  67. }
  68. #region Read Value
  69. case NodeType.Convert:
  70. {
  71. // cast( 4.1 as signed)
  72. ExpressionNode_Convert convert = data;
  73. Type targetType = convert.valueType?.ToType();
  74. if (targetType == typeof(object)) return EvalExpression(arg, convert.body);
  75. // Nullable
  76. if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
  77. string targetDbType = GetColumnDbType(targetType);
  78. var sourceType = convert.body.Member_GetType();
  79. if (sourceType != null)
  80. {
  81. if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
  82. if (targetDbType == GetColumnDbType(sourceType)) return EvalExpression(arg, convert.body);
  83. }
  84. if (targetDbType == "datetime")
  85. {
  86. return $"DATETIME({EvalExpression(arg, convert.body)})";
  87. }
  88. return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
  89. }
  90. case nameof(ExpressionType.Add):
  91. {
  92. ExpressionNode_Binary binary = data;
  93. // ##1 String Add
  94. if (data.valueType?.ToType() == typeof(string))
  95. {
  96. // select ifNull( cast( (userFatherId) as text ) , '' ) from `User`
  97. return $"{BuildSqlSentence(binary.left)} || {BuildSqlSentence(binary.right)}";
  98. string BuildSqlSentence(ExpressionNode node)
  99. {
  100. if (node.nodeType == NodeType.Constant)
  101. {
  102. ExpressionNode_Constant constant = node;
  103. if (constant.value == null) return "''";
  104. else return $"cast( ({EvalExpression(arg, node)}) as text )";
  105. }
  106. else
  107. return $"ifNull( cast( ({EvalExpression(arg, node)}) as text ) , '')";
  108. }
  109. }
  110. // ##2 Numberic Add
  111. return $"{EvalExpression(arg, binary.left)} + {EvalExpression(arg, binary.right)}";
  112. }
  113. case nameof(ExpressionType.Coalesce):
  114. {
  115. ExpressionNode_Binary binary = data;
  116. return $"COALESCE({EvalExpression(arg, binary.left)},{EvalExpression(arg, binary.right)})";
  117. }
  118. case nameof(ExpressionType.Conditional):
  119. {
  120. // IIF(`t0`.`fatherId` is not null, true, false)
  121. ExpressionNode_Conditional conditional = data;
  122. return $"IIF({EvalExpression(arg, conditional.Conditional_GetTest())},{EvalExpression(arg, conditional.Conditional_GetIfTrue())},{EvalExpression(arg, conditional.Conditional_GetIfFalse())})";
  123. }
  124. #endregion
  125. }
  126. return base.EvalExpression(arg, data);
  127. }
  128. #endregion
  129. #region PrepareCreate
  130. public override string PrepareTryCreateTable(IEntityDescriptor entityDescriptor)
  131. {
  132. /* //sql
  133. CREATE TABLE IF NOT EXISTS "User" (
  134. id int NOT NULL PRIMARY KEY,
  135. name varchar(100) DEFAULT NULL,
  136. birth date DEFAULT NULL,
  137. fatherId int DEFAULT NULL,
  138. motherId int DEFAULT NULL
  139. ) ;
  140. */
  141. List<string> sqlFields = new();
  142. // #1 primary key
  143. sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY");
  144. // #2 columns
  145. entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
  146. return $@"
  147. CREATE TABLE IF NOT EXISTS {DelimitTableName(entityDescriptor)} (
  148. {string.Join(",\r\n ", sqlFields)}
  149. )";
  150. string GetColumnSql(IColumnDescriptor column)
  151. {
  152. var columnDbType = column.databaseType ?? GetColumnDbType(column.type);
  153. // name varchar(100) DEFAULT NULL
  154. return $" {DelimitIdentifier(column.columnName)} {columnDbType} {(column.isNullable ? "DEFAULT NULL" : "NOT NULL")}";
  155. }
  156. }
  157. protected override string GetColumnDbType(Type type)
  158. {
  159. type = TypeUtil.GetUnderlyingType(type);
  160. if (type == typeof(DateTime))
  161. return "datetime";
  162. if (type == typeof(string))
  163. return "text";
  164. if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
  165. return "real";
  166. if (type == typeof(bool) || type.Name.ToLower().Contains("int")) return "integer";
  167. throw new NotSupportedException("unsupported column type:" + type.Name);
  168. }
  169. #endregion
  170. public override string PrepareTryDropTable(IEntityDescriptor entityDescriptor)
  171. {
  172. // drop table if exists "User";
  173. return $@"drop table if exists {DelimitTableName(entityDescriptor)};";
  174. }
  175. public override string PrepareTruncate(IEntityDescriptor entityDescriptor)
  176. {
  177. // delete from 'User';
  178. return $@"delete from {DelimitTableName(entityDescriptor)};";
  179. }
  180. }
  181. }