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

ScriptDom クエリ生成時に識別子を[]で囲む

.NET SQL Server

SqlScriptGenerator クラス (Microsoft.SqlServer.TransactSql.ScriptDom) でクエリを生成する際のオプション
SqlScriptGeneratorOptions クラス (Microsoft.SqlServer.TransactSql.ScriptDom) では、識別子を で囲むオプションがありません。
ですが、ちょっと工夫をすることで識別子を
で囲むことが出来ます。


こんな Visitor を実装して、

public class IdentifierSquareQuote : TSqlConcreteFragmentVisitor {
  public override void Visit(Identifier node) {
    if (node.QuoteType == QuoteType.NotQuoted) {
      node.QuoteType = QuoteType.SquareBracket;
    }
    base.Visit(node);
  }
}

パースした TSqlFragment に食わすことで [] で囲む事が出来ます。

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 parsed;
    using (var reader = new StringReader(@"select top (10) id from Table1")) {
      parsed = parser.Parse(reader, out errors);
    }
    parsed.Accept(new IdentifierSquareQuote());
    var generator = new Sql110ScriptGenerator();
    string query;
    generator.GenerateScript(parsed, out query);
    Console.WriteLine(query);
    Console.ReadKey();
  }
}


ただ、これだけだと Function の名前も囲ってしまい、意図していないクエリになってしまいます。

上のコードのクエリを書き換えると…

using (var reader = new StringReader(@"select top (10) id, getdate() from Table1")) {
  parsed = parser.Parse(reader, out errors);
}


これは困るので Visitor の実装を少し変えて Function の名前は省くようにしてみましょう。

public class IdentifierSquareQuote : TSqlConcreteFragmentVisitor {
  private List<TSqlFragment> functionName = new List<TSqlFragment>();
  public override void Visit(FunctionCall node) {
    functionName.Add(node.FunctionName);
    base.Visit(node);
  }
  public override void Visit(Identifier node) {
    if (!functionName.Any(n => n == node)) {
      if (node.QuoteType == QuoteType.NotQuoted) {
        node.QuoteType = QuoteType.SquareBracket;
      }
    }
    base.Visit(node);
  }
}


これで Function の名前に関しては大丈夫ですね!