Skip to content

Instantly share code, notes, and snippets.

@Y-Koji
Created November 10, 2018 05:04
Show Gist options
  • Save Y-Koji/130890749aeeb5ce9e985932d4baa152 to your computer and use it in GitHub Desktop.
Save Y-Koji/130890749aeeb5ce9e985932d4baa152 to your computer and use it in GitHub Desktop.
SQL整形プログラム
using System;
using System.Linq;
using System.Text;
namespace TxtTools
{
public class SqlFormatter
{
private static class Status
{
public static string Sql { get; set; } = string.Empty;
public static int Index { get; set; } = 0;
public static int Length { get => Sql?.Length ?? 0; }
public static int Indent { get; set; } = 0;
public static string IndentString { get; set; } = " ";
public static bool IsIndentAdded { get; set; } = false;
public static bool IsBeforeOnExpression { get; set; } = false;
public static bool IsBeforeBracketExpression { get; set; } = false;
public static bool IsStringExpression { get; set; } = false;
public static StringBuilder Result { get; } = new StringBuilder();
// インデント対象キーワード
public static readonly string[] IndentWords = new string[]
{
"insert into", "update", "select", "delete",
};
// インデントを維持したままこのキーワードだけを一つ低い階層のインデントに挿入する
public static readonly string[] ReIndentWords = new string[]
{
"from", "where", "group by", "order by", "having",
};
// 挿入後改行対象キーワード
public static readonly string[] NewLineWords = new string[]
{
"(", ")",
"or", "and", ",", "on", "right outer join",
};
// 改行後挿入対象キーワード
public static readonly string[] BeforeNewLineWords = new string[]
{
"join", "inner join", "outer join", "left join", "right join",
"right outer join", "left outer join",
};
// インデントを0でクリアする対象のキーワード
public static readonly string[] ClearIndentWords = new string[]
{
";",
};
// 空白で区切るべきキーワード
public static readonly string[] Keywords = new string[]
{
"=", "set", "as", "<", ">", "like", "case",
};
// 文字列等指定文字で囲うタイプのキーワード
public static readonly string[] RecordKeywords = new string[]
{
"\"", "'",
};
public static void AppendIndent(int offset = 0)
{
if (Indent + offset < 0)
{
return;
}
for (int i = 0; i < Indent + offset; i++)
{
Result.Append(IndentString);
}
}
}
private static bool CheckString(string sql, int index, string str)
{
if (index + str.Length < sql.Length - 1)
{
string substr = sql.Substring(index, str.Length);
return substr.ToLower() == str.ToLower();
}
return false;
}
private static string GetString(string sql, int index, int length)
=> sql.Substring(index, length);
private static bool CheckWords(string sql, int index, string[] words, out string result)
{
result = string.Empty;
foreach (var word in words)
{
if (CheckString(sql, index, word))
{
result = GetString(sql, index, word.Length);
return true;
}
}
return false;
}
private static int SkipWhiteSpace(string sql, int index)
{
while (index < sql.Length && string.IsNullOrWhiteSpace(sql[index].ToString()))
index++;
return index;
}
private static string ReadRecord(string sql, int index, string keyword)
{
if (sql[index].ToString() != keyword)
{
throw new InvalidOperationException();
}
StringBuilder sb = new StringBuilder();
sb.Append(sql[index++]);
for(; ;index++)
{
if (0 < index)
{
if (sql[index].ToString() == keyword)
{
if ('\\' != sql[index - 1])
{
// エスケープされていなければ確定
sb.Append(sql[index]);
return sb.ToString();
}
}
}
sb.Append(sql[index]);
}
}
public static string Format(string sql)
{
Status.Sql = sql;
Status.Indent = 0;
Status.IndentString = " ";
Status.Result.Clear();
for (Status.Index = 0; Status.Index < Status.Length;Status.Index++)
{
Status.Index = SkipWhiteSpace(sql, Status.Index);
if (Status.Length <= Status.Index)
{
break;
}
if (CheckWords(Status.Sql, Status.Index, Status.IndentWords, out string indentWord))
{
if (Status.IsIndentAdded)
{
Status.Result.AppendLine();
Status.IsIndentAdded = false;
}
Status.AppendIndent();
Status.Result.AppendLine(indentWord);
Status.Indent++;
Status.Index += indentWord.Length;
continue;
}
if (CheckWords(Status.Sql, Status.Index, Status.ReIndentWords, out string reIndentWord))
{
if (Status.IsIndentAdded)
{
Status.Result.AppendLine();
Status.IsIndentAdded = false;
}
if (Status.IsBeforeOnExpression)
{
Status.IsBeforeOnExpression = false;
Status.Indent--;
}
Status.AppendIndent(-1);
Status.Result.AppendLine(reIndentWord);
Status.Index += reIndentWord.Length;
continue;
}
if (CheckWords(Status.Sql, Status.Index, Status.ClearIndentWords, out string removeIndentWord))
{
if (Status.IsIndentAdded)
{
Status.Result.AppendLine();
}
Status.Result.Remove(Status.Result.Length - 2, 2);
Status.Result.AppendLine(removeIndentWord);
Status.Index += removeIndentWord.Length;
Status.Indent = 0;
Status.IsBeforeOnExpression = false;
continue;
}
if (CheckWords(Status.Sql, Status.Index, Status.NewLineWords, out string spliter))
{
if (Status.IsIndentAdded)
{
Status.IsIndentAdded = false;
}
// 近接キーワード
if (!new string[] { ",", "(", ")", }.Contains(spliter))
{
Status.Result.Append(" ");
}
if (Status.IsBeforeOnExpression)
{
Status.IsBeforeOnExpression = false;
Status.Indent--;
}
Status.Result.AppendLine(spliter);
Status.Index += spliter.Length - 1;
if ("on" == spliter.ToLower())
{
Status.IsBeforeOnExpression = true;
Status.Indent++;
}
if ("(" == spliter.ToLower())
{
Status.IsBeforeBracketExpression = true;
Status.Indent++;
}
if (")" == spliter.ToLower())
{
Status.IsBeforeBracketExpression = false;
Status.Indent--;
}
continue;
}
if (CheckWords(Status.Sql, Status.Index, Status.BeforeNewLineWords, out string beforeNewLineWord))
{
if (Status.IsBeforeOnExpression)
{
Status.IsBeforeOnExpression = false;
Status.Indent--;
}
Status.Result.AppendLine();
Status.AppendIndent();
Status.Result.Append(beforeNewLineWord);
Status.Index += beforeNewLineWord.Length;
}
if (CheckWords(Status.Sql, Status.Index, Status.Keywords, out string keyword))
{
Status.Result.Append(" ").Append(keyword).Append(" ");
Status.Index += keyword.Length;
continue;
}
if (CheckWords(Status.Sql, Status.Index, Status.RecordKeywords, out string startKeyword))
{
string record = ReadRecord(Status.Sql, Status.Index, startKeyword);
Status.Result.Append(record);
Status.Index += record.Length - 1;
continue;
}
if (!Status.IsIndentAdded)
{
Status.AppendIndent();
Status.IsIndentAdded = true;
}
Status.Result.Append(Status.Sql[Status.Index]);
}
return Status.Result.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment