Skip to content

Commit 9e787c2

Browse files
SAP HANA improvements (#4682)
* - merge sap providers into one - enable cte support for SAP Hana - enable CTE tests for HANA and Informix * Update Source/LinqToDB/DataProvider/SapHana/SapHanaDataProvider.cs Co-authored-by: Stuart Turner <[email protected]> --------- Co-authored-by: Stuart Turner <[email protected]>
1 parent 0b3713d commit 9e787c2

14 files changed

+255
-319
lines changed

Source/LinqToDB/DataProvider/SapHana/SapHanaBulkCopy.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private async Task<BulkCopyRowsCopied> ProviderSpecificCopyInternalAsync<T>(
111111

112112
if (options.KeepIdentity == true) hanaOptions |= SapHanaProviderAdapter.HanaBulkCopyOptions.KeepIdentity;
113113

114-
using (var bc = _provider.Adapter.CreateBulkCopy(connection, hanaOptions, transaction))
114+
using (var bc = _provider.Adapter.CreateBulkCopy!(connection, hanaOptions, transaction))
115115
{
116116
if (options.NotifyAfter != 0 && options.RowsCopiedCallback != null)
117117
{
@@ -140,7 +140,7 @@ private async Task<BulkCopyRowsCopied> ProviderSpecificCopyInternalAsync<T>(
140140
bc.DestinationTableName = tableName;
141141

142142
for (var i = 0; i < columns.Count; i++)
143-
bc.ColumnMappings.Add(_provider.Adapter.CreateBulkCopyColumnMapping(i, columns[i].ColumnName));
143+
bc.ColumnMappings.Add(_provider.Adapter.CreateBulkCopyColumnMapping!(i, columns[i].ColumnName));
144144

145145
var rd = createDataReader(columns);
146146

@@ -188,7 +188,7 @@ private BulkCopyRowsCopied ProviderSpecificCopyInternal<T>(
188188

189189
if (options.KeepIdentity == true) hanaOptions |= SapHanaProviderAdapter.HanaBulkCopyOptions.KeepIdentity;
190190

191-
using (var bc = _provider.Adapter.CreateBulkCopy(connection, hanaOptions, transaction))
191+
using (var bc = _provider.Adapter.CreateBulkCopy!(connection, hanaOptions, transaction))
192192
{
193193
if (options.NotifyAfter != 0 && options.RowsCopiedCallback != null)
194194
{
@@ -217,7 +217,7 @@ private BulkCopyRowsCopied ProviderSpecificCopyInternal<T>(
217217
bc.DestinationTableName = tableName;
218218

219219
for (var i = 0; i < columns.Count; i++)
220-
bc.ColumnMappings.Add(_provider.Adapter.CreateBulkCopyColumnMapping(i, columns[i].ColumnName));
220+
bc.ColumnMappings.Add(_provider.Adapter.CreateBulkCopyColumnMapping!(i, columns[i].ColumnName));
221221

222222
var rd = createDataReader(columns);
223223

Source/LinqToDB/DataProvider/SapHana/SapHanaDataProvider.cs

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data;
44
using System.Data.Common;
55
using System.Globalization;
6+
using System.Linq;
67
using System.Threading;
78
using System.Threading.Tasks;
89

@@ -16,10 +17,15 @@ namespace LinqToDB.DataProvider.SapHana
1617
using SqlProvider;
1718
using Translation;
1819

19-
public class SapHanaDataProvider : DynamicDataProviderBase<SapHanaProviderAdapter>
20+
sealed class SapHanaNativeDataProvider : SapHanaDataProvider { public SapHanaNativeDataProvider() : base(ProviderName.SapHanaNative, SapHanaProvider.Unmanaged) { } }
21+
sealed class SapHanaOdbcDataProvider : SapHanaDataProvider { public SapHanaOdbcDataProvider () : base(ProviderName.SapHanaOdbc , SapHanaProvider.ODBC ) { } }
22+
23+
public abstract class SapHanaDataProvider : DynamicDataProviderBase<SapHanaProviderAdapter>
2024
{
21-
public SapHanaDataProvider() : base(ProviderName.SapHanaNative, MappingSchemaInstance, SapHanaProviderAdapter.GetInstance())
25+
protected SapHanaDataProvider(string name, SapHanaProvider provider) : base(name, MappingSchemaInstance.Get(provider), SapHanaProviderAdapter.GetInstance(provider))
2226
{
27+
Provider = provider;
28+
2329
SqlProviderFlags.IsParameterOrderDependent = true;
2430
//Exception: Sap.Data.Hana.HanaException
2531
//Message: single-row query returns more than one row
@@ -32,18 +38,21 @@ public SapHanaDataProvider() : base(ProviderName.SapHanaNative, MappingSchemaIns
3238
SqlProviderFlags.IsApplyJoinSupported = true;
3339
SqlProviderFlags.IsCrossApplyJoinSupportsCondition = true;
3440
SqlProviderFlags.IsOuterApplyJoinSupportsCondition = true;
41+
SqlProviderFlags.IsCommonTableExpressionsSupported = true;
3542

36-
_sqlOptimizer = new SapHanaNativeSqlOptimizer(SqlProviderFlags);
43+
_sqlOptimizer = new SapHanaSqlOptimizer(SqlProviderFlags);
3744
}
3845

46+
private SapHanaProvider Provider { get; }
47+
3948
protected override IMemberTranslator CreateMemberTranslator()
4049
{
4150
return new SapHanaMemberTranslator();
4251
}
4352

4453
public override SchemaProvider.ISchemaProvider GetSchemaProvider()
4554
{
46-
return new SapHanaSchemaProvider();
55+
return Provider == SapHanaProvider.Unmanaged ? new SapHanaSchemaProvider() : new SapHanaOdbcSchemaProvider();
4756
}
4857

4958
public override TableOptions SupportedTableOptions =>
@@ -54,30 +63,35 @@ public override SchemaProvider.ISchemaProvider GetSchemaProvider()
5463

5564
public override ISqlBuilder CreateSqlBuilder(MappingSchema mappingSchema, DataOptions dataOptions)
5665
{
57-
return new SapHanaSqlBuilder(this, mappingSchema, dataOptions, GetSqlOptimizer(dataOptions), SqlProviderFlags);
66+
return Provider switch
67+
{
68+
SapHanaProvider.Unmanaged =>
69+
new SapHanaSqlBuilder(this, mappingSchema, dataOptions, GetSqlOptimizer(dataOptions), SqlProviderFlags),
70+
_ =>
71+
new SapHanaOdbcSqlBuilder(this, mappingSchema, dataOptions, GetSqlOptimizer(dataOptions), SqlProviderFlags),
72+
};
5873
}
5974

6075
readonly ISqlOptimizer _sqlOptimizer;
6176

62-
public override ISqlOptimizer GetSqlOptimizer(DataOptions dataOptions)
63-
{
64-
return _sqlOptimizer;
65-
}
77+
public override ISqlOptimizer GetSqlOptimizer(DataOptions dataOptions) => _sqlOptimizer;
6678

6779
public override Type ConvertParameterType(Type type, DbDataType dataType)
6880
{
6981
if (type.IsNullable())
7082
type = type.ToUnderlying();
83+
7184
#if NET6_0_OR_GREATER
72-
if (type == typeof(DateOnly))
85+
if (Provider == SapHanaProvider.Unmanaged && type == typeof(DateOnly))
7386
type = typeof(DateTime);
7487
#endif
7588

7689
switch (dataType.DataType)
7790
{
7891
case DataType.NChar:
7992
case DataType.Char:
80-
type = typeof (string);
93+
if (Provider == SapHanaProvider.Unmanaged)
94+
type = typeof (string);
8195
break;
8296
case DataType.Boolean: if (type == typeof(bool)) return typeof(byte); break;
8397
case DataType.Guid : if (type == typeof(Guid)) return typeof(string); break;
@@ -115,38 +129,51 @@ protected override void SetParameterType(DataConnection dataConnection, DbParame
115129
if (parameter is BulkCopyReader.Parameter)
116130
return;
117131

118-
SapHanaProviderAdapter.HanaDbType? type = null;
119-
switch (dataType.DataType)
132+
if (Provider == SapHanaProvider.Unmanaged)
120133
{
121-
case DataType.Text : type = SapHanaProviderAdapter.HanaDbType.Text; break;
122-
case DataType.Image: type = SapHanaProviderAdapter.HanaDbType.Blob; break;
123-
}
134+
SapHanaProviderAdapter.HanaDbType? type = null;
135+
switch (dataType.DataType)
136+
{
137+
case DataType.Text : type = SapHanaProviderAdapter.HanaDbType.Text; break;
138+
case DataType.Image: type = SapHanaProviderAdapter.HanaDbType.Blob; break;
139+
}
124140

125-
if (type != null)
126-
{
127-
var param = TryGetProviderParameter(dataConnection, parameter);
128-
if (param != null)
141+
if (type != null)
129142
{
130-
Adapter.SetDbType(param, type.Value);
131-
return;
143+
var param = TryGetProviderParameter(dataConnection, parameter);
144+
if (param != null)
145+
{
146+
Adapter.SetDbType!(param, type.Value);
147+
return;
148+
}
132149
}
133-
}
134150

135-
switch (dataType.DataType)
151+
switch (dataType.DataType)
152+
{
153+
// fallback types
154+
case DataType.Text : parameter.DbType = DbType.String; return;
155+
case DataType.Image: parameter.DbType = DbType.Binary; return;
156+
case DataType.NText : parameter.DbType = DbType.Xml; return;
157+
case DataType.Binary: parameter.DbType = DbType.Binary; return;
158+
}
159+
}
160+
else
136161
{
137-
// fallback types
138-
case DataType.Text : parameter.DbType = DbType.String; return;
139-
case DataType.Image : parameter.DbType = DbType.Binary; return;
140-
141-
case DataType.NText : parameter.DbType = DbType.Xml; return;
142-
case DataType.Binary: parameter.DbType = DbType.Binary; return;
162+
switch (dataType.DataType)
163+
{
164+
case DataType.Boolean : parameter.DbType = DbType.Byte; return;
165+
case DataType.DateTime2: parameter.DbType = DbType.DateTime; return;
166+
}
143167
}
144168

145169
base.SetParameterType(dataConnection, parameter, dataType);
146170
}
147171

148172
public override BulkCopyRowsCopied BulkCopy<T>(DataOptions options, ITable<T> table, IEnumerable<T> source)
149173
{
174+
if (Provider == SapHanaProvider.ODBC)
175+
return base.BulkCopy(options, table, source);
176+
150177
return new SapHanaBulkCopy(this).BulkCopy(
151178
options.BulkCopyOptions.BulkCopyType == BulkCopyType.Default ?
152179
options.FindOrDefault(SapHanaOptions.Default).BulkCopyType :
@@ -156,9 +183,11 @@ public override BulkCopyRowsCopied BulkCopy<T>(DataOptions options, ITable<T> ta
156183
source);
157184
}
158185

159-
public override Task<BulkCopyRowsCopied> BulkCopyAsync<T>(DataOptions options, ITable<T> table,
160-
IEnumerable<T> source, CancellationToken cancellationToken)
186+
public override Task<BulkCopyRowsCopied> BulkCopyAsync<T>(DataOptions options, ITable<T> table, IEnumerable<T> source, CancellationToken cancellationToken)
161187
{
188+
if (Provider == SapHanaProvider.ODBC)
189+
return base.BulkCopyAsync(options, table, source, cancellationToken);
190+
162191
return new SapHanaBulkCopy(this).BulkCopyAsync(
163192
options.BulkCopyOptions.BulkCopyType == BulkCopyType.Default ?
164193
options.FindOrDefault(SapHanaOptions.Default).BulkCopyType :
@@ -169,9 +198,11 @@ public override Task<BulkCopyRowsCopied> BulkCopyAsync<T>(DataOptions options, I
169198
cancellationToken);
170199
}
171200

172-
public override Task<BulkCopyRowsCopied> BulkCopyAsync<T>(DataOptions options, ITable<T> table,
173-
IAsyncEnumerable<T> source, CancellationToken cancellationToken)
201+
public override Task<BulkCopyRowsCopied> BulkCopyAsync<T>(DataOptions options, ITable<T> table, IAsyncEnumerable<T> source, CancellationToken cancellationToken)
174202
{
203+
if (Provider == SapHanaProvider.ODBC)
204+
return base.BulkCopyAsync(options, table, source, cancellationToken);
205+
175206
return new SapHanaBulkCopy(this).BulkCopyAsync(
176207
options.BulkCopyOptions.BulkCopyType == BulkCopyType.Default ?
177208
options.FindOrDefault(SapHanaOptions.Default).BulkCopyType :
@@ -184,10 +215,44 @@ public override Task<BulkCopyRowsCopied> BulkCopyAsync<T>(DataOptions options, I
184215

185216
public override bool? IsDBNullAllowed(DataOptions options, DbDataReader reader, int idx)
186217
{
187-
// provider fails to set AllowDBNull for some results
188-
return true;
218+
if (Provider == SapHanaProvider.Unmanaged)
219+
{
220+
// provider fails to set AllowDBNull for some results
221+
return true;
222+
}
223+
224+
try
225+
{
226+
return base.IsDBNullAllowed(options, reader, idx);
227+
}
228+
catch (OverflowException)
229+
{
230+
// https://github.com/dotnet/runtime/issues/40654
231+
return true;
232+
}
233+
}
234+
235+
public override IExecutionScope? ExecuteScope(DataConnection dataConnection) => Provider == SapHanaProvider.ODBC ? new InvariantCultureRegion(null) : null;
236+
237+
public override DbCommand InitCommand(DataConnection dataConnection, DbCommand command, CommandType commandType, string commandText, DataParameter[]? parameters, bool withParameters)
238+
{
239+
if (Provider == SapHanaProvider.ODBC && commandType == CommandType.StoredProcedure)
240+
{
241+
commandText = $"{{ CALL {commandText} ({string.Join(",", (parameters ?? []).Select(x => "?"))}) }}";
242+
commandType = CommandType.Text;
243+
}
244+
245+
return base.InitCommand(dataConnection, command, commandType, commandText, parameters, withParameters);
189246
}
190247

191-
private static readonly MappingSchema MappingSchemaInstance = new SapHanaMappingSchema.NativeMappingSchema();
248+
public override IQueryParametersNormalizer GetQueryParameterNormalizer() => Provider == SapHanaProvider.ODBC ? NoopQueryParametersNormalizer.Instance : base.GetQueryParameterNormalizer();
249+
250+
static class MappingSchemaInstance
251+
{
252+
public static readonly MappingSchema NativeMappingSchema = new SapHanaMappingSchema.NativeMappingSchema();
253+
public static readonly MappingSchema OdbcMappingSchema = new SapHanaMappingSchema.OdbcMappingSchema();
254+
255+
public static MappingSchema Get(SapHanaProvider provider) => provider == SapHanaProvider.Unmanaged ? NativeMappingSchema : OdbcMappingSchema;
256+
}
192257
}
193258
}

Source/LinqToDB/DataProvider/SapHana/SapHanaFactory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ IDataProvider IDataProviderFactory.GetDataProvider(IEnumerable<NamedValue> attri
1616

1717
var provider = assemblyName switch
1818
{
19-
SapHanaProviderAdapter.AssemblyName => SapHanaProvider.Unmanaged,
20-
OdbcProviderAdapter.AssemblyName => SapHanaProvider.ODBC,
21-
_ => SapHanaProvider.AutoDetect
19+
SapHanaProviderAdapter.UnmanagedAssemblyName => SapHanaProvider.Unmanaged,
20+
OdbcProviderAdapter.AssemblyName => SapHanaProvider.ODBC,
21+
_ => SapHanaProvider.AutoDetect
2222
};
2323

2424
return SapHanaTools.GetDataProvider(provider);

Source/LinqToDB/DataProvider/SapHana/SapHanaNativeSqlOptimizer.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)