Skip to content

Commit b0fccdb

Browse files
Carl Sverretavanesov
authored andcommitted
Implement robust ParseDataType to skip unknown elements.
MySQL documentation is not strict about DTD_IDENTIFIER field. It says: The DTD_IDENTIFIER value contains the type name and possibly other information such as the precision or length. So we should not fail on additional elements appearing after the type and parse only known stuff. Signed-off-by: Carl Sverre <[email protected]>
1 parent 178f8d3 commit b0fccdb

File tree

2 files changed

+44
-39
lines changed

2 files changed

+44
-39
lines changed

src/MySqlConnector/Core/CachedProcedure.cs

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,6 @@ internal static List<CachedParameter> ParseParameters(string parametersSql)
194194

195195
internal static string ParseDataType(string sql, out bool unsigned, out int length)
196196
{
197-
if (sql.EndsWith(" ZEROFILL", StringComparison.OrdinalIgnoreCase))
198-
sql = sql.Substring(0, sql.Length - 9);
199-
unsigned = false;
200-
if (sql.EndsWith(" UNSIGNED", StringComparison.OrdinalIgnoreCase))
201-
{
202-
unsigned = true;
203-
sql = sql.Substring(0, sql.Length - 9);
204-
}
205197
sql = Regex.Replace(sql, " (CHARSET|CHARACTER SET) [A-Za-z0-9_]+", "", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
206198
sql = Regex.Replace(sql, " (COLLATE) [A-Za-z0-9_]+", "", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
207199
sql = Regex.Replace(sql, @"ENUM\s*\([^)]+\)", "ENUM", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
@@ -214,40 +206,40 @@ internal static string ParseDataType(string sql, out bool unsigned, out int leng
214206
sql = Regex.Replace(sql, @"\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\)", "");
215207
}
216208

217-
sql = sql.Trim();
218-
219-
// normalize alternate data type names
220-
if (sql.Equals("BOOL", StringComparison.OrdinalIgnoreCase) || sql.Equals("BOOLEAN", StringComparison.OrdinalIgnoreCase))
221-
{
222-
sql = "TINYINT";
223-
length = 1;
224-
}
225-
else if (sql.Equals("INTEGER", StringComparison.OrdinalIgnoreCase))
226-
{
227-
sql = "INT";
228-
}
229-
else if (sql.Equals("NUMERIC", StringComparison.OrdinalIgnoreCase) || sql.Equals("FIXED", StringComparison.OrdinalIgnoreCase))
230-
{
231-
sql = "DECIMAL";
232-
}
233-
else if (sql.Equals("REAL", StringComparison.OrdinalIgnoreCase) || sql.Equals("DOUBLE PRECISION", StringComparison.OrdinalIgnoreCase))
209+
var typeMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
234210
{
235-
sql = "DOUBLE";
236-
}
237-
else if (sql.Equals("NVARCHAR", StringComparison.OrdinalIgnoreCase) || sql.Equals("CHARACTER VARYING", StringComparison.OrdinalIgnoreCase) || sql.Equals("NATIONAL VARCHAR", StringComparison.OrdinalIgnoreCase))
238-
{
239-
sql = "VARCHAR";
240-
}
241-
else if (sql.Equals("NCHAR", StringComparison.OrdinalIgnoreCase) || sql.Equals("CHARACTER", StringComparison.OrdinalIgnoreCase) || sql.Equals("NATIONAL CHAR", StringComparison.OrdinalIgnoreCase))
242-
{
243-
sql = "CHAR";
244-
}
245-
else if (sql.Equals("CHAR BYTE", StringComparison.OrdinalIgnoreCase))
211+
{"BOOL", "TINYINT"},
212+
{"BOOLEAN", "TINYINT"},
213+
{"INTEGER", "INT"},
214+
{"NUMERIC", "DECIMAL"},
215+
{"FIXED", "DECIMAL"},
216+
{"REAL", "DOUBLE"},
217+
{"DOUBLE PRECISION", "DOUBLE"},
218+
{"NVARCHAR", "VARCHAR"},
219+
{"CHARACTER VARYING", "VARCHAR"},
220+
{"NATIONAL VARCHAR", "VARCHAR"},
221+
{"NCHAR", "CHAR"},
222+
{"CHARACTER", "CHAR"},
223+
{"NATIONAL CHAR", "CHAR"},
224+
{"CHAR BYTE", "BINARY"}
225+
};
226+
227+
var list = sql.Trim().Split(new char[] {' '});
228+
var type = string.Empty;
229+
230+
if (list.Length < 2 || !typeMapping.TryGetValue(list[0] + ' ' + list[1], out type))
246231
{
247-
sql = "BINARY";
232+
if (typeMapping.TryGetValue(list[0], out type))
233+
{
234+
if (list[0].StartsWith("BOOL", StringComparison.OrdinalIgnoreCase))
235+
{
236+
length = 1;
237+
}
238+
}
248239
}
249240

250-
return sql;
241+
unsigned = list.Contains("UNSIGNED", StringComparer.OrdinalIgnoreCase);
242+
return type ?? list[0];
251243
}
252244

253245
private static CachedParameter CreateCachedParameter(int ordinal, string? direction, string name, string dataType, bool unsigned, int length, string originalSql)

tests/MySqlConnector.Tests/CachedProcedureTests.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ param5 bigint(20) unsigned zerofill,
166166
param1 boolean,
167167
param2 nvarchar,
168168
param3 real(20,10),
169-
-- ignored INT
169+
-- ignored INT
170170
param4 INTEGER(3)
171171
",
172172
new object[]
@@ -183,20 +183,33 @@ param4 INTEGER(3)
183183
[Theory]
184184
[InlineData("INT", "INT", false, 0)]
185185
[InlineData("INTEGER", "INT", false, 0)]
186+
[InlineData("INTEGER UNSIGNED", "INT", true, 0)]
186187
[InlineData("INT(11)", "INT", false, 11)]
187188
[InlineData("INTEGER(11)", "INT", false, 11)]
188189
[InlineData("INT(11) UNSIGNED", "INT", true, 11)]
190+
[InlineData("INT(11) UNSIGNED NOT NULL", "INT", true, 11)]
191+
[InlineData("INT(11) UNSIGNED NULL", "INT", true, 11)]
192+
[InlineData("INT(11) UNSIGNED NULL DEFAULT NULL", "INT", true, 11)]
189193
[InlineData("INT(11) ZEROFILL", "INT", false, 11)]
190194
[InlineData("INT(11) UNSIGNED ZEROFILL", "INT", true, 11)]
191195
[InlineData("BIGINT(20)", "BIGINT", false, 20)]
192196
[InlineData("TINYINT(1) UNSIGNED", "TINYINT", true, 1)]
193197
[InlineData("BOOL", "TINYINT", false, 1)]
198+
[InlineData("Bool", "TINYINT", false, 1)]
194199
[InlineData("NUMERIC(30,20)", "DECIMAL", false, 30)]
195200
[InlineData("VARCHAR(300)", "VARCHAR", false, 300)]
196201
[InlineData("VARCHAR(300) CHARSET utf8mb4", "VARCHAR", false, 300)]
197202
[InlineData("VARCHAR(300) COLLATE ascii_general_ci", "VARCHAR", false, 300)]
203+
[InlineData("VARCHAR(300) COLLATE ascii_general_ci NOT NULL DEFAULT 'test'", "VARCHAR", false, 300)]
204+
[InlineData("CHARACTER VARYING(300) COLLATE ascii_general_ci NOT NULL DEFAULT 'test'", "VARCHAR", false, 300)]
205+
[InlineData("NATIONAL VARCHAR(50) COLLATE ascii_general_ci NOT NULL DEFAULT 'test'", "VARCHAR", false, 50)]
198206
[InlineData("BINARY(16)", "BINARY", false, 16)]
207+
[InlineData("CHAR BYTE(16)", "BINARY", false, 16)]
199208
[InlineData("CHAR(36)", "CHAR", false, 36)]
209+
[InlineData("REAL", "DOUBLE", false, 0)]
210+
[InlineData("REAL NOT NULL DEFAULT 0", "DOUBLE", false, 0)]
211+
[InlineData("NUMERIC(12)", "DECIMAL", false, 12)]
212+
[InlineData("FIXED(12)", "DECIMAL", false, 12)]
200213
[InlineData("ENUM('a','b','c')", "ENUM", false, 0)]
201214
public void ParseDataType(string sql, string expectedDataType, bool expectedUnsigned, int expectedLength)
202215
{

0 commit comments

Comments
 (0)