Skip to content

Commit 782a765

Browse files
committed
Test data type of OUT parameters. Fixes #682
1 parent 060f94c commit 782a765

File tree

5 files changed

+117
-6
lines changed

5 files changed

+117
-6
lines changed

src/MySqlConnector/Core/CachedProcedure.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ internal sealed class CachedProcedure
7575
cmd.CommandText = @"SELECT COUNT(*)
7676
FROM information_schema.routines
7777
WHERE ROUTINE_SCHEMA = @schema AND ROUTINE_NAME = @component;
78-
SELECT ORDINAL_POSITION, PARAMETER_MODE, PARAMETER_NAME, DATA_TYPE, DTD_IDENTIFIER
78+
SELECT ORDINAL_POSITION, PARAMETER_MODE, PARAMETER_NAME, DTD_IDENTIFIER
7979
FROM information_schema.parameters
8080
WHERE SPECIFIC_SCHEMA = @schema AND SPECIFIC_NAME = @component
8181
ORDER BY ORDINAL_POSITION";
@@ -90,13 +90,14 @@ FROM information_schema.parameters
9090

9191
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
9292
{
93+
var dataType = ParseDataType(reader.GetString(3), out var unsigned, out var length);
9394
parameters.Add(new CachedParameter(
9495
reader.GetInt32(0),
9596
!reader.IsDBNull(1) ? reader.GetString(1) : null,
9697
!reader.IsDBNull(2) ? reader.GetString(2) : null,
97-
reader.GetString(3),
98-
reader.GetString(4).IndexOf("unsigned", StringComparison.OrdinalIgnoreCase) != -1,
99-
0
98+
dataType,
99+
unsigned,
100+
length
100101
));
101102
}
102103
}

src/MySqlConnector/Core/TypeMapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ private TypeMapper()
2626
// boolean
2727
var typeBoolean = AddDbTypeMapping(new DbTypeMapping(typeof(bool), new[] { DbType.Boolean }, convert: o => Convert.ToBoolean(o)));
2828
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: false, length: 1, columnSize: 1, simpleDataTypeName: "BOOL", createFormat: "BOOL"));
29-
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: true, length: 1));
3029

3130
// integers
3231
var typeSbyte = AddDbTypeMapping(new DbTypeMapping(typeof(sbyte), new[] { DbType.SByte }, convert: o => Convert.ToSByte(o)));
@@ -38,6 +37,7 @@ private TypeMapper()
3837
var typeLong = AddDbTypeMapping(new DbTypeMapping(typeof(long), new[] { DbType.Int64 }, convert: o => Convert.ToInt64(o)));
3938
var typeUlong = AddDbTypeMapping(new DbTypeMapping(typeof(ulong), new[] { DbType.UInt64 }, convert: o => Convert.ToUInt64(o)));
4039
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeSbyte, MySqlDbType.Byte, isUnsigned: false));
40+
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true, length: 1));
4141
AddColumnTypeMetadata(new ColumnTypeMetadata("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true));
4242
AddColumnTypeMetadata(new ColumnTypeMetadata("SMALLINT", typeShort, MySqlDbType.Int16, isUnsigned: false));
4343
AddColumnTypeMetadata(new ColumnTypeMetadata("SMALLINT", typeUshort, MySqlDbType.UInt16, isUnsigned: true));

tests/MySqlConnector.Tests/CachedProcedureTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,5 +172,28 @@ param4 INTEGER(3)
172172
},
173173
};
174174
}
175+
176+
[Theory]
177+
[InlineData("INT", "INT", false, 0)]
178+
[InlineData("INTEGER", "INT", false, 0)]
179+
[InlineData("INT(11)", "INT", false, 11)]
180+
[InlineData("INTEGER(11)", "INT", false, 11)]
181+
[InlineData("INT(11) UNSIGNED", "INT", true, 11)]
182+
[InlineData("INT(11) ZEROFILL", "INT", false, 11)]
183+
[InlineData("INT(11) UNSIGNED ZEROFILL", "INT", true, 11)]
184+
[InlineData("BIGINT(20)", "BIGINT", false, 20)]
185+
[InlineData("TINYINT(1) UNSIGNED", "TINYINT", true, 1)]
186+
[InlineData("BOOL", "TINYINT", false, 1)]
187+
[InlineData("NUMERIC(30,20)", "DECIMAL", false, 30)]
188+
[InlineData("VARCHAR(300)", "VARCHAR", false, 300)]
189+
[InlineData("VARCHAR(300) CHARSET utf8mb4", "VARCHAR", false, 300)]
190+
[InlineData("VARCHAR(300) COLLATE ascii_general_ci", "VARCHAR", false, 300)]
191+
[InlineData("BINARY(16)", "BINARY", false, 16)]
192+
[InlineData("CHAR(36)", "CHAR", false, 36)]
193+
public void ParseDataType(string sql, string expectedDataType, bool expectedUnsigned, int expectedLength)
194+
{
195+
var dataType = CachedProcedure.ParseDataType(sql, out var unsigned, out var length);
196+
Assert.Equal((expectedDataType, expectedUnsigned, expectedLength), (dataType, unsigned, length));
197+
}
175198
}
176199
}

tests/MySqlConnector.Tests/TypeMapperTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void ConversionTest(object original, DbType dbType, object expected)
5757
[Theory]
5858
[InlineData("bit", false, 0, DbType.UInt64)]
5959
[InlineData("tinyint", false, 1, DbType.Boolean)]
60-
[InlineData("tinyint", true, 1, DbType.Boolean)]
60+
[InlineData("tinyint", true, 1, DbType.Byte)]
6161
[InlineData("tinyint", false, 0, DbType.SByte)]
6262
[InlineData("tinyint", true, 0, DbType.Byte)]
6363
[InlineData("smallint", false, 0, DbType.Int16)]

tests/SideBySide/DataTypes.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Data;
23
using System.Data.Common;
34
using System.Globalization;
45
using System.IO;
@@ -1329,6 +1330,92 @@ public void GetColumnSchema(string column, string table, MySqlDbType mySqlDbType
13291330
}
13301331
#endif
13311332

1333+
[Theory]
1334+
[InlineData("Bit1", "datatypes_bits", MySqlDbType.Bit, "BIT", typeof(ulong), 3, 1ul)]
1335+
[InlineData("Bit32", "datatypes_bits", MySqlDbType.Bit, "BIT", typeof(ulong), 3, 1ul)]
1336+
[InlineData("Bit64", "datatypes_bits", MySqlDbType.Bit, "BIT", typeof(ulong), 3, 1ul)]
1337+
[InlineData("Binary", "datatypes_blobs", MySqlDbType.Binary, "BINARY(100)", typeof(byte[]), 2, null)]
1338+
[InlineData("VarBinary", "datatypes_blobs", MySqlDbType.VarBinary, "VARBINARY(100)", typeof(byte[]), 2, new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF })]
1339+
[InlineData("TinyBlob", "datatypes_blobs", MySqlDbType.TinyBlob, "TINYBLOB", typeof(byte[]), 2, new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF })]
1340+
[InlineData("Blob", "datatypes_blobs", MySqlDbType.Blob, "BLOB", typeof(byte[]), 2, new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF })]
1341+
[InlineData("MediumBlob", "datatypes_blobs", MySqlDbType.MediumBlob, "MEDIUMBLOB", typeof(byte[]), 2, new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF })]
1342+
[InlineData("LongBlob", "datatypes_blobs", MySqlDbType.LongBlob, "LONGBLOB", typeof(byte[]), 2, new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF })]
1343+
[InlineData("guidbin", "datatypes_blobs", MySqlDbType.Binary, "BINARY(16)", typeof(byte[]), 2, new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF })]
1344+
#if BASELINE
1345+
[InlineData("Boolean", "datatypes_bools", MySqlDbType.Byte, "BOOL", typeof(sbyte), 3, (sbyte) 1)]
1346+
[InlineData("TinyInt1", "datatypes_bools", MySqlDbType.Byte, "TINYINT(1)", typeof(sbyte), 3, (sbyte) 1)]
1347+
#else
1348+
[InlineData("Boolean", "datatypes_bools", MySqlDbType.Bool, "BOOL", typeof(bool), 3, true)]
1349+
[InlineData("TinyInt1", "datatypes_bools", MySqlDbType.Bool, "TINYINT(1)", typeof(bool), 3, true)]
1350+
#endif
1351+
[InlineData("TinyInt1U", "datatypes_bools", MySqlDbType.UByte, "TINYINT(1) UNSIGNED", typeof(byte), 3, (byte) 1)]
1352+
[InlineData("char38", "datatypes_guids", MySqlDbType.String, "CHAR(38)", typeof(string), 2, "0")]
1353+
[InlineData("char38bin", "datatypes_guids", MySqlDbType.String, "CHAR(38)", typeof(string), 2, "0")]
1354+
[InlineData("SByte", "datatypes_integers", MySqlDbType.Byte, "TINYINT", typeof(sbyte), 4, (sbyte) 127)]
1355+
[InlineData("Byte", "datatypes_integers", MySqlDbType.UByte, "TINYINT UNSIGNED", typeof(byte), 4, (byte) 255)]
1356+
[InlineData("Int16", "datatypes_integers", MySqlDbType.Int16, "SMALLINT", typeof(short), 4, (short) 32767)]
1357+
[InlineData("UInt16", "datatypes_integers", MySqlDbType.UInt16, "SMALLINT UNSIGNED", typeof(ushort), 4, (ushort) 65535)]
1358+
[InlineData("Int24", "datatypes_integers", MySqlDbType.Int24, "MEDIUMINT", typeof(int), 4, 8388607)]
1359+
[InlineData("UInt24", "datatypes_integers", MySqlDbType.UInt24, "MEDIUMINT UNSIGNED", typeof(uint), 4, 16777215u)]
1360+
[InlineData("Int32", "datatypes_integers", MySqlDbType.Int32, "INT", typeof(int), 4, 2147483647)]
1361+
[InlineData("UInt32", "datatypes_integers", MySqlDbType.UInt32, "INT UNSIGNED", typeof(uint), 4, 4294967295u)]
1362+
[InlineData("Int64", "datatypes_integers", MySqlDbType.Int64, "BIGINT", typeof(long), 4, 9223372036854775807L)]
1363+
[InlineData("UInt64", "datatypes_integers", MySqlDbType.UInt64, "BIGINT UNSIGNED", typeof(ulong), 4, 18446744073709551615ul)]
1364+
[InlineData("Single", "datatypes_reals", MySqlDbType.Float, "FLOAT", typeof(float), 3, -3.40282e38f)]
1365+
[InlineData("Double", "datatypes_reals", MySqlDbType.Double, "DOUBLE", typeof(double), 3, -1.7976931348623157e308)]
1366+
[InlineData("SmallDecimal", "datatypes_reals", MySqlDbType.NewDecimal, "DECIMAL(5,2)", typeof(decimal), 3, null)]
1367+
[InlineData("MediumDecimal", "datatypes_reals", MySqlDbType.NewDecimal, "DECIMAL(28,8)", typeof(decimal), 3, null)]
1368+
[InlineData("BigDecimal", "datatypes_reals", MySqlDbType.NewDecimal, "DECIMAL(50,30)", typeof(decimal), 3, null)]
1369+
[InlineData("utf8", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR(300)", typeof(string), 3, "ASCII")]
1370+
[InlineData("latin1", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR(300)", typeof(string), 3, "ASCII")]
1371+
[InlineData("Date", "datatypes_times", MySqlDbType.Date, "DATE", typeof(DateTime), 2, null)]
1372+
[InlineData("DateTime", "datatypes_times", MySqlDbType.DateTime, "DATETIME", typeof(DateTime), 2, null)]
1373+
[InlineData("Timestamp", "datatypes_times", MySqlDbType.Timestamp, "TIMESTAMP", typeof(DateTime), 2, null)]
1374+
[InlineData("Time", "datatypes_times", MySqlDbType.Time, "TIME", typeof(TimeSpan), 2, null)]
1375+
[InlineData("Year", "datatypes_times", MySqlDbType.Year, "YEAR", typeof(int), 2, 1901)]
1376+
#if !BASELINE
1377+
[InlineData("value", "datatypes_json_core", MySqlDbType.JSON, "JSON", typeof(string), 4, "[]")]
1378+
[InlineData("Geometry", "datatypes_geometry", MySqlDbType.Geometry, "GEOMETRY", typeof(byte[]), 2, null)]
1379+
#endif
1380+
public void StoredProcedureOutParameter(string column, string table, MySqlDbType mySqlDbType, string dataTypeName, Type dataType, int rowid, object expectedValue)
1381+
{
1382+
if (table == "datatypes_json_core" && !AppConfig.SupportsJson)
1383+
return;
1384+
1385+
using (var command = Connection.CreateCommand())
1386+
{
1387+
command.CommandText = $@"drop procedure if exists sp_{column};
1388+
create procedure sp_{column} (IN row_id INTEGER, OUT outparam {dataTypeName})
1389+
begin
1390+
SELECT `{column}` INTO outparam FROM {table} WHERE rowid = row_id;
1391+
end;";
1392+
command.ExecuteNonQuery();
1393+
}
1394+
1395+
using (var command = Connection.CreateCommand())
1396+
{
1397+
command.CommandText = $"sp_{column}";
1398+
command.CommandType = CommandType.StoredProcedure;
1399+
1400+
var parameter = command.CreateParameter();
1401+
parameter.ParameterName = "row_id";
1402+
parameter.Value = rowid;
1403+
command.Parameters.Add(parameter);
1404+
1405+
parameter = command.CreateParameter();
1406+
parameter.ParameterName = "outparam";
1407+
parameter.Direction = ParameterDirection.Output;
1408+
command.Parameters.Add(parameter);
1409+
1410+
command.ExecuteNonQuery();
1411+
Assert.IsType(dataType, parameter.Value);
1412+
Assert.Equal(mySqlDbType, parameter.MySqlDbType);
1413+
1414+
if (expectedValue is object)
1415+
Assert.Equal(expectedValue, parameter.Value);
1416+
}
1417+
}
1418+
13321419
private static byte[] CreateByteArray(int size)
13331420
{
13341421
var data = new byte[size];

0 commit comments

Comments
 (0)