Skip to content

Commit 5ca5ffe

Browse files
committed
Merge branch NH-3412, containing backported fixes for paging on SQL Server (NH-2977 plus some followup changes).
2 parents 01a8d31 + 15e4e4a commit 5ca5ffe

20 files changed

+1607
-257
lines changed

src/NHibernate.Test/DialectTest/MsSql2005DialectFixture.cs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void OnlyOffsetLimit()
7575
var d = new MsSql2005Dialect();
7676

7777
SqlString str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), null, new SqlString("10"));
78-
Assert.That(str.ToString(), Is.EqualTo("select distinct TOP (10) c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"));
78+
Assert.That(str.ToString(), Is.EqualTo("select distinct TOP (10) c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"));
7979
}
8080

8181
[Test]
@@ -222,7 +222,85 @@ public void GetIfExistsDropConstraintTest_For_Schema_other_than_dbo()
222222
public void GetLimitStringWithSqlComments()
223223
{
224224
var d = new MsSql2005Dialect();
225-
Assert.Throws<NotSupportedException>(() => d.GetLimitString(new SqlString(" /* criteria query */ SELECT p from lcdtm"), null, new SqlString("2")));
225+
var limitSqlQuery = d.GetLimitString(new SqlString(" /* criteria query */ SELECT p from lcdtm"), null, new SqlString("2"));
226+
Assert.That(limitSqlQuery, Is.Not.Null);
227+
Assert.That(limitSqlQuery.ToString(), Is.EqualTo(" /* criteria query */ SELECT TOP (2) p from lcdtm"));
228+
}
229+
230+
[Test]
231+
public void GetLimitStringWithSqlCommonTableExpression()
232+
{
233+
const string SQL = @"
234+
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
235+
( -- Anchor member definition
236+
SELECT ManagerID, EmployeeID, Title, Deptid, 0 AS Level
237+
FROM MyEmployees
238+
WHERE ManagerID IS NULL
239+
240+
UNION ALL
241+
242+
-- Recursive member definition
243+
SELECT e.ManagerID, e.EmployeeID, e.Title, e.Deptid, Level + 1
244+
FROM MyEmployees AS e
245+
INNER JOIN DirectReports AS ON e.ManagerID = d.EmployeeID
246+
)
247+
-- Statement that executes the CTE
248+
SELECT ManagerID, EmployeeID, Title, Level
249+
FROM DirectReports";
250+
251+
const string EXPECTED_SQL = @"
252+
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
253+
( -- Anchor member definition
254+
SELECT ManagerID, EmployeeID, Title, Deptid, 0 AS Level
255+
FROM MyEmployees
256+
WHERE ManagerID IS NULL
257+
258+
UNION ALL
259+
260+
-- Recursive member definition
261+
SELECT e.ManagerID, e.EmployeeID, e.Title, e.Deptid, Level + 1
262+
FROM MyEmployees AS e
263+
INNER JOIN DirectReports AS ON e.ManagerID = d.EmployeeID
264+
)
265+
-- Statement that executes the CTE
266+
SELECT TOP (2) ManagerID, EmployeeID, Title, Level
267+
FROM DirectReports";
268+
269+
var d = new MsSql2005Dialect();
270+
var limitSqlQuery = d.GetLimitString(new SqlString(SQL), null, new SqlString("2"));
271+
Assert.That(limitSqlQuery, Is.Not.Null);
272+
Assert.That(limitSqlQuery.ToString(), Is.EqualTo(EXPECTED_SQL));
273+
}
274+
275+
[Test]
276+
public void DontReturnLimitStringForStoredProcedureCall()
277+
{
278+
VerifyLimitStringForStoredProcedureCalls("EXEC sp_stored_procedures");
279+
VerifyLimitStringForStoredProcedureCalls(@"
280+
DECLARE @id int
281+
SELECT @id = id FROM persons WHERE name LIKE ?
282+
EXEC get_person_summary @id");
283+
VerifyLimitStringForStoredProcedureCalls(@"
284+
DECLARE @id int
285+
SELECT DISTINCT TOP 1 @id = id FROM persons WHERE name LIKE ?
286+
EXEC get_person_summary @id");
287+
VerifyLimitStringForStoredProcedureCalls(@"
288+
DECLARE @id int
289+
SELECT DISTINCT TOP (?) PERCENT WITH TIES @id = id FROM persons WHERE name LIKE ?
290+
EXEC get_person_summary @id");
291+
}
292+
293+
private static void VerifyLimitStringForStoredProcedureCalls(string sql)
294+
{
295+
var d = new MsSql2005Dialect();
296+
var limitSql = d.GetLimitString(new SqlString(sql), null, new SqlString("2"));
297+
Assert.That(limitSql, Is.Null, "Limit only: {0}", sql);
298+
299+
limitSql = d.GetLimitString(new SqlString(sql), new SqlString("10"), null);
300+
Assert.That(limitSql, Is.Null, "Offset only: {0}", sql);
301+
302+
limitSql = d.GetLimitString(new SqlString(sql), new SqlString("10"), new SqlString("2"));
303+
Assert.That(limitSql, Is.Null, "Limit and Offset: {0}", sql);
226304
}
227305
}
228306
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
using NHibernate.Dialect;
2+
using NHibernate.SqlCommand;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.DialectTest
6+
{
7+
[TestFixture]
8+
public class MsSql2012DialectFixture
9+
{
10+
[Test]
11+
public void GetLimitString()
12+
{
13+
var d = new MsSql2012Dialect();
14+
15+
SqlString str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c.Rating as Rating2_19_0_, c.Last_Name as Last_Name3_19_0, c.First_Name as First_Name4_19_0 from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), new SqlString("111"), new SqlString("222"));
16+
Assert.AreEqual(
17+
"select distinct c.Contact_Id as Contact1_19_0_, c.Rating as Rating2_19_0_, c.Last_Name as Last_Name3_19_0, c.First_Name as First_Name4_19_0 from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
18+
str.ToString());
19+
20+
str = d.GetLimitString(new SqlString("SELECT fish.id FROM fish"), new SqlString("111"), new SqlString("222"));
21+
Assert.AreEqual(
22+
"SELECT fish.id FROM fish ORDER BY CURRENT_TIMESTAMP OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
23+
str.ToString());
24+
25+
str = d.GetLimitString(new SqlString("SELECT DISTINCT fish_.id FROM fish fish_"), new SqlString("111"), new SqlString("222"));
26+
Assert.AreEqual(
27+
"SELECT DISTINCT fish_.id FROM fish fish_ ORDER BY CURRENT_TIMESTAMP OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
28+
str.ToString());
29+
30+
str = d.GetLimitString(new SqlString("SELECT DISTINCT fish_.id as ixx9_ FROM fish fish_"), new SqlString("111"), new SqlString("222"));
31+
Assert.AreEqual(
32+
"SELECT DISTINCT fish_.id as ixx9_ FROM fish fish_ ORDER BY CURRENT_TIMESTAMP OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
33+
str.ToString());
34+
35+
str = d.GetLimitString(new SqlString("SELECT * FROM fish ORDER BY name"), new SqlString("111"), new SqlString("222"));
36+
Assert.AreEqual(
37+
"SELECT * FROM fish ORDER BY name OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
38+
str.ToString());
39+
40+
str = d.GetLimitString(new SqlString("SELECT fish.id, fish.name FROM fish ORDER BY name DESC"), new SqlString("111"), new SqlString("222"));
41+
Assert.AreEqual(
42+
"SELECT fish.id, fish.name FROM fish ORDER BY name DESC OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
43+
str.ToString());
44+
45+
str = d.GetLimitString(new SqlString("SELECT * FROM fish LEFT JOIN (SELECT * FROM meat ORDER BY weight) AS t ORDER BY name DESC"), new SqlString("111"), new SqlString("222"));
46+
Assert.AreEqual(
47+
"SELECT * FROM fish LEFT JOIN (SELECT * FROM meat ORDER BY weight) AS t ORDER BY name DESC OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
48+
str.ToString());
49+
50+
str = d.GetLimitString(new SqlString("SELECT *, (SELECT COUNT(1) FROM fowl WHERE fish_id = fish.id) AS some_count FROM fish"), new SqlString("111"), new SqlString("222"));
51+
Assert.AreEqual(
52+
"SELECT *, (SELECT COUNT(1) FROM fowl WHERE fish_id = fish.id) AS some_count FROM fish ORDER BY CURRENT_TIMESTAMP OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
53+
str.ToString());
54+
55+
str = d.GetLimitString(new SqlString("SELECT * FROM fish WHERE scales = ", Parameter.Placeholder), new SqlString("111"), new SqlString("222"));
56+
Assert.AreEqual(
57+
"SELECT * FROM fish WHERE scales = ? ORDER BY CURRENT_TIMESTAMP OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
58+
str.ToString());
59+
60+
str = d.GetLimitString(new SqlString("SELECT f.Type, COUNT(DISTINCT f.Name) AS Name FROM Fish f GROUP BY f.Type ORDER BY COUNT(DISTINCT f.Name)"), new SqlString("111"), new SqlString("222"));
61+
Assert.AreEqual(
62+
"SELECT f.Type, COUNT(DISTINCT f.Name) AS Name FROM Fish f GROUP BY f.Type ORDER BY COUNT(DISTINCT f.Name) OFFSET 111 ROWS FETCH FIRST 222 ROWS ONLY",
63+
str.ToString());
64+
}
65+
66+
[Test]
67+
public void OnlyOffsetLimit()
68+
{
69+
var d = new MsSql2012Dialect();
70+
71+
SqlString str = d.GetLimitString(new SqlString("select distinct c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name"), null, new SqlString("10"));
72+
Assert.That(str.ToString(), Is.EqualTo("select distinct c.Contact_Id as Contact1_19_0_, c._Rating as Rating2_19_0_ from dbo.Contact c where COALESCE(c.Rating, 0) > 0 order by c.Rating desc , c.Last_Name , c.First_Name OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY"));
73+
}
74+
75+
[Test]
76+
public void GetLimitStringWithSqlComments()
77+
{
78+
var d = new MsSql2012Dialect();
79+
var limitSqlQuery = d.GetLimitString(new SqlString(" /* criteria query */ SELECT p from lcdtm"), null, new SqlString("2"));
80+
Assert.That(limitSqlQuery, Is.Not.Null);
81+
Assert.That(limitSqlQuery.ToString(), Is.EqualTo(" /* criteria query */ SELECT p from lcdtm ORDER BY CURRENT_TIMESTAMP OFFSET 0 ROWS FETCH FIRST 2 ROWS ONLY"));
82+
}
83+
84+
[Test]
85+
public void GetLimitStringWithSqlCommonTableExpression()
86+
{
87+
const string SQL = @"
88+
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
89+
( -- Anchor member definition
90+
SELECT ManagerID, EmployeeID, Title, Deptid, 0 AS Level
91+
FROM MyEmployees
92+
WHERE ManagerID IS NULL
93+
94+
UNION ALL
95+
96+
-- Recursive member definition
97+
SELECT e.ManagerID, e.EmployeeID, e.Title, e.Deptid, Level + 1
98+
FROM MyEmployees AS e
99+
INNER JOIN DirectReports AS ON e.ManagerID = d.EmployeeID
100+
)
101+
-- Statement that executes the CTE
102+
SELECT ManagerID, EmployeeID, Title, Level
103+
FROM DirectReports";
104+
105+
const string EXPECTED_SQL = @"
106+
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
107+
( -- Anchor member definition
108+
SELECT ManagerID, EmployeeID, Title, Deptid, 0 AS Level
109+
FROM MyEmployees
110+
WHERE ManagerID IS NULL
111+
112+
UNION ALL
113+
114+
-- Recursive member definition
115+
SELECT e.ManagerID, e.EmployeeID, e.Title, e.Deptid, Level + 1
116+
FROM MyEmployees AS e
117+
INNER JOIN DirectReports AS ON e.ManagerID = d.EmployeeID
118+
)
119+
-- Statement that executes the CTE
120+
SELECT ManagerID, EmployeeID, Title, Level
121+
FROM DirectReports ORDER BY CURRENT_TIMESTAMP OFFSET 0 ROWS FETCH FIRST 2 ROWS ONLY";
122+
123+
var d = new MsSql2012Dialect();
124+
var limitSqlQuery = d.GetLimitString(new SqlString(SQL), null, new SqlString("2"));
125+
Assert.That(limitSqlQuery, Is.Not.Null);
126+
Assert.That(limitSqlQuery.ToString(), Is.EqualTo(EXPECTED_SQL));
127+
}
128+
129+
[Test]
130+
public void DontReturnLimitStringForStoredProcedureCall()
131+
{
132+
VerifyLimitStringForStoredProcedureCalls("EXEC sp_stored_procedures");
133+
VerifyLimitStringForStoredProcedureCalls(@"
134+
DECLARE @id int
135+
SELECT @id = id FROM persons WHERE name LIKE ?
136+
EXEC get_person_summary @id");
137+
VerifyLimitStringForStoredProcedureCalls(@"
138+
DECLARE @id int
139+
SELECT DISTINCT TOP 1 @id = id FROM persons WHERE name LIKE ?
140+
EXEC get_person_summary @id");
141+
VerifyLimitStringForStoredProcedureCalls(@"
142+
DECLARE @id int
143+
SELECT DISTINCT TOP (?) PERCENT WITH TIES @id = id FROM persons WHERE name LIKE ?
144+
EXEC get_person_summary @id");
145+
}
146+
147+
private static void VerifyLimitStringForStoredProcedureCalls(string sql)
148+
{
149+
var d = new MsSql2012Dialect();
150+
var limitSql = d.GetLimitString(new SqlString(sql), null, new SqlString("2"));
151+
Assert.That(limitSql, Is.Null, "Limit only: {0}", sql);
152+
153+
limitSql = d.GetLimitString(new SqlString(sql), new SqlString("10"), null);
154+
Assert.That(limitSql, Is.Null, "Offset only: {0}", sql);
155+
156+
limitSql = d.GetLimitString(new SqlString(sql), new SqlString("10"), new SqlString("2"));
157+
Assert.That(limitSql, Is.Null, "Limit and Offset: {0}", sql);
158+
}
159+
}
160+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using NHibernate.Cfg.MappingSchema;
2+
using NUnit.Framework;
3+
4+
namespace NHibernate.Test.NHSpecificTest.NH2977
5+
{
6+
/// <summary>
7+
/// Fixture using 'by code' mappings
8+
/// </summary>
9+
/// <remarks>
10+
/// This fixture is identical to <see cref="Fixture" /> except the <see cref="Entity" /> mapping is performed
11+
/// by code in the GetMappings method, and does not require the <c>Mappings.hbm.xml</c> file. Use this approach
12+
/// if you prefer.
13+
/// </remarks>
14+
public class ByCodeFixture : TestCaseMappingByCode
15+
{
16+
protected override HbmMapping GetMappings()
17+
{
18+
return new HbmMapping();
19+
}
20+
21+
protected override bool AppliesTo(Dialect.Dialect dialect)
22+
{
23+
return dialect is Dialect.MsSql2000Dialect;
24+
}
25+
26+
[Test]
27+
public void CanGetUniqueStoredProcedureResult()
28+
{
29+
using (ISession session = OpenSession())
30+
using (session.BeginTransaction())
31+
{
32+
var result = session.CreateSQLQuery("EXEC sp_stored_procedures ?")
33+
.SetString(0, "sp_help")
34+
.UniqueResult();
35+
Assert.That(result, Is.Not.Null);
36+
}
37+
}
38+
39+
[Test]
40+
public void CanLimitStoredProcedureResults()
41+
{
42+
using (ISession session = OpenSession())
43+
using (session.BeginTransaction())
44+
{
45+
var result = session.CreateSQLQuery("EXEC sp_stored_procedures")
46+
.SetMaxResults(5)
47+
.List();
48+
Assert.That(result, Has.Count.EqualTo(5));
49+
}
50+
}
51+
}
52+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@
219219
<Compile Include="DialectTest\FunctionTests\SubstringSupportFixture.cs" />
220220
<Compile Include="DialectTest\FunctionTests\SequenceSupportFixture.cs" />
221221
<Compile Include="DialectTest\LockHintAppenderFixture.cs" />
222+
<Compile Include="DialectTest\MsSql2012DialectFixture.cs" />
222223
<Compile Include="DialectTest\MsSqlCe40DialectFixture.cs" />
223224
<Compile Include="DialectTest\SchemaTests\ColumnMetaDataFixture.cs" />
224225
<Compile Include="DriverTest\DbProviderFactoryDriveConnectionCommandProviderTest.cs" />
@@ -1036,6 +1037,7 @@
10361037
<Compile Include="NHSpecificTest\NH2960\Fixture.cs" />
10371038
<Compile Include="NHSpecificTest\NH2959\Entity.cs" />
10381039
<Compile Include="NHSpecificTest\NH2959\Fixture.cs" />
1040+
<Compile Include="NHSpecificTest\NH2977\FixtureByCode.cs" />
10391041
<Compile Include="NHSpecificTest\NH3010\FixtureWithBatcher.cs" />
10401042
<Compile Include="NHSpecificTest\NH3010\FixtureWithNoBatcher.cs" />
10411043
<Compile Include="NHSpecificTest\NH3010\Model.cs" />
@@ -1111,6 +1113,7 @@
11111113
<Compile Include="ReadOnly\TextHolder.cs" />
11121114
<Compile Include="ReadOnly\VersionedNode.cs" />
11131115
<Compile Include="RecordingInterceptor.cs" />
1116+
<Compile Include="SqlCommandTest\SqlTokenizerFixture.cs" />
11141117
<Compile Include="Stateless\Contact.cs" />
11151118
<Compile Include="Stateless\Country.cs" />
11161119
<Compile Include="Stateless\FetchingLazyCollections\LazyCollectionFetchTests.cs" />

0 commit comments

Comments
 (0)