読者です 読者をやめる 読者になる 読者になる

TSqlFragment と Visitor

.NET SQL Server

まだまだ ScriptDom ネタです。
TSqlParser でパースした結果は、TSqlFragment として返ってきます。
TSqlFragment クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
TSqlFragment の子階層のインスタンスは、Visitor を使うことで簡単にアクセス出来ます。


Visitor は下記の2種類あり、どちらかを継承して必要な Visit メソッドを override します。
TSqlFragmentVisitor クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
TSqlConcreteFragmentVisitor クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
TSqlFragmentVisitor と TSqlConcreteFragmentVisitor の違いは、Visitor メソッドのパラメータに渡されるインスタンスが、パラメータの型だけか、そこから派生している型も含むかの違いになっています。

Visitor 派生型を含む
TSqlFragmentVisitor
TSqlConcreteFragmentVisitor ×


実際に試してみましょう。
select で指定している項目の数と、"*" の数を数えてみます。
項目の数は、
SelectElement クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
の派生型が出てきた回数を数えれば OK です。
"*" の数は、SelectElement を継承している
SelectStarExpression クラス (Microsoft.SqlServer.TransactSql.ScriptDom)
を数えれば OK です。

SelectElement の数を数える Visitor

public class SelectElementVisitor : TSqlFragmentVisitor {
  public int Count { get; set; }
  public override void Visit(SelectElement node) {
    Count++;
    base.Visit(node);
  }
}

SelectStarExpression の数を数える Visitor

public class SelectStarVisitor : TSqlConcreteFragmentVisitor {
  public int Count { get; set; }
  public override void Visit(SelectStarExpression node) {
    Count++;
    base.Visit(node);
  }
}
using Microsoft.SqlServer.TransactSql.ScriptDom;
using System;
using System.Collections.Generic;
using System.IO;

class Program {
  static void Main(string[] args) {
    var parser = new TSql110Parser(false);
    IList<ParseError> errors;
    TSqlFragment f;
    using (var reader = new StringReader(@"SELECT @Id = A.Id, @Name = B.Name 
FROM ( SELECT * FROM Table1 WHERE Id = 1) A 
  INNER JOIN Table2 B ON ( A.USERID = B.ID )")) {
      f = parser.Parse(reader, out errors);
    }
    
    var v1 = new SelectElementVisitor();
    var v2 = new SelectStarVisitor();
    f.Accept(v1);
    f.Accept(v2);

    Console.WriteLine(v1.Count); // 3
    Console.WriteLine(v2.Count); // 1
    Console.ReadKey();
  }
}


クラス名からも想像出来ると思いますが、Visitor パターン を使っています。これを使えばパースした結果に対して色々出来そうですね!