Skip to content

Commit 729d340

Browse files
David EllingsworthDavid Ellingsworth
David Ellingsworth
authored and
David Ellingsworth
committed
GH-3530: The Oracle driver does not support DbDataReader.GetChar and does not convert strings to datetime values. Add a custom OracleDbDataReader to address these issues.
1 parent 32a5034 commit 729d340

File tree

3 files changed

+95
-38
lines changed

3 files changed

+95
-38
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data.Common;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using NHibernate.Driver;
8+
9+
namespace NHibernate.AdoNet
10+
{
11+
public class OracleDbDataReader : DbDataReaderWrapper
12+
{
13+
private readonly string _timestampFormat;
14+
15+
public OracleDbDataReader(DbDataReader reader, string timestampFormat)
16+
: base(reader)
17+
{
18+
_timestampFormat = timestampFormat;
19+
}
20+
21+
// Oracle driver does not implement GetChar
22+
public override char GetChar(int ordinal)
23+
{
24+
var str = GetString(ordinal);
25+
26+
return str is null ? throw new InvalidCastException() : str[0];
27+
}
28+
29+
public override DateTime GetDateTime(int ordinal)
30+
{
31+
var value = DataReader[ordinal];
32+
33+
if (value is string && _timestampFormat != null)
34+
{
35+
return ParseDate((string)value);
36+
}
37+
38+
return (DateTime) value;
39+
}
40+
41+
private DateTime ParseDate(string value)
42+
{
43+
throw new NotImplementedException($"Should parse '{value}' using '{_timestampFormat}'");
44+
}
45+
}
46+
}

src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,42 @@
1212
using System.Collections.Generic;
1313
using System.Data;
1414
using System.Data.Common;
15-
using System.Threading;
16-
using System.Threading.Tasks;
1715
using NHibernate.AdoNet;
1816
using NHibernate.Engine.Query;
1917
using NHibernate.SqlTypes;
2018
using NHibernate.Util;
2119

2220
namespace NHibernate.Driver
2321
{
22+
using System.Threading.Tasks;
23+
using System.Threading;
2424
public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
2525
{
26-
private partial class OracleDbCommandWrapper : DbCommandWrapper
27-
{
2826

29-
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
27+
public override Task<DbDataReader> ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
28+
{
29+
if (!SuppressDecimalInvalidCastException && _suppressDecimalInvalidCastExceptionSetter == null)
3030
{
31-
cancellationToken.ThrowIfCancellationRequested();
32-
var reader = await (Command.ExecuteReaderAsync(behavior, cancellationToken)).ConfigureAwait(false);
33-
_suppressDecimalInvalidCastExceptionSetter(reader, true);
31+
throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer");
32+
}
33+
if (cancellationToken.IsCancellationRequested)
34+
{
35+
return Task.FromCanceled<DbDataReader>(cancellationToken);
36+
}
37+
return InternalExecuteReaderAsync();
38+
async Task<DbDataReader> InternalExecuteReaderAsync()
39+
{
40+
41+
var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
42+
43+
if (SuppressDecimalInvalidCastException)
44+
{
45+
_suppressDecimalInvalidCastExceptionSetter(reader, true);
46+
}
47+
48+
string dateFormat = GetDateFormat(command.Connection);
3449

35-
return reader;
50+
return new OracleDbDataReader(reader, dateFormat);
3651
}
3752
}
3853
}

src/NHibernate/Driver/OracleDataClientDriverBase.cs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
using System.Collections.Generic;
33
using System.Data;
44
using System.Data.Common;
5-
using System.Threading;
6-
using System.Threading.Tasks;
75
using NHibernate.AdoNet;
86
using NHibernate.Engine.Query;
97
using NHibernate.SqlTypes;
@@ -21,24 +19,6 @@ namespace NHibernate.Driver
2119
/// </remarks>
2220
public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
2321
{
24-
private partial class OracleDbCommandWrapper : DbCommandWrapper
25-
{
26-
private readonly Action<object, bool> _suppressDecimalInvalidCastExceptionSetter;
27-
28-
public OracleDbCommandWrapper(DbCommand command, Action<object, bool> suppressDecimalInvalidCastExceptionSetter) : base(command)
29-
{
30-
_suppressDecimalInvalidCastExceptionSetter = suppressDecimalInvalidCastExceptionSetter;
31-
}
32-
33-
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
34-
{
35-
var reader = Command.ExecuteReader(behavior);
36-
_suppressDecimalInvalidCastExceptionSetter(reader, true);
37-
38-
return reader;
39-
}
40-
}
41-
4222
private const string _commandClassName = "OracleCommand";
4323

4424
private static readonly SqlType _guidSqlType = new SqlType(DbType.Binary, 16);
@@ -54,6 +34,8 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
5434
private readonly object _oracleDbTypeBinaryDouble;
5535
private readonly object _oracleDbTypeBinaryFloat;
5636

37+
private readonly Func<object, object> _oracleGetSessionInfo;
38+
private readonly Func<object, string> _oracleGetTimeStampFormat;
5739
/// <summary>
5840
/// Default constructor.
5941
/// </summary>
@@ -89,6 +71,10 @@ private OracleDataClientDriverBase(string driverAssemblyName, string clientNames
8971
{
9072
_suppressDecimalInvalidCastExceptionSetter = DelegateHelper.BuildPropertySetter<bool>(oracleDataReader, "SuppressGetDecimalInvalidCastException");
9173
}
74+
75+
var oracleGlobalization = ReflectHelper.TypeFromAssembly(clientNamespace + ".OracleGlobalization", driverAssemblyName, true);
76+
_oracleGetTimeStampFormat = DelegateHelper.BuildPropertyGetter<string>(oracleGlobalization, "TimeStampFormat");
77+
_oracleGetSessionInfo = DelegateHelper.BuildFunc<object>(TypeOfConnection, "GetSessionInfo");
9278
}
9379

9480
/// <inheritdoc/>
@@ -221,25 +207,35 @@ protected override void OnBeforePrepare(DbCommand command)
221207
command.Parameters.Insert(0, outCursor);
222208
}
223209

224-
public override DbCommand CreateCommand()
210+
public override DbDataReader ExecuteReader(DbCommand command)
225211
{
226-
var command = base.CreateCommand();
227-
if (!SuppressDecimalInvalidCastException)
212+
if (!SuppressDecimalInvalidCastException && _suppressDecimalInvalidCastExceptionSetter == null)
228213
{
229-
return command;
214+
throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer");
230215
}
231216

232-
if (_suppressDecimalInvalidCastExceptionSetter == null)
217+
var reader = command.ExecuteReader();
218+
219+
if (SuppressDecimalInvalidCastException)
233220
{
234-
throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer");
221+
_suppressDecimalInvalidCastExceptionSetter(reader, true);
235222
}
236223

237-
return new OracleDbCommandWrapper(command, _suppressDecimalInvalidCastExceptionSetter);
224+
string timestampFormat = GetDateFormat(command.Connection);
225+
226+
return new OracleDbDataReader(reader, timestampFormat);
238227
}
239228

240-
public override DbCommand UnwrapDbCommand(DbCommand command)
229+
private string GetDateFormat(DbConnection connection)
241230
{
242-
return command is OracleDbCommandWrapper wrapper ? wrapper.Command : command;
231+
if (_oracleGetSessionInfo == null && _oracleGetTimeStampFormat == null)
232+
{
233+
return null;
234+
}
235+
236+
var sessionInfo = _oracleGetSessionInfo(connection);
237+
238+
return _oracleGetTimeStampFormat(sessionInfo);
243239
}
244240

245241
System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(OracleDataClientBatchingBatcherFactory);

0 commit comments

Comments
 (0)