Skip to content

Commit 312e562

Browse files
committed
Expand "DataTypes" schema.
Define all the columns from the MSDN documentation; some are still filled with default values. Backwards-compatibility is checked by: https://github.com/bgrainger/MySqlSchemaChecker Results are: https://github.com/mysql-net/MySqlConnector/wiki/DataTypes-Schema-Collection
1 parent 5ad78a5 commit 312e562

File tree

4 files changed

+108
-66
lines changed

4 files changed

+108
-66
lines changed

src/MySqlConnector/MySqlClient/SchemaProvider.cs

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Data;
5-
using System.Linq;
5+
using MySql.Data.MySqlClient.Types;
66

77
namespace MySql.Data.MySqlClient
88
{
@@ -45,32 +45,85 @@ private void FillMetadataCollections(DataTable dataTable)
4545

4646
private void FillDataTypes(DataTable dataTable)
4747
{
48-
dataTable.Columns.AddRange(new[] { // The names come from DbMetaDataColumnNames
49-
new DataColumn("DataType", typeof(string)),
48+
dataTable.Columns.AddRange(new[]
49+
{
5050
new DataColumn("TypeName", typeof(string)),
5151
new DataColumn("ProviderDbType", typeof(int)),
52-
new DataColumn("IsUnsigned", typeof(bool))
52+
new DataColumn("ColumnSize", typeof(long)),
53+
new DataColumn("CreateFormat", typeof(string)),
54+
new DataColumn("CreateParameters", typeof(string)),
55+
new DataColumn("DataType", typeof(string)),
56+
new DataColumn("IsAutoIncrementable", typeof(bool)),
57+
new DataColumn("IsBestMatch", typeof(bool)),
58+
new DataColumn("IsCaseSensitive", typeof(bool)),
59+
new DataColumn("IsFixedLength", typeof(bool)),
60+
new DataColumn("IsFixedPrecisionScale", typeof(bool)),
61+
new DataColumn("IsLong", typeof(bool)),
62+
new DataColumn("IsNullable", typeof(bool)),
63+
new DataColumn("IsSearchable", typeof(bool)),
64+
new DataColumn("IsSearchableWithLike", typeof(bool)),
65+
new DataColumn("IsUnsigned", typeof(bool)),
66+
new DataColumn("MaximumScale", typeof(short)),
67+
new DataColumn("MinimumScale", typeof(short)),
68+
new DataColumn("IsConcurrencyType", typeof(bool)),
69+
new DataColumn("IsLiteralSupported", typeof(bool)),
70+
new DataColumn("LiteralPrefix", typeof(string)),
71+
new DataColumn("LiteralSuffix", typeof(string)),
72+
new DataColumn("NativeDataType", typeof(string)),
5373
});
5474

55-
// Column type mappings:
56-
var colTypes = Types.TypeMapper.Instance.GetColumnMappings();
57-
foreach (var map in colTypes)
75+
var clrTypes = new HashSet<string>();
76+
foreach (var columnType in TypeMapper.Instance.GetColumnTypeMetadata())
5877
{
59-
var dbTypeMap = map.DbTypeMapping;
60-
var dbType = dbTypeMap.DbTypes.FirstOrDefault();
61-
dataTable.Rows.Add(dbTypeMap.ClrType.FullName, map.DataTypeName, (int)dbType, map.Unsigned);
62-
}
78+
// hard-code a few types to not appear in the schema table
79+
var mySqlDbType = columnType.MySqlDbType;
80+
if (mySqlDbType == MySqlDbType.Decimal || mySqlDbType == MySqlDbType.Newdate || mySqlDbType == MySqlDbType.Null || mySqlDbType == MySqlDbType.VarString)
81+
continue;
82+
if (mySqlDbType == MySqlDbType.Bool && columnType.IsUnsigned)
83+
continue;
6384

64-
// Data type mappings:
65-
foreach (MySqlDbType mapItem in Enum.GetValues(typeof(MySqlDbType)))
66-
{
67-
var typeName = Enum.GetName(typeof(MySqlDbType), mapItem);
68-
var dbType = Types.TypeMapper.Instance.GetDbTypeForMySqlDbType(mapItem);
69-
var map = Types.TypeMapper.Instance.GetDbTypeMapping(dbType);
70-
if (map != null) // MySqlDbType.Set is not supported by the mapper.
71-
{
72-
dataTable.Rows.Add(map.ClrType.FullName, Enum.GetName(typeof(MySqlDbType), mapItem).ToLower(), (int)dbType, typeName.Contains("UInt") || typeName.Contains("UByte"));
73-
}
85+
// set miscellaneous properties in code (rather than being data-driven)
86+
var clrType = columnType.DbTypeMapping.ClrType;
87+
var clrTypeName = clrType.ToString();
88+
var dataTypeName = mySqlDbType == MySqlDbType.Guid ? "GUID" :
89+
mySqlDbType == MySqlDbType.Bool ? "BOOL" : columnType.DataTypeName;
90+
var isAutoIncrementable = mySqlDbType == MySqlDbType.Byte || mySqlDbType == MySqlDbType.Int16 || mySqlDbType == MySqlDbType.Int24 || mySqlDbType == MySqlDbType.Int32 || mySqlDbType == MySqlDbType.Int64 ||
91+
mySqlDbType == MySqlDbType.UByte || mySqlDbType == MySqlDbType.UInt16 || mySqlDbType == MySqlDbType.UInt24 || mySqlDbType == MySqlDbType.UInt32 || mySqlDbType == MySqlDbType.UInt64;
92+
var isBestMatch = clrTypes.Add(clrTypeName);
93+
var isFixedLength = isAutoIncrementable ||
94+
mySqlDbType == MySqlDbType.Date || mySqlDbType == MySqlDbType.DateTime || mySqlDbType == MySqlDbType.Time || mySqlDbType == MySqlDbType.Timestamp ||
95+
mySqlDbType == MySqlDbType.Double || mySqlDbType == MySqlDbType.Float || mySqlDbType == MySqlDbType.Year || mySqlDbType == MySqlDbType.Guid || mySqlDbType == MySqlDbType.Bool;
96+
var isFixedPrecisionScale = isFixedLength ||
97+
mySqlDbType == MySqlDbType.Bit || mySqlDbType == MySqlDbType.NewDecimal;
98+
var isLong = mySqlDbType == MySqlDbType.Blob || mySqlDbType == MySqlDbType.MediumBlob || mySqlDbType == MySqlDbType.LongBlob;
99+
100+
// map ColumnTypeMetadata to the row for this data type
101+
var createFormatParts = columnType.CreateFormat.Split(';');
102+
dataTable.Rows.Add(
103+
dataTypeName,
104+
(int) mySqlDbType,
105+
columnType.ColumnSize,
106+
createFormatParts[0],
107+
createFormatParts.Length == 1 ? null : createFormatParts[1],
108+
clrTypeName,
109+
isAutoIncrementable,
110+
isBestMatch,
111+
false,
112+
isFixedLength,
113+
isFixedPrecisionScale,
114+
isLong,
115+
true,
116+
clrType != typeof(byte[]),
117+
clrType == typeof(string),
118+
columnType.IsUnsigned,
119+
DBNull.Value,
120+
DBNull.Value,
121+
DBNull.Value,
122+
true,
123+
DBNull.Value,
124+
DBNull.Value,
125+
null
126+
);
74127
}
75128
}
76129

src/MySqlConnector/MySqlClient/Types/ColumnTypeMetadata.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@ internal sealed class ColumnTypeMetadata
44
{
55
public static string CreateLookupKey(string columnTypeName, bool isUnsigned, int length) => $"{columnTypeName}|{(isUnsigned ? "u" : "s")}|{length}";
66

7-
public ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool unsigned = false, bool binary = false, int length = 0, string simpleDataTypeName = null)
7+
public ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string simpleDataTypeName = null, string createFormat = null, long columnSize = 0)
88
{
99
DataTypeName = dataTypeName;
1010
SimpleDataTypeName = simpleDataTypeName ?? dataTypeName;
11+
CreateFormat = createFormat ?? (dataTypeName + (isUnsigned ? " UNSIGNED" : ""));
1112
DbTypeMapping = dbTypeMapping;
1213
MySqlDbType = mySqlDbType;
13-
Unsigned = unsigned;
14+
ColumnSize = columnSize;
15+
IsUnsigned = isUnsigned;
1416
Binary = binary;
1517
Length = length;
1618
}
1719

1820
public string DataTypeName { get; }
1921
public string SimpleDataTypeName { get; }
22+
public string CreateFormat { get; }
2023
public DbTypeMapping DbTypeMapping { get; }
2124
public MySqlDbType MySqlDbType { get; }
2225
public bool Binary { get; }
23-
public bool Unsigned { get; }
26+
public long ColumnSize { get; }
27+
public bool IsUnsigned { get; }
2428
public int Length { get; }
2529

26-
public string CreateLookupKey() => CreateLookupKey(DataTypeName, Unsigned, Length);
30+
public string CreateLookupKey() => CreateLookupKey(DataTypeName, IsUnsigned, Length);
2731
}
2832
}

src/MySqlConnector/MySqlClient/Types/TypeMapper.cs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ private TypeMapper()
2020

2121
// boolean
2222
var typeBoolean = AddDbTypeMapping(new DbTypeMapping(typeof(bool), new[] { DbType.Boolean }, convert: o => Convert.ToBoolean(o)));
23-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeBoolean, MySqlDbType.Bool, unsigned: false, length: 1, simpleDataTypeName: "BOOL"));
24-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeBoolean, MySqlDbType.Bool, unsigned: true, length: 1));
23+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: false, length: 1, columnSize: 1, simpleDataTypeName: "BOOL", createFormat: "BOOL"));
24+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: true, length: 1));
2525

2626
// integers
2727
var typeSbyte = AddDbTypeMapping(new DbTypeMapping(typeof(sbyte), new[] { DbType.SByte }, convert: o => Convert.ToSByte(o)));
@@ -32,49 +32,49 @@ private TypeMapper()
3232
var typeUint = AddDbTypeMapping(new DbTypeMapping(typeof(uint), new[] { DbType.UInt32 }, convert: o => Convert.ToUInt32(o)));
3333
var typeLong = AddDbTypeMapping(new DbTypeMapping(typeof(long), new[] { DbType.Int64 }, convert: o => Convert.ToInt64(o)));
3434
var typeUlong = AddDbTypeMapping(new DbTypeMapping(typeof(ulong), new[] { DbType.UInt64 }, convert: o => Convert.ToUInt64(o)));
35-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeSbyte, MySqlDbType.Byte, unsigned: false));
36-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeByte, MySqlDbType.UByte, unsigned: true));
37-
AddColumnTypeMetadata(new ColumnTypeMetadata("SMALLINT", typeShort, MySqlDbType.Int16, unsigned: false));
38-
AddColumnTypeMetadata(new ColumnTypeMetadata("SMALLINT", typeUshort, MySqlDbType.UInt16, unsigned: true));
39-
AddColumnTypeMetadata(new ColumnTypeMetadata("INT", typeInt, MySqlDbType.Int32, unsigned: false));
40-
AddColumnTypeMetadata(new ColumnTypeMetadata("INT", typeUint, MySqlDbType.UInt32, unsigned: true));
41-
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMINT", typeInt, MySqlDbType.Int24, unsigned: false));
42-
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMINT", typeUint, MySqlDbType.UInt24, unsigned: true));
43-
AddColumnTypeMetadata(new ColumnTypeMetadata("BIGINT", typeLong, MySqlDbType.Int64, unsigned: false));
44-
AddColumnTypeMetadata(new ColumnTypeMetadata("BIGINT", typeUlong, MySqlDbType.UInt64, unsigned: true));
35+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeSbyte, MySqlDbType.Byte, isUnsigned: false));
36+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true));
37+
AddColumnTypeMetadata(new ColumnTypeMetadata("SMALLINT", typeShort, MySqlDbType.Int16, isUnsigned: false));
38+
AddColumnTypeMetadata(new ColumnTypeMetadata("SMALLINT", typeUshort, MySqlDbType.UInt16, isUnsigned: true));
39+
AddColumnTypeMetadata(new ColumnTypeMetadata("INT", typeInt, MySqlDbType.Int32, isUnsigned: false));
40+
AddColumnTypeMetadata(new ColumnTypeMetadata("INT", typeUint, MySqlDbType.UInt32, isUnsigned: true));
41+
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMINT", typeInt, MySqlDbType.Int24, isUnsigned: false));
42+
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMINT", typeUint, MySqlDbType.UInt24, isUnsigned: true));
43+
AddColumnTypeMetadata(new ColumnTypeMetadata("BIGINT", typeLong, MySqlDbType.Int64, isUnsigned: false));
44+
AddColumnTypeMetadata(new ColumnTypeMetadata("BIGINT", typeUlong, MySqlDbType.UInt64, isUnsigned: true));
4545
AddColumnTypeMetadata(new ColumnTypeMetadata("BIT", typeUlong, MySqlDbType.Bit));
4646

4747
// decimals
4848
var typeDecimal = AddDbTypeMapping(new DbTypeMapping(typeof(decimal), new[] { DbType.Decimal, DbType.Currency, DbType.VarNumeric }, convert: o => Convert.ToDecimal(o)));
4949
var typeDouble = AddDbTypeMapping(new DbTypeMapping(typeof(double), new[] { DbType.Double }, convert: o => Convert.ToDouble(o)));
5050
var typeFloat = AddDbTypeMapping(new DbTypeMapping(typeof(float), new[] { DbType.Single }, convert: o => Convert.ToSingle(o)));
51-
AddColumnTypeMetadata(new ColumnTypeMetadata("DECIMAL", typeDecimal, MySqlDbType.NewDecimal));
51+
AddColumnTypeMetadata(new ColumnTypeMetadata("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, createFormat: "DECIMAL({0},{1});precision,scale"));
5252
AddColumnTypeMetadata(new ColumnTypeMetadata("DECIMAL", typeDecimal, MySqlDbType.Decimal));
5353
AddColumnTypeMetadata(new ColumnTypeMetadata("DOUBLE", typeDouble, MySqlDbType.Double));
5454
AddColumnTypeMetadata(new ColumnTypeMetadata("FLOAT", typeFloat, MySqlDbType.Float));
5555

5656
// string
5757
var typeFixedString = AddDbTypeMapping(new DbTypeMapping(typeof(string), new[] { DbType.StringFixedLength, DbType.AnsiStringFixedLength }, convert: Convert.ToString));
5858
var typeString = AddDbTypeMapping(new DbTypeMapping(typeof(string), new[] { DbType.String, DbType.AnsiString, DbType.Xml }, convert: Convert.ToString));
59-
AddColumnTypeMetadata(new ColumnTypeMetadata("CHAR", typeFixedString, MySqlDbType.String));
60-
AddColumnTypeMetadata(new ColumnTypeMetadata("VARCHAR", typeString, MySqlDbType.VarChar));
59+
AddColumnTypeMetadata(new ColumnTypeMetadata("VARCHAR", typeString, MySqlDbType.VarChar, createFormat: "VARCHAR({0});size"));
6160
AddColumnTypeMetadata(new ColumnTypeMetadata("VARCHAR", typeString, MySqlDbType.VarString));
62-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYTEXT", typeString, MySqlDbType.TinyText, simpleDataTypeName: "VARCHAR"));
63-
AddColumnTypeMetadata(new ColumnTypeMetadata("TEXT", typeString, MySqlDbType.Text, simpleDataTypeName: "VARCHAR"));
64-
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMTEXT", typeString, MySqlDbType.MediumText, simpleDataTypeName: "VARCHAR"));
65-
AddColumnTypeMetadata(new ColumnTypeMetadata("LONGTEXT", typeString, MySqlDbType.LongText, simpleDataTypeName: "VARCHAR"));
61+
AddColumnTypeMetadata(new ColumnTypeMetadata("CHAR", typeFixedString, MySqlDbType.String, createFormat: "CHAR({0});size"));
62+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYTEXT", typeString, MySqlDbType.TinyText, columnSize: byte.MaxValue, simpleDataTypeName: "VARCHAR"));
63+
AddColumnTypeMetadata(new ColumnTypeMetadata("TEXT", typeString, MySqlDbType.Text, columnSize: ushort.MaxValue, simpleDataTypeName: "VARCHAR"));
64+
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMTEXT", typeString, MySqlDbType.MediumText, columnSize: 16777215, simpleDataTypeName: "VARCHAR"));
65+
AddColumnTypeMetadata(new ColumnTypeMetadata("LONGTEXT", typeString, MySqlDbType.LongText, columnSize: uint.MaxValue, simpleDataTypeName: "VARCHAR"));
6666
AddColumnTypeMetadata(new ColumnTypeMetadata("ENUM", typeString, MySqlDbType.Enum));
6767
AddColumnTypeMetadata(new ColumnTypeMetadata("SET", typeString, MySqlDbType.Set));
6868
AddColumnTypeMetadata(new ColumnTypeMetadata("JSON", typeString, MySqlDbType.JSON));
6969

7070
// binary
7171
var typeBinary = AddDbTypeMapping(new DbTypeMapping(typeof(byte[]), new[] { DbType.Binary }));
72-
AddColumnTypeMetadata(new ColumnTypeMetadata("BLOB", typeBinary, MySqlDbType.Blob, binary: true, simpleDataTypeName: "BLOB"));
73-
AddColumnTypeMetadata(new ColumnTypeMetadata("BINARY", typeBinary, MySqlDbType.Binary, binary: true, simpleDataTypeName: "BLOB"));
74-
AddColumnTypeMetadata(new ColumnTypeMetadata("VARBINARY", typeBinary, MySqlDbType.VarBinary, binary: true, simpleDataTypeName: "BLOB"));
75-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYBLOB", typeBinary, MySqlDbType.TinyBlob, binary: true, simpleDataTypeName: "BLOB"));
76-
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMBLOB", typeBinary, MySqlDbType.MediumBlob, binary: true, simpleDataTypeName: "BLOB"));
77-
AddColumnTypeMetadata(new ColumnTypeMetadata("LONGBLOB", typeBinary, MySqlDbType.LongBlob, binary: true, simpleDataTypeName: "BLOB"));
72+
AddColumnTypeMetadata(new ColumnTypeMetadata("BLOB", typeBinary, MySqlDbType.Blob, binary: true, columnSize: ushort.MaxValue, simpleDataTypeName: "BLOB"));
73+
AddColumnTypeMetadata(new ColumnTypeMetadata("BINARY", typeBinary, MySqlDbType.Binary, binary: true, simpleDataTypeName: "BLOB", createFormat: "BINARY({0});length"));
74+
AddColumnTypeMetadata(new ColumnTypeMetadata("VARBINARY", typeBinary, MySqlDbType.VarBinary, binary: true, simpleDataTypeName: "BLOB", createFormat: "VARBINARY({0});length"));
75+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYBLOB", typeBinary, MySqlDbType.TinyBlob, binary: true, columnSize: byte.MaxValue, simpleDataTypeName: "BLOB"));
76+
AddColumnTypeMetadata(new ColumnTypeMetadata("MEDIUMBLOB", typeBinary, MySqlDbType.MediumBlob, binary: true, columnSize: 16777215, simpleDataTypeName: "BLOB"));
77+
AddColumnTypeMetadata(new ColumnTypeMetadata("LONGBLOB", typeBinary, MySqlDbType.LongBlob, binary: true, columnSize: uint.MaxValue, simpleDataTypeName: "BLOB"));
7878

7979
// spatial
8080
AddColumnTypeMetadata(new ColumnTypeMetadata("GEOMETRY", typeBinary, MySqlDbType.Geometry, binary: true));
@@ -99,7 +99,7 @@ private TypeMapper()
9999

100100
// guid
101101
var typeGuid = AddDbTypeMapping(new DbTypeMapping(typeof(Guid), new[] { DbType.Guid }, convert: o => Guid.Parse(Convert.ToString(o))));
102-
AddColumnTypeMetadata(new ColumnTypeMetadata("CHAR", typeGuid, MySqlDbType.Guid, length: 36, simpleDataTypeName: "CHAR(36)"));
102+
AddColumnTypeMetadata(new ColumnTypeMetadata("CHAR", typeGuid, MySqlDbType.Guid, length: 36, simpleDataTypeName: "CHAR(36)", createFormat: "CHAR(36)"));
103103

104104
// null
105105
var typeNull = AddDbTypeMapping(new DbTypeMapping(typeof(object), new[] { DbType.Object }, convert: o => null));

tests/MySqlConnector.Tests/ConnectionTests.cs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,6 @@ public void AuthPluginNameNotNullTerminated()
129129
}
130130
}
131131

132-
[Fact]
133-
public void GetSchemaHasDataTypesCollection()
134-
{
135-
using (var connection = new MySqlConnection(m_csb.ConnectionString))
136-
{
137-
var dataTypes = connection.GetSchema("DataTypes");
138-
Assert.Equal("DataType", dataTypes.Columns[0].ColumnName);
139-
Assert.True(dataTypes.Rows != null);
140-
Assert.True(dataTypes.Rows.Count > 15);
141-
var row1 = String.Join(",", dataTypes.Rows[0].ItemArray);
142-
Assert.Equal("System.Boolean,TINYINT,3,False", row1);
143-
}
144-
}
145-
146-
147132
private static async Task WaitForConditionAsync<T>(T expected, Func<T> getValue)
148133
{
149134
var sw = Stopwatch.StartNew();

0 commit comments

Comments
 (0)