Skip to content

Commit baa206f

Browse files
committed
Implement GetColumnSchema. Fixes #182
1 parent 964c477 commit baa206f

File tree

3 files changed

+201
-24
lines changed

3 files changed

+201
-24
lines changed

src/MySqlConnector/MySqlClient/MySqlDataReader.cs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Collections.ObjectModel;
45
using System.Data;
56
using System.Data.Common;
67
using System.Globalization;
8+
using System.Linq;
79
using System.Threading;
810
using System.Threading.Tasks;
911
using MySql.Data.MySqlClient.Results;
@@ -13,6 +15,9 @@
1315
namespace MySql.Data.MySqlClient
1416
{
1517
public sealed class MySqlDataReader : DbDataReader
18+
#if NETSTANDARD1_3 || NETSTANDARD2_0
19+
, IDbColumnSchemaGenerator
20+
#endif
1621
{
1722
public override bool NextResult() =>
1823
NextResultAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
@@ -219,6 +224,21 @@ public override void Close()
219224
}
220225
#endif
221226

227+
#if NETSTANDARD1_3 || NETSTANDARD2_0
228+
public ReadOnlyCollection<DbColumn> GetColumnSchema()
229+
#else
230+
public ReadOnlyCollection<MySqlDbColumn> GetColumnSchema()
231+
#endif
232+
{
233+
return GetResultSet().ColumnDefinitions
234+
.Select((c, n) => new MySqlDbColumn(n, c, GetFieldType(n), GetDataTypeName(n)))
235+
#if NETSTANDARD1_3 || NETSTANDARD2_0
236+
.Cast<DbColumn>()
237+
#endif
238+
.ToList()
239+
.AsReadOnly();
240+
}
241+
222242
public override T GetFieldValue<T>(int ordinal)
223243
{
224244
if (typeof(T) == typeof(DateTimeOffset))
@@ -339,34 +359,28 @@ internal DataTable BuildSchemaTable()
339359
columns.Add(isLong);
340360
columns.Add(isReadOnly);
341361

342-
for (var i = 0; i < colDefinitions.Length; ++i)
362+
foreach (MySqlDbColumn column in GetColumnSchema())
343363
{
344-
var col = colDefinitions[i];
345364
var schemaRow = schemaTable.NewRow();
346-
schemaRow[columnName] = col.Name;
347-
schemaRow[ordinal] = i;
348-
var fieldType = GetFieldType(i);
349-
schemaRow[dataType] = fieldType;
350-
var columnSize = fieldType == typeof(string) || fieldType == typeof(Guid) ?
351-
col.ColumnLength / SerializationUtility.GetBytesPerCharacter(col.CharacterSet) :
352-
col.ColumnLength;
353-
schemaRow[size] = columnSize > int.MaxValue ? int.MaxValue : unchecked((int) columnSize);
354-
schemaRow[providerType] = col.ColumnType;
355-
schemaRow[isLong] = col.ColumnLength > 255 && ((col.ColumnFlags & ColumnFlags.Blob) != 0 || col.ColumnType == ColumnType.TinyBlob || col.ColumnType == ColumnType.Blob || col.ColumnType == ColumnType.MediumBlob || col.ColumnType == ColumnType.LongBlob);
365+
schemaRow[columnName] = column.ColumnName;
366+
schemaRow[ordinal] = column.ColumnOrdinal;
367+
schemaRow[dataType] = column.DataType;
368+
schemaRow[size] = column.ColumnSize;
369+
schemaRow[providerType] = column.ProviderType;
370+
schemaRow[isLong] = column.IsLong;
356371
schemaRow[isUnique] = false;
357-
schemaRow[isKey] = (col.ColumnFlags & ColumnFlags.PrimaryKey) != 0;
358-
schemaRow[allowDBNull] = (col.ColumnFlags & ColumnFlags.NotNull) == 0;
359-
schemaRow[scale] = col.Decimals;
360-
if (col.ColumnType == ColumnType.Decimal || col.ColumnType == ColumnType.NewDecimal)
361-
schemaRow[precision] = col.ColumnLength - 2 + ((col.ColumnFlags & ColumnFlags.Unsigned) != 0 ? 1 : 0);
362-
363-
schemaRow[baseCatalogName] = null;
364-
schemaRow[baseColumnName] = col.PhysicalName;
365-
schemaRow[baseSchemaName] = col.SchemaName;
366-
schemaRow[baseTableName] = col.PhysicalTable;
367-
schemaRow[isAutoIncrement] = (col.ColumnFlags & ColumnFlags.AutoIncrement) != 0;
372+
schemaRow[isKey] = column.IsKey;
373+
schemaRow[allowDBNull] = column.AllowDBNull;
374+
schemaRow[scale] = column.NumericScale;
375+
schemaRow[precision] = column.NumericPrecision.GetValueOrDefault();
376+
377+
schemaRow[baseCatalogName] = column.BaseCatalogName;
378+
schemaRow[baseColumnName] = column.BaseColumnName;
379+
schemaRow[baseSchemaName] = column.BaseSchemaName;
380+
schemaRow[baseTableName] = column.BaseTableName;
381+
schemaRow[isAutoIncrement] = column.IsAutoIncrement;
368382
schemaRow[isRowVersion] = false;
369-
schemaRow[isReadOnly] = false;
383+
schemaRow[isReadOnly] = column.IsReadOnly;
370384

371385
schemaTable.Rows.Add(schemaRow);
372386
schemaRow.AcceptChanges();
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Data.Common;
3+
using MySql.Data.Serialization;
4+
5+
namespace MySql.Data.MySqlClient
6+
{
7+
public sealed class MySqlDbColumn
8+
#if NETSTANDARD1_3 || NETSTANDARD2_0
9+
: DbColumn
10+
#endif
11+
{
12+
internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, Type type, string dataTypeName)
13+
{
14+
var columnSize = type == typeof(string) || type == typeof(Guid) ?
15+
column.ColumnLength / SerializationUtility.GetBytesPerCharacter(column.CharacterSet) :
16+
column.ColumnLength;
17+
18+
AllowDBNull = (column.ColumnFlags & ColumnFlags.NotNull) == 0;
19+
BaseCatalogName = null;
20+
BaseColumnName = column.PhysicalName;
21+
BaseSchemaName = column.SchemaName;
22+
BaseTableName = column.PhysicalTable;
23+
ColumnName = column.Name;
24+
ColumnOrdinal = ordinal;
25+
ColumnSize = columnSize > int.MaxValue ? int.MaxValue : unchecked((int) columnSize);
26+
DataType = type;
27+
DataTypeName = dataTypeName;
28+
IsAliased = column.PhysicalName != column.Name;
29+
IsAutoIncrement = (column.ColumnFlags & ColumnFlags.AutoIncrement) != 0;
30+
IsExpression = false;
31+
IsHidden = false;
32+
IsKey = (column.ColumnFlags & ColumnFlags.PrimaryKey) != 0;
33+
IsLong = column.ColumnLength > 255 &&
34+
((column.ColumnFlags & ColumnFlags.Blob) != 0 || column.ColumnType == ColumnType.TinyBlob || column.ColumnType == ColumnType.Blob || column.ColumnType == ColumnType.MediumBlob || column.ColumnType == ColumnType.LongBlob);
35+
IsReadOnly = false;
36+
IsUnique = (column.ColumnFlags & ColumnFlags.UniqueKey) != 0;
37+
if (column.ColumnType == ColumnType.Decimal || column.ColumnType == ColumnType.NewDecimal)
38+
NumericPrecision = (int) (column.ColumnLength - 2 + ((column.ColumnFlags & ColumnFlags.Unsigned) != 0 ? 1 : 0));
39+
NumericScale = column.Decimals;
40+
ProviderType = (int) column.ColumnType;
41+
}
42+
43+
public int ProviderType { get; }
44+
45+
#if !NETSTANDARD1_3 && !NETSTANDARD2_0
46+
public bool? AllowDBNull { get; }
47+
public string BaseCatalogName { get; }
48+
public string BaseColumnName { get; }
49+
public string BaseSchemaName { get; }
50+
public string BaseTableName { get; }
51+
public string ColumnName { get; }
52+
public int? ColumnOrdinal { get; }
53+
public int? ColumnSize { get; }
54+
public Type DataType { get; }
55+
public string DataTypeName { get; }
56+
public bool? IsAliased { get; }
57+
public bool? IsAutoIncrement { get; }
58+
public bool? IsExpression { get; }
59+
public bool? IsHidden { get; }
60+
public bool? IsIdentity { get; }
61+
public bool? IsKey { get; }
62+
public bool? IsLong { get; }
63+
public bool? IsReadOnly { get; }
64+
public bool? IsUnique { get; }
65+
public int? NumericPrecision { get; }
66+
public int? NumericScale { get; }
67+
public string UdtAssemblyQualifiedName { get; }
68+
#endif
69+
}
70+
}

tests/SideBySide/DataTypes.cs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,99 @@ public void GetSchemaTable(string column, string table, int columnSize, Type dat
703703
}
704704
#endif
705705

706+
#if !BASELINE
707+
[Theory]
708+
[InlineData("Bit1", "datatypes_bits", "BIT", 1, typeof(ulong), "N", -1, 0)]
709+
[InlineData("Bit32", "datatypes_bits", "BIT", 32, typeof(ulong), "N", -1, 0)]
710+
[InlineData("Bit64", "datatypes_bits", "BIT", 64, typeof(ulong), "N", -1, 0)]
711+
[InlineData("Binary", "datatypes_blobs", "BLOB", 100, typeof(byte[]), "N", -1, 0)]
712+
[InlineData("VarBinary", "datatypes_blobs", "BLOB", 100, typeof(byte[]), "N", -1, 0)]
713+
[InlineData("TinyBlob", "datatypes_blobs", "BLOB", 255, typeof(byte[]), "N", -1, 0)]
714+
[InlineData("Blob", "datatypes_blobs", "BLOB", 65535, typeof(byte[]), "LN", -1, 0)]
715+
[InlineData("MediumBlob", "datatypes_blobs", "BLOB", 16777215, typeof(byte[]), "LN", -1, 0)]
716+
[InlineData("LongBlob", "datatypes_blobs", "BLOB", int.MaxValue, typeof(byte[]), "LN", -1, 0)]
717+
[InlineData("guidbin", "datatypes_blobs", "BLOB", 16, typeof(byte[]), "N", -1, 0)]
718+
[InlineData("rowid", "datatypes_bools", "INT", 11, typeof(int), "AK", -1, 0)]
719+
[InlineData("Boolean", "datatypes_bools", "BOOL", 1, typeof(bool), "N", -1, 0)]
720+
[InlineData("TinyInt1", "datatypes_bools", "BOOL", 1, typeof(bool), "N", -1, 0)]
721+
[InlineData("size", "datatypes_enums", "ENUM", 7, typeof(string), "N", -1, 0)]
722+
[InlineData("color", "datatypes_enums", "ENUM", 6, typeof(string), "", -1, 0)]
723+
[InlineData("char38", "datatypes_guids", "CHAR(38)", 38, typeof(string), "N", -1, 0)]
724+
[InlineData("char38bin", "datatypes_guids", "CHAR(38)", 38, typeof(string), "N", -1, 0)]
725+
[InlineData("text", "datatypes_guids", "VARCHAR", 65535, typeof(string), "LN", -1, 0)]
726+
[InlineData("blob", "datatypes_guids", "BLOB", 65535, typeof(byte[]), "LN", -1, 0)]
727+
[InlineData("SByte", "datatypes_integers", "TINYINT", 4, typeof(sbyte), "N", -1, 0)]
728+
[InlineData("Byte", "datatypes_integers", "TINYINT", 3, typeof(byte), "N", -1, 0)]
729+
[InlineData("Int16", "datatypes_integers", "SMALLINT", 6, typeof(short), "N", -1, 0)]
730+
[InlineData("UInt16", "datatypes_integers", "SMALLINT", 5, typeof(ushort), "N", -1, 0)]
731+
[InlineData("Int24", "datatypes_integers", "MEDIUMINT", 9, typeof(int), "N", -1, 0)]
732+
[InlineData("UInt24", "datatypes_integers", "MEDIUMINT", 8, typeof(uint), "N", -1, 0)]
733+
[InlineData("Int32", "datatypes_integers", "INT", 11, typeof(int), "N", -1, 0)]
734+
[InlineData("UInt32", "datatypes_integers", "INT", 10, typeof(uint), "N", -1, 0)]
735+
[InlineData("Int64", "datatypes_integers", "BIGINT", 20, typeof(long), "N", -1, 0)]
736+
[InlineData("UInt64", "datatypes_integers", "BIGINT", 20, typeof(ulong), "N", -1, 0)]
737+
[InlineData("value", "datatypes_json_core", "JSON", int.MaxValue, typeof(string), "LN", -1, 0)]
738+
[InlineData("Single", "datatypes_reals", "FLOAT", 12, typeof(float), "N", -1, 31)]
739+
[InlineData("Double", "datatypes_reals", "DOUBLE", 22, typeof(double), "N", -1, 31)]
740+
[InlineData("SmallDecimal", "datatypes_reals", "DECIMAL", 7, typeof(decimal), "N", 5, 2)]
741+
[InlineData("MediumDecimal", "datatypes_reals", "DECIMAL", 30, typeof(decimal), "N", 28, 8)]
742+
[InlineData("BigDecimal", "datatypes_reals", "DECIMAL", 52, typeof(decimal), "N", 50, 30)]
743+
[InlineData("value", "datatypes_set", "SET", 12, typeof(string), "N", -1, 0)]
744+
[InlineData("utf8", "datatypes_strings", "VARCHAR", 300, typeof(string), "N", -1, 0)]
745+
[InlineData("utf8bin", "datatypes_strings", "VARCHAR", 300, typeof(string), "N", -1, 0)]
746+
[InlineData("latin1", "datatypes_strings", "VARCHAR", 300, typeof(string), "N", -1, 0)]
747+
[InlineData("latin1bin", "datatypes_strings", "VARCHAR", 300, typeof(string), "N", -1, 0)]
748+
[InlineData("cp1251", "datatypes_strings", "VARCHAR", 300, typeof(string), "N", -1, 0)]
749+
[InlineData("guid", "datatypes_strings", "CHAR(36)", 36, typeof(Guid), "N", -1, 0)]
750+
[InlineData("guidbin", "datatypes_strings", "CHAR(36)", 36, typeof(Guid), "N", -1, 0)]
751+
[InlineData("Date", "datatypes_times", "DATE", 10, typeof(DateTime), "N", -1, 0)]
752+
[InlineData("DateTime", "datatypes_times", "DATETIME", 26, typeof(DateTime), "N", -1, 6)]
753+
[InlineData("Timestamp", "datatypes_times", "TIMESTAMP", 26, typeof(DateTime), "N", -1, 6)]
754+
[InlineData("Time", "datatypes_times", "TIME", 17, typeof(TimeSpan), "N", -1, 6)]
755+
[InlineData("Year", "datatypes_times", "YEAR", 4, typeof(int), "N", -1, 0)]
756+
public void GetColumnSchema(string column, string table, string dataTypeName, int columnSize, Type dataType, string flags, int precision, int scale)
757+
{
758+
if (table == "datatypes_json_core" && !AppConfig.SupportsJson)
759+
return;
760+
761+
var isAutoIncrement = flags.IndexOf('A') != -1;
762+
var isKey = flags.IndexOf('K') != -1;
763+
var isLong = flags.IndexOf('L') != -1;
764+
var allowDbNull = flags.IndexOf('N') != -1;
765+
var realPrecision = precision == -1 ? default(int?) : precision;
766+
767+
using (var command = m_database.Connection.CreateCommand())
768+
{
769+
command.CommandText = $"select `{column}` from `{table}`;";
770+
using (var reader = command.ExecuteReader())
771+
{
772+
var columns = reader.GetColumnSchema();
773+
Assert.Single(columns);
774+
var schema = columns[0];
775+
Assert.Equal(allowDbNull, schema.AllowDBNull);
776+
Assert.Equal(column, schema.BaseColumnName);
777+
Assert.Equal(m_database.Connection.Database, schema.BaseSchemaName);
778+
Assert.Equal(table, schema.BaseTableName);
779+
Assert.Equal(column, schema.ColumnName);
780+
Assert.Equal(0, schema.ColumnOrdinal);
781+
Assert.Equal(dataType, schema.DataType);
782+
Assert.Equal(dataTypeName, schema.DataTypeName);
783+
Assert.Equal(columnSize, schema.ColumnSize);
784+
Assert.False(schema.IsAliased.Value);
785+
Assert.Equal(isAutoIncrement, schema.IsAutoIncrement);
786+
Assert.False(schema.IsExpression.Value);
787+
Assert.False(schema.IsHidden.Value);
788+
Assert.Equal(isKey, schema.IsKey);
789+
Assert.Equal(isLong, schema.IsLong);
790+
Assert.False(schema.IsReadOnly.Value);
791+
Assert.False(schema.IsUnique.Value);
792+
Assert.Equal(realPrecision, schema.NumericPrecision);
793+
Assert.Equal(scale, schema.NumericScale);
794+
}
795+
}
796+
}
797+
#endif
798+
706799
private static byte[] CreateByteArray(int size)
707800
{
708801
var data = new byte[size];

0 commit comments

Comments
 (0)