Skip to content

Support criteria aliases in SqlProjection and SQLCriterion #2358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 92 additions & 5 deletions src/NHibernate.Test/Async/Criteria/CriteriaQueryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.Collections.Generic;
using NHibernate.Dialect;
using NHibernate.Criterion;
using NHibernate.Linq;
using NHibernate.SqlCommand;
using NHibernate.Transform;
using NHibernate.Type;
Expand Down Expand Up @@ -873,7 +874,7 @@ public async Task ProjectionsTestAsync()
ProjectionList p2 = Projections.ProjectionList()
.Add(Projections.Min("StudentNumber"))
.Add(Projections.Avg("StudentNumber"))
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -922,6 +923,92 @@ public async Task ProjectionsTestAsync()
s.Close();
}

[Test]
public async Task TestSQLProjectionWithAliasesAsync()
{
using(ISession s = OpenSession())
using(ITransaction t = s.BeginTransaction())
{
Course course = new Course();
course.CourseCode = "HIB";
course.Description = "Hibernate Training";
await (s.SaveAsync(course));

Student gavin = new Student();
gavin.Name = "Gavin King";
gavin.StudentNumber = 667;
await (s.SaveAsync(gavin));

Student xam = new Student();
xam.Name = "Max Rydahl Andersen";
xam.StudentNumber = 101;
await (s.SaveAsync(xam));

Enrolment enrolment = new Enrolment();
enrolment.Course = course;
enrolment.CourseCode = course.CourseCode;
enrolment.Semester = 1;
enrolment.Year = 1999;
enrolment.Student = xam;
enrolment.StudentNumber = xam.StudentNumber;
xam.Enrolments.Add(enrolment);
await (s.SaveAsync(enrolment));

enrolment = new Enrolment();
enrolment.Course = course;
enrolment.CourseCode = course.CourseCode;
enrolment.Semester = 3;
enrolment.Year = 1998;
enrolment.Student = gavin;
enrolment.StudentNumber = gavin.StudentNumber;
gavin.Enrolments.Add(enrolment);
await (s.SaveAsync(enrolment));
await (t.CommitAsync());
}

using (var s = OpenSession())
{
Student studentSubquery = null;
var subquery = QueryOver.Of(() => studentSubquery)
.And(
Restrictions.Sql("{e}.studentId = 667 and {studentSubquery}.studentId = 667")
.AddAliases("e", "studentSubquery")).Select(Projections.Id());

var uniqueResult = await (s.CreateCriteria(typeof(Student))
.Add(Subqueries.Exists(subquery.DetachedCriteria))
.AddOrder(Order.Asc("Name"))
.CreateCriteria("Enrolments", "e")
.AddOrder(Order.Desc("Year"))
.AddOrder(Order.Desc("Semester"))
.CreateCriteria("Course", "c")
.AddOrder(Order.Asc("Description"))
.SetProjection(
Projections.Sql(
"{alias}.studentId as studentNumber, {e}.Semester as semester,"
+ " {c}.CourseCode as courseCode, {c}.Description as descr",
new string[] {"studentNumber", "semester", "courseCode", "descr"},
new[]
{
TypeFactory.HeuristicType(typeof(long)),
TypeFactory.HeuristicType(typeof(short)),
TypeFactory.HeuristicType(typeof(string)),
TypeFactory.HeuristicType(typeof(string)),
}).AddAliases("e", "c"))
.UniqueResultAsync());

Assert.That(uniqueResult, Is.Not.Null);
}

using (var s = OpenSession())
using (s.BeginTransaction())
{
await (s.Query<Enrolment>().DeleteAsync());
await (s.Query<Student>().DeleteAsync());
await (s.Query<Course>().DeleteAsync());
await (s.GetCurrentTransaction().CommitAsync());
}
}

[Test]
public async Task CloningProjectionsTestAsync()
{
Expand Down Expand Up @@ -1066,7 +1153,7 @@ public async Task CloningProjectionsTestAsync()
ProjectionList p2 = Projections.ProjectionList()
.Add(Projections.Min("StudentNumber"))
.Add(Projections.Avg("StudentNumber"))
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -1366,7 +1453,7 @@ public async Task ProjectionsUsingPropertyAsync()
ProjectionList p2 = Projections.ProjectionList()
.Add(Property.ForName("StudentNumber").Min())
.Add(Property.ForName("StudentNumber").Avg())
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -1859,7 +1946,7 @@ public async Task CloningProjectionsUsingPropertyAsync()
ProjectionList p2 = Projections.ProjectionList()
.Add(Property.ForName("StudentNumber").Min())
.Add(Property.ForName("StudentNumber").Avg())
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -2452,7 +2539,7 @@ public async Task SqlExpressionWithParametersAsync()
{
ICriteria c = session.CreateCriteria(typeof(Student));
c.Add(Expression.Eq("StudentNumber", (long)232));
c.Add(Expression.Sql("2 = ?", 1, NHibernateUtil.Int32));
c.Add(Restrictions.Sql("2 = ?", 1, NHibernateUtil.Int32));

Student gavin = new Student();
gavin.Name = "Gavin King";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,19 @@ public async Task ExecutableCriteriaAsync()

// SQLCriterion
dc = DetachedCriteria.For(typeof(Student))
.Add(Expression.Sql("{alias}.Name = 'Gavin'"));
.Add(Restrictions.Sql("{alias}.Name = 'Gavin'"));
await (SerializeAndListAsync(dc));

// SQLProjection
dc = DetachedCriteria.For(typeof(Enrolment))
.SetProjection(Projections.SqlProjection("1 as constOne, count(*) as countStar",
.SetProjection(Projections.Sql("1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }));
await (SerializeAndListAsync(dc));

dc = DetachedCriteria.For(typeof(Student))
.SetProjection(
Projections.SqlGroupProjection("COUNT({alias}.studentId), {alias}.preferredCourseCode",
Projections.Sql("COUNT({alias}.studentId), {alias}.preferredCourseCode",
"{alias}.preferredCourseCode",
new string[] { "studentsOfCourse", "CourseCode" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public async Task SelectSqlProjectionTestAsync()
ICriteria c = session.CreateCriteria(typeof(ProjectionTestClass));

c.SetProjection(Projections.ProjectionList()
.Add(Projections.SqlProjection("Avg({alias}.Pay) as MyPay",
.Add(Projections.Sql("Avg({alias}.Pay) as MyPay",
new string[] { "MyPay" },
new IType[] { NHibernateUtil.Double })));

Expand Down
4 changes: 2 additions & 2 deletions src/NHibernate.Test/Async/NHSpecificTest/NH1792/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@ public async Task PageWithRawSqlSubqueryWithOrderByAsync()

IList<Product> results =
await (session.CreateCriteria<Product>().Add(
Expression.Sql("{alias}.Id in (Select " + top + " p.Id from Product p order by Name)")).Add(Restrictions.Gt("Id", 0)).
Restrictions.Sql("{alias}.Id in (Select " + top + " p.Id from Product p order by Name)")).Add(Restrictions.Gt("Id", 0)).
SetMaxResults(3).ListAsync<Product>());

Assert.AreEqual(3, results.Count);
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/NHibernate.Test/Async/NHSpecificTest/NH2318/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public async Task CriteriaTrimFunctionsWithParametersAsync()
"trim",
NHibernateUtil.String,
Projections.Constant("f"),
Projections.SqlProjection("from", null, null), // Silly hack to get "from" as a second argument.
Projections.Sql("from", null, null), // Silly hack to get "from" as a second argument.
Projections.Property("Name")),
"irst"));
IList<A> items = await (criteria.ListAsync<A>());
Expand Down
97 changes: 92 additions & 5 deletions src/NHibernate.Test/Criteria/CriteriaQueryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using NHibernate.Dialect;
using NHibernate.Criterion;
using NHibernate.Linq;
using NHibernate.SqlCommand;
using NHibernate.Transform;
using NHibernate.Type;
Expand Down Expand Up @@ -970,7 +971,7 @@ public void ProjectionsTest()
ProjectionList p2 = Projections.ProjectionList()
.Add(Projections.Min("StudentNumber"))
.Add(Projections.Avg("StudentNumber"))
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -1019,6 +1020,92 @@ public void ProjectionsTest()
s.Close();
}

[Test]
public void TestSQLProjectionWithAliases()
{
using(ISession s = OpenSession())
using(ITransaction t = s.BeginTransaction())
{
Course course = new Course();
course.CourseCode = "HIB";
course.Description = "Hibernate Training";
s.Save(course);

Student gavin = new Student();
gavin.Name = "Gavin King";
gavin.StudentNumber = 667;
s.Save(gavin);

Student xam = new Student();
xam.Name = "Max Rydahl Andersen";
xam.StudentNumber = 101;
s.Save(xam);

Enrolment enrolment = new Enrolment();
enrolment.Course = course;
enrolment.CourseCode = course.CourseCode;
enrolment.Semester = 1;
enrolment.Year = 1999;
enrolment.Student = xam;
enrolment.StudentNumber = xam.StudentNumber;
xam.Enrolments.Add(enrolment);
s.Save(enrolment);

enrolment = new Enrolment();
enrolment.Course = course;
enrolment.CourseCode = course.CourseCode;
enrolment.Semester = 3;
enrolment.Year = 1998;
enrolment.Student = gavin;
enrolment.StudentNumber = gavin.StudentNumber;
gavin.Enrolments.Add(enrolment);
s.Save(enrolment);
t.Commit();
}

using (var s = OpenSession())
{
Student studentSubquery = null;
var subquery = QueryOver.Of(() => studentSubquery)
.And(
Restrictions.Sql("{e}.studentId = 667 and {studentSubquery}.studentId = 667")
.AddAliases("e", "studentSubquery")).Select(Projections.Id());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be cases when user will want to .AddAliases(...) later? If not I think it is better to add params[] aliases parameter to the Restrictions.Sql method instead of introducing a new method.


var uniqueResult = s.CreateCriteria(typeof(Student))
.Add(Subqueries.Exists(subquery.DetachedCriteria))
.AddOrder(Order.Asc("Name"))
.CreateCriteria("Enrolments", "e")
.AddOrder(Order.Desc("Year"))
.AddOrder(Order.Desc("Semester"))
.CreateCriteria("Course", "c")
.AddOrder(Order.Asc("Description"))
.SetProjection(
Projections.Sql(
"{alias}.studentId as studentNumber, {e}.Semester as semester,"
+ " {c}.CourseCode as courseCode, {c}.Description as descr",
new string[] {"studentNumber", "semester", "courseCode", "descr"},
new[]
{
TypeFactory.HeuristicType(typeof(long)),
TypeFactory.HeuristicType(typeof(short)),
TypeFactory.HeuristicType(typeof(string)),
TypeFactory.HeuristicType(typeof(string)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little side-track rant: I know, it is a copy-paste from other test, but columns and types have Connascence of Position and CoV which is a sign of a bad design. (NHibernate has this across all code-base, but I think we should start progressing to remove that.

Copy link
Member Author

@bahusoid bahusoid May 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree we should fight it. Though not sure about this place.. This is dictated by method signature. No? To avoid it we need to provide method where all related data is supplied in single parameter. Such wrapping in tests seems excessive...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is not only in tests. The test reflect real-like use cases.

}).AddAliases("e", "c"))
.UniqueResult();

Assert.That(uniqueResult, Is.Not.Null);
}

using (var s = OpenSession())
using (s.BeginTransaction())
{
s.Query<Enrolment>().Delete();
s.Query<Student>().Delete();
s.Query<Course>().Delete();
s.GetCurrentTransaction().Commit();
}
}

[Test]
public void CloningProjectionsTest()
{
Expand Down Expand Up @@ -1163,7 +1250,7 @@ public void CloningProjectionsTest()
ProjectionList p2 = Projections.ProjectionList()
.Add(Projections.Min("StudentNumber"))
.Add(Projections.Avg("StudentNumber"))
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -1463,7 +1550,7 @@ public void ProjectionsUsingProperty()
ProjectionList p2 = Projections.ProjectionList()
.Add(Property.ForName("StudentNumber").Min())
.Add(Property.ForName("StudentNumber").Avg())
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -1956,7 +2043,7 @@ public void CloningProjectionsUsingProperty()
ProjectionList p2 = Projections.ProjectionList()
.Add(Property.ForName("StudentNumber").Min())
.Add(Property.ForName("StudentNumber").Avg())
.Add(Projections.SqlProjection(
.Add(Projections.Sql(
"1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }
Expand Down Expand Up @@ -2575,7 +2662,7 @@ public void SqlExpressionWithParameters()
{
ICriteria c = session.CreateCriteria(typeof(Student));
c.Add(Expression.Eq("StudentNumber", (long)232));
c.Add(Expression.Sql("2 = ?", 1, NHibernateUtil.Int32));
c.Add(Restrictions.Sql("2 = ?", 1, NHibernateUtil.Int32));

Student gavin = new Student();
gavin.Name = "Gavin King";
Expand Down
12 changes: 6 additions & 6 deletions src/NHibernate.Test/Criteria/DetachedCriteriaSerializable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,18 +262,18 @@ public void SubqueriesExpressions()
[Test]
public void SQLCriterion()
{
ICriterion c = Expression.Sql("SELECT Name FROM Student");
ICriterion c = Restrictions.Sql("SELECT Name FROM Student");
NHAssert.IsSerializable(c);
}

[Test]
public void SQLProjection()
{
IProjection p = Projections.SqlProjection("COUNT(*)",
IProjection p = Projections.Sql("COUNT(*)",
new string[] { "tStudent" }, new IType[] { NHibernateUtil.Int32 });
NHAssert.IsSerializable(p);
p =
Projections.SqlGroupProjection("COUNT({alias}.studentId), {alias}.preferredCourseCode",
Projections.Sql("COUNT({alias}.studentId), {alias}.preferredCourseCode",
"{alias}.preferredCourseCode",
new string[] { "studentsOfCourse", "CourseCode" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 });
Expand Down Expand Up @@ -404,19 +404,19 @@ public void ExecutableCriteria()

// SQLCriterion
dc = DetachedCriteria.For(typeof(Student))
.Add(Expression.Sql("{alias}.Name = 'Gavin'"));
.Add(Restrictions.Sql("{alias}.Name = 'Gavin'"));
SerializeAndList(dc);

// SQLProjection
dc = DetachedCriteria.For(typeof(Enrolment))
.SetProjection(Projections.SqlProjection("1 as constOne, count(*) as countStar",
.SetProjection(Projections.Sql("1 as constOne, count(*) as countStar",
new String[] { "constOne", "countStar" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }));
SerializeAndList(dc);

dc = DetachedCriteria.For(typeof(Student))
.SetProjection(
Projections.SqlGroupProjection("COUNT({alias}.studentId), {alias}.preferredCourseCode",
Projections.Sql("COUNT({alias}.studentId), {alias}.preferredCourseCode",
"{alias}.preferredCourseCode",
new string[] { "studentsOfCourse", "CourseCode" },
new IType[] { NHibernateUtil.Int32, NHibernateUtil.Int32 }));
Expand Down
Loading