From 74a6ab5859980b49b6d597561848c43c1525b393 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Sat, 29 Dec 2018 17:22:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20in=20=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E6=88=90=E5=AD=90=E8=A1=A8=20exists=20?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs | 34 +++- .../PostgreSQL/Curd/PostgreSQLSelectTest.cs | 33 +++- .../SqlServer/Curd/SqlServerDeleteTest.cs | 20 +- .../SqlServer/Curd/SqlServerSelectTest.cs | 33 +++- FreeSql.Tests/UnitTest1.cs | 12 +- FreeSql/FreeSql.csproj | 2 +- FreeSql/Internal/CommonExpression.cs | 88 ++++++++- FreeSql/Internal/Model/SelectTableInfo.cs | 2 +- FreeSql/MySql/Curd/MySqlSelect.cs | 2 + FreeSql/MySql/MySqlExpression.cs | 152 ++++++++------- FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs | 2 + FreeSql/PostgreSQL/PostgreSQLExpression.cs | 182 +++++++++--------- FreeSql/SqlServer/Curd/SqlServerSelect.cs | 2 + FreeSql/SqlServer/SqlServerExpression.cs | 156 +++++++-------- 14 files changed, 455 insertions(+), 265 deletions(-) diff --git a/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs b/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs index fcf58af3..dd77d193 100644 --- a/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs +++ b/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs @@ -520,11 +520,41 @@ namespace FreeSql.Tests.MySql { query.ToList(); } [Fact] - public void GroupBy() { + public void WhereExists() { + var sql2222 = select.Where(a => select.Where(b => b.Id == a.Id).Any()).ToList(); + + sql2222 = select.Where(a => + select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) + + .Offset(a.Id) + + .Any() + ).Any() + ).ToList(); } [Fact] - public void Having() { + public void GroupBy() { + var groupby = select.From((s, b, c) => s + .Where(a => a.Id == 1) + ) + .GroupBy((a, b, c) => new { tt2 = a.Title.Substring(0, 2), mod4 = a.Id % 4 }) + .Having(a => a.Count() > 0 && a.Avg(a.Key.mod4) > 0 && a.Max(a.Key.mod4) > 0) + .Having(a => a.Count() < 300 || a.Avg(a.Key.mod4) < 100) + .OrderBy(a => a.Key.tt2) + .OrderByDescending(a => a.Count()) + .ToList(a => new { + a.Key.tt2, + cou1 = a.Count(), + arg1 = a.Avg(a.Key.mod4), + ccc2 = a.Key.tt2 ?? "now()", + //ccc = Convert.ToDateTime("now()"), partby = Convert.ToDecimal("sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)") + }); } + [Fact] + public void ToAggregate() { + var sql = select.ToAggregate(a => new { sum = a.Sum(a.Key.Id + 11.11), avg = a.Avg(a.Key.Id), count = a.Count(), max = a.Max(a.Key.Id), min = a.Min(a.Key.Id) }); + } + [Fact] public void OrderBy() { } diff --git a/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs b/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs index b495a636..37d31af2 100644 --- a/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs +++ b/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLSelectTest.cs @@ -520,10 +520,39 @@ namespace FreeSql.Tests.PostgreSQL { query.ToList(); } [Fact] - public void GroupBy() { + public void WhereExists() { + var sql2222 = select.Where(a => select.Where(b => b.Id == a.Id).Any()).ToList(); + + sql2222 = select.Where(a => + select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) + + .Offset(a.Id) + + .Any() + ).Any() + ).ToList(); } [Fact] - public void Having() { + public void GroupBy() { + var groupby = select.From((s, b, c) => s + .Where(a => a.Id == 1) + ) + .GroupBy((a, b, c) => new { tt2 = a.Title.Substring(0, 2), mod4 = a.Id % 4 }) + .Having(a => a.Count() > 0 && a.Avg(a.Key.mod4) > 0 && a.Max(a.Key.mod4) > 0) + .Having(a => a.Count() < 300 || a.Avg(a.Key.mod4) < 100) + .OrderBy(a => a.Key.tt2) + .OrderByDescending(a => a.Count()) + .ToList(a => new { + a.Key.tt2, + cou1 = a.Count(), + arg1 = a.Avg(a.Key.mod4), + ccc2 = a.Key.tt2 ?? "now()", + //ccc = Convert.ToDateTime("now()"), partby = Convert.ToDecimal("sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)") + }); + } + [Fact] + public void ToAggregate() { + var sql = select.ToAggregate(a => new { sum = a.Sum(a.Key.Id + 11.11), avg = a.Avg(a.Key.Id), count = a.Count(), max = a.Max(a.Key.Id), min = a.Min(a.Key.Id) }); } [Fact] public void OrderBy() { diff --git a/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs b/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs index 86b79b56..2134fa77 100644 --- a/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs +++ b/FreeSql.Tests/SqlServer/Curd/SqlServerDeleteTest.cs @@ -9,11 +9,11 @@ namespace FreeSql.Tests.SqlServer { IDelete delete => g.sqlserver.Delete(); //�������� - [Table(Name = "tb_topic")] + [Table(Name = "tb_topic22211")] class Topic { [Column(IsIdentity = true, IsPrimary = true)] public int Id { get; set; } - public int Clicks { get; set; } + public int? Clicks { get; set; } public TestTypeInfo Type { get; set; } public string Title { get; set; } public DateTime CreateTime { get; set; } @@ -23,35 +23,35 @@ namespace FreeSql.Tests.SqlServer { public void Dywhere() { Assert.Null(g.sqlserver.Delete().ToSql()); var sql = g.sqlserver.Delete(new[] { 1, 2 }).ToSql(); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] = 1 OR [Id] = 2)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] = 1 OR [Id] = 2)", sql); sql = g.sqlserver.Delete(new Topic { Id = 1, Title = "test" }).ToSql(); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] = 1)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] = 1)", sql); sql = g.sqlserver.Delete(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).ToSql(); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] = 1 OR [Id] = 2)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] = 1 OR [Id] = 2)", sql); sql = g.sqlserver.Delete(new { id = 1 }).ToSql(); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] = 1)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] = 1)", sql); } [Fact] public void Where() { var sql = delete.Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] = 1)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] = 1)", sql); sql = delete.Where("id = ?id", new { id = 1 }).ToSql().Replace("\r\n", ""); - Assert.Equal("DELETE FROM [tb_topic] WHERE (id = ?id)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE (id = ?id)", sql); var item = new Topic { Id = 1, Title = "newtitle" }; sql = delete.Where(item).ToSql().Replace("\r\n", ""); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] = 1)", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] = 1)", sql); var items = new List(); for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); sql = delete.Where(items).ToSql().Replace("\r\n", ""); - Assert.Equal("DELETE FROM [tb_topic] WHERE ([Id] IN (1,2,3,4,5,6,7,8,9,10))", sql); + Assert.Equal("DELETE FROM [tb_topic22211] WHERE ([Id] IN (1,2,3,4,5,6,7,8,9,10))", sql); } [Fact] public void WhereExists() { diff --git a/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs b/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs index 838d1426..dc34cdf7 100644 --- a/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs +++ b/FreeSql.Tests/SqlServer/Curd/SqlServerSelectTest.cs @@ -443,10 +443,39 @@ namespace FreeSql.Tests.SqlServer { query.ToList(); } [Fact] - public void GroupBy() { + public void WhereExists() { + var sql2222 = select.Where(a => select.Where(b => b.Id == a.Id).Any()).ToList(); + + sql2222 = select.Where(a => + select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) + + .Offset(a.Id) + + .Any() + ).Any() + ).ToList(); } [Fact] - public void Having() { + public void GroupBy() { + var groupby = select.From((s, b, c) => s + .Where(a => a.Id == 1) + ) + .GroupBy((a, b, c) => new { tt2 = a.Title.Substring(0, 2), mod4 = a.Id % 4 }) + .Having(a => a.Count() > 0 && a.Avg(a.Key.mod4) > 0 && a.Max(a.Key.mod4) > 0) + .Having(a => a.Count() < 300 || a.Avg(a.Key.mod4) < 100) + .OrderBy(a => a.Key.tt2) + .OrderByDescending(a => a.Count()) + .ToList(a => new { + a.Key.tt2, + cou1 = a.Count(), + arg1 = a.Avg(a.Key.mod4), + ccc2 = a.Key.tt2 ?? "now()", + //ccc = Convert.ToDateTime("now()"), partby = Convert.ToDecimal("sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)") + }); + } + [Fact] + public void ToAggregate() { + var sql = select.ToAggregate(a => new { sum = a.Sum(a.Key.Id + 11.11), avg = a.Avg(a.Key.Id), count = a.Count(), max = a.Max(a.Key.Id), min = a.Min(a.Key.Id) }); } [Fact] public void OrderBy() { diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index ddff1556..795ed340 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -20,6 +20,16 @@ namespace FreeSql.Tests { [Fact] public void Test1() { + var sql2222 = select.Where(a => + select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) + + .Offset(a.Id) + + .Any() + ).Any() + ).ToSql(); + + var groupby = g.mysql.Select().From((s, b, c) => s .Where(a => a.Id == 1) ) @@ -28,7 +38,7 @@ namespace FreeSql.Tests { .Having(a => a.Count() < 300 || a.Avg(a.Key.mod4) < 100) .OrderBy(a => a.Key.tt2) .OrderByDescending(a => a.Count()) - .ToList(a => new { a.Key.tt2, cou1 = a.Count(), arg1 = a.Avg(a.Key.mod4), + .ToSql(a => new { a.Key.tt2, cou1 = a.Count(), arg1 = a.Avg(a.Key.mod4), ccc2 = a.Key.tt2 ?? "now()", //ccc = Convert.ToDateTime("now()"), partby = Convert.ToDecimal("sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)") }); diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index 7293b4ec..e82ce6c4 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -3,7 +3,7 @@ netstandard2.0 0.0.0.1 - 0.0.3 + 0.0.4 0.0.0.0 true diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index f24d78fd..a051f47e 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Text; namespace FreeSql.Internal { @@ -223,6 +224,67 @@ namespace FreeSql.Internal { case "Min": return $"min({ExpressionLambdaToSql(exp3.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; } } + if (callType.FullName.StartsWith("FreeSql.ISelect`")) { //子表查询 + if (exp3.Method.Name == "Any") { //exists + var exp3Stack = new Stack(); + var exp3tmp = exp3.Object; + while (exp3tmp != null) { + exp3Stack.Push(exp3tmp); + switch (exp3tmp.NodeType) { + case ExpressionType.Call: exp3tmp = (exp3tmp as MethodCallExpression).Object; continue; + case ExpressionType.MemberAccess: exp3tmp = (exp3tmp as MemberExpression).Expression; continue; + } + break; + } + object fsql = null; + List fsqltables = null; + var fsqltable1SetAlias = false; + Type fsqlType = null; + while (exp3Stack.Any()) { + exp3tmp = exp3Stack.Pop(); + if (exp3tmp.Type.FullName.StartsWith("FreeSql.ISelect`") && fsql == null) { + fsql = Expression.Lambda(exp3tmp).Compile().DynamicInvoke(); + fsqlType = fsql?.GetType(); + if (fsqlType == null) break; + fsqlType.GetField("_limit", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(fsql, 1); + fsqltables = fsqlType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(fsql) as List; + //fsqltables[0].Alias = $"{_tables[0].Alias}_{fsqltables[0].Alias}"; + fsqltables.AddRange(_tables.Select(a => new SelectTableInfo { + Alias = a.Type == SelectTableInfoType.Parent ? a.Alias : $"__parent_{a.Alias}_parent__", + On = "1=1", + Table = a.Table, + Type = SelectTableInfoType.Parent + })); + } else if (fsqlType != null) { + var call3Exp = exp3tmp as MethodCallExpression; + var method = fsqlType.GetMethod(call3Exp.Method.Name, call3Exp.Arguments.Select(a => a.Type).ToArray()); + if (call3Exp.Method.ContainsGenericParameters) method.MakeGenericMethod(call3Exp.Method.GetGenericArguments()); + var parms = method.GetParameters(); + var args = new object[call3Exp.Arguments.Count]; + for (var a = 0; a < args.Length; a++) { + var argExp = (call3Exp.Arguments[a] as UnaryExpression)?.Operand; + if (argExp != null && argExp.NodeType == ExpressionType.Lambda) { + if (fsqltable1SetAlias == false) { + fsqltables[0].Alias = (argExp as LambdaExpression).Parameters.First().Name; + fsqltable1SetAlias = true; + } + } + args[a] = argExp; + //if (args[a] == null) ExpressionLambdaToSql(call3Exp.Arguments[a], fsqltables, null, null, SelectTableInfoType.From, true); + } + method.Invoke(fsql, args); + } + } + if (fsql != null) { + var sql = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { "1" })?.ToString(); + if (string.IsNullOrEmpty(sql) == false) { + foreach (var tb in _tables) + sql = sql.Replace($"__parent_{tb.Alias}_parent__", tb.Alias); + return $"exists({sql})"; + } + } + } + } var other3Exp = ExpressionLambdaToSqlOther(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp; throw new Exception($"未现实函数表达式 {exp3} 解析"); @@ -287,6 +349,19 @@ namespace FreeSql.Internal { if (isQuoteName) name = _common.QuoteSqlName(name); return name; } + Func getOrAddTable = (tbtmp, alias) => { + var finds = _tables.Where((a2, c2) => a2.Table.CsName == tbtmp.CsName).ToArray(); //外部表,内部表一起查 + if (finds.Length > 1) { + finds = _tables.Where((a2, c2) => a2.Table.CsName == tbtmp.CsName && a2.Type == SelectTableInfoType.Parent && a2.Alias == $"__parent_{alias}_parent__").ToArray(); //查询外部表 + if (finds.Any() == false) { + finds = _tables.Where((a2, c2) => a2.Table.CsName == tbtmp.CsName && a2.Type != SelectTableInfoType.Parent).ToArray(); //查询内部表 + if (finds.Length > 1) finds = _tables.Where((a2, c2) => a2.Table.CsName == tbtmp.CsName && a2.Type != SelectTableInfoType.Parent && a2.Alias == alias).ToArray(); + } + } + var find = finds.FirstOrDefault(); + if (find == null) _tables.Add(find = new SelectTableInfo { Table = tbtmp, Alias = alias, On = null, Type = tbtype }); + return find; + }; TableInfo tb2 = null; string alias2 = "", name2 = ""; @@ -298,6 +373,7 @@ namespace FreeSql.Internal { throw new NotImplementedException("未现实 MemberAccess 下的 Constant"); case ExpressionType.Parameter: case ExpressionType.MemberAccess: + var exp2Type = exp2.Type; if (exp2Type.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`")) exp2Type = exp2Type.GenericTypeArguments.FirstOrDefault() ?? exp2.Type; var tb2tmp = _common.GetTableByEntity(exp2Type); @@ -306,10 +382,7 @@ namespace FreeSql.Internal { if (tb2tmp != null) { if (exp2.NodeType == ExpressionType.Parameter) alias2 = (exp2 as ParameterExpression).Name; else alias2 = $"{alias2}__{mp2.Member.Name}"; - var find2s = _tables.Where((a2, c2) => a2.Table.CsName == tb2tmp.CsName).ToArray(); - if (find2s.Length > 1) find2s = _tables.Where((a2, c2) => a2.Table.CsName == tb2tmp.CsName && a2.Alias == alias2).ToArray(); - find2 = find2s.FirstOrDefault(); - if (find2 == null) _tables.Add(find2 = new SelectTableInfo { Table = tb2tmp, Alias = alias2, On = null, Type = tbtype }); + find2 = getOrAddTable(tb2tmp, alias2); alias2 = find2.Alias; tb2 = tb2tmp; } @@ -318,12 +391,7 @@ namespace FreeSql.Internal { if (_selectColumnMap != null) { var tb3 = _common.GetTableByEntity(mp2.Type); if (tb3 != null) { - var alias3 = $"{alias2}__{mp2.Member.Name}"; - var find3s = _tables.Where((a3, c3) => a3.Table.CsName == tb3.CsName).ToArray(); - if (find3s.Length > 1) find3s = _tables.Where((a3, c3) => a3.Table.CsName == tb3.CsName && a3.Alias == alias3).ToArray(); - var find3 = find3s.FirstOrDefault(); - if (find3 == null) _tables.Add(find3 = new SelectTableInfo { Table = tb3, Alias = alias3, On = null, Type = tbtype }); - alias3 = find3.Alias; + var find3 = getOrAddTable(tb2tmp, $"{alias2}__{mp2.Member.Name}"); foreach (var tb3c in tb3.Columns.Values) _selectColumnMap.Add(new SelectColumnInfo { Table = find3, Column = tb3c }); diff --git a/FreeSql/Internal/Model/SelectTableInfo.cs b/FreeSql/Internal/Model/SelectTableInfo.cs index 7f7f12fe..d01cd3f1 100644 --- a/FreeSql/Internal/Model/SelectTableInfo.cs +++ b/FreeSql/Internal/Model/SelectTableInfo.cs @@ -5,5 +5,5 @@ public string On { get; set; } public SelectTableInfoType Type { get; set; } } - enum SelectTableInfoType { From, LeftJoin, InnerJoin, RightJoin } + enum SelectTableInfoType { From, LeftJoin, InnerJoin, RightJoin, Parent } } diff --git a/FreeSql/MySql/Curd/MySqlSelect.cs b/FreeSql/MySql/Curd/MySqlSelect.cs index 2fce182b..3c7660d8 100644 --- a/FreeSql/MySql/Curd/MySqlSelect.cs +++ b/FreeSql/MySql/Curd/MySqlSelect.cs @@ -29,6 +29,7 @@ namespace FreeSql.MySql.Curd { if (a < tbsfrom.Length - 1) sb.Append(", "); } foreach (var tb in tbsjoin) { + if (tb.Type == SelectTableInfoType.Parent) continue; switch (tb.Type) { case SelectTableInfoType.LeftJoin: sb.Append(" \r\nLEFT JOIN "); @@ -46,6 +47,7 @@ namespace FreeSql.MySql.Curd { var sbqf = new StringBuilder(); foreach (var tb in _tables) { + if (tb.Type == SelectTableInfoType.Parent) continue; if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false) sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")"); } diff --git a/FreeSql/MySql/MySqlExpression.cs b/FreeSql/MySql/MySqlExpression.cs index 2f770e97..451bf466 100644 --- a/FreeSql/MySql/MySqlExpression.cs +++ b/FreeSql/MySql/MySqlExpression.cs @@ -12,6 +12,7 @@ namespace FreeSql.MySql { public MySqlExpression(CommonUtils common) : base(common) { } internal override string ExpressionLambdaToSqlOther(Expression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.NodeType) { case ExpressionType.Call: var callExp = exp as MethodCallExpression; @@ -27,12 +28,12 @@ namespace FreeSql.MySql { } if (objType == null) objType = callExp.Method.DeclaringType; if (objType != null) { - var left = objExp == null ? null : ExpressionLambdaToSql(objExp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = objExp == null ? null : getExp(objExp); if (objType.IsArray == true) { switch (callExp.Method.Name) { case "Contains": //判断 in - return $"({ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}) in {left}"; + return $"({getExp(callExp.Arguments[argIndex])}) in {left}"; } } } @@ -43,7 +44,7 @@ namespace FreeSql.MySql { arrSb.Append("("); for (var a = 0; a < arrExp.Expressions.Count; a++) { if (a > 0) arrSb.Append(","); - arrSb.Append(ExpressionLambdaToSql(arrExp.Expressions[a], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)); + arrSb.Append(getExp(arrExp.Expressions[a])); } return arrSb.Append(")").ToString(); } @@ -118,19 +119,20 @@ namespace FreeSql.MySql { } internal override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { case "IsNullOrEmpty": - var arg1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var arg1 = getExp(exp.Arguments[0]); return $"({arg1} is null or {arg1} = '')"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); switch (exp.Method.Name) { case "StartsWith": case "EndsWith": case "Contains": - var args0Value = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var args0Value = getExp(exp.Arguments[0]); if (args0Value == "NULL") return $"({left}) IS NULL"; if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}"; if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}"; @@ -139,26 +141,26 @@ namespace FreeSql.MySql { case "ToLower": return $"lower({left})"; case "ToUpper": return $"upper({left})"; case "Substring": - var substrArgs1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var substrArgs1 = getExp(exp.Arguments[0]); if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString(); else substrArgs1 += "+1"; if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})"; - return $"substr({left}, {substrArgs1}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})"; case "IndexOf": - var indexOfFindStr = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var indexOfFindStr = getExp(exp.Arguments[0]); if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") { - var locateArgs1 = ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var locateArgs1 = getExp(exp.Arguments[1]); if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString(); else locateArgs1 += "+1"; return $"(locate({left}, {indexOfFindStr}, {locateArgs1})-1)"; } return $"(locate({left}, {indexOfFindStr})-1)"; case "PadLeft": - if (exp.Arguments.Count == 1) return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})"; + return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "PadRight": - if (exp.Arguments.Count == 1) return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})"; + return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "Trim": case "TrimStart": case "TrimEnd": @@ -174,63 +176,65 @@ namespace FreeSql.MySql { argsTrim01s = arritem.Expressions.ToArray(); } foreach (var argsTrim01 in argsTrim01s) { - if (exp.Method.Name == "Trim") left = $"trim({ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} from {left})"; - if (exp.Method.Name == "TrimStart") left = $"trim(leading {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} from {left})"; - if (exp.Method.Name == "TrimEnd") left = $"trim(trailing {ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} from {left})"; + if (exp.Method.Name == "Trim") left = $"trim({getExp(argsTrim01)} from {left})"; + if (exp.Method.Name == "TrimStart") left = $"trim(leading {getExp(argsTrim01)} from {left})"; + if (exp.Method.Name == "TrimEnd") left = $"trim(trailing {getExp(argsTrim01)} from {left})"; } } return left; - case "Replace": return $"replace({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"strcmp({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "CompareTo": return $"strcmp({left}, {getExp(exp.Arguments[0])})"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; } } throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.Method.Name) { - case "Abs": return $"abs({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sign": return $"sign({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Floor": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Ceiling": return $"ceiling({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Abs": return $"abs({getExp(exp.Arguments[0])})"; + case "Sign": return $"sign({getExp(exp.Arguments[0])})"; + case "Floor": return $"floor({getExp(exp.Arguments[0])})"; + case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})"; case "Round": - if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Exp": return $"exp({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Log": return $"log({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Log10": return $"log10({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Pow": return $"pow({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sqrt": return $"sqrt({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Cos": return $"cos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sin": return $"sin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Tan": return $"tan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Acos": return $"acos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Asin": return $"asin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Atan": return $"atan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Atan2": return $"atan2({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Truncate": return $"truncate({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, 0)"; + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + return $"round({getExp(exp.Arguments[0])})"; + case "Exp": return $"exp({getExp(exp.Arguments[0])})"; + case "Log": return $"log({getExp(exp.Arguments[0])})"; + case "Log10": return $"log10({getExp(exp.Arguments[0])})"; + case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})"; + case "Cos": return $"cos({getExp(exp.Arguments[0])})"; + case "Sin": return $"sin({getExp(exp.Arguments[0])})"; + case "Tan": return $"tan({getExp(exp.Arguments[0])})"; + case "Acos": return $"acos({getExp(exp.Arguments[0])})"; + case "Asin": return $"asin({getExp(exp.Arguments[0])})"; + case "Atan": return $"atan({getExp(exp.Arguments[0])})"; + case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Truncate": return $"truncate({getExp(exp.Arguments[0])}, 0)"; } throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "Compare": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} - ({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; - case "DaysInMonth": return $"dayofmonth(last_day(concat({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, '-', {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, '-01')))"; - case "Equals": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} = {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))"; + case "DaysInMonth": return $"dayofmonth(last_day(concat({getExp(exp.Arguments[0])}, '-', {getExp(exp.Arguments[1])}, '-01')))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; case "IsLeapYear": - var isLeapYearArgs1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var isLeapYearArgs1 = getExp(exp.Arguments[0]); return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)"; - case "Parse": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as datetime)"; + case "Parse": return $"cast({getExp(exp.Arguments[0])} as datetime)"; case "ParseExact": case "TryParse": - case "TryParseExact": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as datetime)"; + case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as datetime)"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var args1 = exp.Arguments.Count == 0 ? null : ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); switch (exp.Method.Name) { case "Add": return $"date_add({left}, interval ({args1}) microsecond)"; case "AddDays": return $"date_add({left}, interval ({args1}) day)"; @@ -247,60 +251,62 @@ namespace FreeSql.MySql { if (exp.Arguments[0].Type.FullName == "System.TimeSpan" || exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault()?.FullName == "System.TimeSpan") return $"date_sub({left}, interval ({args1}) microsecond)"; break; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"(({left}) - ({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; + case "CompareTo": return $"(({left}) - ({getExp(exp.Arguments[0])}))"; case "ToString": return $"date_format({left}, '%Y-%m-%d %H:%i:%s.%f')"; } } throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "Compare": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}-({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; - case "Equals": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} = {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "FromDays": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{(long)1000000 * 60 * 60 * 24})"; - case "FromHours": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{(long)1000000 * 60 * 60})"; - case "FromMilliseconds": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*1000)"; - case "FromMinutes": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{(long)1000000 * 60})"; - case "FromSeconds": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*1000000)"; - case "FromTicks": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})/10)"; - case "Parse": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as signed)"; + case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})"; + case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})"; + case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)"; + case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})"; + case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)"; + case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)"; + case "Parse": return $"cast({getExp(exp.Arguments[0])} as signed)"; case "ParseExact": case "TryParse": - case "TryParseExact": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as signed)"; + case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as signed)"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var args1 = exp.Arguments.Count == 0 ? null : ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); switch (exp.Method.Name) { case "Add": return $"({left}+{args1})"; case "Subtract": return $"({left}-({args1}))"; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"({left}-({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; + case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))"; case "ToString": return $"cast({left} as char)"; } } throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "ToBoolean": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} not in ('0','false'))"; - case "ToByte": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as unsigned)"; - case "ToChar": return $"substr(cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as char), 1, 1)"; - case "ToDateTime": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as datetime)"; - case "ToDecimal": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as decimal(36,18))"; - case "ToDouble": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as decimal(32,16))"; + case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))"; + case "ToByte": return $"cast({getExp(exp.Arguments[0])} as unsigned)"; + case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as char), 1, 1)"; + case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as datetime)"; + case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))"; + case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))"; case "ToInt16": case "ToInt32": case "ToInt64": - case "ToSByte": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as signed)"; - case "ToSingle": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as decimal(14,7))"; - case "ToString": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as char)"; + case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as signed)"; + case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))"; + case "ToString": return $"cast({getExp(exp.Arguments[0])} as char)"; case "ToUInt16": case "ToUInt32": - case "ToUInt64": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as unsigned)"; + case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as unsigned)"; } } throw new Exception($"MySqlExpression 未现实函数表达式 {exp} 解析"); diff --git a/FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs b/FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs index 51dcb00b..42de1c7c 100644 --- a/FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs +++ b/FreeSql/PostgreSQL/Curd/PostgreSQLSelect.cs @@ -29,6 +29,7 @@ namespace FreeSql.PostgreSQL.Curd { if (a < tbsfrom.Length - 1) sb.Append(", "); } foreach (var tb in tbsjoin) { + if (tb.Type == SelectTableInfoType.Parent) continue; switch (tb.Type) { case SelectTableInfoType.LeftJoin: sb.Append(" \r\nLEFT JOIN "); @@ -46,6 +47,7 @@ namespace FreeSql.PostgreSQL.Curd { var sbqf = new StringBuilder(); foreach (var tb in _tables) { + if (tb.Type == SelectTableInfoType.Parent) continue; if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false) sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")"); } diff --git a/FreeSql/PostgreSQL/PostgreSQLExpression.cs b/FreeSql/PostgreSQL/PostgreSQLExpression.cs index 97568ea1..30a1e0f8 100644 --- a/FreeSql/PostgreSQL/PostgreSQLExpression.cs +++ b/FreeSql/PostgreSQL/PostgreSQLExpression.cs @@ -13,9 +13,10 @@ namespace FreeSql.PostgreSQL { public PostgreSQLExpression(CommonUtils common) : base(common) { } internal override string ExpressionLambdaToSqlOther(Expression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.NodeType) { case ExpressionType.ArrayLength: - var arrOperExp = ExpressionLambdaToSql((exp as UnaryExpression).Operand, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var arrOperExp = getExp((exp as UnaryExpression)); if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)"; return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end"; case ExpressionType.Call: @@ -32,7 +33,7 @@ namespace FreeSql.PostgreSQL { } if (objType == null) objType = callExp.Method.DeclaringType; if (objType != null) { - var left = objExp == null ? null : ExpressionLambdaToSql(objExp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = objExp == null ? null : getExp(objExp); if (objType.IsArray == true) { switch (callExp.Method.Name) { case "Any": @@ -40,7 +41,7 @@ namespace FreeSql.PostgreSQL { return $"(case when {left} is null then 0 else array_length({left},1) end > 0)"; case "Contains": //判断 in 或 array @> array - var right1 = ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var right1 = getExp(callExp.Arguments[argIndex]); if (left.StartsWith("array[") || left.EndsWith("]")) return $"{right1} in ({left.Substring(6, left.Length - 7)})"; if (left.StartsWith("(") || left.EndsWith(")")) @@ -49,7 +50,7 @@ namespace FreeSql.PostgreSQL { return $"({left} @> array[{right1}])"; case "Concat": if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; - var right2 = ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var right2 = getExp(callExp.Arguments[argIndex]); if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]"; return $"({left} || {right2})"; case "GetLength": @@ -67,17 +68,17 @@ namespace FreeSql.PostgreSQL { switch (callExp.Method.Name) { case "Any": return $"(jsonb_array_length(coalesce({left},'[]')) > 0)"; case "Contains": - var json = ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var json = getExp(callExp.Arguments[argIndex]); if (json.StartsWith("'") && json.EndsWith("'")) return $"(coalesce({left},'{{}}') @> {_common.FormatSql("{0}", JToken.Parse(json.Trim('\'')))})"; return $"(coalesce({left},'{{}}') @> ({json})::jsonb)"; - case "ContainsKey": return $"(coalesce({left},'{{}}') ? {ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "ContainsKey": return $"(coalesce({left},'{{}}') ? {getExp(callExp.Arguments[argIndex])})"; case "Concat": - var right2 = ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var right2 = getExp(callExp.Arguments[argIndex]); return $"(coalesce({left},'{{}}') || {right2})"; case "LongCount": case "Count": return $"jsonb_array_length(coalesce({left},'[]'))"; case "Parse": - var json2 = ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var json2 = getExp(callExp.Arguments[argIndex]); if (json2.StartsWith("'") && json2.EndsWith("'")) return _common.FormatSql("{0}", JToken.Parse(json2.Trim('\''))); return $"({json2})::jsonb"; } @@ -86,10 +87,10 @@ namespace FreeSql.PostgreSQL { if (objType.FullName == typeof(Dictionary).FullName) { switch (callExp.Method.Name) { case "Contains": - var right = ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var right = getExp(callExp.Arguments[argIndex]); return $"({left} @> ({right}))"; - case "ContainsKey": return $"({left} ? {ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Concat": return $"({left} || {ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "ContainsKey": return $"({left} ? {getExp(callExp.Arguments[argIndex])})"; + case "Concat": return $"({left} || {getExp(callExp.Arguments[argIndex])})"; case "GetLength": case "GetLongLength": case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end"; @@ -105,7 +106,7 @@ namespace FreeSql.PostgreSQL { if (memParentExp?.FullName == "System.Byte[]") return null; if (memParentExp != null) { if (memParentExp.IsArray == true) { - var left = ExpressionLambdaToSql(memExp.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(memExp.Expression); if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; switch (memExp.Member.Name) { case "Length": @@ -116,14 +117,14 @@ namespace FreeSql.PostgreSQL { case "Newtonsoft.Json.Linq.JToken": case "Newtonsoft.Json.Linq.JObject": case "Newtonsoft.Json.Linq.JArray": - var left = ExpressionLambdaToSql(memExp.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(memExp.Expression); switch (memExp.Member.Name) { case "Count": return $"jsonb_array_length(coalesce({left},'[]'))"; } break; } if (memParentExp.FullName == typeof(Dictionary).FullName) { - var left = ExpressionLambdaToSql(memExp.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(memExp.Expression); switch (memExp.Member.Name) { case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end"; case "Keys": return $"akeys({left})"; @@ -138,7 +139,7 @@ namespace FreeSql.PostgreSQL { arrSb.Append("array["); for (var a = 0; a < arrExp.Expressions.Count; a++) { if (a > 0) arrSb.Append(","); - arrSb.Append(ExpressionLambdaToSql(arrExp.Expressions[a], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)); + arrSb.Append(getExp(arrExp.Expressions[a])); } return arrSb.Append("]").ToString(); } @@ -213,24 +214,25 @@ namespace FreeSql.PostgreSQL { } internal override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { case "IsNullOrEmpty": - var arg1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var arg1 = getExp(exp.Arguments[0]); return $"({arg1} is null or {arg1} = '')"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); switch (exp.Method.Name) { case "StartsWith": case "EndsWith": case "Contains": - var args0Value = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var args0Value = getExp(exp.Arguments[0]); if (args0Value == "NULL") return $"({left}) IS NULL"; var likeOpt = "LIKE"; if (exp.Arguments.Count > 1) { if (exp.Arguments[1].Type == typeof(bool) || - exp.Arguments[1].Type == typeof(StringComparison) && ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName).Contains("IgnoreCase")) likeOpt = "ILIKE"; + exp.Arguments[1].Type == typeof(StringComparison) && getExp(exp.Arguments[0]).Contains("IgnoreCase")) likeOpt = "ILIKE"; } if (exp.Method.Name == "StartsWith") return $"({left}) {likeOpt} {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(({args0Value})::varchar || '%')")}"; if (exp.Method.Name == "EndsWith") return $"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::varchar)")}"; @@ -239,18 +241,18 @@ namespace FreeSql.PostgreSQL { case "ToLower": return $"lower({left})"; case "ToUpper": return $"upper({left})"; case "Substring": - var substrArgs1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var substrArgs1 = getExp(exp.Arguments[0]); if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString(); else substrArgs1 += "+1"; if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})"; - return $"substr({left}, {substrArgs1}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "IndexOf": return $"(strpos({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})-1)"; + return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})"; + case "IndexOf": return $"(strpos({left}, {getExp(exp.Arguments[0])})-1)"; case "PadLeft": - if (exp.Arguments.Count == 1) return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})"; + return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "PadRight": - if (exp.Arguments.Count == 1) return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})"; + return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "Trim": case "TrimStart": case "TrimEnd": @@ -268,7 +270,7 @@ namespace FreeSql.PostgreSQL { argsTrim01s = arritem.Expressions.ToArray(); } foreach (var argsTrim01 in argsTrim01s) { - var trimChr = ExpressionLambdaToSql(argsTrim01, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName).Trim('\''); + var trimChr = getExp(argsTrim01).Trim('\''); if (trimChr.Length == 1) trimArg1 += trimChr; else trimArg2 += $" || ({trimChr})"; } @@ -277,57 +279,59 @@ namespace FreeSql.PostgreSQL { if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; return left; - case "Replace": return $"replace({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"case when {left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} then 0 when {left} > {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} then 1 else -1 end"; - case "Equals": return $"({left} = ({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::varchar)"; + case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end"; + case "Equals": return $"({left} = ({getExp(exp.Arguments[0])})::varchar)"; } } throw new Exception($"PostgreSQLExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.Method.Name) { - case "Abs": return $"abs({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sign": return $"sign({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Floor": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Ceiling": return $"ceiling({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Abs": return $"abs({getExp(exp.Arguments[0])})"; + case "Sign": return $"sign({getExp(exp.Arguments[0])})"; + case "Floor": return $"floor({getExp(exp.Arguments[0])})"; + case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})"; case "Round": - if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Exp": return $"exp({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Log": return $"log({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Log10": return $"log10({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Pow": return $"pow({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sqrt": return $"sqrt({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Cos": return $"cos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sin": return $"sin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Tan": return $"tan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Acos": return $"acos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Asin": return $"asin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Atan": return $"atan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Atan2": return $"atan2({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Truncate": return $"trunc({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, 0)"; + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + return $"round({getExp(exp.Arguments[0])})"; + case "Exp": return $"exp({getExp(exp.Arguments[0])})"; + case "Log": return $"log({getExp(exp.Arguments[0])})"; + case "Log10": return $"log10({getExp(exp.Arguments[0])})"; + case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})"; + case "Cos": return $"cos({getExp(exp.Arguments[0])})"; + case "Sin": return $"sin({getExp(exp.Arguments[0])})"; + case "Tan": return $"tan({getExp(exp.Arguments[0])})"; + case "Acos": return $"acos({getExp(exp.Arguments[0])})"; + case "Asin": return $"asin({getExp(exp.Arguments[0])})"; + case "Atan": return $"atan({getExp(exp.Arguments[0])})"; + case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Truncate": return $"trunc({getExp(exp.Arguments[0])}, 0)"; } throw new Exception($"PostgreSQLExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "Compare": return $"extract(epoch from ({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp-({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp)"; - case "DaysInMonth": return $"extract(day from ({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} || '-' || {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} || '-01')::timestamp+'1 month'::interval-'1 day'::interval)"; - case "Equals": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp = ({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp)"; + case "Compare": return $"extract(epoch from ({getExp(exp.Arguments[0])})::timestamp-({getExp(exp.Arguments[1])})::timestamp)"; + case "DaysInMonth": return $"extract(day from ({getExp(exp.Arguments[0])} || '-' || {getExp(exp.Arguments[1])} || '-01')::timestamp+'1 month'::interval-'1 day'::interval)"; + case "Equals": return $"(({getExp(exp.Arguments[0])})::timestamp = ({getExp(exp.Arguments[1])})::timestamp)"; case "IsLeapYear": - var isLeapYearArgs1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var isLeapYearArgs1 = getExp(exp.Arguments[0]); return $"(({isLeapYearArgs1})::int8%4=0 AND ({isLeapYearArgs1})::int8%100<>0 OR ({isLeapYearArgs1})::int8%400=0)"; - case "Parse": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp"; + case "Parse": return $"({getExp(exp.Arguments[0])})::timestamp"; case "ParseExact": case "TryParse": - case "TryParseExact": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp"; + case "TryParseExact": return $"({getExp(exp.Arguments[0])})::timestamp"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var args1 = exp.Arguments.Count == 0 ? null : ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); switch (exp.Method.Name) { case "Add": return $"(({left})::timestamp+(({args1})||' microseconds')::interval)"; case "AddDays": return $"(({left})::timestamp+(({args1})||' day')::interval)"; @@ -344,60 +348,62 @@ namespace FreeSql.PostgreSQL { if (exp.Arguments[0].Type.FullName == "System.TimeSpan" || exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault()?.FullName == "System.TimeSpan") return $"(({left})::timestamp-(({args1})||' microseconds')::interval)"; break; - case "Equals": return $"({left} = ({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp)"; - case "CompareTo": return $"extract(epoch from ({left})::timestamp-({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp)"; + case "Equals": return $"({left} = ({getExp(exp.Arguments[0])})::timestamp)"; + case "CompareTo": return $"extract(epoch from ({left})::timestamp-({getExp(exp.Arguments[0])})::timestamp)"; case "ToString": return $"to_char({left}, 'YYYY-MM-DD HH24:MI:SS.US')"; } } throw new Exception($"PostgreSQLExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "Compare": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}-({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; - case "Equals": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} = {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "FromDays": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{(long)1000000 * 60 * 60 * 24})"; - case "FromHours": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{(long)1000000 * 60 * 60})"; - case "FromMilliseconds": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*1000)"; - case "FromMinutes": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{(long)1000000 * 60})"; - case "FromSeconds": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*1000000)"; - case "FromTicks": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})/10)"; - case "Parse": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int8"; + case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})"; + case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})"; + case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)"; + case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})"; + case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)"; + case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)"; + case "Parse": return $"({getExp(exp.Arguments[0])})::int8"; case "ParseExact": case "TryParse": - case "TryParseExact": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int8"; + case "TryParseExact": return $"({getExp(exp.Arguments[0])})::int8"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var args1 = exp.Arguments.Count == 0 ? null : ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); switch (exp.Method.Name) { case "Add": return $"({left}+{args1})"; case "Subtract": return $"({left}-({args1}))"; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"({left}-({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; + case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))"; case "ToString": return $"({left})::varchar"; } } throw new Exception($"PostgreSQLExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "ToBoolean": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::varchar not in ('0','false','f','no'))"; - case "ToByte": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int2"; - case "ToChar": return $"substr(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::char, 1, 1)"; - case "ToDateTime": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::timestamp"; - case "ToDecimal": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::numeric"; - case "ToDouble": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::float8"; - case "ToInt16": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int2"; - case "ToInt32": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int4"; - case "ToInt64": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int8"; - case "ToSByte": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int2"; - case "ToSingle": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::float4"; - case "ToString": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::varchar"; - case "ToUInt16": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int2"; - case "ToUInt32": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int4"; - case "ToUInt64": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})::int8"; + case "ToBoolean": return $"(({getExp(exp.Arguments[0])})::varchar not in ('0','false','f','no'))"; + case "ToByte": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToChar": return $"substr(({getExp(exp.Arguments[0])})::char, 1, 1)"; + case "ToDateTime": return $"({getExp(exp.Arguments[0])})::timestamp"; + case "ToDecimal": return $"({getExp(exp.Arguments[0])})::numeric"; + case "ToDouble": return $"({getExp(exp.Arguments[0])})::float8"; + case "ToInt16": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToInt32": return $"({getExp(exp.Arguments[0])})::int4"; + case "ToInt64": return $"({getExp(exp.Arguments[0])})::int8"; + case "ToSByte": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToSingle": return $"({getExp(exp.Arguments[0])})::float4"; + case "ToString": return $"({getExp(exp.Arguments[0])})::varchar"; + case "ToUInt16": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToUInt32": return $"({getExp(exp.Arguments[0])})::int4"; + case "ToUInt64": return $"({getExp(exp.Arguments[0])})::int8"; } } throw new Exception($"PostgreSQLExpression 未现实函数表达式 {exp} 解析"); diff --git a/FreeSql/SqlServer/Curd/SqlServerSelect.cs b/FreeSql/SqlServer/Curd/SqlServerSelect.cs index 21582a8d..387fc2a5 100644 --- a/FreeSql/SqlServer/Curd/SqlServerSelect.cs +++ b/FreeSql/SqlServer/Curd/SqlServerSelect.cs @@ -40,6 +40,7 @@ namespace FreeSql.SqlServer.Curd { if (a < tbsfrom.Length - 1) sb.Append(", "); } foreach (var tb in tbsjoin) { + if (tb.Type == SelectTableInfoType.Parent) continue; switch (tb.Type) { case SelectTableInfoType.LeftJoin: sb.Append(" \r\nLEFT JOIN "); @@ -57,6 +58,7 @@ namespace FreeSql.SqlServer.Curd { var sbqf = new StringBuilder(); foreach (var tb in _tables) { + if (tb.Type == SelectTableInfoType.Parent) continue; if (string.IsNullOrEmpty(tb.Table.SelectFilter) == false) sbqf.Append(" AND (").Append(tb.Table.SelectFilter.Replace("a.", $"{tb.Alias}.")).Append(")"); } diff --git a/FreeSql/SqlServer/SqlServerExpression.cs b/FreeSql/SqlServer/SqlServerExpression.cs index 9f5e1e21..405c51ac 100644 --- a/FreeSql/SqlServer/SqlServerExpression.cs +++ b/FreeSql/SqlServer/SqlServerExpression.cs @@ -12,6 +12,7 @@ namespace FreeSql.SqlServer { public SqlServerExpression(CommonUtils common) : base(common) { } internal override string ExpressionLambdaToSqlOther(Expression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.NodeType) { case ExpressionType.Call: var callExp = exp as MethodCallExpression; @@ -27,12 +28,12 @@ namespace FreeSql.SqlServer { } if (objType == null) objType = callExp.Method.DeclaringType; if (objType != null) { - var left = objExp == null ? null : ExpressionLambdaToSql(objExp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = objExp == null ? null : getExp(objExp); if (objType.IsArray == true) { switch (callExp.Method.Name) { case "Contains": //判断 in - return $"({ExpressionLambdaToSql(callExp.Arguments[argIndex], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}) in {left}"; + return $"({getExp(callExp.Arguments[argIndex])}) in {left}"; } } } @@ -43,7 +44,7 @@ namespace FreeSql.SqlServer { arrSb.Append("("); for (var a = 0; a < arrExp.Expressions.Count; a++) { if (a > 0) arrSb.Append(","); - arrSb.Append(ExpressionLambdaToSql(arrExp.Expressions[a], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)); + arrSb.Append(getExp(arrExp.Expressions[a])); } return arrSb.Append(")").ToString(); } @@ -118,19 +119,20 @@ namespace FreeSql.SqlServer { } internal override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { case "IsNullOrEmpty": - var arg1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var arg1 = getExp(exp.Arguments[0]); return $"({arg1} is null or {arg1} = '')"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); switch (exp.Method.Name) { case "StartsWith": case "EndsWith": case "Contains": - var args0Value = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var args0Value = getExp(exp.Arguments[0]); if (args0Value == "NULL") return $"({left}) IS NULL"; if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(cast({args0Value} as nvarchar)+'%')")}"; if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%'+cast({args0Value} as nvarchar))")}"; @@ -139,80 +141,82 @@ namespace FreeSql.SqlServer { case "ToLower": return $"lower({left})"; case "ToUpper": return $"upper({left})"; case "Substring": - var substrArgs1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var substrArgs1 = getExp(exp.Arguments[0]); if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString(); else substrArgs1 += "+1"; if (exp.Arguments.Count == 1) return $"left({left}, {substrArgs1})"; - return $"substring({left}, {substrArgs1}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + return $"substring({left}, {substrArgs1}, {getExp(exp.Arguments[1])})"; case "IndexOf": - var indexOfFindStr = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var indexOfFindStr = getExp(exp.Arguments[0]); if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") { - var locateArgs1 = ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var locateArgs1 = getExp(exp.Arguments[1]); if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString(); else locateArgs1 += "+1"; return $"(charindex({left}, {indexOfFindStr}, {locateArgs1})-1)"; } return $"(charindex({left}, {indexOfFindStr})-1)"; case "PadLeft": - if (exp.Arguments.Count == 1) return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"lpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})"; + return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "PadRight": - if (exp.Arguments.Count == 1) return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"rpad({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})"; + return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "Trim": return $"ltrim(rtrim({left}))"; case "TrimStart": return $"ltrim({left})"; case "TrimEnd": return $"rtrim({left})"; - case "Replace": return $"replace({left}, {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"({left} - {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "CompareTo": return $"({left} - {getExp(exp.Arguments[0])})"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; } } throw new Exception($"SqlServerExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.Method.Name) { - case "Abs": return $"abs({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sign": return $"sign({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Floor": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Ceiling": return $"ceiling({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Abs": return $"abs({getExp(exp.Arguments[0])})"; + case "Sign": return $"sign({getExp(exp.Arguments[0])})"; + case "Floor": return $"floor({getExp(exp.Arguments[0])})"; + case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})"; case "Round": - if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - return $"round({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, 0)"; - case "Exp": return $"exp({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Log": return $"log({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Log10": return $"log10({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Pow": return $"power({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sqrt": return $"sqrt({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Cos": return $"cos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Sin": return $"sin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Tan": return $"tan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Acos": return $"acos({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Asin": return $"asin({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Atan": return $"atan({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Atan2": return $"atan2({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}, {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "Truncate": return $"floor({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + return $"round({getExp(exp.Arguments[0])}, 0)"; + case "Exp": return $"exp({getExp(exp.Arguments[0])})"; + case "Log": return $"log({getExp(exp.Arguments[0])})"; + case "Log10": return $"log10({getExp(exp.Arguments[0])})"; + case "Pow": return $"power({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})"; + case "Cos": return $"cos({getExp(exp.Arguments[0])})"; + case "Sin": return $"sin({getExp(exp.Arguments[0])})"; + case "Tan": return $"tan({getExp(exp.Arguments[0])})"; + case "Acos": return $"acos({getExp(exp.Arguments[0])})"; + case "Asin": return $"asin({getExp(exp.Arguments[0])})"; + case "Atan": return $"atan({getExp(exp.Arguments[0])})"; + case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Truncate": return $"floor({getExp(exp.Arguments[0])})"; } throw new Exception($"SqlServerExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "Compare": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} - ({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; - case "DaysInMonth": return $"datepart(day, dateadd(day, -1, dateadd(month, 1, cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as varchar) + '-' + cast({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as varchar) + '-1')))"; - case "Equals": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} = {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; + case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))"; + case "DaysInMonth": return $"datepart(day, dateadd(day, -1, dateadd(month, 1, cast({getExp(exp.Arguments[0])} as varchar) + '-' + cast({getExp(exp.Arguments[1])} as varchar) + '-1')))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; case "IsLeapYear": - var isLeapYearArgs1 = ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var isLeapYearArgs1 = getExp(exp.Arguments[0]); return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)"; - case "Parse": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as datetime)"; + case "Parse": return $"cast({getExp(exp.Arguments[0])} as datetime)"; case "ParseExact": case "TryParse": - case "TryParseExact": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as datetime)"; + case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as datetime)"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var args1 = exp.Arguments.Count == 0 ? null : ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); switch (exp.Method.Name) { case "Add": return $"dateadd(second, {args1}, {left})"; case "AddDays": return $"dateadd(day, {args1}, {left})"; @@ -229,60 +233,62 @@ namespace FreeSql.SqlServer { if (exp.Arguments[0].Type.FullName == "System.TimeSpan" || exp.Arguments[0].Type.GenericTypeArguments.FirstOrDefault()?.FullName == "System.TimeSpan") return $"dateadd(second, {args1}*-1, {left})"; break; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"(({left}) - ({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; + case "CompareTo": return $"(({left}) - ({getExp(exp.Arguments[0])}))"; case "ToString": return $"convert(varchar, {left}, 121)"; } } throw new Exception($"SqlServerExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "Compare": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}-({ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; - case "Equals": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} = {ExpressionLambdaToSql(exp.Arguments[1], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "FromDays": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{60 * 60 * 24})"; - case "FromHours": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*{60 * 60})"; - case "FromMilliseconds": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})/1000)"; - case "FromMinutes": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})*60)"; - case "FromSeconds": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "FromTicks": return $"(({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})/10000000)"; - case "Parse": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as bigint)"; + case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})"; + case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})"; + case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)"; + case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)"; + case "FromSeconds": return $"({getExp(exp.Arguments[0])})"; + case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)"; + case "Parse": return $"cast({getExp(exp.Arguments[0])} as bigint)"; case "ParseExact": case "TryParse": - case "TryParseExact": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as bigint)"; + case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as bigint)"; } } else { - var left = ExpressionLambdaToSql(exp.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); - var args1 = exp.Arguments.Count == 0 ? null : ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); switch (exp.Method.Name) { case "Add": return $"({left}+{args1})"; case "Subtract": return $"({left}-({args1}))"; - case "Equals": return $"({left} = {ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)})"; - case "CompareTo": return $"({left}-({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)}))"; + case "Equals": return $"({left} = {getExp(exp.Arguments[0])})"; + case "CompareTo": return $"({left}-({getExp(exp.Arguments[0])}))"; case "ToString": return $"cast({left} as varchar)"; } } throw new Exception($"SqlServerExpression 未现实函数表达式 {exp} 解析"); } internal override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, List _tables, List _selectColumnMap, Func getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName) { + Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); if (exp.Object == null) { switch (exp.Method.Name) { - case "ToBoolean": return $"({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} not in ('0','false'))"; - case "ToByte": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as tinyint)"; - case "ToChar": return $"substring(cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as nvarchar),1,1)"; - case "ToDateTime": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as datetime)"; - case "ToDecimal": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as decimal(36,18))"; - case "ToDouble": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as decimal(32,16))"; - case "ToInt16": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as smallint)"; - case "ToInt32": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as int)"; - case "ToInt64": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as bigint)"; - case "ToSByte": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as tinyint)"; - case "ToSingle": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as decimal(14,7))"; - case "ToString": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as nvarchar)"; - case "ToUInt16": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as smallint)"; - case "ToUInt32": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as int)"; - case "ToUInt64": return $"cast({ExpressionLambdaToSql(exp.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName)} as bigint)"; + case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))"; + case "ToByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)"; + case "ToChar": return $"substring(cast({getExp(exp.Arguments[0])} as nvarchar),1,1)"; + case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as datetime)"; + case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as decimal(36,18))"; + case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as decimal(32,16))"; + case "ToInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)"; + case "ToInt32": return $"cast({getExp(exp.Arguments[0])} as int)"; + case "ToInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)"; + case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as tinyint)"; + case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as decimal(14,7))"; + case "ToString": return $"cast({getExp(exp.Arguments[0])} as nvarchar)"; + case "ToUInt16": return $"cast({getExp(exp.Arguments[0])} as smallint)"; + case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as int)"; + case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as bigint)"; } } throw new Exception($"SqlServerExpression 未现实函数表达式 {exp} 解析");