Skip to content

Commit 88e66ab

Browse files
authored
Merge pull request #481 from oskarb/nh3403-param-length-test
Additional tests in StringTypeWithLengthFixture related to NH-3403 and NH-3036
2 parents 619d0b9 + 4e204ed commit 88e66ab

File tree

3 files changed

+247
-78
lines changed

3 files changed

+247
-78
lines changed

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3302,7 +3302,6 @@
33023302
<EmbeddedResource Include="NHSpecificTest\NH2913\Mappings.hbm.xml">
33033303
<SubType>Designer</SubType>
33043304
</EmbeddedResource>
3305-
<EmbeddedResource Include="TypesTest\StringClassWithLength.hbm.xml" />
33063305
<EmbeddedResource Include="NHSpecificTest\NH2846\Mappings.hbm.xml" />
33073306
<EmbeddedResource Include="NHSpecificTest\NH1007\Mappings.hbm.xml" />
33083307
<EmbeddedResource Include="NHSpecificTest\NH2907\Mappings.hbm.xml" />

src/NHibernate.Test/TypesTest/StringClassWithLength.hbm.xml

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 247 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,308 @@
11
using System;
2-
using System.Collections;
2+
using System.Linq;
3+
using NHibernate.Cfg.MappingSchema;
34
using NHibernate.Criterion;
45
using NHibernate.Dialect;
56
using NHibernate.Driver;
7+
using NHibernate.Exceptions;
8+
using NHibernate.Linq;
9+
using NHibernate.Mapping.ByCode;
610
using NUnit.Framework;
711

812
namespace NHibernate.Test.TypesTest
913
{
1014
/// <summary>
11-
/// Summary description for StringTypeWithLengthFixture.
15+
/// Various tests regarding handling of size of query parameters.
1216
/// </summary>
1317
[TestFixture]
14-
public class StringTypeWithLengthFixture : TypeFixtureBase
18+
public class StringTypeWithLengthFixture : TestCaseMappingByCode
1519
{
16-
protected override string TypeName
20+
private int GetLongStringMappedLength()
1721
{
18-
get { return "String"; }
19-
}
22+
// This is a bit ugly...
23+
//
24+
// Return a value that should be the largest possible length of a string column
25+
// in the corresponding database. Note that the actual column type selected by the dialect
26+
// depends on this value, so it must be the largest possible value for the type
27+
// that the dialect will pick. Doesn't matter if the dialect can pick another
28+
// type for an even larger size.
2029

30+
if (Dialect is Oracle8iDialect)
31+
return 2000;
2132

22-
protected override IList Mappings
23-
{
24-
get
25-
{
26-
return new string[]
27-
{
28-
String.Format("TypesTest.{0}ClassWithLength.hbm.xml", TypeName)
29-
};
30-
}
33+
if (Dialect is MySQLDialect)
34+
return 65535;
35+
36+
return 4000;
3137
}
3238

33-
protected override bool AppliesTo(Dialect.Dialect dialect)
39+
protected override HbmMapping GetMappings()
3440
{
35-
// this test only works where the driver has set an explicit length on the IDbDataParameter
36-
return dialect is MsSql2008Dialect;
41+
var mapper = new ModelMapper();
42+
mapper.Class<StringClass>(ca =>
43+
{
44+
ca.Lazy(false);
45+
ca.Id(x => x.Id, map => map.Generator(Generators.Assigned));
46+
ca.Property(x => x.StringValue, map => map.Length(10));
47+
ca.Property(x => x.LongStringValue, map => map.Length(GetLongStringMappedLength()));
48+
});
49+
50+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
3751
}
3852

53+
3954
[Test]
40-
public void NhThrowsOnTooLong()
55+
[Description("Values longer than the maximum possible string length " +
56+
"should raise an exception if they would otherwise be truncated.")]
57+
public void ShouldPreventInsertionOfVeryLongStringThatWouldBeTruncated()
4158
{
42-
int maxStringLength = 4000;
43-
PropertyValueException ex = Assert.Throws<PropertyValueException>(() =>
59+
// This test case is for when the current driver will use a parameter size
60+
// that is significantly larger than the mapped column size (e.g. SqlClientDriver currently).
61+
62+
// Note: This test could possible be written as
63+
// "database must raise an error OR it must store and return the full value"
64+
// to avoid this dialect specific exception.
65+
if (Dialect is SQLiteDialect)
66+
Assert.Ignore("SQLite does not enforce specified string lengths.");
67+
68+
int maxStringLength = GetLongStringMappedLength();
69+
70+
var ex = Assert.Catch<Exception>(
71+
() =>
4472
{
4573
using (ISession s = OpenSession())
4674
{
47-
StringClass b = new StringClass();
48-
b.LongStringValue = new string('x', maxStringLength + 1);
75+
StringClass b = new StringClass {LongStringValue = new string('x', maxStringLength + 1)};
4976
s.Save(b);
5077
s.Flush();
5178
}
5279
});
5380

54-
Assert.That(ex.Message, Iz.EqualTo("Error dehydrating property value for NHibernate.Test.TypesTest.StringClass.LongStringValue"));
55-
Assert.That(ex.InnerException, Iz.TypeOf<HibernateException>());
56-
Assert.That(ex.InnerException.Message, Iz.EqualTo("The length of the string value exceeds the length configured in the mapping/parameter."));
81+
AssertFailedInsertExceptionDetailsAndEmptyTable(ex);
5782
}
5883

5984
[Test]
60-
public void DbThrowsOnTooLong()
85+
[Description("Values longer than the mapped string length " +
86+
"should raise an exception if they would otherwise be truncated.")]
87+
public void ShouldPreventInsertionOfTooLongStringThatWouldBeTruncated()
6188
{
62-
bool dbThrewError = false;
89+
// Note: This test could possible be written as
90+
// "database must raise an error OR it must store and return the full value"
91+
// to avoid this dialect specific exception.
92+
if (Dialect is SQLiteDialect)
93+
Assert.Ignore("SQLite does not enforce specified string lengths.");
6394

64-
try
65-
{
66-
using (ISession s = OpenSession())
95+
var ex = Assert.Catch<Exception>(
96+
() =>
6797
{
68-
StringClass b = new StringClass();
69-
b.StringValue = "0123456789a";
70-
s.Save(b);
71-
s.Flush();
72-
}
98+
using (ISession s = OpenSession())
99+
{
100+
StringClass b = new StringClass {StringValue = "0123456789a"};
101+
s.Save(b);
102+
s.Flush();
103+
}
104+
},
105+
"An exception was expected when trying to put too large a value into a column.");
106+
107+
AssertFailedInsertExceptionDetailsAndEmptyTable(ex);
108+
}
109+
110+
111+
private void AssertFailedInsertExceptionDetailsAndEmptyTable(Exception ex)
112+
{
113+
// We can get different sort of exceptions.
114+
if (ex is PropertyValueException)
115+
{
116+
// Some drivers/dialects set explicit parameter sizes, in which case we expect NH to
117+
// raise a PropertyValueException (to avoid ADO.NET from silently truncating).
118+
119+
Assert.That(
120+
ex.Message,
121+
Is.StringStarting("Error dehydrating property value for NHibernate.Test.TypesTest.StringClass."));
122+
123+
Assert.That(ex.InnerException, Is.TypeOf<HibernateException>());
124+
125+
Assert.That(
126+
ex.InnerException.Message,
127+
Is.EqualTo("The length of the string value exceeds the length configured in the mapping/parameter."));
128+
}
129+
else if (Dialect is MsSqlCeDialect && ex is InvalidOperationException)
130+
{
131+
Assert.That(ex.Message, Is.StringContaining("max=4000, len=4001"));
73132
}
74-
catch
133+
else
75134
{
76-
dbThrewError = true;
135+
// In other cases, we expect the database itself to raise an error. This case
136+
// will also happen if the driver does set an explicit parameter size, but that
137+
// size is larger than the mapped column size.
138+
Assert.That(ex, Is.TypeOf<GenericADOException>());
77139
}
78140

79-
Assert.That(dbThrewError, "Database did not throw an error when trying to put too large a value into a column");
141+
// In any case, nothing should have been inserted.
142+
using (ISession s = OpenSession())
143+
{
144+
Assert.That(s.Query<StringClass>().ToList(), Is.Empty);
145+
}
80146
}
81147

148+
82149
[Test]
83150
public void CriteriaLikeParameterCanExceedColumnSize()
84151
{
85-
if (!(sessions.ConnectionProvider.Driver is SqlClientDriver))
86-
Assert.Ignore("This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column.");
152+
Exception exception = CatchException<Exception>(
153+
() =>
154+
{
155+
using (ISession s = OpenSession())
156+
using (s.BeginTransaction())
157+
{
158+
s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"});
159+
s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"});
87160

88-
using (ISession s = OpenSession())
89-
using (ITransaction t = s.BeginTransaction())
90-
{
91-
s.Save(new StringClass() { Id = 1, StringValue = "AAAAAAAAAB" });
92-
s.Save(new StringClass() { Id = 2, StringValue = "BAAAAAAAAA" });
161+
var aaItems =
162+
s.CreateCriteria<StringClass>()
163+
.Add(Restrictions.Like("StringValue", "%AAAAAAAAA%"))
164+
.List();
93165

94-
var aaItems =
95-
s.CreateCriteria<StringClass>()
96-
.Add(Restrictions.Like("StringValue", "%AAAAAAAAA%"))
97-
.List();
166+
Assert.That(aaItems.Count, Is.EqualTo(2));
167+
}
168+
});
98169

99-
Assert.That(aaItems.Count, Is.EqualTo(2));
100-
}
170+
171+
// This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column.
172+
AssertExpectedFailureOrNoException(
173+
exception,
174+
(sessions.ConnectionProvider.Driver is OdbcDriver));
101175
}
102176

103177
[Test]
104178
public void HqlLikeParameterCanExceedColumnSize()
105179
{
106-
if (!(sessions.ConnectionProvider.Driver is SqlClientDriver))
107-
Assert.Ignore("This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column.");
180+
Exception exception = CatchException<Exception>(
181+
() =>
182+
{
183+
using (ISession s = OpenSession())
184+
using (s.BeginTransaction())
185+
{
186+
s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"});
187+
s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"});
108188

109-
using (ISession s = OpenSession())
110-
using (ITransaction t = s.BeginTransaction())
189+
var aaItems =
190+
s.CreateQuery("from StringClass s where s.StringValue like :likeValue")
191+
.SetParameter("likeValue", "%AAAAAAAAA%")
192+
.List();
193+
194+
Assert.That(aaItems.Count, Is.EqualTo(2));
195+
}
196+
});
197+
198+
199+
// This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column.
200+
AssertExpectedFailureOrNoException(
201+
exception,
202+
(sessions.ConnectionProvider.Driver is OdbcDriver));
203+
}
204+
205+
206+
[Test]
207+
public void CriteriaEqualityParameterCanExceedColumnSize()
208+
{
209+
// We should be able to query a column with a value longer than
210+
// the specified column size, to avoid tedious exceptions.
211+
212+
Exception exception = CatchException<Exception>(
213+
() =>
214+
{
215+
using (ISession s = OpenSession())
216+
using (s.BeginTransaction())
217+
{
218+
s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"});
219+
s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"});
220+
221+
var aaItems =
222+
s.CreateCriteria<StringClass>()
223+
.Add(Restrictions.Eq("StringValue", "AAAAAAAAABx"))
224+
.List();
225+
226+
Assert.That(aaItems.Count, Is.EqualTo(0));
227+
}
228+
});
229+
230+
// Doesn't work on Firebird due to Firebird not figuring out parameter types on its own.
231+
// This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column.
232+
AssertExpectedFailureOrNoException(
233+
exception,
234+
(Dialect is FirebirdDialect) || (sessions.ConnectionProvider.Driver is OdbcDriver));
235+
}
236+
237+
238+
[Test]
239+
public void HqlEqualityParameterCanExceedColumnSize()
240+
{
241+
// We should be able to query a column with a value longer than
242+
// the specified column size, to avoid tedious exceptions.
243+
244+
Exception exception = CatchException<Exception>(
245+
() =>
246+
{
247+
using (ISession s = OpenSession())
248+
using (s.BeginTransaction())
249+
{
250+
s.Save(new StringClass {Id = 1, StringValue = "AAAAAAAAAB"});
251+
s.Save(new StringClass {Id = 2, StringValue = "BAAAAAAAAA"});
252+
253+
var aaItems =
254+
s.CreateQuery("from StringClass s where s.StringValue = :likeValue")
255+
.SetParameter("likeValue", "AAAAAAAAABx")
256+
.List();
257+
258+
Assert.That(aaItems.Count, Is.EqualTo(0));
259+
}
260+
});
261+
262+
// Doesn't work on Firebird due to Firebird not figuring out parameter types on its own.
263+
// This test fails against the ODBC driver. The driver would need to be override to allow longer parameter sizes than the column.
264+
AssertExpectedFailureOrNoException(
265+
exception,
266+
(Dialect is FirebirdDialect) || (sessions.ConnectionProvider.Driver is OdbcDriver));
267+
}
268+
269+
270+
/// <summary>
271+
/// Some test cases doesn't work during some scenarios for well-known reasons. If the test
272+
/// fails under these circumstances, mark it as IGNORED. If it _stops_ failing, mark it
273+
/// as a FAILURE so that it can be investigated.
274+
/// </summary>
275+
private void AssertExpectedFailureOrNoException(Exception exception, bool requireExceptionAndIgnoreTest)
276+
{
277+
if (requireExceptionAndIgnoreTest)
111278
{
112-
s.Save(new StringClass() { Id = 1, StringValue = "AAAAAAAAAB" });
113-
s.Save(new StringClass() { Id = 2, StringValue = "BAAAAAAAAA" });
279+
Assert.NotNull(
280+
exception,
281+
"Test was expected to have a well-known, but ignored, failure for the current configuration. If " +
282+
"that expected failure no longer occurs, it may now be possible to remove this exception.");
283+
284+
Assert.Ignore("This test is known to fail for the current configuration.");
285+
}
286+
287+
// If the above didn't ignore the exception, it's for real - rethrow to trigger test failure.
288+
if (exception != null)
289+
throw new Exception("Wrapped exception.", exception);
290+
}
114291

115-
var aaItems =
116-
s.CreateQuery("from StringClass s where s.StringValue like :likeValue")
117-
.SetParameter("likeValue", "%AAAAAAAAA%")
118-
.List();
119292

120-
Assert.That(aaItems.Count, Is.EqualTo(2));
293+
private TException CatchException<TException>(System.Action action)
294+
where TException : Exception
295+
{
296+
try
297+
{
298+
action();
121299
}
300+
catch (TException exception)
301+
{
302+
return exception;
303+
}
304+
305+
return null;
122306
}
123307
}
124308
}

0 commit comments

Comments
 (0)