[[JAVAの記事一覧]] *目次 [#pccd97cd] #contents *Java SQL Parserを調査する [#zb549a4a] *動機 [#d8f88e2b] SQLを解析することで、SQLを動的に解析して単体試験仕様書の作成を自動化したい そのためには次の項目を自動抽出したい。 *自動抽出希望項目 [#y3bf4f8c] -変数 Oracleのプレースホルダー -テーブル -検索条件 -結果項目 さらにいうと、 -検索条件が外部結合なのかどうなのか -結果項目が外部結合の項目なのかどうなのか *その他達成しようとしていること [#k8df5a88] SQLを解析し、Excelで定義してある和名を検索し和名表示に置換する。 そのためには、SQL解析にて、次の対応表を作成する。 **テーブルの別名とテーブル名 [#jda39e70] **テーブル名とテーブル和名 [#d8333138] **WHERE句などの解析方法について考える [#pd22017c] WHERE句を解析するには正規表現などを駆使して文法を解析するのもいいが、できれば文法の定義ファイルをもとにパーサを生成し、そのパーサをもとに解析したい。 *全体的な感想 [#v89144f2] 簡単な文法レベルでいいから手軽にパーサを生成して活用できるようになりたいと思う。 *Java SQL Parser [#tc368e00] **ANTLR [#d305b35b] http://www.antlr.org/grammar/list まずは上記のFAQから読み始めたほうがいいが、英語なので、英語が苦手ならば、日本語の説明サイトがあったので、そちらをみたほうがいい。 ***使い方の参考URL [#x934b7b7] http://www.limy.org/program/java/antlr/step1.html ***特徴 [#zd36fbf9] -ANSI SQL 文法に則っているらしい -本格的なツールだが、学習コストが高い。 -過去のコードは微調整が必要なので、修正できるだけの文法知識が必要 -英語の本が出版されている模様 -グラフィカルなIDEはJREがインストールされていればダブルクリックで動作する -外部にの文法を定義するファイルが必要で拡張子は.gがつかわれている -手軽にできる方法があるのかもしれないが、作りがオールマイティすぎて資料が膨大で探しづらい。 ***使い方 [#y4a50bfe] よくわからない状態で、ためしにEclipseのビルドパスに外部Jarとしてぶっこんで、 http://www.antlr.org/wiki/display/ANTLR3/ANTLR+Cheat+Sheet をためしてみたが、TLexerとTParserが見つからない、どうやら事前に生成するようだ。 ***expr.g [#b5fa1b0a] expr.gで下記の内容を保存してみる。 class ExprParser extends Parser; expr: mexpr ((PLUS|MINUS) mexpr)* ; mexpr: atom (STAR atom)* ; atom: INT | LPAREN expr RPAREN ; class ExprLexer extends Lexer; options { k=2; // needed for newline junk charVocabulary='\u0000'..'\u007F'; // allow ascii } LPAREN: '(' ; RPAREN: ')' ; PLUS : '+' ; MINUS : '-' ; STAR : '*' ; INT : ('0'..'9')+ ; WS : ( ' ' | '\r' '\n' | '\n' | '\t' ) {$setType(Token.SKIP);} ; 次のようにコマンドラインで実行する java -classpath antlr-3.2.jar antlr.Tool expr.g なにやらファイルが生成された ***PLSQLの定義ファイルはこちら [#e34a0fb0] http://www.antlr.org/grammar/1174072667394/PLSQLGrammar.g 上記ファイルをダウンロードして下記のように打ち込むと java -classpath antlr-3.2.jar antlr.Tool PLSQLGrammar.g クラスファイルが出来上がる。 で、下記のクラスを要求している -SoftwareMetrics そのファイルは下記よりダウンロード可である。 ***SoftwareMetricsクラスはこちら [#vc0354bc] http://www.antlr.org/grammar/1174072667394/SoftwareMetrics.java でこのSoftwareMetricsはいろいろ摩訶不思議な設定が必要だが、その方法は下記よりダウンロード可能 ***PLSQLMain [#m1041498] http://www.antlr.org/grammar/1174072667394/PLSQLMain.java ***eclipse plugin [#i37aa57a] http://antlreclipse.sourceforge.net/ *** [#wa6d7ae4] http://openjdk.java.net/projects/compiler-grammar/antlrworks/ ***感想 [#u57d8490] -IDEがすばらしい -JavaSDKの開発者も使ってるんじゃないのか http://openjdk.java.net/projects/compiler-grammar/antlrworks/ -グラフィック表示がすばらしい -いろいろな言語へのAPIが提供されている。 -コンパイルすんの?これ。 **Apache Derby. [#dd48fb97] Apache Derby http://db.apache.org/derby/ はApacheのDBのサブプロジェクトでしてそのApache Derbyにパーサがあるらしい .native() メソッドを見るといいようだ。 **JSqlParser [#z990ac26] http://jsqlparser.sourceforge.net/ SQLを解析してJavaクラスの階層構造に変換するらしい ***特徴 [#de654001] -Visitorパターンをつかったプログラミングが身に付くというか要求される。 -Oracle特有の外部結合の(+)とかプレースホルダーとかは対応していないので、事前にちょいと整形が必要だ。 -ただ、SELECT文の場合とか、UPDATE文の場合とかを事前にこちら側で判断しておかないといけない仕組みになっている。まあ、SQLの文字列にSELECTやらUPDATEがはいっているとかで判断すればいいんだろうけど。おしゃれではない。 ***使い方:準備 [#v0afcae4] ダウンロードしてきたjarのパスをzipに変換して解凍し、 そのなかからlibフォルダにjarがあるので、そいつをeclipseのビルドパスに外部jarとして取り込ませる。 ***サンプル [#ye6afd41] サイトのサンプルがちょっと手直しが必要だったので直して、日本語訳しておきます。 TablesNamesFinderのStringValueはEclipseの自動補完を使うとjava.langの方をつかうので、 import net.sf.jsqlparser.expression.StringValue; としておきましょう JoinVisitor(だっけか?)は削除しておきます。 CCJSqlParserManager pm = new CCJSqlParserManager(); /* * Oracleのプレースホルダーは対応してないので、''で括るなどしましょう * :AAA -> ':AAA' * Oracleの外部結合である(+)も対応していないので、削っておきましょう * (+) -> 削除 */ String sql = "SELECT * FROM MY_TABLE1, MY_TABLE2, (SELECT * FROM MY_TABLE3) LEFT OUTER JOIN MY_TABLE4 "+ " WHERE ID = (SELECT MAX(ID) FROM MY_TABLE5) AND ID2 IN (SELECT * FROM MY_TABLE6)" ; net.sf.jsqlparser.statement.Statement statement = pm.parse(new StringReader(sql)); /* 対象のSQL文字列が何を行うか(たとえばSELECTなのかINSERTなのか...)に応じて、 StatementVisitorをimplementsで実装したクラスをつかってください。 とりあえずここでは例としてSELECT用のselectStatementをつかっています。 */ if (statement instanceof Select) { Select selectStatement = (Select) statement; TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); List tableList = tablesNamesFinder.getTableList(selectStatement); for (Iterator iter = tableList.iterator(); iter.hasNext();) { System.out.println(iter.next()); } } **Zql [#cb147f36] http://www.gibello.com/code/zql/ Javaで書かれたSQLのParser OracleのDECODEとかの関数がデフォルトで定義されていない p = new ZqlParser(); p.addCustomFunction("DECODE", 0); とするが、引数の数値のエラーがでたまま解析が実行できない。 下記のアドレスが参考になりそうだが、MDLがよくわからんので役にはたたない。 http://blogs.oracle.com/warehousebuilder/2007/08/14/ ***Demoの動かし方 [#n19f9fef] cmd でDOSプロンプト起動 cd xxxデモのあるディレクトリ でデモファイルがあるディレクトリをカレントディレクトリにする。 READMEには java ZDemo queries.num とあるが、パスを通しておく java -cp .;..\classes ZDemo queries.num ***Demo実行結果 [#a0b1ada9] select * from num [a = 1.0, b = 1.0, c = 1.0, d = 1.0, e = 1.0] [a = 2.0, b = 2.0, c = 2.0, d = 2.0, e = 2.0] [a = 1.0, b = 2.0, c = 3.0, d = 4.0, e = 5.0] [a = 5.0, b = 4.0, c = 3.0, d = 2.0, e = 1.0] select * from num where ((1 + 1) = 2) [a = 1.0, b = 1.0, c = 1.0, d = 1.0, e = 1.0] [a = 2.0, b = 2.0, c = 2.0, d = 2.0, e = 2.0] [a = 1.0, b = 2.0, c = 3.0, d = 4.0, e = 5.0] [a = 5.0, b = 4.0, c = 3.0, d = 2.0, e = 1.0] select ((((a + b) + c) + d) + e) from num 5.0 10.0 15.0 15.0 略 ***ちなみにもうひとつのデモを試す [#udc6effd] java -cp .;..\classes StringDemo "select * from num where foo = bar order by fuga;" 結果 select * from num where (foo = bar) order by fuga ASC **BYACC/J [#hf6fdc85] http://byaccj.sourceforge.net/ YACCと互換性あるそうな。 **Java Cup [#f82fb0f2] http://www2.cs.tum.edu/projects/cup/ JavaのParser生成ライブラリー **JFlex [#s987734b] JFlexはこちら http://jflex.de/ **jparsec [#e9851712] http://jparsec.codehaus.org/ YACCのような位置づけである。 YACCとの違いは外部ファイルを必要としないとか。 ***特徴 [#f4851a4c] -演算子を優先とする文法 -正確なエラー箇所 -再利用可能な豊富な再結合用関数 -BNFの宣言可能なAPI