391 lines
16 KiB
C#
391 lines
16 KiB
C#
/*
|
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
|
* All rights reserved.
|
|
*/
|
|
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using DynamORM.Tests.Helpers;
|
|
using DynamORM.TypedSql;
|
|
using NUnit.Framework;
|
|
|
|
namespace DynamORM.Tests.TypedSql
|
|
{
|
|
[TestFixture]
|
|
public class TypedSqlDslTests : TestsBase
|
|
{
|
|
private static string NormalizeSql(string sql)
|
|
{
|
|
int index = 0;
|
|
return Regex.Replace(sql, @"\[\$[^\]]+\]", m => string.Format("[${0}]", index++));
|
|
}
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
CreateTestDatabase();
|
|
CreateDynamicDatabase(
|
|
DynamicDatabaseOptions.SingleConnection |
|
|
DynamicDatabaseOptions.SingleTransaction |
|
|
DynamicDatabaseOptions.SupportLimitOffset |
|
|
DynamicDatabaseOptions.SupportNoLock);
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
DestroyDynamicDatabase();
|
|
DestroyTestDatabase();
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlWithFunctionsAndOrdering()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(
|
|
u => Sql.Count().As("cnt"),
|
|
u => Sql.Coalesce<string>(u.Col(x => x.Code), Sql.Val("N/A")).As("code_value"))
|
|
.GroupBySql(u => Sql.Coalesce<string>(u.Col(x => x.Code), Sql.Val("N/A")))
|
|
.HavingSql(u => Sql.Count().Gt(1))
|
|
.OrderBySql(u => Sql.Count().Desc());
|
|
|
|
Assert.AreEqual(
|
|
"SELECT COUNT(*) AS \"cnt\", COALESCE(u.\"user_code\", [$0]) AS \"code_value\" FROM \"sample_users\" AS u GROUP BY COALESCE(u.\"user_code\", [$1]) HAVING (COUNT(*) > [$2]) ORDER BY COUNT(*) DESC",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlWithCase()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(u => Sql.Case<string>()
|
|
.When(u.Col(x => x.Code).Eq("A"), "Alpha")
|
|
.When(u.Col(x => x.Code).Eq("B"), "Beta")
|
|
.Else("Other")
|
|
.As("category"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT CASE WHEN (u.\"user_code\" = [$0]) THEN [$1] WHEN (u.\"user_code\" = [$2]) THEN [$3] ELSE [$4] END AS \"category\" FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestJoinOnSql()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.Join<TypedFluentUser>(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id)).And(r.Col(a => a.Code).IsNotNull())))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON ((u.\"id_user\" = x.\"id_user\") AND (x.\"user_code\" IS NOT NULL))",
|
|
cmd.CommandText());
|
|
}
|
|
|
|
[Test]
|
|
public void TestUpdateSetSqlAndWhereSql()
|
|
{
|
|
var cmd = Database.UpdateTyped<Users>()
|
|
.SetSql(u => u.Code, u => Sql.Coalesce<string>(Sql.Val("900"), u.Col(x => x.Code)))
|
|
.WhereSql(u => u.Col(x => x.Id).Eq(1).And(u.Col(x => x.Code).IsNotNull()));
|
|
|
|
Assert.AreEqual(
|
|
"UPDATE \"sample_users\" SET \"code\" = COALESCE([$0], \"code\") WHERE ((\"id\" = [$1]) AND (\"code\" IS NOT NULL))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestInsertSqlSupportsCase()
|
|
{
|
|
var cmd = Database.InsertTyped<Users>()
|
|
.InsertSql(u => u.Code, u => Sql.Coalesce<string>(Sql.Val("901"), Sql.Val("fallback")))
|
|
.InsertSql(u => u.First, u => Sql.Case<string>().When(Sql.Val(1).Eq(1), "Typed").Else("Other"));
|
|
|
|
Assert.AreEqual(
|
|
"INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES (COALESCE([$0], [$1]), CASE WHEN ([$2] = [$3]) THEN [$4] ELSE [$5] END)",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestDeleteWhereSql()
|
|
{
|
|
var cmd = Database.DeleteTyped<Users>()
|
|
.WhereSql(u => u.Col(x => x.Id).Eq(2).And(u.Col(x => x.Code).NotEq("X")));
|
|
|
|
Assert.AreEqual(
|
|
"DELETE FROM \"sample_users\" WHERE ((\"id\" = [$0]) AND (\"code\" <> [$1]))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsLikeInAndBetween()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => u.Col(x => x.Code).Like("A%")
|
|
.And(u.Col(x => x.Id).In(1, 2, 3))
|
|
.And(u.Col(x => x.Id).Between(1, 10)))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (((u.\"user_code\" LIKE [$0]) AND (u.\"id_user\" IN([$1], [$2], [$3]))) AND (u.\"id_user\" BETWEEN [$4] AND [$5]))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsNotIn()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => u.Col(x => x.Id).NotIn(1, 2))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" NOT IN([$0], [$1]))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsEmptyNotIn()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => u.Col(x => x.Id).NotIn(new int[0]))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (1 = 1)",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsPatternHelpers()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => u.Col(x => x.Code).StartsWith("AB")
|
|
.And(u.Col(x => x.Code).EndsWith("YZ"))
|
|
.And(u.Col(x => x.Code).Contains("MID")))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (((u.\"user_code\" LIKE [$0]) AND (u.\"user_code\" LIKE [$1])) AND (u.\"user_code\" LIKE [$2]))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsEmptyIn()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => u.Col(x => x.Id).In(new int[0]))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (1 = 0)",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsStandardFunctions()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(
|
|
u => Sql.Sum<int>(u.Col(x => x.Id)).As("sum_id"),
|
|
u => Sql.Avg<int>(u.Col(x => x.Id)).As("avg_id"),
|
|
u => Sql.Min<int>(u.Col(x => x.Id)).As("min_id"),
|
|
u => Sql.Max<int>(u.Col(x => x.Id)).As("max_id"),
|
|
u => Sql.Abs<int>(Sql.Val(-5)).As("abs_value"),
|
|
u => Sql.Upper(u.Col(x => x.Code)).As("upper_code"),
|
|
u => Sql.Lower(u.Col(x => x.Code)).As("lower_code"),
|
|
u => Sql.Trim(u.Col(x => x.Code)).As("trim_code"),
|
|
u => Sql.Length(u.Col(x => x.Code)).As("len_code"),
|
|
u => Sql.NullIf<string>(u.Col(x => x.Code), Sql.Val("X")).As("nullif_code"),
|
|
u => Sql.CurrentTimestamp().As("ts"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT SUM(u.\"id_user\") AS \"sum_id\", AVG(u.\"id_user\") AS \"avg_id\", MIN(u.\"id_user\") AS \"min_id\", MAX(u.\"id_user\") AS \"max_id\", ABS([$0]) AS \"abs_value\", UPPER(u.\"user_code\") AS \"upper_code\", LOWER(u.\"user_code\") AS \"lower_code\", TRIM(u.\"user_code\") AS \"trim_code\", LENGTH(u.\"user_code\") AS \"len_code\", NULLIF(u.\"user_code\", [$1]) AS \"nullif_code\", CURRENT_TIMESTAMP AS \"ts\" FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsArithmeticExpressions()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(
|
|
u => (u.Col(x => x.Id) + Sql.Val(1)).As("plus_one"),
|
|
u => u.Col(x => x.Id).Sub(2).As("minus_two"),
|
|
u => u.Col(x => x.Id).Mul(3).As("times_three"),
|
|
u => u.Col(x => x.Id).Div(4).As("div_four"),
|
|
u => u.Col(x => x.Id).Mod(5).As("mod_five"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT (u.\"id_user\" + [$0]) AS \"plus_one\", (u.\"id_user\" - [$1]) AS \"minus_two\", (u.\"id_user\" * [$2]) AS \"times_three\", (u.\"id_user\" / [$3]) AS \"div_four\", (u.\"id_user\" % [$4]) AS \"mod_five\" FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsCustomFunction()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(u => Sql.Func<string>("CUSTOM_FUNC", u.Col(x => x.Code), Sql.Val(5)).As("custom_value"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT CUSTOM_FUNC(u.\"user_code\", [$0]) AS \"custom_value\" FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsWildcardAll()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(u => u.All());
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.* FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsJoinedAliasHelpers()
|
|
{
|
|
var other = Sql.Table<TypedFluentUser>("x");
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.Join<TypedFluentUser>(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id))))
|
|
.SelectSql(
|
|
u => u.All(),
|
|
u => other.All(),
|
|
u => other.Col(x => x.Code).As("joined_code"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.*, x.*, x.\"user_code\" AS \"joined_code\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsJoinedAliasHelpers()
|
|
{
|
|
var other = Sql.Table<TypedFluentUser>("x");
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.Join<TypedFluentUser>(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id))))
|
|
.WhereSql(u => other.Col(x => x.Code).IsNotNull())
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\") WHERE (x.\"user_code\" IS NOT NULL)",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsScalarSubQuery()
|
|
{
|
|
var sq = Database.From(x => x.sample_users.As("x"))
|
|
.Select(x => x.x.id_user)
|
|
.Where(x => x.x.user_code == "A");
|
|
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(u => Sql.SubQuery<long>(sq).As("sub_id"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])) AS \"sub_id\" FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsExists()
|
|
{
|
|
var sq = Database.From(x => x.sample_users.As("x"))
|
|
.Select(x => x.x.id_user)
|
|
.Where(x => x.x.user_code == "A");
|
|
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => Sql.Exists(sq))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestSelectSqlSupportsTypedSubQueryHelper()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(u => Sql.SubQuery<TypedFluentUser, long>(
|
|
Database,
|
|
sq => sq
|
|
.SelectSql(x => x.Col(a => a.Id))
|
|
.WhereSql(x => x.Col(a => a.Code).Eq("A")),
|
|
"x").As("sub_id"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])) AS \"sub_id\" FROM \"sample_users\" AS u",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestOrderBySqlSupportsNullOrderingAndRawFragments()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.SelectSql(u => u.Col(x => x.Id))
|
|
.OrderBySql(
|
|
u => u.Col(x => x.Code).Asc().NullsLast(),
|
|
u => Sql.RawOrder("2 DESC"));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u ORDER BY u.\"user_code\" ASC NULLS LAST, 2 DESC",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestWhereSqlSupportsTypedExistsHelper()
|
|
{
|
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
|
.WhereSql(u => Sql.Exists<TypedFluentUser>(
|
|
Database,
|
|
sq => sq
|
|
.SelectSql(x => x.Col(a => a.Id))
|
|
.WhereSql(x => x.Col(a => a.Code).Eq("A")),
|
|
"x"))
|
|
.SelectSql(u => u.Col(x => x.Id));
|
|
|
|
Assert.AreEqual(
|
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestInsertSqlObjectProjection()
|
|
{
|
|
var sq = Database.From(x => x.sample_users.As("x"))
|
|
.Select(x => x.x.user_code)
|
|
.Where(x => x.x.id_user == 1);
|
|
|
|
var cmd = Database.InsertTyped<Users>()
|
|
.InsertSql(u => new
|
|
{
|
|
Code = Sql.SubQuery<string>(sq),
|
|
First = Sql.Upper(Sql.Val("typed"))
|
|
});
|
|
|
|
Assert.AreEqual(
|
|
"INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ((SELECT x.\"user_code\" FROM \"sample_users\" AS x WHERE (x.\"id_user\" = [$0])), UPPER([$1]))",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
|
|
[Test]
|
|
public void TestUpdateSqlObjectProjection()
|
|
{
|
|
var sq = Database.From(x => x.sample_users.As("x"))
|
|
.Select(x => x.x.user_code)
|
|
.Where(x => x.x.id_user == 1);
|
|
|
|
var cmd = Database.UpdateTyped<Users>()
|
|
.SetSql(u => new
|
|
{
|
|
Code = Sql.SubQuery<string>(sq),
|
|
First = Sql.Lower(Sql.Val("TYPED"))
|
|
})
|
|
.WhereSql(u => u.Col(x => x.Id).Eq(1));
|
|
|
|
Assert.AreEqual(
|
|
"UPDATE \"sample_users\" SET \"code\" = (SELECT x.\"user_code\" FROM \"sample_users\" AS x WHERE (x.\"id_user\" = [$0])), \"first\" = LOWER([$1]) WHERE (\"id\" = [$2])",
|
|
NormalizeSql(cmd.CommandText()));
|
|
}
|
|
}
|
|
}
|