diff --git a/src/NHibernate/AdoNet/OracleDbDataReader.cs b/src/NHibernate/AdoNet/OracleDbDataReader.cs new file mode 100644 index 0000000000..2ea16582e1 --- /dev/null +++ b/src/NHibernate/AdoNet/OracleDbDataReader.cs @@ -0,0 +1,46 @@ +using System; +using System.Data.Common; + +namespace NHibernate.AdoNet +{ + public class OracleDbDataReader : DbDataReaderWrapper + { + private readonly string _timestampFormat; + + public OracleDbDataReader(DbDataReader reader, string timestampFormat) + : base(reader) + { + _timestampFormat = timestampFormat; + } + + // Oracle driver does not implement GetChar + public override char GetChar(int ordinal) + { + var value = DataReader[ordinal]; + + return value switch + { + string { Length: > 0 } s => s[0], + _ => (char) value + }; + } + + public override DateTime GetDateTime(int ordinal) + { + var value = DataReader[ordinal]; + + if (value is string && _timestampFormat != null) + { + return ParseDate((string)value); + } + + return (DateTime) value; + } + + private DateTime ParseDate(string value) + { + // Need to implment rules according to https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Format-Models.html#GUID-49B32A81-0904-433E-B7FE-51606672183A + throw new NotImplementedException($"Should parse '{value}' using '{_timestampFormat}'"); + } + } +} diff --git a/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs b/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs index 67979f576a..cd50b7526a 100644 --- a/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs +++ b/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs @@ -12,8 +12,6 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Threading; -using System.Threading.Tasks; using NHibernate.AdoNet; using NHibernate.Engine.Query; using NHibernate.SqlTypes; @@ -21,18 +19,35 @@ namespace NHibernate.Driver { + using System.Threading.Tasks; + using System.Threading; public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider { - private partial class OracleDbCommandWrapper : DbCommandWrapper - { - protected override async Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) + public override Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken) + { + if (!SuppressDecimalInvalidCastException && _suppressDecimalInvalidCastExceptionSetter == null) { - cancellationToken.ThrowIfCancellationRequested(); - var reader = await (Command.ExecuteReaderAsync(behavior, cancellationToken)).ConfigureAwait(false); - _suppressDecimalInvalidCastExceptionSetter(reader, true); + throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer"); + } + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return InternalExecuteReaderAsync(); + async Task InternalExecuteReaderAsync() + { + + var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false); + + if (SuppressDecimalInvalidCastException) + { + _suppressDecimalInvalidCastExceptionSetter(reader, true); + } + + string timestampFormat = GetDateFormat(command.Connection); - return reader; + return new OracleDbDataReader(reader, timestampFormat); } } } diff --git a/src/NHibernate/Driver/OracleDataClientDriverBase.cs b/src/NHibernate/Driver/OracleDataClientDriverBase.cs index 52324e87c0..3ba5f59ce9 100644 --- a/src/NHibernate/Driver/OracleDataClientDriverBase.cs +++ b/src/NHibernate/Driver/OracleDataClientDriverBase.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Threading; -using System.Threading.Tasks; using NHibernate.AdoNet; using NHibernate.Engine.Query; using NHibernate.SqlTypes; @@ -21,24 +19,6 @@ namespace NHibernate.Driver /// public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider { - private partial class OracleDbCommandWrapper : DbCommandWrapper - { - private readonly Action _suppressDecimalInvalidCastExceptionSetter; - - public OracleDbCommandWrapper(DbCommand command, Action suppressDecimalInvalidCastExceptionSetter) : base(command) - { - _suppressDecimalInvalidCastExceptionSetter = suppressDecimalInvalidCastExceptionSetter; - } - - protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) - { - var reader = Command.ExecuteReader(behavior); - _suppressDecimalInvalidCastExceptionSetter(reader, true); - - return reader; - } - } - private const string _commandClassName = "OracleCommand"; private static readonly SqlType _guidSqlType = new SqlType(DbType.Binary, 16); @@ -54,6 +34,8 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) private readonly object _oracleDbTypeBinaryDouble; private readonly object _oracleDbTypeBinaryFloat; + private readonly Func _oracleGetSessionInfo; + private readonly Func _oracleGetTimeStampFormat; /// /// Default constructor. /// @@ -89,6 +71,10 @@ private OracleDataClientDriverBase(string driverAssemblyName, string clientNames { _suppressDecimalInvalidCastExceptionSetter = DelegateHelper.BuildPropertySetter(oracleDataReader, "SuppressGetDecimalInvalidCastException"); } + + var oracleGlobalization = ReflectHelper.TypeFromAssembly(clientNamespace + ".OracleGlobalization", driverAssemblyName, true); + _oracleGetTimeStampFormat = DelegateHelper.BuildPropertyGetter(oracleGlobalization, "TimeStampFormat"); + _oracleGetSessionInfo = DelegateHelper.BuildFunc(TypeOfConnection, "GetSessionInfo"); } /// @@ -221,25 +207,35 @@ protected override void OnBeforePrepare(DbCommand command) command.Parameters.Insert(0, outCursor); } - public override DbCommand CreateCommand() + public override DbDataReader ExecuteReader(DbCommand command) { - var command = base.CreateCommand(); - if (!SuppressDecimalInvalidCastException) + if (!SuppressDecimalInvalidCastException && _suppressDecimalInvalidCastExceptionSetter == null) { - return command; + throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer"); } - if (_suppressDecimalInvalidCastExceptionSetter == null) + var reader = command.ExecuteReader(); + + if (SuppressDecimalInvalidCastException) { - throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer"); + _suppressDecimalInvalidCastExceptionSetter(reader, true); } - return new OracleDbCommandWrapper(command, _suppressDecimalInvalidCastExceptionSetter); + string timestampFormat = GetDateFormat(command.Connection); + + return new OracleDbDataReader(reader, timestampFormat); } - public override DbCommand UnwrapDbCommand(DbCommand command) + private string GetDateFormat(DbConnection connection) { - return command is OracleDbCommandWrapper wrapper ? wrapper.Command : command; + if (_oracleGetSessionInfo == null && _oracleGetTimeStampFormat == null) + { + return null; + } + + var sessionInfo = _oracleGetSessionInfo(connection); + + return _oracleGetTimeStampFormat(sessionInfo); } System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(OracleDataClientBatchingBatcherFactory);