Created
November 10, 2018 05:04
-
-
Save Y-Koji/130890749aeeb5ce9e985932d4baa152 to your computer and use it in GitHub Desktop.
SQL整形プログラム
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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