Skip to content

Instantly share code, notes, and snippets.

@bgrainger
Last active October 2, 2021 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bgrainger/791cecb647d514a9dd2f3d83b2387e49 to your computer and use it in GitHub Desktop.
Save bgrainger/791cecb647d514a9dd2f3d83b2387e49 to your computer and use it in GitHub Desktop.
async Task Main()
{
var httpClient = new HttpClient();
var html57 = await (await httpClient.GetAsync("https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html")).Content.ReadAsStringAsync();
var html8 = await (await httpClient.GetAsync("https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html")).Content.ReadAsStringAsync();
var valueNames = new Dictionary<int, string>();
foreach (MySqlErrorCode value in Enum.GetValues(typeof(MySqlErrorCode)))
{
var intValue = (int)value;
valueNames[(int)value] = value.ToString();
}
var allErrors = new Dictionary<int, (string Name, string Description)>();
ReadErrorsFromHtml(allErrors, valueNames, html57);
ReadErrorsFromHtml(allErrors, valueNames, html8);
const string fileName = @"MySqlErrorCode.g.cs";
using (var writer = new StreamWriter(fileName))
{
writer.WriteLine(@"namespace MySqlConnector;
/// <summary>
/// MySQL Server error codes. Taken from <a href=""https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html"">Server Error Codes and Messages</a>.
/// </summary>
[System.CodeDom.Compiler.GeneratedCode(""https://gist.github.com/bgrainger/791cecb647d514a9dd2f3d83b2387e49"", ""5"")]
public enum MySqlErrorCode
{
DelimiterNotSupported = -3,
/// <summary>
/// Not all rows from the source supplied to <see cref=""MySqlBulkCopy""/> were copied to <see cref=""MySqlBulkCopy.DestinationTableName""/>.
/// </summary>
BulkCopyFailed = -2,
/// <summary>
/// The timeout period specified by <see cref=""MySqlCommand.CommandTimeout""/> elapsed before the operation completed.
/// </summary>
CommandTimeoutExpired = -1,");
foreach (var kvp in allErrors.OrderBy(x => x.Key))
{
writer.WriteLine();
writer.Write($@" /// <summary>
/// {kvp.Value.Description}
/// </summary>
{kvp.Value.Name} = {kvp.Key},
");
}
writer.WriteLine(@"}");
}
File.WriteAllBytes(fileName, File.ReadAllBytes(fileName).Where(x => x != 0x0D).ToArray());
}
void ReadErrorsFromHtml(Dictionary<int, (string, string)> allErrors, Dictionary<int, string> valueNames, string html)
{
var messageOverrides = new Dictionary<int, string>
{
{ 1064, "You have an error in your SQL syntax" },
{ 1317, "Query execution was interrupted" },
};
var seenErrors = new HashSet<int>();
using (var reader = new StringReader(html))
{
int errorCode = 0;
string errorName = null;
string errorMessage = null;
string line;
while ((line = reader.ReadLine()) != null)
{
if (errorName != null)
{
if (line.StartsWith(" </p>", StringComparison.OrdinalIgnoreCase))
{
if (errorMessage != null)
{
if (seenErrors.Add(errorCode))
{
errorMessage = Regex.Replace(errorMessage, @"\s+", " ").Replace(" Message: ", "");
// try to match official name if known
if (!valueNames.TryGetValue(errorCode, out var valueName))
{
// otherwise synthesise a name by CamelCasing the error code
valueName = Regex.Replace(errorName, @"^(WARN|ER)_", "_") + "_";
valueName = Regex.Replace(valueName, @"(?<!_)[A-Z]", x => x.Groups[0].Value.ToLowerInvariant());
// replace some error code abbreviations with spelled-out words for the enum value name
valueName = valueName.Replace("Cant_", "Cannot_");
valueName = valueName.Replace("Cond_Item_", "ConditionItem_");
valueName = valueName.Replace("Dup_", "Duplicate_");
valueName = valueName.Replace("Fk_", "ForeignKey_");
valueName = valueName.Replace("Ft_", "FullText_");
valueName = valueName.Replace("Jt_", "JsonTable_");
valueName = valueName.Replace("Tf_", "TableFunction_");
valueName = valueName.Replace("Io_", "IO_");
valueName = valueName.Replace("Serverid_", "ServerId_");
valueName = valueName.Replace("Stmt_", "Statement_");
valueName = valueName.Replace("Vcpu_", "VCpu_");
valueName = valueName.Replace("_", "");
}
// NOTE: 'errorMessage' contains a human-readable error message, but it was not authored by us; use a simple description for the comment instead.
string description;
if (messageOverrides.TryGetValue(errorCode, out var messageOverride))
description = $"{messageOverride} ({errorName}).";
else
description = errorName;
if (!allErrors.ContainsKey(errorCode))
allErrors.Add(errorCode, (valueName, description));
}
errorCode = 0;
errorName = null;
errorMessage = null;
}
}
else
{
errorMessage = errorMessage + line;
}
}
else
{
Match match;
if ((match = Regex.Match(line, @"Error number: <code class=""literal"">([0-9]+)</code>; Symbol:")).Success)
{
errorCode = int.Parse(match.Groups[1].Value);
errorName = errorMessage = null;
}
else if ((match = Regex.Match(line, @"<a class=""link"" href=""server-error-reference.html#[^""]+""><code class=""literal"">(.*?)</code></a>")).Success)
{
errorName = match.Groups[1].Value;
}
}
}
}
}
@ejball
Copy link

ejball commented Jun 26, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment