anbt-sql-formatter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/anbt-sql-formatter.gemspec +24 -0
- data/bin/anbt-sql-formatter +50 -0
- data/lgpl-2.1.txt +504 -0
- data/lib/anbt-sql-formatter/coarse-tokenizer.rb +174 -0
- data/lib/anbt-sql-formatter/constants.rb +81 -0
- data/lib/anbt-sql-formatter/exception.rb +30 -0
- data/lib/anbt-sql-formatter/formatter.rb +409 -0
- data/lib/anbt-sql-formatter/helper.rb +73 -0
- data/lib/anbt-sql-formatter/parser.rb +327 -0
- data/lib/anbt-sql-formatter/rule.rb +121 -0
- data/lib/anbt-sql-formatter/token.rb +79 -0
- data/lib/anbt-sql-formatter/version.rb +7 -0
- data/misc/anbt-sql-formatter-customize-example +65 -0
- data/misc/anbt-sql-formatter-for-sakura-editor.js +165 -0
- data/readme.ja.txt +107 -0
- data/readme.txt +58 -0
- data/sample.sql +120 -0
- data/setup.rb +1585 -0
- data/test/helper.rb +17 -0
- data/test/test_coarse-tokenizer.rb +360 -0
- data/test/test_formatter.rb +489 -0
- data/test/test_helper.rb +23 -0
- data/test/test_parser.rb +370 -0
- data/test/test_rule.rb +30 -0
- data/uninstall.rb +20 -0
- metadata +84 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require "pp"
|
4
|
+
|
5
|
+
class AnbtSql
|
6
|
+
class TokenConstants
|
7
|
+
|
8
|
+
# 空文字. TAB,CR等も1つの文字列として含む。
|
9
|
+
SPACE = :space
|
10
|
+
|
11
|
+
# 記号. " <="のような2つで1つの記号もある。
|
12
|
+
SYMBOL = :symbol
|
13
|
+
|
14
|
+
# キーワード. "SELECT", "ORDER"など.
|
15
|
+
KEYWORD = :keyword
|
16
|
+
|
17
|
+
# 名前. テーブル名、列名など。
|
18
|
+
# ダブルクォーテーションが付く場合がある。
|
19
|
+
NAME = :name
|
20
|
+
|
21
|
+
# 値. 数値(整数、実数)、文字列など。
|
22
|
+
VALUE = :value
|
23
|
+
|
24
|
+
# コメント. シングルラインコメントとマルチラインコメントがある。
|
25
|
+
COMMENT = :comment
|
26
|
+
|
27
|
+
# SQL文の終わり.
|
28
|
+
END_OF_SQL = :end_of_sql
|
29
|
+
|
30
|
+
# 解析不可能なトークン. 通常のSQLではありえない。
|
31
|
+
UNKNOWN = :unknown
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
# [_type] type of token
|
37
|
+
# [string] string of token
|
38
|
+
# [pos] ソース文字列の先頭からのトークンの位置をあらわす。
|
39
|
+
# 値は ゼロ(ZERO)オリジン。
|
40
|
+
# デフォルト値 -1 の場合には「位置情報に意味がない」ことをあらわす。
|
41
|
+
#
|
42
|
+
class AbstractToken
|
43
|
+
attr_accessor :_type, :string, :pos
|
44
|
+
|
45
|
+
@_type = nil
|
46
|
+
|
47
|
+
@string = nil
|
48
|
+
|
49
|
+
@pos = -1
|
50
|
+
|
51
|
+
#
|
52
|
+
# このバリューオブジェクトの文字列表現を取得する。
|
53
|
+
#
|
54
|
+
# オブジェクトのシャロー範囲でしか to_s されない点に注意。
|
55
|
+
#
|
56
|
+
# @return:: バリューオブジェクトの文字列表現。
|
57
|
+
#
|
58
|
+
def to_s
|
59
|
+
buf = ""
|
60
|
+
buf << "AbstractAnbtSqlToken["
|
61
|
+
buf << "_type=" + @_type
|
62
|
+
buf << ",string=" + @string
|
63
|
+
buf << ",pos=" + @pos
|
64
|
+
buf << "]"
|
65
|
+
|
66
|
+
buf
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
class Token < AbstractToken
|
72
|
+
def initialize(type, string, pos=nil)
|
73
|
+
@_type = type
|
74
|
+
@string = string
|
75
|
+
|
76
|
+
@pos = pos || -1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
require "pp"
|
5
|
+
|
6
|
+
require "anbt-sql-formatter/formatter"
|
7
|
+
|
8
|
+
=begin
|
9
|
+
rule のプロパティを修正するだけではなく
|
10
|
+
さらに踏み込んでカスタマイズしたい場合は
|
11
|
+
formatter.rb 内の AnbtSql::Formatter#format_list_main_loop
|
12
|
+
などをオーバーライドすると良いでしょう。
|
13
|
+
|
14
|
+
Ruby に詳しくない場合は「Ruby オープンクラス」
|
15
|
+
「Ruby モンキーパッチ」で検索してみてください。
|
16
|
+
|
17
|
+
継承の方が分かりやすいという場合は
|
18
|
+
継承でも良いと思います。
|
19
|
+
=end
|
20
|
+
|
21
|
+
# class AnbtSql::Formatter
|
22
|
+
# def format_list_main_loop(tokens)
|
23
|
+
# # メソッドの処理を書き換える
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
|
27
|
+
|
28
|
+
if $0 == __FILE__
|
29
|
+
src = ""
|
30
|
+
while line = gets
|
31
|
+
src << line
|
32
|
+
end
|
33
|
+
|
34
|
+
rule = AnbtSql::Rule.new
|
35
|
+
|
36
|
+
# rule のプロパティを修正することで
|
37
|
+
# 動作をカスタマイズします。
|
38
|
+
# 詳細については rule.rb をご覧ください。
|
39
|
+
|
40
|
+
# キーワードを大文字・小文字に変換しない
|
41
|
+
rule.keyword = AnbtSql::Rule::KEYWORD_NONE
|
42
|
+
|
43
|
+
# 複数単語のキーワードを登録
|
44
|
+
rule.kw_multi_words << "INNER JOIN"
|
45
|
+
rule.kw_nl_x << "INNER JOIN"
|
46
|
+
|
47
|
+
# "THEN" の前で改行するのをやめさせる
|
48
|
+
rule.kw_nl_x -= ["THEN"]
|
49
|
+
# "WHEN" の前で改行させるようにする
|
50
|
+
rule.kw_nl_x << "WHEN"
|
51
|
+
|
52
|
+
# User defined additional functions:
|
53
|
+
# ユーザ定義関数の追加:
|
54
|
+
%w(count sum substr date).each{|func_name|
|
55
|
+
rule.function_names << func_name.upcase
|
56
|
+
}
|
57
|
+
|
58
|
+
#rule.indent_string = " "
|
59
|
+
#rule.indent_string = "('-')"
|
60
|
+
rule.indent_string = "| "
|
61
|
+
|
62
|
+
formatter = AnbtSql::Formatter.new(rule)
|
63
|
+
result = formatter.format(src)
|
64
|
+
print result
|
65
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
// ���� 3�̃p�X�����g���̊��ɍ��킹�ď��������Ă��������B
|
2
|
+
|
3
|
+
// ruby.exe �̏ꏊ
|
4
|
+
var envPath = "C:\\ruby\\bin\\ruby.exe";
|
5
|
+
|
6
|
+
// �T�N���G�f�B�^�̃}�N����u���Ă���t�H���_
|
7
|
+
var macroDir = "C:\\apps\\sakuraW_rXXXX\\macros\\";
|
8
|
+
|
9
|
+
// anbt-sql-formatter �̃t�H���_
|
10
|
+
var asfHome = "C:\\apps\\anbt-sql-formatter\\";
|
11
|
+
|
12
|
+
|
13
|
+
//================================
|
14
|
+
|
15
|
+
|
16
|
+
function checkPath( path ){
|
17
|
+
if( ! path.match( /\\$/ ) ){
|
18
|
+
path += "\\";
|
19
|
+
}
|
20
|
+
|
21
|
+
return path;
|
22
|
+
}
|
23
|
+
|
24
|
+
macroDir = checkPath( macroDir );
|
25
|
+
asfHome = checkPath( asfHome );
|
26
|
+
|
27
|
+
|
28
|
+
//================================
|
29
|
+
|
30
|
+
|
31
|
+
var scriptPath = asfHome + "bin\\anbt-sql-formatter";
|
32
|
+
var libDir = asfHome + "lib";
|
33
|
+
|
34
|
+
var macroPath = macroDir + "anbt-sql-formatter-for-sakura-editor.js";
|
35
|
+
var tempFileSrc = macroDir + "____temp_src.txt";
|
36
|
+
var tempFileDest = macroDir + "____temp_dest.txt";
|
37
|
+
|
38
|
+
var timeoutSec = 10;
|
39
|
+
|
40
|
+
|
41
|
+
//================================
|
42
|
+
|
43
|
+
|
44
|
+
var ForReading = 1;
|
45
|
+
var ForWriting = 2;
|
46
|
+
|
47
|
+
var wShell = new ActiveXObject( "WScript.Shell" );
|
48
|
+
|
49
|
+
|
50
|
+
//================================
|
51
|
+
|
52
|
+
|
53
|
+
function pathExists( varName, type ){
|
54
|
+
var path = eval(varName);
|
55
|
+
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
|
56
|
+
var typeMsg;
|
57
|
+
var result = true;
|
58
|
+
|
59
|
+
switch(type){
|
60
|
+
case "file":
|
61
|
+
typeMsg = "�t�@�C��";
|
62
|
+
if( ! fso.FileExists( path ) ){
|
63
|
+
result = false;
|
64
|
+
}
|
65
|
+
break;
|
66
|
+
case "folder":
|
67
|
+
typeMsg = "�t�H���_";
|
68
|
+
if( ! fso.FolderExists( path ) ){
|
69
|
+
result = false;
|
70
|
+
}
|
71
|
+
break;
|
72
|
+
default:
|
73
|
+
wShell.Popup( "�ϐ� type �̎w�肪�Ԉ���Ă��܂��B" );
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
|
77
|
+
if( ! result ){
|
78
|
+
wShell.Popup( typeMsg + ' "' + path + "\" ��������܂���B\n�ϐ� " + varName + " �̃p�X�w����m�F���Ă��������B" );
|
79
|
+
return false;
|
80
|
+
}else{
|
81
|
+
return true;
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
//================================
|
87
|
+
|
88
|
+
|
89
|
+
function writeFile( path, content ){
|
90
|
+
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
|
91
|
+
var fout = fso.CreateTextFile( path );
|
92
|
+
fout.WriteLine( content );
|
93
|
+
fout.Close();
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
function readFile( path ){
|
98
|
+
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
|
99
|
+
var fout = fso.OpenTextFile( path, ForReading );
|
100
|
+
var content = fout.ReadAll();
|
101
|
+
fout.Close();
|
102
|
+
return content;
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
//================================
|
107
|
+
|
108
|
+
|
109
|
+
function callFromSakuraEditor(){
|
110
|
+
if( ! pathExists( "envPath" , "file" )
|
111
|
+
|| ! pathExists( "macroDir", "folder" )
|
112
|
+
|| ! pathExists( "asfHome" , "folder" )
|
113
|
+
){
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
|
117
|
+
var selectedStr = GetSelectedString(0);
|
118
|
+
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
|
119
|
+
if( fso.FileExists( tempFileSrc ) ){ fso.GetFile( tempFileSrc ).Delete(); }
|
120
|
+
if( fso.FileExists( tempFileDest ) ){ fso.GetFile( tempFileDest ).Delete(); }
|
121
|
+
|
122
|
+
writeFile( tempFileSrc, selectedStr );
|
123
|
+
|
124
|
+
var commandStr = 'cscript "' + macroPath + '"';
|
125
|
+
var vbHide = 0; //�E�B���h�E���\��
|
126
|
+
wShell.Run( commandStr, vbHide, true );
|
127
|
+
|
128
|
+
insText( readFile( tempFileDest ) );
|
129
|
+
|
130
|
+
if( fso.FileExists( tempFileSrc ) ){ fso.GetFile( tempFileSrc ).Delete(); }
|
131
|
+
if( fso.FileExists( tempFileDest ) ){ fso.GetFile( tempFileDest ).Delete(); }
|
132
|
+
}
|
133
|
+
|
134
|
+
|
135
|
+
function callFromCScript(){
|
136
|
+
var commandStr = 'cmd /c ' + envPath + ' -I ' + libDir + ' "' + scriptPath +'" "'+ tempFileSrc + '"' ;
|
137
|
+
var execObj = wShell.Exec( commandStr );
|
138
|
+
|
139
|
+
// �������I�����邩�A�܂��̓^�C���A�E�g����܂ő҂�
|
140
|
+
var startSec = (new Date()).getTime();
|
141
|
+
while( execObj.status == 0){
|
142
|
+
WScript.Sleep( 500 );
|
143
|
+
if( (new Date()).getTime() - startSec > timeoutSec ){ break; }
|
144
|
+
}
|
145
|
+
|
146
|
+
var result;
|
147
|
+
if( execObj.exitCode == 0){
|
148
|
+
result = execObj.StdOut.ReadAll();
|
149
|
+
}else{
|
150
|
+
result = execObj.StdErr.ReadAll();
|
151
|
+
}
|
152
|
+
writeFile( tempFileDest, result );
|
153
|
+
}
|
154
|
+
|
155
|
+
|
156
|
+
//================================
|
157
|
+
|
158
|
+
|
159
|
+
if( typeof(Editor) != "undefined" ){
|
160
|
+
callFromSakuraEditor();
|
161
|
+
}else if( typeof(WScript) != "undefined" ){
|
162
|
+
callFromCScript();
|
163
|
+
}else{
|
164
|
+
wShell.Popup( "�Ăяo�������킩��܂���B" );
|
165
|
+
}
|
data/readme.ja.txt
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
= anbt-sql-formatter
|
2
|
+
|
3
|
+
(C) 2010 sonota <yosiot8753@gmail.com>
|
4
|
+
|
5
|
+
|
6
|
+
== 概要
|
7
|
+
|
8
|
+
Java製の SQL整形ツール BlancoSqlFormatter
|
9
|
+
|
10
|
+
blancoSqlFormatter - SQL整形ライブラリ
|
11
|
+
http://www.igapyon.jp/blanco/blancosqlformatter.html
|
12
|
+
|
13
|
+
を Ruby に移植したものです。
|
14
|
+
完全な移植を目指したものではなく、改造が加わっています。
|
15
|
+
|
16
|
+
|
17
|
+
== インストール
|
18
|
+
|
19
|
+
$ ruby setup.rb
|
20
|
+
|
21
|
+
|
22
|
+
== 使い方
|
23
|
+
|
24
|
+
標準入力で SQL を渡します。
|
25
|
+
|
26
|
+
$ echo "select a,b from c;" | anbt-sql-formatter
|
27
|
+
SELECT
|
28
|
+
a
|
29
|
+
, b
|
30
|
+
FROM
|
31
|
+
c
|
32
|
+
;
|
33
|
+
$
|
34
|
+
|
35
|
+
後は適宜 Emacs などエディタと連携させたりして使ってください。
|
36
|
+
|
37
|
+
|
38
|
+
== ライセンス
|
39
|
+
|
40
|
+
GNU Lesser General Public License.
|
41
|
+
|
42
|
+
|
43
|
+
== 著者
|
44
|
+
|
45
|
+
sonota:: Ruby へ移植
|
46
|
+
|
47
|
+
次はオリジナルの Java版のクレジットの写しです。
|
48
|
+
|
49
|
+
渡辺義則 / Yoshinori WATANABE / A-san:: 初期バージョンの開発
|
50
|
+
伊賀敏樹 (Tosiki Iga / いがぴょん):: 維持メンテ担当
|
51
|
+
|
52
|
+
|
53
|
+
== Java版からの変更点覚書
|
54
|
+
|
55
|
+
=== 全般
|
56
|
+
|
57
|
+
* 名前空間 AnbtSql を導入。また、ファイル名を
|
58
|
+
AnbtSqlFormatter.java → formatter.rb
|
59
|
+
といったように変更。
|
60
|
+
|
61
|
+
=== formatter.rb / coarse-tokenizer.rb
|
62
|
+
|
63
|
+
* #format_list から #format_list_main_loop などを分離した。
|
64
|
+
* 粗トーカナイズ処理を coarse-tokenizer.rb に分離。
|
65
|
+
おそらく Java版からの一番大きな変更。
|
66
|
+
本質的に SQL とは関係の薄い文字列とコメントのトーカナイズだけを別に行う。
|
67
|
+
|
68
|
+
|
69
|
+
== その他の変更点
|
70
|
+
|
71
|
+
* フロントエンドのサンプル追加。
|
72
|
+
* テストを追加(特に単体テスト)。
|
73
|
+
|
74
|
+
|
75
|
+
== 処理の概要
|
76
|
+
|
77
|
+
1. AnbtSql::Parser
|
78
|
+
1. 粗トークン(文字列、コメント、それ以外)に分割
|
79
|
+
2. トークンに分割
|
80
|
+
2. AnbtSql::Formatter
|
81
|
+
1. トークンのリストを元にインデント等を整形
|
82
|
+
|
83
|
+
|
84
|
+
== カスタマイズ
|
85
|
+
|
86
|
+
* formatter に与える rule に追加
|
87
|
+
* 関数として扱うキーワードの追加
|
88
|
+
* 特定の改行・インデンテーション規則に対応するキーワードを追加
|
89
|
+
* インデントに用いる文字を指定
|
90
|
+
* キーワードの大文字・小文字指定
|
91
|
+
* もっと細かくカスタマイズしたい場合は
|
92
|
+
AnbtSql::Formatter#format_list_main_loop を
|
93
|
+
継承、またはモンキーパッチング等で修正(オーバーライド)してください。
|
94
|
+
|
95
|
+
|
96
|
+
== テスト
|
97
|
+
|
98
|
+
$ ruby setup.rb test
|
99
|
+
|
100
|
+
|
101
|
+
== その他
|
102
|
+
|
103
|
+
* 一行コメントの次でインデントが乱れる点が修正候補となっていたため
|
104
|
+
修正した。
|
105
|
+
AnbtSql::Formatter#format_list_main_loop 内で対処したが、
|
106
|
+
やっていることは末尾の改行を削っているだけなので
|
107
|
+
もっと前のトークン分割時に対処した方が良いかも。
|
data/readme.txt
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= anbt-sql-formatter
|
2
|
+
|
3
|
+
(C) 2010 sonota (yosiot8753@gmail.com)
|
4
|
+
|
5
|
+
|
6
|
+
== Description
|
7
|
+
|
8
|
+
A tool for SQL formatting ported from
|
9
|
+
{BlancoSqlFormatter}[http://sourceforge.jp/projects/blancofw/releases/?package_id=4732].
|
10
|
+
|
11
|
+
|
12
|
+
== Install
|
13
|
+
|
14
|
+
$ ruby setup.rb
|
15
|
+
|
16
|
+
|
17
|
+
== Usage
|
18
|
+
|
19
|
+
$ echo "select a,b from c;" | anbt-sql-formatter
|
20
|
+
SELECT
|
21
|
+
a
|
22
|
+
, b
|
23
|
+
FROM
|
24
|
+
c
|
25
|
+
;
|
26
|
+
$
|
27
|
+
|
28
|
+
|
29
|
+
== License
|
30
|
+
|
31
|
+
GNU Lesser General Public License.
|
32
|
+
|
33
|
+
|
34
|
+
== Authors
|
35
|
+
|
36
|
+
sonota:: Porting to Ruby
|
37
|
+
|
38
|
+
Following are Authors of BlancoSqlFormatter(original Java version).
|
39
|
+
|
40
|
+
渡辺義則 / Yoshinori WATANABE / A-san:: Early development
|
41
|
+
伊賀敏樹 (Tosiki Iga / いがぴょん):: Maintainance
|
42
|
+
|
43
|
+
|
44
|
+
== Customize
|
45
|
+
|
46
|
+
* In AnbtSql::Rule:
|
47
|
+
* Function names
|
48
|
+
* Rules for linefeed and indentation
|
49
|
+
* Characters for indentation
|
50
|
+
* Upcase or Downcase
|
51
|
+
* More farther:
|
52
|
+
Override AnbtSql::Formatter#format_list_main_loop
|
53
|
+
by inheritance or monkeypathcing.
|
54
|
+
|
55
|
+
|
56
|
+
== Test
|
57
|
+
|
58
|
+
$ ruby setup.rb test
|