each_sql 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ require 'citrus'
2
+ require 'erubis'
3
+ require 'quote_unquote'
4
+
5
+ class EachSQL
6
+ module Parser
7
+ @@parser = {}
8
+ # @param[Symbol] type RDBMS type: :default|:mysql|:oracle|:postgres
9
+ # @param[String] delimiter SQL delimiter
10
+ # @return[Module] Citrus parser
11
+ def self.parser_for type, delimiter = ';'
12
+ # Is there any better way of handling dynamic changes?
13
+
14
+ return @@parser[[type, delimiter]] if @@parser[[type, delimiter]]
15
+
16
+ path = File.join( File.dirname(__FILE__), 'parser/sql.citrus.erb' )
17
+ erb = Erubis::Eruby.new( File.read path )
18
+ suffix = @@parser.length.to_s
19
+
20
+ Citrus.eval erb.result(binding)
21
+
22
+ @@parser[[type, delimiter]] =
23
+ case type
24
+ when :default
25
+ eval "EachSQL::Parser::Default#{suffix}"
26
+ when :mysql
27
+ eval "EachSQL::Parser::MySQL#{suffix}"
28
+ when :oracle
29
+ eval "EachSQL::Parser::Oracle#{suffix}"
30
+ when :postgres, :postgresql
31
+ eval "EachSQL::Parser::PostgreSQL#{suffix}"
32
+ else
33
+ raise NotImplementedError.new(
34
+ "Parser not implemented for #{type}. Try use :default instead.")
35
+ end
36
+ end
37
+ end#Parser
38
+ end#EachSQL
39
+
@@ -0,0 +1,239 @@
1
+ grammar EachSQL::Parser::Default<%= suffix %>
2
+ rule program
3
+ (execution_block | empty | delimiter)* leftover?
4
+ end
5
+
6
+ rule leftover
7
+ .+
8
+ end
9
+
10
+ rule execution_block
11
+ (begin_end_block | sql_statement)
12
+ end
13
+
14
+ rule begin_end_block
15
+ begin_block
16
+ (begin_end_block | sql_statement | empty)+
17
+ end_block
18
+ delimiters
19
+ end
20
+
21
+ rule declare
22
+ /\bdeclare\b/i
23
+ end
24
+
25
+ rule begin_block
26
+ /\bbegin\b/i
27
+ end
28
+
29
+ rule end_block
30
+ # end something
31
+ /\bend\b/i (empty+ chunk*)?
32
+ end
33
+
34
+ rule sql_statement
35
+ (empty | delimiter)*
36
+
37
+ !(begin_block | end_block) chunk
38
+
39
+ (chunk | empty)*
40
+ delimiters
41
+ end
42
+
43
+ rule delimiters
44
+ empty* delimiter
45
+ (empty | delimiter)*
46
+ end
47
+
48
+ rule chunk
49
+ (qword | dqword | btword | word)
50
+ end
51
+
52
+ rule qword
53
+ sq (!sq .)* sq
54
+ end
55
+
56
+ rule dqword
57
+ dq (!dq .)* dq
58
+ end
59
+
60
+ rule btword
61
+ bt (!bt .)* bt
62
+ end
63
+
64
+ rule word
65
+ !(space | delimiter | '/*' | "--" | sq | dq | bt) .
66
+ end
67
+
68
+ rule empty
69
+ c_comment | l_comment | space
70
+ end
71
+
72
+ rule c_comment
73
+ '/*' (!'*/' .)* '*/'
74
+ end
75
+
76
+ rule l_comment
77
+ '--' (![\n\Z] .)*
78
+ end
79
+
80
+ rule space
81
+ [\s]
82
+ end
83
+
84
+ rule sq
85
+ "'"
86
+ end
87
+
88
+ rule dq
89
+ '"'
90
+ end
91
+
92
+ # Not ANSI standard. but.. wouldn't hurt.
93
+ rule bt
94
+ '`'
95
+ end
96
+
97
+ rule delimiter
98
+ <%= delimiter.qq('\"') %>
99
+ end
100
+ end
101
+
102
+ grammar EachSQL::Parser::MySQL<%= suffix %>
103
+ include EachSQL::Parser::Default<%= suffix %>
104
+
105
+ rule root
106
+ (execution_block | empty | delimiter)* leftover?
107
+ end
108
+
109
+ rule execution_block
110
+ (begin_end_block | sql_statement)
111
+ end
112
+
113
+ rule begin_end_block
114
+ begin_block
115
+ (begin_end_block | sql_statement | empty)+
116
+ end_block
117
+ delimiters
118
+ end
119
+
120
+ rule end_block
121
+ # end something
122
+ /\bend\b/i (empty+ chunk*)?
123
+ end
124
+
125
+ rule sql_statement
126
+ (empty | delimiter)*
127
+
128
+ !(begin_block | end_block) chunk
129
+
130
+ (chunk | empty)*
131
+ delimiters
132
+ end
133
+
134
+ rule chunk
135
+ (qword | dqword | btword | word)
136
+ end
137
+
138
+ rule qword
139
+ # Order matters
140
+ sq ("\\'" | (!sq .))* sq
141
+ end
142
+ end
143
+
144
+ grammar EachSQL::Parser::PostgreSQL<%= suffix %>
145
+ include EachSQL::Parser::Default<%= suffix %>
146
+
147
+ # TODO: code dupe
148
+
149
+ rule root
150
+ (execution_block | empty | delimiter)* leftover?
151
+ end
152
+
153
+ rule execution_block
154
+ (begin_end_block | sql_statement)
155
+ end
156
+
157
+ rule begin_end_block
158
+ begin_block
159
+ (begin_end_block | sql_statement | empty)+
160
+ end_block
161
+ delimiters
162
+ end
163
+
164
+ rule end_block
165
+ # end something
166
+ /\bend\b/i (empty+ chunk*)?
167
+ end
168
+
169
+ rule sql_statement
170
+ (empty | delimiter)*
171
+
172
+ !(begin_block | end_block) chunk
173
+
174
+ (chunk | empty)*
175
+ delimiters
176
+ end
177
+
178
+ rule chunk
179
+ (dollar_tag | double_dollar | qword | dqword | btword | word)
180
+ end
181
+
182
+ rule word
183
+ !(space | delimiter | '/*' | "--" | /\$[^$\s]+\$/ | '$$' | sq | dq | bt) .
184
+ end
185
+
186
+ rule dollar_tag
187
+ /(\$[^$\s]+\$).*?\1/m
188
+ end
189
+
190
+ rule double_dollar
191
+ '$$' (!'$$' .)* '$$'
192
+ end
193
+ end
194
+
195
+ grammar EachSQL::Parser::Oracle<%= suffix %>
196
+ include EachSQL::Parser::Default<%= suffix %>
197
+
198
+ rule root
199
+ (execution_block | empty | '/' | delimiter)* leftover?
200
+ end
201
+
202
+ rule execution_block
203
+ (
204
+ creation |
205
+ declare_begin_end_block |
206
+ begin_end_block |
207
+ sql_statement
208
+ )
209
+ end
210
+
211
+ rule declare_begin_end_block
212
+ declare
213
+ sql_statement+
214
+ begin_end_block
215
+ end
216
+
217
+ rule sql_statement
218
+ (empty | delimiter)*
219
+
220
+ !('/' | create | declare | begin_block | end_block) chunk
221
+
222
+ (chunk | empty)*
223
+ delimiters
224
+ end
225
+
226
+ rule creation
227
+ create
228
+ sql_statement+
229
+ (
230
+ ( end_block delimiters ) | begin_end_block
231
+ )
232
+ end
233
+
234
+ rule create
235
+ /\bcreate\b/i (empty+ /or/i empty+ /replace/i)? empty+
236
+ /\b(procedure|function|trigger|package)\b/i
237
+ end
238
+ end
239
+
@@ -2,257 +2,13 @@
2
2
 
3
3
  $LOAD_PATH << File.dirname(__FILE__)
4
4
  require 'helper'
5
+ require 'yaml'
5
6
 
6
7
  class TestEachSql < Test::Unit::TestCase
7
- def setup
8
- @sql = [
9
- "-------------- begin-end block;
10
- declare
11
- /* end; */
12
- /* begin */
13
- null;
14
- null;
15
- null;
16
- begin
17
- /* end */
18
- null;
19
- end",
20
- "-------------- begin-end block;
21
- begin
22
- -- begin-end block;
23
- -- line comment
24
- -- line comment
25
- -- line comment
26
- begin
27
- null;
28
- begin
29
- null;
30
- end;
31
- end;
32
- -- end
33
- /* end */
34
- end",
35
- "select * from a",
36
- "select
37
- *
38
- from
39
- b",
40
- "select 'abc', 'abc;', 'abc''', 'abc/*', 'abc--' from c",
41
-
42
- "select
43
- /*+ help */ *
44
- from
45
- d",
46
- "select * from /* block comment ; */ e",
47
- "select *
48
- from -- line comment ; /* ;; */
49
- f",
50
- "-------------- begin-end block;
51
- declare
52
- /* end; */
53
- /* begin */
54
- null;
55
- null;
56
- null;
57
- begin
58
- -- begin-end block;
59
- -- line comment
60
- -- line comment
61
- -- line comment
62
- begin
63
- null;
64
- begin
65
- null;
66
- end;
67
- end;
68
- -- end
69
- /* end */
70
- end",
71
- "select * from dual",
72
- "select b `begin` from dual",
73
- 'select b "begin" from dual',
74
- 'select
75
- begin , begin.* from begin'
76
- ]
77
-
78
- @oracle = [
79
- 'select * from dual',
80
- 'create /* procedure */ sequence a',
81
- "create package something as
82
- procedure log;
83
- procedure log;
84
- procedure log;
85
- end something;",
86
- "Create or replace Procedure tmmp(p1 number default 'begin', p2 number) as
87
- str number(8, 2) := 1 / 4;
88
- begin
89
- 1 / 2;
90
- begin
91
- 1 / 4;
92
- null;
93
- end;
94
- exception
95
- when others then
96
- raise;
97
- end;",
98
- "-- declaration
99
- declare
100
- a int;
101
- begin
102
- 1 / 2;
103
- begin
104
- 1 / 4;
105
- null;
106
- end;
107
- exception
108
- when others then
109
- raise;
110
- end;",
111
- "begin
112
- null;
113
- end;",
114
- "begin
115
- null;
116
- end;",
117
- "select * from dual",
118
- "select begin, end, create, procedure, end, from dual",
119
- "select * from dual",
120
- "-- TMP_DB_TOOLS_CONV
121
- begin
122
- execute immediate 'DROP TABLE TMP_DB_TOOLS_CONV CASCADE CONSTRAINTS';
123
- exception
124
- when others then
125
- null;
126
- end;"
127
- ]
128
-
129
- @oracle_script = "
130
- select * from dual;
131
- ;;;;;;;
132
- ;;;
133
- ;;
134
-
135
- create /* procedure */ sequence a;
136
- create package something as
137
- procedure log;
138
- procedure log;
139
- procedure log;
140
- end something;
141
- /
142
-
143
- Create or replace Procedure tmmp(p1 number default 'begin', p2 number) as
144
- str number(8, 2) := 1 / 4;
145
- begin
146
- 1 / 2;
147
- begin
148
- 1 / 4;
149
- null;
150
- end;
151
- exception
152
- when others then
153
- raise;
154
- end;
155
- /
156
- -- declaration
157
- declare
158
- a int;
159
- begin
160
- 1 / 2;
161
- begin
162
- 1 / 4;
163
- null;
164
- end;
165
- exception
166
- when others then
167
- raise;
168
- end;
169
- /
170
- begin
171
- null;
172
- end;
173
- /
174
- begin
175
- null;
176
- end;
177
- /
178
- select * from dual;
179
- ;
180
- ;
181
- ;
182
-
183
-
184
- ;;;;;;
185
- ;
186
-
187
- select begin, end, create, procedure, end, from dual;
188
- select * from dual;
189
-
190
- -- TMP_DB_TOOLS_CONV
191
- begin
192
- execute immediate 'DROP TABLE TMP_DB_TOOLS_CONV CASCADE CONSTRAINTS';
193
- exception
194
- when others then
195
- null;
196
- end;
197
- /
198
- "
199
-
200
- @mysql = [
201
- "drop procedure if exists proc",
202
- "create procedure proc(p1 int, p2 int)
203
- begin
204
- null;
205
- begin
206
- null;
207
- end;
208
- end",
209
- "drop procedure if exists proc2",
210
- "create procedure proc(p1 int, p2 int)
211
- begin
212
- null;
213
-
214
- end",
215
- "select * from dual",
216
- "select b `begin` from dual",
217
- 'select b "begin" from dual',
218
- 'select
219
- begin , begin.* from begin'
220
- ]
221
- @mysql_script = "
222
- delimiter //
223
- drop procedure if exists proc //
224
- create procedure proc(p1 int, p2 int)
225
- begin
226
- null;
227
- begin
228
- null;
229
- end;
230
- end //
231
- delimiter ;
232
-
233
- delimiter $$
234
- drop procedure if exists proc2 $$
235
- create procedure proc(p1 int, p2 int)
236
- begin
237
- null;
238
-
239
- end $$
240
- delimiter ;
241
-
242
- select * from dual;;;;;
243
- ;;;select b `begin` from dual;
244
- select b \"begin\" from dual;
245
- select
246
- begin , begin.* from begin"
247
- end
248
-
249
- def test_sql
8
+ def _test_empty
250
9
  [nil, "", " \n" * 10].each do |input|
251
10
  EachSQL(input).each do |sql|
252
- assert false, 'Should not enumerate'
253
- end
254
-
255
- EachSQL(input) do |sql|
11
+ p sql
256
12
  assert false, 'Should not enumerate'
257
13
  end
258
14
 
@@ -262,36 +18,80 @@ select
262
18
  end
263
19
  assert true, 'No error expected'
264
20
  end
21
+ end
22
+
23
+ def _test_parser_cache
24
+ [:default, :mysql, :oracle, :postgres].each do |typ|
25
+ %w[';', '$$', '//'].each do |delim|
26
+ arr =
27
+ 10.times.map {
28
+ EachSQL::Parser.parser_for typ, delim
29
+ }
30
+ p arr
31
+ assert_equal 10, arr.length
32
+ assert_equal 1, arr.uniq.length
33
+ end
34
+ end
35
+
36
+ end
265
37
 
266
- script = @sql.map { |e| e.strip + ';;;;' }.join $/
267
- EachSQL(script).each_with_index do |sql,idx|
268
- puts sql
269
- puts '-' * 40
270
- assert_equal @sql[idx], sql
271
- end
272
- assert_equal EachSQL(script).to_a, EachSQL(script).map { |e| e }
38
+ def test_sql
39
+ common = YAML.load(
40
+ File.read(
41
+ File.join(
42
+ File.dirname(__FILE__), "yml/common.yml")))
43
+ [:default, :mysql, :oracle, :postgres].each do |typ|
44
+ data = YAML.load(
45
+ File.read(
46
+ File.join(
47
+ File.dirname(__FILE__), "yml/#{typ}.yml")))
48
+
49
+ script = nil
50
+ [common, data].each do |d|
51
+ script = d['all']
52
+ EachSQL(script, typ).each_with_index do |sql, idx|
53
+ expect = d['each'][idx].chomp
54
+ puts sql
55
+ puts '-' * 40
56
+ if typ == :oracle
57
+ assert expect == sql || sql == expect + ';',
58
+ [expect, 'x' * 80, sql].join($/)
59
+ else
60
+ assert_equal expect, sql
61
+ end
62
+ end
63
+ end
64
+
65
+ cnt = 0
66
+ EachSQL(script, typ) do |sql|
67
+ cnt += 1
68
+ end
69
+ assert_equal data['each'].length, cnt
70
+ assert_equal cnt, EachSQL(script, typ).to_a.length
71
+ assert_equal EachSQL(script, typ).to_a, EachSQL(script, typ).map { |e| e }
72
+ end
273
73
  end
274
74
 
275
- def test_oracle
276
- EachSQL(@oracle_script, :oracle).each_with_index do |sql,idx|
277
- puts sql
278
- puts '-' * 40
279
- assert_equal @oracle[idx], sql
280
- end
281
- end
282
-
283
- def test_mysql
284
- EachSQL(@mysql_script, :mysql).each_with_index do |sql,idx|
285
- puts sql
286
- puts '-' * 40
287
- assert_equal @mysql[idx], sql
288
- end
289
- end
290
-
291
- def _test_postgres
292
- EachSQL(File.read(File.dirname(__FILE__) + '/postgres.sql'), :postgres).each_with_index do |sql,idx|
293
- puts sql
294
- puts '-' * 40
295
- end
296
- end
75
+ # def test_oracle
76
+ # EachSQL(@oracle_script, :oracle).each_with_index do |sql,idx|
77
+ # puts sql
78
+ # puts '-' * 40
79
+ # assert_equal @oracle[idx], sql
80
+ # end
81
+ # end
82
+
83
+ # def test_mysql
84
+ # EachSQL(@mysql_script, :mysql).each_with_index do |sql,idx|
85
+ # puts sql
86
+ # puts '-' * 40
87
+ # assert_equal @mysql[idx], sql
88
+ # end
89
+ # end
90
+
91
+ # def _test_postgres
92
+ # EachSQL(File.read(File.dirname(__FILE__) + '/postgres.sql'), :postgres).each_with_index do |sql,idx|
93
+ # puts sql
94
+ # puts '-' * 40
95
+ # end
96
+ # end
297
97
  end