【.NET】SQL構文解析ライブラリーのTSQL Parserを使用する

# はじめに
2018年からサーバーリプレース作業（Windows Server 2008R2 → Windows Server 2016）でデータベースを Oracle 11g から PostgreSQL 9.6 に移行作業をしました。

https://qiita.com/yaju/items/7cf5ab6987ecd1d83b13

PostgreSQL 9.6 のサポート終了期限が 2021年11月となっているため、PostgreSQL 12 に移行する準備を進めています。
最新のPostgreSQL 13 にしないのは、現時点(2021/04/18)で某N社のサポートがPostgreSQL 12 までだからです。

|バージョン|初期リリース日|サポート終了期限|
|:--|:--|:--|
|13|2020年09月|2025年11月|
|12|2019年10月|2024年11月|
|11|2018年10月|2023年11月|
|10|2017年10月|2022年11月|
|9.6|2016年09月|2021年11月|

# SQLの構文解析
20近くの既存アプリケーションのSQLを抜き出して、PostgreSQL 9.6とPostgreSQL 12で性能検証をする必要があります。1000近くのSQLがあるため、プログラムを組んで自動で性能検証をしたいと考えました。

自動でやるためには、登録・更新・削除するデータをDB上から抜き出し、該当のSQLに抽出条件や登録・更新データのパラメーターとして当てはめて実行したいのです。

当初は正規表現を多様してSQLを解析しようと考えていたのですが、他言語でSQL文を構文解析するという記事を見つけて、それならC#版でも似たようなのがあるだろうと、NuGetで見つけたのが「TSQL Parser」になります。

https://github.com/bruce-dunwiddie/tsql-parser

# .NET Fiddleの紹介
オンライン上でC#のコードを簡単に試せる「.NET Fiddle」を初めて使用してみました。
他のC#オンラインエディタと違って、NuGet Packageを指定することで「TSQL.Parser」を使用できます。

https://rimever.hatenablog.com/entry/2019/02/23/232535

# ソースコード
https://dotnetfiddle.net/2dgFBb

```csharp
using System;
using TSQL;
using TSQL.Statements;
using TSQL.Tokens;
using System.Collections.Generic;

namespace TSQLParserExample
{
	public class ParseRule 
	{
		public string Field {get;set;}
		public string Operator {get;set;}
		public string FieldValue {get;set;}
		public string SearchOperator { get; set; }
	}
	public class Program
	{
		public static void Main(string[] args)
		{
			TSQLSelectStatement select = TSQLStatementReader.ParseStatements(@"
SELECT id, name
FROM hoge h
WHERE h.id   = @id AND h.name = '@name'
				")[0] as TSQLSelectStatement;
			
			List<ParseRule> rules = new List<ParseRule>();
			
			
			if (select.Where != null)
			{
				Console.WriteLine("WHERE:");
				ParseRule ruleValue = new ParseRule();
				foreach (TSQLToken token in select.Where.Tokens)
				{
					string tokenType = token.Type.ToString();
					string tokenText = token.Text;
					
					switch(tokenType)
					{
						case "Keyword":
							if(string.IsNullOrEmpty(ruleValue.SearchOperator))
							{
								ruleValue.SearchOperator = tokenText;
							}
							else 
							{
								ruleValue.Operator = tokenText;
							}
							break;
						case "Identifier":
							ruleValue.Field = tokenText;
							break;
						case "Variable":
						case "StringLiteral":
						case "NumericLiteral":
							ruleValue.FieldValue = tokenText;
							if(!string.IsNullOrEmpty(ruleValue.SearchOperator) && ruleValue.SearchOperator.Equals("WHERE"))
							{
								ruleValue.SearchOperator = "AND";
							}
							rules.Add(ruleValue);
							ruleValue = new ParseRule();
							break;
						default:
							break;
					}
				}
				
				for(var i = 0; i < rules.Count; i++) 
				{
					var rule1 = rules[i];
					Console.WriteLine("\t Field: " + rule1.Field + "\t Operator: " + rule1.Operator + " \t SearchOperator: " + rule1.SearchOperator + "\t Value: " + rule1.FieldValue);
				}
			}
		}
	}
}
```

## 結果
ちなみにまだ研究中です。
PostgreSQLのNpgsqlのプレースホルダーは「: (コロン)」なのですが、TSQL Parserで変数として認識させるなら「@」に置き換えるとか必要かも知れません。

```
WHERE:
     Field: id     Operator:       SearchOperator: AND     Value: @id
     Field: name     Operator:       SearchOperator: AND     Value: '@name'
```

# 最後に
実は開発はこれからなので、何か気付いたことがあれば追記していきます。

【2022/08/08追記】
応用編を書きました。

https://qiita.com/yaju/items/cf6988f4a6a6707654ce