Skip to content

Commit 2b47d3e

Browse files
Add OracleSuppressDecimalInvalidCastException setting (#3028)
And document the forgotten Oracle binary floating point types setting. Co-authored-by: Frédéric Delaporte <12201973+fredericdelaporte@users.noreply.github.com>
1 parent 55f01c6 commit 2b47d3e

19 files changed

+524
-9
lines changed

doc/reference/modules/configuration.xml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,58 @@ var session = sessions.OpenSession(conn);
12151215
</para>
12161216
</entry>
12171217
</row>
1218+
<row>
1219+
<entry>
1220+
<literal>oracle.use_binary_floating_point_types</literal>
1221+
</entry>
1222+
<entry>
1223+
<para>
1224+
Set whether NHibernate should map .Net <literal>double</literal> and <literal>float</literal>
1225+
to Oracle <literal>BINARY_DOUBLE</literal> and <literal>BINARY_FLOAT</literal> types or use
1226+
Oracle <literal>DOUBLE</literal> and <literal>FLOAT</literal> types.
1227+
</para>
1228+
<para>
1229+
Oracle 10g introduced <literal>BINARY_DOUBLE</literal> and <literal>BINARY_FLOAT</literal>
1230+
types which are compatible with .NET <literal>double</literal> and <literal>float</literal>
1231+
types, while Oracle <literal>DOUBLE</literal> and <literal>FLOAT</literal> are not. Oracle
1232+
<literal>DOUBLE</literal> and <literal>FLOAT</literal> types do not conform to the IEEE
1233+
standard as they are internally implemented as <literal>NUMBER</literal> type, which
1234+
makes them an exact numeric type.
1235+
</para>
1236+
<para>
1237+
<emphasis role="strong">eg.</emphasis>
1238+
<literal>true</literal> for using Oracle <literal>BINARY_DOUBLE</literal> and
1239+
<literal>BINARY_FLOAT</literal> types | <literal>false</literal> for using Oracle
1240+
<literal>DOUBLE</literal> and <literal>FLOAT</literal> types.
1241+
</para>
1242+
<para>
1243+
<literal>false</literal> by default. See
1244+
<ulink url="https://docs.oracle.com/database/121/TTSQL/types.htm#TTSQL126">ANSI SQL data types</ulink>.
1245+
</para>
1246+
</entry>
1247+
</row>
1248+
<row>
1249+
<entry>
1250+
<literal>oracle.suppress_decimal_invalid_cast_exception</literal>
1251+
</entry>
1252+
<entry>
1253+
<para>
1254+
This setting specifies whether to suppress or not the <literal>InvalidCastException</literal>
1255+
and return a rounded-off 28 precision value if the Oracle <literal>NUMBER</literal> value
1256+
has more than a 28 precision.
1257+
</para>
1258+
<para>
1259+
<emphasis role="strong">eg.</emphasis>
1260+
<literal>true</literal> for suppressing the exception | <literal>false</literal> for letting
1261+
the exception be raised.
1262+
</para>
1263+
<para>
1264+
<literal>false</literal> by default. See
1265+
<ulink url="https://docs.oracle.com/en/database/oracle/oracle-data-access-components/19.3/odpnt/DataReaderSuppressGetDecimalInvalidCastException.html">SuppressGetDecimalInvalidCastException</ulink>.
1266+
This setting works only with ODP.NET 19.10 or newer.
1267+
</para>
1268+
</entry>
1269+
</row>
12181270
<row>
12191271
<entry>
12201272
<literal>odbc.explicit_datetime_scale</literal>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Linq;
12+
using NHibernate.Cfg;
13+
using NHibernate.Cfg.MappingSchema;
14+
using NHibernate.Dialect;
15+
using NHibernate.Mapping.ByCode;
16+
using NUnit.Framework;
17+
using NHibernate.Linq;
18+
19+
namespace NHibernate.Test.NHSpecificTest.GH2641
20+
{
21+
using System.Threading.Tasks;
22+
[TestFixture]
23+
public class FixtureAsync : TestCaseMappingByCode
24+
{
25+
protected override void OnSetUp()
26+
{
27+
using (var session = OpenSession())
28+
using (var transaction = session.BeginTransaction())
29+
{
30+
var entity = new Entity {Id = 1, Value = 0.00000000000000422030887989616};
31+
session.Save(entity);
32+
33+
transaction.Commit();
34+
}
35+
}
36+
37+
protected override void OnTearDown()
38+
{
39+
using (var session = OpenSession())
40+
using (var transaction = session.BeginTransaction())
41+
{
42+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
43+
44+
transaction.Commit();
45+
}
46+
}
47+
48+
protected override void Configure(Configuration configuration)
49+
{
50+
configuration.SetProperty(Environment.OracleSuppressDecimalInvalidCastException, "true");
51+
}
52+
53+
protected override bool AppliesTo(Dialect.Dialect dialect)
54+
{
55+
return Dialect is Oracle8iDialect;
56+
}
57+
58+
[Test]
59+
public async Task ShouldNotThrowAsync()
60+
{
61+
using (var session = OpenSession())
62+
{
63+
await (session.Query<Entity>().ToListAsync());
64+
}
65+
}
66+
67+
protected override HbmMapping GetMappings()
68+
{
69+
var mapper = new ModelMapper();
70+
mapper.Class<Entity>(
71+
m =>
72+
{
73+
m.Table("Entity");
74+
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
75+
m.Property(x => x.Value);
76+
});
77+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
78+
}
79+
}
80+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2641
4+
{
5+
[Serializable]
6+
public class Entity
7+
{
8+
public virtual int Id { get; set; }
9+
public virtual double Value { get; set; }
10+
}
11+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System.Linq;
2+
using NHibernate.Cfg;
3+
using NHibernate.Cfg.MappingSchema;
4+
using NHibernate.Dialect;
5+
using NHibernate.Mapping.ByCode;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.NHSpecificTest.GH2641
9+
{
10+
[TestFixture]
11+
public class Fixture : TestCaseMappingByCode
12+
{
13+
protected override void OnSetUp()
14+
{
15+
using (var session = OpenSession())
16+
using (var transaction = session.BeginTransaction())
17+
{
18+
var entity = new Entity {Id = 1, Value = 0.00000000000000422030887989616};
19+
session.Save(entity);
20+
21+
transaction.Commit();
22+
}
23+
}
24+
25+
protected override void OnTearDown()
26+
{
27+
using (var session = OpenSession())
28+
using (var transaction = session.BeginTransaction())
29+
{
30+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
31+
32+
transaction.Commit();
33+
}
34+
}
35+
36+
protected override void Configure(Configuration configuration)
37+
{
38+
configuration.SetProperty(Environment.OracleSuppressDecimalInvalidCastException, "true");
39+
}
40+
41+
protected override bool AppliesTo(Dialect.Dialect dialect)
42+
{
43+
return Dialect is Oracle8iDialect;
44+
}
45+
46+
[Test]
47+
public void ShouldNotThrow()
48+
{
49+
using (var session = OpenSession())
50+
{
51+
session.Query<Entity>().ToList();
52+
}
53+
}
54+
55+
protected override HbmMapping GetMappings()
56+
{
57+
var mapper = new ModelMapper();
58+
mapper.Class<Entity>(
59+
m =>
60+
{
61+
m.Table("Entity");
62+
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
63+
m.Property(x => x.Value);
64+
});
65+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
66+
}
67+
}
68+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Data;
2+
using System.Data.Common;
3+
4+
namespace NHibernate.AdoNet
5+
{
6+
/// <summary>
7+
/// A <see cref="DbCommand"/> wrapper that implements the required members.
8+
/// </summary>
9+
internal partial class DbCommandWrapper : DbCommand
10+
{
11+
public DbCommandWrapper(DbCommand command)
12+
{
13+
Command = command;
14+
}
15+
16+
/// <summary>
17+
/// The wrapped command.
18+
/// </summary>
19+
public DbCommand Command { get; }
20+
21+
/// <inheritdoc />
22+
public override void Cancel()
23+
{
24+
Command.Cancel();
25+
}
26+
27+
/// <inheritdoc />
28+
public override int ExecuteNonQuery()
29+
{
30+
return Command.ExecuteNonQuery();
31+
}
32+
33+
/// <inheritdoc />
34+
public override object ExecuteScalar()
35+
{
36+
return Command.ExecuteScalar();
37+
}
38+
39+
/// <inheritdoc />
40+
public override void Prepare()
41+
{
42+
Command.Prepare();
43+
}
44+
45+
/// <inheritdoc />
46+
public override string CommandText
47+
{
48+
get => Command.CommandText;
49+
set => Command.CommandText = value;
50+
}
51+
52+
/// <inheritdoc />
53+
public override int CommandTimeout
54+
{
55+
get => Command.CommandTimeout;
56+
set => Command.CommandTimeout = value;
57+
}
58+
59+
/// <inheritdoc />
60+
public override CommandType CommandType
61+
{
62+
get => Command.CommandType;
63+
set => Command.CommandType = value;
64+
}
65+
66+
/// <inheritdoc />
67+
public override UpdateRowSource UpdatedRowSource
68+
{
69+
get => Command.UpdatedRowSource;
70+
set => Command.UpdatedRowSource = value;
71+
}
72+
73+
/// <inheritdoc />
74+
protected override DbConnection DbConnection
75+
{
76+
get => Command.Connection;
77+
set => Command.Connection = value;
78+
}
79+
80+
/// <inheritdoc />
81+
protected override DbParameterCollection DbParameterCollection => Command.Parameters;
82+
83+
/// <inheritdoc />
84+
protected override DbTransaction DbTransaction
85+
{
86+
get => Command.Transaction;
87+
set => Command.Transaction = value;
88+
}
89+
90+
/// <inheritdoc />
91+
public override bool DesignTimeVisible
92+
{
93+
get => Command.DesignTimeVisible;
94+
set => Command.DesignTimeVisible = value;
95+
}
96+
97+
/// <inheritdoc />
98+
protected override DbParameter CreateDbParameter()
99+
{
100+
return Command.CreateParameter();
101+
}
102+
103+
/// <inheritdoc />
104+
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
105+
{
106+
return Command.ExecuteReader(behavior);
107+
}
108+
}
109+
}

src/NHibernate/AdoNet/HanaBatchingBatcher.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data.Common;
44
using System.Text;
55
using NHibernate.AdoNet.Util;
6+
using NHibernate.Driver;
67
using NHibernate.Exceptions;
78

89
namespace NHibernate.AdoNet
@@ -34,7 +35,7 @@ public HanaBatchingBatcher(ConnectionManager connectionManager, IInterceptor int
3435
public override void AddToBatch(IExpectation expectation)
3536
{
3637
// HanaCommands are cloneable
37-
if (!(CurrentCommand is ICloneable cloneableCurrentCommand))
38+
if (!(Driver.UnwrapDbCommand(CurrentCommand) is ICloneable cloneableCurrentCommand))
3839
throw new InvalidOperationException("Current command is not an ICloneable");
3940

4041
var batchUpdate = CurrentCommand;

src/NHibernate/AdoNet/MySqlClientBatchingBatcher.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Data.Common;
33
using System.Text;
44
using NHibernate.AdoNet.Util;
5+
using NHibernate.Driver;
56
using NHibernate.Exceptions;
67

78
namespace NHibernate.AdoNet
@@ -63,7 +64,7 @@ public override void AddToBatch(IExpectation expectation)
6364
{
6465
Log.Debug("Adding to batch:{0}", lineWithParameters);
6566
}
66-
currentBatch.Append(batchUpdate);
67+
currentBatch.Append(Driver.UnwrapDbCommand(batchUpdate));
6768

6869
if (currentBatch.CountOfCommands >= batchSize)
6970
{

src/NHibernate/AdoNet/OracleDataClientBatchingBatcher.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data.Common;
44
using System.Text;
55
using NHibernate.AdoNet.Util;
6+
using NHibernate.Driver;
67
using NHibernate.Exceptions;
78

89
namespace NHibernate.AdoNet
@@ -156,9 +157,10 @@ protected override int CountOfStatementsInCurrentBatch
156157
private void SetArrayBindCount(int arraySize)
157158
{
158159
//TODO: cache the property info.
159-
var objType = _currentBatch.GetType();
160+
var command = Driver.UnwrapDbCommand(_currentBatch);
161+
var objType = command.GetType();
160162
var propInfo = objType.GetProperty("ArrayBindCount");
161-
propInfo.SetValue(_currentBatch, arraySize, null);
163+
propInfo.SetValue(command, arraySize, null);
162164
}
163165

164166
public override int BatchSize

0 commit comments

Comments
 (0)