33using System . Data ;
44using System . Data . Common ;
55using System . Globalization ;
6+ using System . Linq ;
67using System . Threading ;
78using 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}
0 commit comments