Skip to content
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

Add OracleSuppressDecimalInvalidCastException setting #3028

Merged
merged 2 commits into from
Mar 13, 2022
Merged
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
52 changes: 52 additions & 0 deletions doc/reference/modules/configuration.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,58 @@ var session = sessions.OpenSession(conn);
</para>
</entry>
</row>
<row>
<entry>
<literal>oracle.use_binary_floating_point_types</literal>
</entry>
<entry>
<para>
Set whether NHibernate should map .Net <literal>double</literal> and <literal>float</literal>
to Oracle <literal>BINARY_DOUBLE</literal> and <literal>BINARY_FLOAT</literal> types or use
Oracle <literal>DOUBLE</literal> and <literal>FLOAT</literal> types.
</para>
<para>
Oracle 10g introduced <literal>BINARY_DOUBLE</literal> and <literal>BINARY_FLOAT</literal>
types which are compatible with .NET <literal>double</literal> and <literal>float</literal>
types, while Oracle <literal>DOUBLE</literal> and <literal>FLOAT</literal> are not. Oracle
<literal>DOUBLE</literal> and <literal>FLOAT</literal> types do not conform to the IEEE
standard as they are internally implemented as <literal>NUMBER</literal> type, which
makes them an exact numeric type.
</para>
<para>
<emphasis role="strong">eg.</emphasis>
<literal>true</literal> for using Oracle <literal>BINARY_DOUBLE</literal> and
<literal>BINARY_FLOAT</literal> types | <literal>false</literal> for using Oracle
<literal>DOUBLE</literal> and <literal>FLOAT</literal> types.
</para>
<para>
<literal>false</literal> by default. See
<ulink url="https://docs.oracle.com/database/121/TTSQL/types.htm#TTSQL126">ANSI SQL data types</ulink>.
</para>
</entry>
</row>
<row>
<entry>
<literal>oracle.suppress_decimal_invalid_cast_exception</literal>
</entry>
<entry>
<para>
This setting specifies whether to suppress or not the <literal>InvalidCastException</literal>
and return a rounded-off 28 precision value if the Oracle <literal>NUMBER</literal> value
has more than a 28 precision.
</para>
<para>
<emphasis role="strong">eg.</emphasis>
<literal>true</literal> for suppressing the exception | <literal>false</literal> for letting
the exception be raised.
</para>
<para>
<literal>false</literal> by default. See
<ulink url="https://docs.oracle.com/en/database/oracle/oracle-data-access-components/19.3/odpnt/DataReaderSuppressGetDecimalInvalidCastException.html">SuppressGetDecimalInvalidCastException</ulink>.
This setting works only with ODP.NET 19.10 or newer.
</para>
</entry>
</row>
<row>
<entry>
<literal>odbc.explicit_datetime_scale</literal>
Expand Down
80 changes: 80 additions & 0 deletions src/NHibernate.Test/Async/NHSpecificTest/GH2641/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System.Linq;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Dialect;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;
using NHibernate.Linq;

namespace NHibernate.Test.NHSpecificTest.GH2641
{
using System.Threading.Tasks;
[TestFixture]
public class FixtureAsync : TestCaseMappingByCode
{
protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
var entity = new Entity {Id = 1, Value = 0.00000000000000422030887989616};
session.Save(entity);

transaction.Commit();
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}
}

protected override void Configure(Configuration configuration)
{
configuration.SetProperty(Environment.OracleSuppressDecimalInvalidCastException, "true");
}

protected override bool AppliesTo(Dialect.Dialect dialect)
{
return Dialect is Oracle8iDialect;
}

[Test]
public async Task ShouldNotThrowAsync()
{
using (var session = OpenSession())
{
await (session.Query<Entity>().ToListAsync());
}
}

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<Entity>(
m =>
{
m.Table("Entity");
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
m.Property(x => x.Value);
});
return mapper.CompileMappingForAllExplicitlyAddedEntities();
}
}
}
11 changes: 11 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH2641/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace NHibernate.Test.NHSpecificTest.GH2641
{
[Serializable]
public class Entity
{
public virtual int Id { get; set; }
public virtual double Value { get; set; }
}
}
68 changes: 68 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH2641/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Linq;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Dialect;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH2641
{
[TestFixture]
public class Fixture : TestCaseMappingByCode
{
protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
var entity = new Entity {Id = 1, Value = 0.00000000000000422030887989616};
session.Save(entity);

transaction.Commit();
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}
}

protected override void Configure(Configuration configuration)
{
configuration.SetProperty(Environment.OracleSuppressDecimalInvalidCastException, "true");
}

protected override bool AppliesTo(Dialect.Dialect dialect)
{
return Dialect is Oracle8iDialect;
}

[Test]
public void ShouldNotThrow()
{
using (var session = OpenSession())
{
session.Query<Entity>().ToList();
}
}

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<Entity>(
m =>
{
m.Table("Entity");
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
m.Property(x => x.Value);
});
return mapper.CompileMappingForAllExplicitlyAddedEntities();
}
}
}
109 changes: 109 additions & 0 deletions src/NHibernate/AdoNet/DbCommandWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System.Data;
using System.Data.Common;

namespace NHibernate.AdoNet
{
/// <summary>
/// A <see cref="DbCommand"/> wrapper that implements the required members.
/// </summary>
internal partial class DbCommandWrapper : DbCommand
{
public DbCommandWrapper(DbCommand command)
{
Command = command;
}

/// <summary>
/// The wrapped command.
/// </summary>
public DbCommand Command { get; }

/// <inheritdoc />
public override void Cancel()
{
Command.Cancel();
}

/// <inheritdoc />
public override int ExecuteNonQuery()
{
return Command.ExecuteNonQuery();
}

/// <inheritdoc />
public override object ExecuteScalar()
{
return Command.ExecuteScalar();
}

/// <inheritdoc />
public override void Prepare()
{
Command.Prepare();
}

/// <inheritdoc />
public override string CommandText
{
get => Command.CommandText;
set => Command.CommandText = value;
}

/// <inheritdoc />
public override int CommandTimeout
{
get => Command.CommandTimeout;
set => Command.CommandTimeout = value;
}

/// <inheritdoc />
public override CommandType CommandType
{
get => Command.CommandType;
set => Command.CommandType = value;
}

/// <inheritdoc />
public override UpdateRowSource UpdatedRowSource
{
get => Command.UpdatedRowSource;
set => Command.UpdatedRowSource = value;
}

/// <inheritdoc />
protected override DbConnection DbConnection
{
get => Command.Connection;
set => Command.Connection = value;
}

/// <inheritdoc />
protected override DbParameterCollection DbParameterCollection => Command.Parameters;

/// <inheritdoc />
protected override DbTransaction DbTransaction
{
get => Command.Transaction;
set => Command.Transaction = value;
}

/// <inheritdoc />
public override bool DesignTimeVisible
{
get => Command.DesignTimeVisible;
set => Command.DesignTimeVisible = value;
}

/// <inheritdoc />
protected override DbParameter CreateDbParameter()
{
return Command.CreateParameter();
}

/// <inheritdoc />
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
return Command.ExecuteReader(behavior);
}
}
}
3 changes: 2 additions & 1 deletion src/NHibernate/AdoNet/HanaBatchingBatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Data.Common;
using System.Text;
using NHibernate.AdoNet.Util;
using NHibernate.Driver;
using NHibernate.Exceptions;

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

var batchUpdate = CurrentCommand;
Expand Down
3 changes: 2 additions & 1 deletion src/NHibernate/AdoNet/MySqlClientBatchingBatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Data.Common;
using System.Text;
using NHibernate.AdoNet.Util;
using NHibernate.Driver;
using NHibernate.Exceptions;

namespace NHibernate.AdoNet
Expand Down Expand Up @@ -63,7 +64,7 @@ public override void AddToBatch(IExpectation expectation)
{
Log.Debug("Adding to batch:{0}", lineWithParameters);
}
currentBatch.Append(batchUpdate);
currentBatch.Append(Driver.UnwrapDbCommand(batchUpdate));

if (currentBatch.CountOfCommands >= batchSize)
{
Expand Down
6 changes: 4 additions & 2 deletions src/NHibernate/AdoNet/OracleDataClientBatchingBatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Data.Common;
using System.Text;
using NHibernate.AdoNet.Util;
using NHibernate.Driver;
using NHibernate.Exceptions;

namespace NHibernate.AdoNet
Expand Down Expand Up @@ -156,9 +157,10 @@ protected override int CountOfStatementsInCurrentBatch
private void SetArrayBindCount(int arraySize)
{
//TODO: cache the property info.
var objType = _currentBatch.GetType();
var command = Driver.UnwrapDbCommand(_currentBatch);
var objType = command.GetType();
var propInfo = objType.GetProperty("ArrayBindCount");
propInfo.SetValue(_currentBatch, arraySize, null);
propInfo.SetValue(command, arraySize, null);
}

public override int BatchSize
Expand Down
Loading