[[JAVAの記事一覧]]

[[構文解析の記事一覧]]

&topicpath;
*目次 [#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

***ANTLRの文法ファイル(.g)をNetBeans Scriptingファイル(.nbs)に変換する [#ad593a02]
http://snakemanshow.blogspot.com/2008/02/antlr.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/




*** Using ANTLRWorks with Java.g [#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パターンをつかったプログラミングが身に付くというか要求される。

Visitorパターンについて知りたい場合は下記のURLなどを見る

Visitorパターンって、Visitorが訪れるイベントしかとれないようなのは、
ありがたくない気がする。
なぜなら、構文は開始を終了が明確になっていないと、構文木を生成できないからだ。
だったら、はじめからjparsecをつかってたほうがいいってことになる。

http://www.aerith.net/design/Visitor-j.html

-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());
			}
		}

**感想 [#s3c3f85a]
サンプルにJavaのコードのパース例とか載っているし、
BNFファイルのパース例とかが載っていて、興味深い。
この手のパースプログラムは、その他のパースプログラムの定義もどん欲に取り込もうとすると
おもわれる。なぜなら移植したほうが、いちから作成するよりも簡単だからだ。

構文木を生成して、別の言語。特に自分自身の言語でのパース定義ソースを生成することが、
便利なのだろう。


**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と互換性あるそうな。

すでに、YACCでの.yのファイルがたくさんある場合。たしかに、新しく定義書を一から作り上げるよりは、すでにあるものを活用した方が手っ取り早いかもしれない。

となると、参考になる構文解析の定義ファイル一覧を持っていないといけない。
ただし、その場合、自分で微調整できるスキルがないとだめだろう。


**Java Cup [#f82fb0f2]
http://www2.cs.tum.edu/projects/cup/

bisonのようなボトムアップ型の構文解析のJavaコードを生成します。

JavaのParser生成ライブラリー

***ボトムアップの構文解析アルゴリズム [#e7dd3241]
bisonのアルゴリズムの解説

http://www.bookshelf.jp/texi/bison/bison-ja_8.html

***補足 [#q45a13be]
ちなみにC言語の構文解析を生成するのはbisonと呼ばれる

トップダウン型のJava構文解析の生成にはJavaCCがある。

**JFlex [#s987734b]
JFlexはこちら

http://jflex.de/

ちなみにC言語の字句解析器生成言語はflexという。

**Pythonでの構文解析 [#r02099da]
Python言語でいうところのSparkモジュールに相当するかも

Pythonのその他の構文解析モジュールについては下記URLがまとまっていた。興味が湧いたら見てみる。

http://nedbatchelder.com/text/python-parsers.html

**Javascript [#r98095fc]
パーサコンビネータを作っちゃう人の記事

http://inforno.net/articles/tag/javascript/

***特徴 [#f4851a4c]
-演算子を優先とする文法
-正確なエラー箇所
-再利用可能な豊富な再結合用関数
-BNFの宣言可能なAPI

*jparsec [#e9851712]
http://jparsec.codehaus.org/

パーサ生成フレームワーク

YACCとの違いは外部ファイルを必要としない点が違う。

Ruby版も存在しており、rparsecという。言語の先頭1文字をとって区別をつけている。
haskell版もあるがこちらが、元になっているので、こちらの名前はparsecという。


***SQLの解析サンプルについて [#of950202]
jparsecをダウンロードし、解凍すると
[jparsec-2.0_src]-[examples]-[src]-[org]-[codehaus]-[jparsec]-[examples]-[sql]
がある。

**Eclipseに取り込む手順 [#o49a3671]
jparsecからダウンロードしてきたファイルを解凍しておきます。

junitのjarファイルも手元になければ、ダウンロードしてきます。

ダウンロードしてきたjunitはjunit-4.18.jarとかバージョン名がついているので、

junit.jarという名前にかえておきます。

junitはjparsecのlibフォルダに格納しておきます。

では、eclipseがわの準備を行ってみましょう。

Eclipseに新規にJavaプロジェクトを作成します。

ファイルメニューのインポートで先ほど解凍してできたフォルダを選択し、それをプロジェクトのsrcディレクトリを指定してとりこみます。

srcフォルダは、4つあります、本体用、本体test用、example用、exampleテスト用

インポート直後は

まだ、プロジェクトのビルドパスにjarが登録されていませんので、コンパイルエラーになっています。

そこで、ビルドパスの設定でparsecのlibフォルダ内のjarをすべて登録します。

コンパイルエラー表示はほぼ消えます。

が、1カ所だけAllTestクラスでエラーになっています。

それは、作者がライブラリをあげたくないからだと
build.xmlの80行目に明記してありました。

こんな感じ、

 AllTests uses jtc, which is an extra dependency that I don't want to upload.

おそらく、Androidのソースを流用したコードだから、著作権の問題であげれないとでも思ったのでしょうか。

それはさておき、
	
build.xmlには、このクラスのみ除外してコンパイルする記述がありました。

要するにいらないんです。この

だからbuild.xmlをいじくりたくなかったので、つぎのようにクラスを書き換えておきました。


 package org.codehaus.jparsec;
 
 //import org.openqa.jtc.junit.TestSuiteBuilder;
 
 import junit.framework.TestSuite;
 
 /**
 *
 * @author benyu
 */
 public class AllTests extends TestSuite {
  public static TestSuite suite() {
    //return TestSuiteBuilder.suite(AllTests.class);
    return null;
  }
 }




**コンパイル方法 [#v14b1801]
build.xmlがあるので、toolは、ソースが公開されていないみたいなので、開発元の方用のantタスクかもしれません。それ以外はコンパイルできました。

**exampleにあるSQLパーサの使い方 [#ded7b6e6]
exampleのテストケースをみると使い方が書いてありました。

***どこに書いてあるかというと [#fae6fde4]
***パッケージ名: [#n1f6c4af]
package org.codehaus.jparsec.examples.sql.parser;

***クラス名: [#i643a3ac]
RelationParserTest

***メソッド名: [#s819af7a]
  public void testSelect() {

***内容の抜粋: [#p5e84e0f]
SQLの問い合わせ文
 select distinct 1, 2 as id from t1, t2
が下記のようにクラスの構造に解析されているのを確認しているテストコードが書かれていました。
    Parser<Relation> parser = RelationParser.select(NUMBER, NUMBER, TABLE);
    assertParser(parser, "select distinct 1, 2 as id from t1, t2",
        new Select(true, 
            Arrays.asList(new Projection(number(1), null), new Projection(number(2), "id")),
            Arrays.asList(table("t1"), table("t2")),
            null, null, null));


***チュートリアル [#sf63f7e4]
http://jparsec.codehaus.org/jparsec2+Tutorial



*構文解析についての考察や疑問 [#b1eb11c3]
**構文解析と正規表現の関係についての考察 [#h2e2d5a5]
すべて正規表現でまかなうことは可能ではないということなのか
文字列は正規表現でマッチングできるが、さらにそれをトークンとして構造的なパターンマッチを行うのが構文解析なのだろうか?

それとも、正規表現を線形的なパターンマッチだとあえて言うならば、構文解析は木という平面的な解析だというのだろうか?

**構文解析とScalaについての考察 [#h95e760f]
構文解析の定義はScala言語のcase match に似ている

Scalaには構文解析ツールとして、パーサコンビネータが標準で用意されている。scala.util.parsing.combinator内に、多くのパッケージ、クラスが定義されている。

***Scalaで電卓を作る [#f329a8c5]

多忙な Java 開発者のための Scala ガイド: 電卓を作る、第 1 回

http://www.ibm.com/developerworks/jp/java/library/j-scala08268.html

多忙な Java 開発者のための Scala ガイド: 電卓を作る、第 2 回

http://www.ibm.com/developerworks/jp/java/library/j-scala10248.html

多忙な Java 開発者のための Scala ガイド: 電卓を作る、第 3 回

http://www.ibm.com/developerworks/jp/java/library/j-scala11218.html

***その他参考URL [#b752ea1d]

構文解析

http://sites.google.com/site/scalamemo/raiburari/parsing


第18回 Scalaとパーザコンビネータ(実装編)

Hello worldのサンプルがのっていてわかりやすいかもです。

http://itpro.nikkeibp.co.jp/article/COLUMN/20100526/348454/

Scala のパーサコンビネータで罠にはまった

http://d.hatena.ne.jp/thinca/20100119/1263837522


**字句解析とクラスについての考察 [#r4ef15da]
そもそも、トークンとはクラスとして置き換えることができそうだ。

たとえば正規表現でデータをマッチさせて、クラスを生成すればいい。

*パーサ開発ソフトウェア [#j91f93e1]
wikiに掲載されていた一覧をのせておきます。

ANTLR 
Bison
Coco/R
GOLD
JavaCC
Lemon Parser
Lex
LRgen
Rebol
SableCC
Spirit Parser Framework
Yacc
-ANTLR 
-Bison
-Coco/R
-GOLD
-JavaCC
-Lemon Parser
-Lex
-LRgen
-Rebol
-SableCC
-Spirit Parser Framework
-Yacc

**JavaCC [#t4b674c6]
***JavaCCによる配列リテラルの表現 [#hbcb40c3]
 Expression[] arrayLiteral() :{...}{
    "[" [expression() ("," expression())*] "]" { ... }
 }
 Expression expression() :{...}{...}

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS