Skip to content

Commit 50ab2b5

Browse files
author
Tien Nguyen
committed
Fix issue 183 where exception not throw in ExecuteScalar()
Addressed issue 184 where SchemaTableBuilder failed because of duplicate column names return from the "sp_oledb_getindexinfo"
1 parent 886baa2 commit 50ab2b5

File tree

6 files changed

+102
-38
lines changed

6 files changed

+102
-38
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp70</s:String></wpf:ResourceDictionary>

src/AdoNetCore.AseClient/AseDataReader.cs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
namespace AdoNetCore.AseClient
1212
{
13+
/// <summary>
14+
/// ASE implementation of <see cref="IDataReader"/>
15+
/// </summary>
1316
public sealed class AseDataReader : DbDataReader
1417
{
1518
private TableResult _currentTable;
@@ -61,16 +64,19 @@ internal void CompleteAdding()
6164
_results.CompleteAdding();
6265
}
6366

67+
/// <inheritdoc />
6468
public override bool GetBoolean(int i)
6569
{
6670
return GetPrimitive(i, (value, provider) => value.ToBoolean(provider));
6771
}
6872

73+
/// <inheritdoc />
6974
public override byte GetByte(int i)
7075
{
7176
return GetPrimitive(i, (value, provider) => value.ToByte(provider));
7277
}
7378

79+
/// <inheritdoc />
7480
public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferOffset, int length)
7581
{
7682
if (IsDBNull(i))
@@ -134,11 +140,13 @@ public override long GetBytes(int i, long fieldOffset, byte[] buffer, int buffer
134140
return bytesToRead;
135141
}
136142

143+
/// <inheritdoc />
137144
public override char GetChar(int i)
138145
{
139146
return GetPrimitive(i, (value, provider) => value.ToChar(provider));
140147
}
141148

149+
/// <inheritdoc />
142150
public override long GetChars(int i, long fieldOffset, char[] buffer, int bufferOffset, int length)
143151
{
144152
if (IsDBNull(i))
@@ -201,6 +209,7 @@ public override long GetChars(int i, long fieldOffset, char[] buffer, int buffer
201209
return charsToRead;
202210
}
203211

212+
/// <inheritdoc />
204213
public override string GetDataTypeName(int ordinal)
205214
{
206215
if (_currentTable == null || ordinal < 0)
@@ -216,11 +225,13 @@ public override string GetDataTypeName(int ordinal)
216225
return _currentTable .Formats[ordinal].GetDataTypeName();
217226
}
218227

228+
/// <inheritdoc />
219229
public override IEnumerator GetEnumerator()
220230
{
221231
return new AseDataReaderEnumerator(this);
222232
}
223233

234+
/// <inheritdoc />
224235
public override DateTime GetDateTime(int i)
225236
{
226237
var obj = GetNonNullValue(i);
@@ -253,16 +264,19 @@ public TimeSpan GetTimeSpan(int i)
253264
}
254265
}
255266

267+
/// <inheritdoc />
256268
public override decimal GetDecimal(int i)
257269
{
258270
return GetPrimitive(i, (value, provider) => value.ToDecimal(provider));
259271
}
260272

273+
/// <inheritdoc />
261274
public override double GetDouble(int i)
262275
{
263276
return GetPrimitive(i, (value, provider) => value.ToDouble(provider));
264277
}
265278

279+
/// <inheritdoc />
266280
public override Type GetFieldType(int i)
267281
{
268282
var format = GetFormat(i);
@@ -271,11 +285,13 @@ public override Type GetFieldType(int i)
271285
: TypeMap.GetNetType(format, true);
272286
}
273287

288+
/// <inheritdoc />
274289
public override float GetFloat(int i)
275290
{
276291
return GetPrimitive(i, (value, provider) => value.ToSingle(provider));
277292
}
278293

294+
/// <inheritdoc />
279295
public override Guid GetGuid(int i)
280296
{
281297
if (IsDBNull(i))
@@ -350,6 +366,7 @@ private T GetPrimitive<T>(int i, Func<IConvertible, IFormatProvider, T> convert)
350366
throw new InvalidCastException($"Cannot convert from {GetFieldType(i)} to {nameof(T)}");
351367
}
352368

369+
/// <inheritdoc />
353370
public override string GetString(int i)
354371
{
355372
var obj = GetNonNullValue(i);
@@ -377,6 +394,7 @@ public override string GetString(int i)
377394
// throw new NotImplementedException();
378395
//}
379396

397+
/// <inheritdoc />
380398
public override string GetName(int i)
381399
{
382400
var format = GetFormat(i);
@@ -388,6 +406,7 @@ public override string GetName(int i)
388406
return format.DisplayColumnName;
389407
}
390408

409+
/// <inheritdoc />
391410
public override int GetOrdinal(string name)
392411
{
393412
if (string.IsNullOrEmpty(name))
@@ -417,6 +436,7 @@ public override int GetOrdinal(string name)
417436
throw new ArgumentException();
418437
}
419438

439+
/// <inheritdoc />
420440
public override object GetValue(int i)
421441
{
422442
if (!ValueExists(i))
@@ -441,6 +461,7 @@ private object GetNonNullValue(int i)
441461
return obj;
442462
}
443463

464+
/// <inheritdoc />
444465
public override int GetValues(object[] values)
445466
{
446467
var num = values.Length;
@@ -465,6 +486,7 @@ public override int GetValues(object[] values)
465486
return num;
466487
}
467488

489+
/// <inheritdoc />
468490
public override bool IsDBNull(int i)
469491
{
470492
if (!ValueExists(i))
@@ -475,14 +497,19 @@ public override bool IsDBNull(int i)
475497
return CurrentRow.Items[i] == DBNull.Value;
476498
}
477499

500+
/// <inheritdoc />
478501
public override int FieldCount => _currentTable?.Formats?.Length ?? 0;
479502

503+
/// <inheritdoc />
480504
public override int VisibleFieldCount => FieldCount;
481505

506+
/// <inheritdoc />
482507
public override bool HasRows => CurrentRowCount > 0;
483508

509+
/// <inheritdoc />
484510
public override object this[int ordinal] => GetValue(ordinal);
485511

512+
/// <inheritdoc />
486513
public override object this[string name] => GetValue(GetOrdinal(name));
487514

488515
public
@@ -511,6 +538,7 @@ void Close()
511538
}
512539

513540
#if ENABLE_SYSTEM_DATA_COMMON_EXTENSIONS
541+
/// <inheritdoc />
514542
public override DataTable GetSchemaTable()
515543
{
516544
EnsureSchemaTable();
@@ -633,16 +661,15 @@ public override bool Read()
633661
return false;
634662
}
635663

664+
/// <inheritdoc />
636665
public override int Depth => 0;
666+
667+
/// <inheritdoc />
637668
public override bool IsClosed => _currentTable == null;
638669
private int _finalRecordsAffected = -1;
639-
public override int RecordsAffected
640-
{
641-
get
642-
{
643-
return _currentTable != null && _currentResult < _totalResults ? _currentTable.RecordsAffected : _finalRecordsAffected;
644-
}
645-
}
670+
671+
/// <inheritdoc />
672+
public override int RecordsAffected => _currentTable != null && _currentResult < _totalResults ? _currentTable.RecordsAffected : _finalRecordsAffected;
646673

647674
public void SetRecordsAffected(int value)
648675
{

src/AdoNetCore.AseClient/Internal/FormatItem.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ public static FormatItem ReadForRow(Stream stream, Encoding enc, TokenType srcTo
156156
default:
157157
throw new ArgumentException($"Unexpected token type: {srcTokenType}.", nameof(srcTokenType));
158158
}
159+
159160
ReadTypeInfo(format, stream, enc);
160161

161162
Logger.Instance?.WriteLine($" <- {format.ColumnName}: {format.DataType} (len: {format.Length}) (ut:{format.UserType}) (status:{format.RowStatus}) (loc:{format.LocaleInfo}) format names available: ColumnLabel [{format.ColumnLabel}], ColumnName [{format.ColumnName}], CatalogName [{format.CatalogName}], ParameterName [{format.ParameterName}], SchemaName [{format.SchemaName}], TableName [{format.TableName}]");
@@ -488,5 +489,15 @@ public string GetDataTypeName()
488489
return string.Empty;
489490
}
490491
}
492+
493+
public bool IsKey() => (RowStatus & RowFormatItemStatus.TDS_ROW_KEY) == RowFormatItemStatus.TDS_ROW_KEY;
494+
495+
public bool IsIdentity() => (RowStatus & RowFormatItemStatus.TDS_ROW_IDENTITY) == RowFormatItemStatus.TDS_ROW_IDENTITY;
496+
497+
public bool IsUnique()
498+
{
499+
return (DataType == TdsDataType.TDS_VARBINARY || DataType == TdsDataType.TDS_BINARY) && UserType == 80 || IsIdentity();
500+
}
501+
491502
}
492503
}

src/AdoNetCore.AseClient/Internal/Handler/ResponseParameterTokenHandler.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Data;
3+
using System.Linq;
34
using AdoNetCore.AseClient.Enum;
45
using AdoNetCore.AseClient.Interface;
56
using AdoNetCore.AseClient.Token;
@@ -30,14 +31,12 @@ public void Handle(IToken token)
3031
switch (token)
3132
{
3233
case ReturnStatusToken t:
33-
foreach (AseParameter parameter in _parameters)
34+
foreach (var parameter in _parameters.OfType<AseParameter>().Where(p => p.Direction == ParameterDirection.ReturnValue))
3435
{
35-
if (parameter.Direction == ParameterDirection.ReturnValue)
36-
{
37-
parameter.Value = t.Status;
38-
}
36+
parameter.Value = t.Status;
3937
}
4038
break;
39+
4140
case ParametersToken t:
4241
var dict = new Dictionary<string, ParametersToken.Parameter>();
4342

@@ -46,12 +45,9 @@ public void Handle(IToken token)
4645
dict[p.Format.ParameterName] = p;
4746
}
4847

49-
foreach (AseParameter parameter in _parameters)
48+
foreach (var parameter in _parameters.OfType<AseParameter>().Where(p => p.IsOutput && dict.ContainsKey(p.ParameterName)))
5049
{
51-
if (parameter.IsOutput && dict.ContainsKey(parameter.ParameterName))
52-
{
53-
parameter.Value = dict[parameter.ParameterName].Value;
54-
}
50+
parameter.Value = dict[parameter.ParameterName].Value;
5551
}
5652
break;
5753
default:

src/AdoNetCore.AseClient/Internal/InternalConnection.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ private void InternalExecuteQueryAsync(AseCommand command, AseTransaction transa
393393
}
394394
catch (Exception ex)
395395
{
396-
readerSource.TrySetException(ex); // If we have already begun returning data, then this will get lost.
396+
// If we have already begun returning data, then this will get lost.
397+
if (!readerSource.TrySetException(ex)) throw;
397398
}
398399
}
399400

src/AdoNetCore.AseClient/Internal/SchemaTableBuilder.cs

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Data;
55
using System.Data.Common;
6+
using System.Linq;
67
using AdoNetCore.AseClient.Enum;
78

89
namespace AdoNetCore.AseClient.Internal
@@ -33,7 +34,10 @@ public DataTable BuildSchemaTable()
3334
var table = new DataTable("SchemaTable");
3435
InitTableStructure(table);
3536
var fillResults = FillTableFromFormats(table);
36-
TryLoadKeyInfo(table, fillResults.BaseTableNameValue, fillResults.BaseSchemaNameValue, fillResults.BaseCatalogNameValue);
37+
38+
if (!string.IsNullOrWhiteSpace(fillResults.BaseTableNameValue) && !string.IsNullOrWhiteSpace(fillResults.BaseCatalogNameValue))
39+
TryLoadKeyInfo(table, fillResults.BaseTableNameValue, fillResults.BaseSchemaNameValue, fillResults.BaseCatalogNameValue);
40+
3741
return table;
3842
}
3943

@@ -92,8 +96,8 @@ private FillTableResults FillTableFromFormats(DataTable table)
9296
row[SchemaTableColumn.ColumnSize] = column.Length ?? -1;
9397
row[SchemaTableColumn.NumericPrecision] = column.Precision ?? -1;
9498
row[SchemaTableColumn.NumericScale] = column.Scale ?? -1;
95-
row[SchemaTableColumn.IsUnique] = false; // This gets set below.
96-
row[SchemaTableColumn.IsKey] = false; // This gets set below - no idea why TDS_ROW_KEY is never set.
99+
row[SchemaTableColumn.IsUnique] = column.IsUnique(); // This gets set below.
100+
row[SchemaTableColumn.IsKey] = column.IsKey(); // This gets set below - no idea why TDS_ROW_KEY is never set.
97101
row[SchemaTableOptionalColumn.BaseServerName] = string.Empty;
98102
row[SchemaTableOptionalColumn.BaseCatalogName] = column.CatalogName;
99103
row[SchemaTableColumn.BaseColumnName] = column.ColumnName;
@@ -133,6 +137,7 @@ private void TryLoadKeyInfo(DataTable table, string baseTableNameValue, string b
133137
{
134138
throw new InvalidOperationException("Invalid AseCommand.Connection");
135139
}
140+
136141
if (_connection.State != ConnectionState.Open)
137142
{
138143
throw new InvalidOperationException("Invalid AseCommand.Connection.ConnectionState");
@@ -163,28 +168,41 @@ private void TryLoadKeyInfo(DataTable table, string baseTableNameValue, string b
163168

164169
try
165170
{
171+
var columnMetadata = new List<ColumnMetadata>();
166172
using (var keyInfoDataReader = command.ExecuteReader())
167173
{
168174
while (keyInfoDataReader.Read())
169175
{
170-
var keyColumnName = keyInfoDataReader["COLUMN_NAME"].ToString();
171-
var keySchemaName = keyInfoDataReader["TABLE_SCHEMA"].ToString();
172-
var keyCatalogName = keyInfoDataReader["TABLE_CATALOG"].ToString();
176+
columnMetadata.Add(new ColumnMetadata
177+
{
178+
Name = keyInfoDataReader["COLUMN_NAME"].ToString(),
179+
Schema = keyInfoDataReader["TABLE_SCHEMA"].ToString(),
180+
Catalog = keyInfoDataReader["TABLE_CATALOG"].ToString(),
181+
IsKey = (bool) keyInfoDataReader["PRIMARY_KEY"],
182+
IsUnique = (bool) keyInfoDataReader["UNIQUE"]
183+
});
184+
}
185+
}
173186

174-
foreach (DataRow row in table.Rows)
187+
// Check for duplicate name and if we found any then give it a miss
188+
if (!columnMetadata.Any() || columnMetadata.GroupBy(c => c.Name)
189+
.Any(g => g.Count() > 1)) return;
190+
191+
foreach (var metadata in columnMetadata)
192+
{
193+
foreach (var row in table.Rows.OfType<DataRow>()
194+
.Where(r =>
195+
string.Equals(metadata.Name, r[SchemaTableColumn.BaseColumnName].ToString(),
196+
StringComparison.OrdinalIgnoreCase) &&
197+
string.Equals(metadata.Schema, r[SchemaTableColumn.BaseSchemaName].ToString(),
198+
StringComparison.OrdinalIgnoreCase) &&
199+
string.Equals(metadata.Catalog, r[SchemaTableOptionalColumn.BaseCatalogName].ToString(),
200+
StringComparison.OrdinalIgnoreCase)))
201+
{
202+
// Use the base column name in case the column is aliased.
175203
{
176-
// Use the base column name in case the column is aliased.
177-
if (
178-
string.Equals(keyColumnName, row[SchemaTableColumn.BaseColumnName].ToString(),
179-
StringComparison.OrdinalIgnoreCase) &&
180-
string.Equals(keySchemaName, row[SchemaTableColumn.BaseSchemaName].ToString(),
181-
StringComparison.OrdinalIgnoreCase) &&
182-
string.Equals(keyCatalogName, row[SchemaTableOptionalColumn.BaseCatalogName].ToString(),
183-
StringComparison.OrdinalIgnoreCase))
184-
{
185-
row[SchemaTableColumn.IsKey] = (bool) keyInfoDataReader["PRIMARY_KEY"];
186-
row[SchemaTableColumn.IsUnique] = (bool) keyInfoDataReader["UNIQUE"];
187-
}
204+
row[SchemaTableColumn.IsKey] = metadata.IsKey;
205+
row[SchemaTableColumn.IsUnique] = metadata.IsUnique;
188206
}
189207
}
190208
}
@@ -196,6 +214,15 @@ private void TryLoadKeyInfo(DataTable table, string baseTableNameValue, string b
196214
}
197215
}
198216

217+
private class ColumnMetadata
218+
{
219+
public string Name { get; set; }
220+
public string Schema { get; set; }
221+
public string Catalog { get; set; }
222+
public bool IsKey { get; set; }
223+
public bool IsUnique { get; set; }
224+
}
225+
199226
private class FillTableResults
200227
{
201228
public string BaseCatalogNameValue { get; set; }

0 commit comments

Comments
 (0)