/* * 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("u") .SelectSql( u => Sql.Count().As("cnt"), u => Sql.Coalesce(u.Col(x => x.Code), Sql.Val("N/A")).As("code_value")) .GroupBySql(u => Sql.Coalesce(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("u") .SelectSql(u => Sql.Case() .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("u") .Join(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() .SetSql(u => u.Code, u => Sql.Coalesce(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() .InsertSql(u => u.Code, u => Sql.Coalesce(Sql.Val("901"), Sql.Val("fallback"))) .InsertSql(u => u.First, u => Sql.Case().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() .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("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("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("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("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("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("u") .SelectSql( u => Sql.Sum(u.Col(x => x.Id)).As("sum_id"), u => Sql.Avg(u.Col(x => x.Id)).As("avg_id"), u => Sql.Min(u.Col(x => x.Id)).As("min_id"), u => Sql.Max(u.Col(x => x.Id)).As("max_id"), u => Sql.Abs(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(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("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("u") .SelectSql(u => Sql.Func("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("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("x"); var cmd = Database.FromTyped("u") .Join(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("x"); var cmd = Database.FromTyped("u") .Join(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("u") .SelectSql(u => Sql.SubQuery(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("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("u") .SelectSql(u => Sql.SubQuery( 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("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("u") .WhereSql(u => Sql.Exists( 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() .InsertSql(u => new { Code = Sql.SubQuery(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() .SetSql(u => new { Code = Sql.SubQuery(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())); } [Test] public void TestSelectSqlSupportsPositionalJoinedContexts() { var cmd = Database.FromTyped("u") .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) .SelectSql( (u, c) => u.Col(x => x.IdUser).As("user_id"), (u, c) => c.Col(x => x.Users).As("client_users")); Assert.AreEqual( "SELECT u.\"Id_User\" AS \"user_id\", c.\"Users\" AS \"client_users\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\")", NormalizeSql(cmd.CommandText())); } [Test] public void TestWhereGroupHavingAndOrderBySupportPositionalJoinedContexts() { var cmd = Database.FromTyped("u") .Join(j => j.Inner().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) .SelectSql( (u, c) => u.Col(x => x.IdUser), (u, c) => Sql.Count(c.Col(x => x.UserId)).As("cnt")) .WhereSql((u, c) => u.Col(x => x.Active).Eq(1).And(c.Col(x => x.Deleted).Eq(0))) .GroupBySql( (u, c) => u.Col(x => x.IdUser), (u, c) => c.Col(x => x.Users)) .HavingSql((u, c) => Sql.Count(c.Col(x => x.UserId)).Gt(0)) .OrderBySql( (u, c) => c.Col(x => x.Users).Asc(), (u, c) => Sql.Count(c.Col(x => x.UserId)).Desc()); Assert.AreEqual( "SELECT u.\"Id_User\", COUNT(c.\"User_Id\") AS \"cnt\" FROM \"dbo\".\"Users\" AS u INNER JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") WHERE ((u.\"Active\" = [$0]) AND (c.\"Deleted\" = [$1])) GROUP BY u.\"Id_User\", c.\"Users\" HAVING (COUNT(c.\"User_Id\") > [$2]) ORDER BY c.\"Users\" ASC, COUNT(c.\"User_Id\") DESC", NormalizeSql(cmd.CommandText())); } [Test] public void TestPositionalJoinedContextsSupportThreeJoins() { var cmd = Database.FromTyped("u") .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) .Join(j => j.Left().As("r").OnSql((u, r) => u.Col(x => x.IdUser).Eq(r.Col(x => x.UserId)))) .Join(j => j.Left().As("g").OnSql((u, g) => u.Col(x => x.IdUser).Eq(g.Col(x => x.UserId)))) .SelectSql( (u, c, r, g) => u.Col(x => x.IdUser), (u, c, r, g) => c.Col(x => x.Users).As("client_users"), (u, c, r, g) => r.Col(x => x.RoleName).As("role_name"), (u, c, r, g) => g.Col(x => x.RegionCode).As("region_code")) .WhereSql( (u, c, r, g) => c.Col(x => x.Deleted).Eq(0).And(r.Col(x => x.RoleId).Gt(0)).And(g.Col(x => x.RegionId).Gt(0))) .OrderBySql( (u, c, r, g) => r.Col(x => x.RoleName).Asc(), (u, c, r, g) => g.Col(x => x.RegionCode).Desc()); Assert.AreEqual( "SELECT u.\"Id_User\", c.\"Users\" AS \"client_users\", r.\"Role_Name\" AS \"role_name\", g.\"Region_Code\" AS \"region_code\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (u.\"Id_User\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (u.\"Id_User\" = g.\"User_Id\") WHERE (((c.\"Deleted\" = [$0]) AND (r.\"Role_Id\" > [$1])) AND (g.\"Region_Id\" > [$2])) ORDER BY r.\"Role_Name\" ASC, g.\"Region_Code\" DESC", NormalizeSql(cmd.CommandText())); } [Test] public void TestPositionalJoinedContextsSupportFourJoinedTables() { var cmd = Database.FromTyped("u") .Join(j => j.Left().As("b").OnSql((u, b) => u.Col(x => x.Id).Eq(b.Col(x => x.Id)))) .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.Id).Eq(c.Col(x => x.Id)))) .Join(j => j.Left().As("d").OnSql((u, d) => u.Col(x => x.Id).Eq(d.Col(x => x.Id)))) .Join(j => j.Left().As("e").OnSql((u, e) => u.Col(x => x.Id).Eq(e.Col(x => x.Id)))) .SelectSql( (u, b, c, d, e) => u.Col(x => x.Id).As("root_id"), (u, b, c, d, e) => b.Col(x => x.Code).As("b_code"), (u, b, c, d, e) => c.Col(x => x.Code).As("c_code"), (u, b, c, d, e) => d.Col(x => x.Code).As("d_code"), (u, b, c, d, e) => e.Col(x => x.Code).As("e_code")) .WhereSql( (u, b, c, d, e) => b.Col(x => x.Code).IsNotNull() .And(c.Col(x => x.Code).IsNotNull()) .And(d.Col(x => x.Code).IsNotNull()) .And(e.Col(x => x.Code).IsNotNull())); Assert.AreEqual( "SELECT u.\"id_user\" AS \"root_id\", b.\"user_code\" AS \"b_code\", c.\"user_code\" AS \"c_code\", d.\"user_code\" AS \"d_code\", e.\"user_code\" AS \"e_code\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS b ON (u.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (u.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (u.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (u.\"id_user\" = e.\"id_user\") WHERE ((((b.\"user_code\" IS NOT NULL) AND (c.\"user_code\" IS NOT NULL)) AND (d.\"user_code\" IS NOT NULL)) AND (e.\"user_code\" IS NOT NULL))", NormalizeSql(cmd.CommandText())); } [Test] public void TestPositionalJoinedContextsValidateRequestedType() { var builder = Database.FromTyped("u") .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))); var ex = Assert.Throws(() => builder.SelectSql((u, r) => r.Col(x => x.RoleId).As("role_id"))); Assert.AreEqual( "Typed join context at position 1 is DynamORM.Tests.Helpers.TypedJoinUserClient, not DynamORM.Tests.Helpers.TypedJoinUserRole.", ex.Message); } [Test] public void TestPositionalJoinedContextsValidateRequestedPosition() { var builder = Database.FromTyped("u") .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))); var ex = Assert.Throws(() => builder.SelectSql((u, c, r) => r.Col(x => x.RoleId).As("role_id"))); Assert.AreEqual( "Typed join context at position 2 is not available.", ex.Message); } } }