twowaysql 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.skip +7 -0
- data/Manifest.txt +4 -4
- data/README.txt +6 -6
- data/issues/issue-2a172da16d3a59adc8448f2e36ec48b36951e02d.yaml +30 -0
- data/issues/issue-664986b219202ff1948cab717b56e7540f493561.yaml +10 -2
- data/issues/issue-6daccddf089d11d42bf016897da98f70cf5ab46c.yaml +15 -3
- data/issues/issue-a185b4247f64c1104bc49ebf823b57b5de3d06f8.yaml +26 -0
- data/issues/project.yaml +6 -2
- data/lib/twowaysql/parser.rb +96 -69
- data/lib/twowaysql/parser.y +94 -67
- data/lib/twowaysql/template.rb +6 -5
- data/lib/twowaysql/version.rb +1 -1
- data/spec/large_sql_spec.rb +90 -0
- data/spec/twowaysql_spec.rb +17 -0
- data/tasks/deployment.rake +7 -8
- data/tasks/ditz.rake +19 -0
- data/tasks/rcov.rake +5 -0
- data/tasks/website.rake +1 -1
- metadata +6 -6
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -82
data/History.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== release_0_4 / 2008-09-29
|
2
|
+
* line number support on parse error
|
3
|
+
* integrate coverage report into website
|
4
|
+
* integrate ditz to 'push-button release'
|
5
|
+
* Change ambiguous class name TwoWaySQL::Result to TwoWaySQL::MergeResult
|
6
|
+
|
1
7
|
== release_0_3 / 2008-09-10
|
2
8
|
* better whitespace handling
|
3
9
|
* Whitespace compaction mode
|
data/Manifest.skip
ADDED
data/Manifest.txt
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
History.txt
|
2
2
|
License.txt
|
3
|
+
Manifest.skip
|
3
4
|
Manifest.txt
|
4
5
|
README.txt
|
5
6
|
Rakefile
|
@@ -10,6 +11,7 @@ issues/issue-1cee7e821865a216674832b0186bd92792680571.yaml
|
|
10
11
|
issues/issue-25efcfc383f3b0f6c0e2730ae7c2975bb2b3de26.yaml
|
11
12
|
issues/issue-279105dd0d9f03514d318f5eab5e99c4c2d47fda.yaml
|
12
13
|
issues/issue-28cde89ed3eb306957edc90595b1d16bf43daf42.yaml
|
14
|
+
issues/issue-2a172da16d3a59adc8448f2e36ec48b36951e02d.yaml
|
13
15
|
issues/issue-39023ea09e17e2d64bcef03aa59cdfe38b78ad5b.yaml
|
14
16
|
issues/issue-4bc308d55ae91f266e656162a4147d356de1166c.yaml
|
15
17
|
issues/issue-5c973ef5bb074eacca0c6c84f7d27c4267773ea8.yaml
|
@@ -17,6 +19,7 @@ issues/issue-664986b219202ff1948cab717b56e7540f493561.yaml
|
|
17
19
|
issues/issue-6daccddf089d11d42bf016897da98f70cf5ab46c.yaml
|
18
20
|
issues/issue-897995fa10377eabdf597e8e7692f17087c76923.yaml
|
19
21
|
issues/issue-901f65630639507c8b05b466790e9f22256c6450.yaml
|
22
|
+
issues/issue-a185b4247f64c1104bc49ebf823b57b5de3d06f8.yaml
|
20
23
|
issues/issue-bd38c1cdc965d73dd629a81db2de1bcdcf4b10b8.yaml
|
21
24
|
issues/issue-dca4b19aa13de59838b33e03252bf824670a2d12.yaml
|
22
25
|
issues/issue-f1bd40de5458397d9b142ea3e197e5264e0dcdbf.yaml
|
@@ -30,10 +33,6 @@ lib/twowaysql/parser.rb
|
|
30
33
|
lib/twowaysql/parser.y
|
31
34
|
lib/twowaysql/template.rb
|
32
35
|
lib/twowaysql/version.rb
|
33
|
-
script/console
|
34
|
-
script/destroy
|
35
|
-
script/generate
|
36
|
-
script/txt2html
|
37
36
|
setup.rb
|
38
37
|
spec/large_sql_spec.rb
|
39
38
|
spec/learning_regex_spec.rb
|
@@ -44,5 +43,6 @@ tasks/deployment.rake
|
|
44
43
|
tasks/ditz.rake
|
45
44
|
tasks/environment.rake
|
46
45
|
tasks/racc.rake
|
46
|
+
tasks/rcov.rake
|
47
47
|
tasks/rspec.rake
|
48
48
|
tasks/website.rake
|
data/README.txt
CHANGED
@@ -375,10 +375,10 @@ Default is true. When true, parser preserves original whitespaces. When false, p
|
|
375
375
|
EOS
|
376
376
|
template = TwoWaySQL::Template.parse(sql, :preserve_space => false)
|
377
377
|
|
378
|
-
|
378
|
+
merged = template.merge(:job => 'MANAGER', :deptno => 30)
|
379
379
|
|
380
|
-
|
381
|
-
|
380
|
+
merged.sql #=> "SELECT * FROM emp WHERE job = ? AND deptno = ? "
|
381
|
+
merged.bound_variables #=> ["MANAGER", 30]
|
382
382
|
|
383
383
|
|
384
384
|
==== :preserve_comment (default is false)
|
@@ -400,7 +400,7 @@ Default is false. When true, parser preserves original actual comments. When fal
|
|
400
400
|
EOS
|
401
401
|
template = TwoWaySQL::Template.parse(sql, :preserve_comment => true)
|
402
402
|
|
403
|
-
|
403
|
+
merged = template.merge(:job => 'MANAGER', :deptno => 30)
|
404
404
|
|
405
405
|
expected = <<-EOS
|
406
406
|
SELECT
|
@@ -415,8 +415,8 @@ Default is false. When true, parser preserves original actual comments. When fal
|
|
415
415
|
job = ?
|
416
416
|
AND deptno = ?
|
417
417
|
EOS
|
418
|
-
|
419
|
-
|
418
|
+
merged.sql == expected #=> true
|
419
|
+
merged.bound_variables #=> ["MANAGER", 30]
|
420
420
|
|
421
421
|
|
422
422
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
--- !ditz.rubyforge.org,2008-03-06/issue
|
2
|
+
title: integrate coverage report into website
|
3
|
+
desc: integrate rcov coverage report into website
|
4
|
+
type: :task
|
5
|
+
component: twowaysql
|
6
|
+
release: release_0_4
|
7
|
+
reporter: takuto <takuto.wada@gmail.com>
|
8
|
+
status: :closed
|
9
|
+
disposition: :fixed
|
10
|
+
creation_time: 2008-09-29 09:44:41.700346 Z
|
11
|
+
references: []
|
12
|
+
|
13
|
+
id: 2a172da16d3a59adc8448f2e36ec48b36951e02d
|
14
|
+
log_events:
|
15
|
+
- - 2008-09-29 09:44:42.828987 Z
|
16
|
+
- takuto <takuto.wada@gmail.com>
|
17
|
+
- created
|
18
|
+
- ""
|
19
|
+
- - 2008-09-29 17:34:57.206684 Z
|
20
|
+
- takuto <takuto.wada@gmail.com>
|
21
|
+
- changed status from unstarted to in_progress
|
22
|
+
- ""
|
23
|
+
- - 2008-09-29 17:40:10.806903 Z
|
24
|
+
- takuto <takuto.wada@gmail.com>
|
25
|
+
- assigned to release release_0_4 from unassigned
|
26
|
+
- ""
|
27
|
+
- - 2008-09-29 17:40:53.649456 Z
|
28
|
+
- takuto <takuto.wada@gmail.com>
|
29
|
+
- closed with disposition fixed
|
30
|
+
- ""
|
@@ -5,8 +5,8 @@ type: :feature
|
|
5
5
|
component: twowaysql
|
6
6
|
release: release_0_4
|
7
7
|
reporter: takuto <takuto.wada@gmail.com>
|
8
|
-
status: :
|
9
|
-
disposition:
|
8
|
+
status: :closed
|
9
|
+
disposition: :fixed
|
10
10
|
creation_time: 2008-09-06 07:38:16.872853 Z
|
11
11
|
references: []
|
12
12
|
|
@@ -24,3 +24,11 @@ log_events:
|
|
24
24
|
- takuto <takuto.wada@gmail.com>
|
25
25
|
- assigned to release release_0_4 from release_0_3
|
26
26
|
- ""
|
27
|
+
- - 2008-09-29 10:12:48.697610 Z
|
28
|
+
- takuto <takuto.wada@gmail.com>
|
29
|
+
- changed status from unstarted to in_progress
|
30
|
+
- ""
|
31
|
+
- - 2008-09-29 16:07:05.605087 Z
|
32
|
+
- takuto <takuto.wada@gmail.com>
|
33
|
+
- closed with disposition fixed
|
34
|
+
- simple implementation. now scanner passes token position with token itself.
|
@@ -3,10 +3,10 @@ title: integrate ditz to 'push-button release'
|
|
3
3
|
desc: integrate 'ditz release' and 'ditz changelog' into rake task
|
4
4
|
type: :task
|
5
5
|
component: twowaysql
|
6
|
-
release:
|
6
|
+
release: release_0_4
|
7
7
|
reporter: takuto <takuto.wada@gmail.com>
|
8
|
-
status: :
|
9
|
-
disposition:
|
8
|
+
status: :closed
|
9
|
+
disposition: :fixed
|
10
10
|
creation_time: 2008-09-07 17:34:42.830046 Z
|
11
11
|
references: []
|
12
12
|
|
@@ -16,3 +16,15 @@ log_events:
|
|
16
16
|
- takuto <takuto.wada@gmail.com>
|
17
17
|
- created
|
18
18
|
- ""
|
19
|
+
- - 2008-09-29 18:11:54.103095 Z
|
20
|
+
- takuto <takuto.wada@gmail.com>
|
21
|
+
- assigned to release release_0_4 from unassigned
|
22
|
+
- ""
|
23
|
+
- - 2008-09-29 18:13:04.946986 Z
|
24
|
+
- takuto <takuto.wada@gmail.com>
|
25
|
+
- changed status from unstarted to in_progress
|
26
|
+
- ""
|
27
|
+
- - 2008-09-29 18:20:38.750419 Z
|
28
|
+
- takuto <takuto.wada@gmail.com>
|
29
|
+
- closed with disposition fixed
|
30
|
+
- ""
|
@@ -0,0 +1,26 @@
|
|
1
|
+
--- !ditz.rubyforge.org,2008-03-06/issue
|
2
|
+
title: Change ambiguous class name TwoWaySQL::Result to TwoWaySQL::MergeResult
|
3
|
+
desc: it is merge result, not a result of db execution.
|
4
|
+
type: :task
|
5
|
+
component: twowaysql
|
6
|
+
release: release_0_4
|
7
|
+
reporter: takuto <takuto.wada@gmail.com>
|
8
|
+
status: :closed
|
9
|
+
disposition: :fixed
|
10
|
+
creation_time: 2008-09-19 18:14:34.843930 Z
|
11
|
+
references: []
|
12
|
+
|
13
|
+
id: a185b4247f64c1104bc49ebf823b57b5de3d06f8
|
14
|
+
log_events:
|
15
|
+
- - 2008-09-19 18:14:36.258181 Z
|
16
|
+
- takuto <takuto.wada@gmail.com>
|
17
|
+
- created
|
18
|
+
- ""
|
19
|
+
- - 2008-09-19 18:16:09.295390 Z
|
20
|
+
- takuto <takuto.wada@gmail.com>
|
21
|
+
- changed status from unstarted to in_progress
|
22
|
+
- ""
|
23
|
+
- - 2008-09-19 18:17:35.545561 Z
|
24
|
+
- takuto <takuto.wada@gmail.com>
|
25
|
+
- closed with disposition fixed
|
26
|
+
- ""
|
data/issues/project.yaml
CHANGED
@@ -46,10 +46,14 @@ releases:
|
|
46
46
|
- 0.2.1 release
|
47
47
|
- !ditz.rubyforge.org,2008-03-06/release
|
48
48
|
name: release_0_4
|
49
|
-
status: :
|
50
|
-
release_time:
|
49
|
+
status: :released
|
50
|
+
release_time: 2008-09-29 18:30:36.862798 Z
|
51
51
|
log_events:
|
52
52
|
- - 2008-09-10 06:27:14.808896 Z
|
53
53
|
- takuto <takuto.wada@gmail.com>
|
54
54
|
- created
|
55
55
|
- ""
|
56
|
+
- - 2008-09-29 18:30:36.862808 Z
|
57
|
+
- takuto <takuto.wada@gmail.com>
|
58
|
+
- released
|
59
|
+
- ""
|
data/lib/twowaysql/parser.rb
CHANGED
@@ -11,7 +11,7 @@ module TwoWaySQL
|
|
11
11
|
|
12
12
|
class Parser < Racc::Parser
|
13
13
|
|
14
|
-
module_eval <<'..end lib/twowaysql/parser.y modeval..
|
14
|
+
module_eval <<'..end lib/twowaysql/parser.y modeval..id30c07c2d1a', 'lib/twowaysql/parser.y', 134
|
15
15
|
|
16
16
|
require 'strscan'
|
17
17
|
|
@@ -57,64 +57,91 @@ OR_PATTERN = /\A(\ *OR)\b/i
|
|
57
57
|
def parse( io )
|
58
58
|
@q = []
|
59
59
|
io.each_line(nil) do |whole|
|
60
|
-
s = StringScanner.new(whole)
|
61
|
-
until s.eos? do
|
62
|
-
case
|
63
|
-
when s.scan(AND_PATTERN)
|
64
|
-
@q.push [ :AND, s[1] ]
|
65
|
-
when s.scan(OR_PATTERN)
|
66
|
-
@q.push [ :OR, s[1] ]
|
67
|
-
when s.scan(SPACES_PATTERN)
|
68
|
-
@q.push [ :SPACES, s[1] ]
|
69
|
-
when s.scan(QUESTION_PATTERN)
|
70
|
-
@q.push [ :QUESTION, nil ]
|
71
|
-
when s.scan(COMMA_PATTERN)
|
72
|
-
@q.push [ :COMMA, ',' ]
|
73
|
-
when s.scan(LPAREN_PATTERN)
|
74
|
-
@q.push [ :LPAREN, '(' ]
|
75
|
-
when s.scan(RPAREN_PATTERN)
|
76
|
-
@q.push [ :RPAREN, ')' ]
|
77
|
-
when s.scan(ELSE_PATTERN)
|
78
|
-
@q.push [ :ELSE, nil ]
|
79
|
-
when s.scan(ACTUAL_COMMENT_PATTERN)
|
80
|
-
@q.push [ :ACTUAL_COMMENT, [s[1], s[2]] ] if @preserve_comment
|
81
|
-
when s.scan(BEGIN_END_PATTERN)
|
82
|
-
@q.push [ s[2].intern, nil ]
|
83
|
-
when s.scan(CONDITIONAL_PATTERN)
|
84
|
-
@q.push [ s[2].intern, s[3] ]
|
85
|
-
when s.scan(EMBED_VARIABLE_PATTERN)
|
86
|
-
@q.push [ :EMBED_VARIABLE, s[2] ]
|
87
|
-
when s.scan(PAREN_BIND_VARIABLE_PATTERN)
|
88
|
-
@q.push [ :PAREN_BIND_VARIABLE, s[2] ]
|
89
|
-
when s.scan(BIND_VARIABLE_PATTERN)
|
90
|
-
@q.push [ :BIND_VARIABLE, s[2] ]
|
91
|
-
when s.scan(STRING_LITERAL_PATTERN)
|
92
|
-
@q.push [ :STRING_LITERAL, s[1] ]
|
93
|
-
when s.scan(SPLIT_TOKEN_PATTERN)
|
94
|
-
@q.push [ :IDENT, s[1] ]
|
95
|
-
when s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
|
96
|
-
raise Racc::ParseError, "## unmatched comment. cannot parse [#{s.rest}]"
|
97
|
-
when s.scan(LITERAL_PATTERN) ## other string token
|
98
|
-
@q.push [ :IDENT, s[1] ]
|
99
|
-
when s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
|
100
|
-
#drop semicolon at input end
|
101
|
-
else
|
102
|
-
raise Racc::ParseError, "## cannot parse [#{s.rest}]"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
60
|
+
@s = StringScanner.new(whole)
|
106
61
|
end
|
107
|
-
|
108
|
-
|
62
|
+
scan_str
|
63
|
+
|
64
|
+
# @q.push [ false, nil ]
|
65
|
+
@q.push [ false, [@s.pos, nil] ]
|
109
66
|
|
110
67
|
## cal racc's private parse method
|
111
68
|
do_parse
|
112
69
|
end
|
113
70
|
|
71
|
+
## called by racc
|
114
72
|
def next_token
|
115
73
|
@q.shift
|
116
74
|
end
|
117
|
-
|
75
|
+
|
76
|
+
|
77
|
+
def scan_str
|
78
|
+
until @s.eos? do
|
79
|
+
case
|
80
|
+
when @s.scan(AND_PATTERN)
|
81
|
+
@q.push [ :AND, [@s.pos, @s[1]] ]
|
82
|
+
when @s.scan(OR_PATTERN)
|
83
|
+
@q.push [ :OR, [@s.pos, @s[1]] ]
|
84
|
+
when @s.scan(SPACES_PATTERN)
|
85
|
+
@q.push [ :SPACES, [@s.pos, @s[1]] ]
|
86
|
+
when @s.scan(QUESTION_PATTERN)
|
87
|
+
@q.push [ :QUESTION, [@s.pos, nil] ]
|
88
|
+
when @s.scan(COMMA_PATTERN)
|
89
|
+
@q.push [ :COMMA, [@s.pos, ','] ]
|
90
|
+
when @s.scan(LPAREN_PATTERN)
|
91
|
+
@q.push [ :LPAREN, [@s.pos, '('] ]
|
92
|
+
when @s.scan(RPAREN_PATTERN)
|
93
|
+
@q.push [ :RPAREN, [@s.pos, ')'] ]
|
94
|
+
when @s.scan(ELSE_PATTERN)
|
95
|
+
@q.push [ :ELSE, [@s.pos, nil] ]
|
96
|
+
when @s.scan(ACTUAL_COMMENT_PATTERN)
|
97
|
+
@q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
|
98
|
+
when @s.scan(BEGIN_END_PATTERN)
|
99
|
+
@q.push [ @s[2].intern, [@s.pos, nil] ]
|
100
|
+
when @s.scan(CONDITIONAL_PATTERN)
|
101
|
+
@q.push [ @s[2].intern, [@s.pos, @s[3]] ]
|
102
|
+
when @s.scan(EMBED_VARIABLE_PATTERN)
|
103
|
+
@q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
|
104
|
+
when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
|
105
|
+
@q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
|
106
|
+
when @s.scan(BIND_VARIABLE_PATTERN)
|
107
|
+
@q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
|
108
|
+
when @s.scan(STRING_LITERAL_PATTERN)
|
109
|
+
@q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
|
110
|
+
when @s.scan(SPLIT_TOKEN_PATTERN)
|
111
|
+
@q.push [ :IDENT, [@s.pos, @s[1]] ]
|
112
|
+
when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
|
113
|
+
raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
|
114
|
+
when @s.scan(LITERAL_PATTERN) ## other string token
|
115
|
+
@q.push [ :IDENT, [@s.pos, @s[1]] ]
|
116
|
+
when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
|
117
|
+
#drop semicolon at input end
|
118
|
+
else
|
119
|
+
raise Racc::ParseError, "syntax error near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def on_error(t, v, vstack)
|
126
|
+
## cursor in value-stack is an array of two items,
|
127
|
+
## that have position value as 0th item. like [731, "ctx[:limit] "]
|
128
|
+
cursor = vstack.find do |tokens|
|
129
|
+
tokens.size == 2 and tokens[0].kind_of?(Fixnum)
|
130
|
+
end
|
131
|
+
pos = cursor[0]
|
132
|
+
line = line_no(pos)
|
133
|
+
rest = @s.string[pos .. -1]
|
134
|
+
raise Racc::ParseError, "syntax error near line:[#{line}], str:[#{rest}]"
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def line_no(pos)
|
139
|
+
lines = 0
|
140
|
+
scanned = @s.string[0..(pos)]
|
141
|
+
scanned.each_line { lines += 1 }
|
142
|
+
lines
|
143
|
+
end
|
144
|
+
..end lib/twowaysql/parser.y modeval..id30c07c2d1a
|
118
145
|
|
119
146
|
##### racc 1.4.5 generates ###
|
120
147
|
|
@@ -334,7 +361,7 @@ module_eval <<'.,.,', 'lib/twowaysql/parser.y', 26
|
|
334
361
|
|
335
362
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 31
|
336
363
|
def _reduce_8( val, _values, result )
|
337
|
-
result = IfNode.new( val[0], val[1], val[2] )
|
364
|
+
result = IfNode.new( val[0][1], val[1], val[2] )
|
338
365
|
result
|
339
366
|
end
|
340
367
|
.,.,
|
@@ -361,70 +388,70 @@ module_eval <<'.,.,', 'lib/twowaysql/parser.y', 40
|
|
361
388
|
|
362
389
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 49
|
363
390
|
def _reduce_14( val, _values, result )
|
364
|
-
result = SubStatementNode.new( val[0], val[1] )
|
391
|
+
result = SubStatementNode.new( val[0][1], val[1] )
|
365
392
|
result
|
366
393
|
end
|
367
394
|
.,.,
|
368
395
|
|
369
396
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 54
|
370
397
|
def _reduce_15( val, _values, result )
|
371
|
-
result = SubStatementNode.new( val[0], val[1] )
|
398
|
+
result = SubStatementNode.new( val[0][1], val[1] )
|
372
399
|
result
|
373
400
|
end
|
374
401
|
.,.,
|
375
402
|
|
376
403
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 59
|
377
404
|
def _reduce_16( val, _values, result )
|
378
|
-
result = LiteralNode.new( val[0] )
|
405
|
+
result = LiteralNode.new( val[0][1] )
|
379
406
|
result
|
380
407
|
end
|
381
408
|
.,.,
|
382
409
|
|
383
410
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 63
|
384
411
|
def _reduce_17( val, _values, result )
|
385
|
-
result = LiteralNode.new( val[0] )
|
412
|
+
result = LiteralNode.new( val[0][1] )
|
386
413
|
result
|
387
414
|
end
|
388
415
|
.,.,
|
389
416
|
|
390
417
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 67
|
391
418
|
def _reduce_18( val, _values, result )
|
392
|
-
result = LiteralNode.new( val[0] )
|
419
|
+
result = LiteralNode.new( val[0][1] )
|
393
420
|
result
|
394
421
|
end
|
395
422
|
.,.,
|
396
423
|
|
397
424
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 71
|
398
425
|
def _reduce_19( val, _values, result )
|
399
|
-
result = LiteralNode.new( val[0] )
|
426
|
+
result = LiteralNode.new( val[0][1] )
|
400
427
|
result
|
401
428
|
end
|
402
429
|
.,.,
|
403
430
|
|
404
431
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 75
|
405
432
|
def _reduce_20( val, _values, result )
|
406
|
-
result = WhiteSpaceNode.new( val[0], @preserve_space )
|
433
|
+
result = WhiteSpaceNode.new( val[0][1], @preserve_space )
|
407
434
|
result
|
408
435
|
end
|
409
436
|
.,.,
|
410
437
|
|
411
438
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 79
|
412
439
|
def _reduce_21( val, _values, result )
|
413
|
-
result = LiteralNode.new( val[0] )
|
440
|
+
result = LiteralNode.new( val[0][1] )
|
414
441
|
result
|
415
442
|
end
|
416
443
|
.,.,
|
417
444
|
|
418
445
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 83
|
419
446
|
def _reduce_22( val, _values, result )
|
420
|
-
result = LiteralNode.new( val[0] )
|
447
|
+
result = LiteralNode.new( val[0][1] )
|
421
448
|
result
|
422
449
|
end
|
423
450
|
.,.,
|
424
451
|
|
425
452
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 87
|
426
453
|
def _reduce_23( val, _values, result )
|
427
|
-
result = LiteralNode.new( val[0] )
|
454
|
+
result = LiteralNode.new( val[0][1] )
|
428
455
|
result
|
429
456
|
end
|
430
457
|
.,.,
|
@@ -439,7 +466,7 @@ module_eval <<'.,.,', 'lib/twowaysql/parser.y', 92
|
|
439
466
|
|
440
467
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 96
|
441
468
|
def _reduce_25( val, _values, result )
|
442
|
-
result = ActualCommentNode.new( val[0][
|
469
|
+
result = ActualCommentNode.new( val[0][1] , val[0][2] )
|
443
470
|
result
|
444
471
|
end
|
445
472
|
.,.,
|
@@ -450,49 +477,49 @@ module_eval <<'.,.,', 'lib/twowaysql/parser.y', 96
|
|
450
477
|
|
451
478
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 103
|
452
479
|
def _reduce_28( val, _values, result )
|
453
|
-
result = BindVariableNode.new( val[0] )
|
480
|
+
result = BindVariableNode.new( val[0][1] )
|
454
481
|
result
|
455
482
|
end
|
456
483
|
.,.,
|
457
484
|
|
458
485
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 107
|
459
486
|
def _reduce_29( val, _values, result )
|
460
|
-
result = BindVariableNode.new( val[0] )
|
487
|
+
result = BindVariableNode.new( val[0][1] )
|
461
488
|
result
|
462
489
|
end
|
463
490
|
.,.,
|
464
491
|
|
465
492
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 111
|
466
493
|
def _reduce_30( val, _values, result )
|
467
|
-
result = BindVariableNode.new( val[0] )
|
494
|
+
result = BindVariableNode.new( val[0][1] )
|
468
495
|
result
|
469
496
|
end
|
470
497
|
.,.,
|
471
498
|
|
472
499
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 115
|
473
500
|
def _reduce_31( val, _values, result )
|
474
|
-
result = BindVariableNode.new( val[0] )
|
501
|
+
result = BindVariableNode.new( val[0][1] )
|
475
502
|
result
|
476
503
|
end
|
477
504
|
.,.,
|
478
505
|
|
479
506
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 119
|
480
507
|
def _reduce_32( val, _values, result )
|
481
|
-
result = ParenBindVariableNode.new( val[0] )
|
508
|
+
result = ParenBindVariableNode.new( val[0][1] )
|
482
509
|
result
|
483
510
|
end
|
484
511
|
.,.,
|
485
512
|
|
486
513
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 124
|
487
514
|
def _reduce_33( val, _values, result )
|
488
|
-
result = EmbedVariableNode.new( val[0] )
|
515
|
+
result = EmbedVariableNode.new( val[0][1] )
|
489
516
|
result
|
490
517
|
end
|
491
518
|
.,.,
|
492
519
|
|
493
520
|
module_eval <<'.,.,', 'lib/twowaysql/parser.y', 128
|
494
521
|
def _reduce_34( val, _values, result )
|
495
|
-
result = EmbedVariableNode.new( val[0] )
|
522
|
+
result = EmbedVariableNode.new( val[0][1] )
|
496
523
|
result
|
497
524
|
end
|
498
525
|
.,.,
|
data/lib/twowaysql/parser.y
CHANGED
@@ -27,7 +27,7 @@ begin_stmt : BEGIN stmt_list END
|
|
27
27
|
|
28
28
|
if_stmt : IF sub_stmt else_stmt END
|
29
29
|
{
|
30
|
-
result = IfNode.new( val[0], val[1], val[2] )
|
30
|
+
result = IfNode.new( val[0][1], val[1], val[2] )
|
31
31
|
}
|
32
32
|
|
33
33
|
else_stmt : ELSE sub_stmt
|
@@ -45,45 +45,45 @@ sub_stmt : and_stmt
|
|
45
45
|
|
46
46
|
and_stmt : AND stmt_list
|
47
47
|
{
|
48
|
-
result = SubStatementNode.new( val[0], val[1] )
|
48
|
+
result = SubStatementNode.new( val[0][1], val[1] )
|
49
49
|
}
|
50
50
|
|
51
51
|
or_stmt : OR stmt_list
|
52
52
|
{
|
53
|
-
result = SubStatementNode.new( val[0], val[1] )
|
53
|
+
result = SubStatementNode.new( val[0][1], val[1] )
|
54
54
|
}
|
55
55
|
|
56
56
|
primary : IDENT
|
57
57
|
{
|
58
|
-
result = LiteralNode.new( val[0] )
|
58
|
+
result = LiteralNode.new( val[0][1] )
|
59
59
|
}
|
60
60
|
| STRING_LITERAL
|
61
61
|
{
|
62
|
-
result = LiteralNode.new( val[0] )
|
62
|
+
result = LiteralNode.new( val[0][1] )
|
63
63
|
}
|
64
64
|
| AND
|
65
65
|
{
|
66
|
-
result = LiteralNode.new( val[0] )
|
66
|
+
result = LiteralNode.new( val[0][1] )
|
67
67
|
}
|
68
68
|
| OR
|
69
69
|
{
|
70
|
-
result = LiteralNode.new( val[0] )
|
70
|
+
result = LiteralNode.new( val[0][1] )
|
71
71
|
}
|
72
72
|
| SPACES
|
73
73
|
{
|
74
|
-
result = WhiteSpaceNode.new( val[0], @preserve_space )
|
74
|
+
result = WhiteSpaceNode.new( val[0][1], @preserve_space )
|
75
75
|
}
|
76
76
|
| COMMA
|
77
77
|
{
|
78
|
-
result = LiteralNode.new( val[0] )
|
78
|
+
result = LiteralNode.new( val[0][1] )
|
79
79
|
}
|
80
80
|
| LPAREN
|
81
81
|
{
|
82
|
-
result = LiteralNode.new( val[0] )
|
82
|
+
result = LiteralNode.new( val[0][1] )
|
83
83
|
}
|
84
84
|
| RPAREN
|
85
85
|
{
|
86
|
-
result = LiteralNode.new( val[0] )
|
86
|
+
result = LiteralNode.new( val[0][1] )
|
87
87
|
}
|
88
88
|
| QUESTION
|
89
89
|
{
|
@@ -92,39 +92,39 @@ primary : IDENT
|
|
92
92
|
}
|
93
93
|
| ACTUAL_COMMENT
|
94
94
|
{
|
95
|
-
result = ActualCommentNode.new( val[0][
|
95
|
+
result = ActualCommentNode.new( val[0][1] , val[0][2] )
|
96
96
|
}
|
97
97
|
| bind_var
|
98
98
|
| embed_var
|
99
99
|
|
100
100
|
bind_var : BIND_VARIABLE STRING_LITERAL
|
101
101
|
{
|
102
|
-
result = BindVariableNode.new( val[0] )
|
102
|
+
result = BindVariableNode.new( val[0][1] )
|
103
103
|
}
|
104
104
|
| BIND_VARIABLE SPACES STRING_LITERAL
|
105
105
|
{
|
106
|
-
result = BindVariableNode.new( val[0] )
|
106
|
+
result = BindVariableNode.new( val[0][1] )
|
107
107
|
}
|
108
108
|
| BIND_VARIABLE IDENT
|
109
109
|
{
|
110
|
-
result = BindVariableNode.new( val[0] )
|
110
|
+
result = BindVariableNode.new( val[0][1] )
|
111
111
|
}
|
112
112
|
| BIND_VARIABLE SPACES IDENT
|
113
113
|
{
|
114
|
-
result = BindVariableNode.new( val[0] )
|
114
|
+
result = BindVariableNode.new( val[0][1] )
|
115
115
|
}
|
116
116
|
| PAREN_BIND_VARIABLE
|
117
117
|
{
|
118
|
-
result = ParenBindVariableNode.new( val[0] )
|
118
|
+
result = ParenBindVariableNode.new( val[0][1] )
|
119
119
|
}
|
120
120
|
|
121
121
|
embed_var : EMBED_VARIABLE IDENT
|
122
122
|
{
|
123
|
-
result = EmbedVariableNode.new( val[0] )
|
123
|
+
result = EmbedVariableNode.new( val[0][1] )
|
124
124
|
}
|
125
125
|
| EMBED_VARIABLE SPACES IDENT
|
126
126
|
{
|
127
|
-
result = EmbedVariableNode.new( val[0] )
|
127
|
+
result = EmbedVariableNode.new( val[0][1] )
|
128
128
|
}
|
129
129
|
|
130
130
|
end
|
@@ -176,60 +176,87 @@ OR_PATTERN = /\A(\ *OR)\b/i
|
|
176
176
|
def parse( io )
|
177
177
|
@q = []
|
178
178
|
io.each_line(nil) do |whole|
|
179
|
-
s = StringScanner.new(whole)
|
180
|
-
until s.eos? do
|
181
|
-
case
|
182
|
-
when s.scan(AND_PATTERN)
|
183
|
-
@q.push [ :AND, s[1] ]
|
184
|
-
when s.scan(OR_PATTERN)
|
185
|
-
@q.push [ :OR, s[1] ]
|
186
|
-
when s.scan(SPACES_PATTERN)
|
187
|
-
@q.push [ :SPACES, s[1] ]
|
188
|
-
when s.scan(QUESTION_PATTERN)
|
189
|
-
@q.push [ :QUESTION, nil ]
|
190
|
-
when s.scan(COMMA_PATTERN)
|
191
|
-
@q.push [ :COMMA, ',' ]
|
192
|
-
when s.scan(LPAREN_PATTERN)
|
193
|
-
@q.push [ :LPAREN, '(' ]
|
194
|
-
when s.scan(RPAREN_PATTERN)
|
195
|
-
@q.push [ :RPAREN, ')' ]
|
196
|
-
when s.scan(ELSE_PATTERN)
|
197
|
-
@q.push [ :ELSE, nil ]
|
198
|
-
when s.scan(ACTUAL_COMMENT_PATTERN)
|
199
|
-
@q.push [ :ACTUAL_COMMENT, [s[1], s[2]] ] if @preserve_comment
|
200
|
-
when s.scan(BEGIN_END_PATTERN)
|
201
|
-
@q.push [ s[2].intern, nil ]
|
202
|
-
when s.scan(CONDITIONAL_PATTERN)
|
203
|
-
@q.push [ s[2].intern, s[3] ]
|
204
|
-
when s.scan(EMBED_VARIABLE_PATTERN)
|
205
|
-
@q.push [ :EMBED_VARIABLE, s[2] ]
|
206
|
-
when s.scan(PAREN_BIND_VARIABLE_PATTERN)
|
207
|
-
@q.push [ :PAREN_BIND_VARIABLE, s[2] ]
|
208
|
-
when s.scan(BIND_VARIABLE_PATTERN)
|
209
|
-
@q.push [ :BIND_VARIABLE, s[2] ]
|
210
|
-
when s.scan(STRING_LITERAL_PATTERN)
|
211
|
-
@q.push [ :STRING_LITERAL, s[1] ]
|
212
|
-
when s.scan(SPLIT_TOKEN_PATTERN)
|
213
|
-
@q.push [ :IDENT, s[1] ]
|
214
|
-
when s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
|
215
|
-
raise Racc::ParseError, "## unmatched comment. cannot parse [#{s.rest}]"
|
216
|
-
when s.scan(LITERAL_PATTERN) ## other string token
|
217
|
-
@q.push [ :IDENT, s[1] ]
|
218
|
-
when s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
|
219
|
-
#drop semicolon at input end
|
220
|
-
else
|
221
|
-
raise Racc::ParseError, "## cannot parse [#{s.rest}]"
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
179
|
+
@s = StringScanner.new(whole)
|
225
180
|
end
|
226
|
-
|
227
|
-
|
181
|
+
scan_str
|
182
|
+
|
183
|
+
# @q.push [ false, nil ]
|
184
|
+
@q.push [ false, [@s.pos, nil] ]
|
228
185
|
|
229
186
|
## cal racc's private parse method
|
230
187
|
do_parse
|
231
188
|
end
|
232
189
|
|
190
|
+
## called by racc
|
233
191
|
def next_token
|
234
192
|
@q.shift
|
235
193
|
end
|
194
|
+
|
195
|
+
|
196
|
+
def scan_str
|
197
|
+
until @s.eos? do
|
198
|
+
case
|
199
|
+
when @s.scan(AND_PATTERN)
|
200
|
+
@q.push [ :AND, [@s.pos, @s[1]] ]
|
201
|
+
when @s.scan(OR_PATTERN)
|
202
|
+
@q.push [ :OR, [@s.pos, @s[1]] ]
|
203
|
+
when @s.scan(SPACES_PATTERN)
|
204
|
+
@q.push [ :SPACES, [@s.pos, @s[1]] ]
|
205
|
+
when @s.scan(QUESTION_PATTERN)
|
206
|
+
@q.push [ :QUESTION, [@s.pos, nil] ]
|
207
|
+
when @s.scan(COMMA_PATTERN)
|
208
|
+
@q.push [ :COMMA, [@s.pos, ','] ]
|
209
|
+
when @s.scan(LPAREN_PATTERN)
|
210
|
+
@q.push [ :LPAREN, [@s.pos, '('] ]
|
211
|
+
when @s.scan(RPAREN_PATTERN)
|
212
|
+
@q.push [ :RPAREN, [@s.pos, ')'] ]
|
213
|
+
when @s.scan(ELSE_PATTERN)
|
214
|
+
@q.push [ :ELSE, [@s.pos, nil] ]
|
215
|
+
when @s.scan(ACTUAL_COMMENT_PATTERN)
|
216
|
+
@q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
|
217
|
+
when @s.scan(BEGIN_END_PATTERN)
|
218
|
+
@q.push [ @s[2].intern, [@s.pos, nil] ]
|
219
|
+
when @s.scan(CONDITIONAL_PATTERN)
|
220
|
+
@q.push [ @s[2].intern, [@s.pos, @s[3]] ]
|
221
|
+
when @s.scan(EMBED_VARIABLE_PATTERN)
|
222
|
+
@q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
|
223
|
+
when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
|
224
|
+
@q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
|
225
|
+
when @s.scan(BIND_VARIABLE_PATTERN)
|
226
|
+
@q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
|
227
|
+
when @s.scan(STRING_LITERAL_PATTERN)
|
228
|
+
@q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
|
229
|
+
when @s.scan(SPLIT_TOKEN_PATTERN)
|
230
|
+
@q.push [ :IDENT, [@s.pos, @s[1]] ]
|
231
|
+
when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
|
232
|
+
raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
|
233
|
+
when @s.scan(LITERAL_PATTERN) ## other string token
|
234
|
+
@q.push [ :IDENT, [@s.pos, @s[1]] ]
|
235
|
+
when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
|
236
|
+
#drop semicolon at input end
|
237
|
+
else
|
238
|
+
raise Racc::ParseError, "syntax error near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
def on_error(t, v, vstack)
|
245
|
+
## cursor in value-stack is an array of two items,
|
246
|
+
## that have position value as 0th item. like [731, "ctx[:limit] "]
|
247
|
+
cursor = vstack.find do |tokens|
|
248
|
+
tokens.size == 2 and tokens[0].kind_of?(Fixnum)
|
249
|
+
end
|
250
|
+
pos = cursor[0]
|
251
|
+
line = line_no(pos)
|
252
|
+
rest = @s.string[pos .. -1]
|
253
|
+
raise Racc::ParseError, "syntax error near line:[#{line}], str:[#{rest}]"
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
def line_no(pos)
|
258
|
+
lines = 0
|
259
|
+
scanned = @s.string[0..(pos)]
|
260
|
+
scanned.each_line { lines += 1 }
|
261
|
+
lines
|
262
|
+
end
|
data/lib/twowaysql/template.rb
CHANGED
@@ -44,12 +44,12 @@ module TwoWaySQL
|
|
44
44
|
#
|
45
45
|
# === Return
|
46
46
|
#
|
47
|
-
# TwoWaySQL::
|
47
|
+
# TwoWaySQL::MergeResult object that represents merge result
|
48
48
|
#
|
49
49
|
def merge(data)
|
50
50
|
c = Context.new(data)
|
51
51
|
@root.accept(c)
|
52
|
-
|
52
|
+
MergeResult.new(c)
|
53
53
|
end
|
54
54
|
alias mungle merge
|
55
55
|
|
@@ -60,7 +60,7 @@ module TwoWaySQL
|
|
60
60
|
end
|
61
61
|
|
62
62
|
|
63
|
-
# TwoWaySQL::
|
63
|
+
# TwoWaySQL::MergeResult represents merge result of template and data.
|
64
64
|
# it contains SQL string with placeholders, and bound variables associated with placeholders.
|
65
65
|
#
|
66
66
|
# === Usage
|
@@ -69,7 +69,7 @@ module TwoWaySQL
|
|
69
69
|
# merged.sql #=> "SELECT * FROM emp WHERE job = ? AND deptno = ?"
|
70
70
|
# merged.bound_variables #=> ["HOGE", 30]
|
71
71
|
#
|
72
|
-
class
|
72
|
+
class MergeResult
|
73
73
|
def initialize(context)
|
74
74
|
@context = context
|
75
75
|
end
|
@@ -84,9 +84,10 @@ module TwoWaySQL
|
|
84
84
|
end
|
85
85
|
|
86
86
|
# return array of variables which indices are corresponding to placeholders.
|
87
|
+
# alias 'vars' is available for short-hand.
|
87
88
|
#
|
88
89
|
# === Return
|
89
|
-
#
|
90
|
+
# array of bound variables
|
90
91
|
#
|
91
92
|
def bound_variables
|
92
93
|
@context.bound_variables
|
data/lib/twowaysql/version.rb
CHANGED
data/spec/large_sql_spec.rb
CHANGED
@@ -236,4 +236,94 @@ EOS
|
|
236
236
|
end
|
237
237
|
end
|
238
238
|
|
239
|
+
|
240
|
+
|
241
|
+
|
242
|
+
describe "guess line number on multi-line SQL syntax error" do
|
243
|
+
|
244
|
+
it "non-nested unmatched IF-END" do
|
245
|
+
sql = <<-EOS
|
246
|
+
SELECT
|
247
|
+
i.id AS item_id
|
248
|
+
,d.display_name AS display_name
|
249
|
+
|
250
|
+
FROM
|
251
|
+
some_schema.item i
|
252
|
+
INNER JOIN some_schema.item_detail d
|
253
|
+
ON i.id = d.item_id
|
254
|
+
|
255
|
+
/*IF ctx[:order_by] */ ORDER BY /*$ctx[:order_by]*/i.id /*$ctx[:order]*/ASC /* no END comment */
|
256
|
+
/*IF ctx[:limit] */ LIMIT /*ctx[:limit]*/10/*END*/
|
257
|
+
/*IF ctx[:offset] */ OFFSET /*ctx[:offset]*/0/*END*/
|
258
|
+
EOS
|
259
|
+
begin
|
260
|
+
TwoWaySQL::Template.parse(sql)
|
261
|
+
rescue Racc::ParseError => e
|
262
|
+
e.to_s.should match(/line:\[10\]/)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
it "no-END on input end" do
|
268
|
+
sql = <<-EOS
|
269
|
+
SELECT
|
270
|
+
i.id AS item_id
|
271
|
+
,d.display_name AS display_name
|
272
|
+
|
273
|
+
FROM
|
274
|
+
some_schema.item i
|
275
|
+
INNER JOIN some_schema.item_detail d
|
276
|
+
ON i.id = d.item_id
|
277
|
+
|
278
|
+
/*IF ctx[:order_by] */ ORDER BY /*$ctx[:order_by]*/i.id /*$ctx[:order]*/ASC/*END*/
|
279
|
+
/*IF ctx[:limit] */ LIMIT /*ctx[:limit]*/10/*END*/
|
280
|
+
/*IF ctx[:offset] */ OFFSET /*ctx[:offset]*/0
|
281
|
+
EOS
|
282
|
+
begin
|
283
|
+
TwoWaySQL::Template.parse(sql)
|
284
|
+
rescue Racc::ParseError => e
|
285
|
+
e.to_s.should match(/line:\[12\]/)
|
286
|
+
e.to_s.should match(/OFFSET \/\*ctx\[:offset\]\*\/0/)
|
287
|
+
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
it "unmatched BEGIN-END" do
|
293
|
+
sql = <<-EOS
|
294
|
+
SELECT DISTINCT
|
295
|
+
i.id AS item_id
|
296
|
+
,d.display_name AS display_name
|
297
|
+
,h.status AS status_id
|
298
|
+
,i.unique_name AS unique_name
|
299
|
+
,i.created_on
|
300
|
+
FROM
|
301
|
+
some_schema.item i
|
302
|
+
INNER JOIN some_schema.item_detail d
|
303
|
+
ON i.id = d.item_id
|
304
|
+
INNER JOIN some_schema.item_history h
|
305
|
+
ON i.id = h.item_id
|
306
|
+
|
307
|
+
/*BEGIN*/WHERE /* line:14 */
|
308
|
+
/*IF ctx[:name] */i.name ILIKE /*ctx[:name]*/'hoge%'/*END*/
|
309
|
+
/*IF ctx[:display_name] */AND d.display_name ILIKE /*ctx[:display_name]*/'hoge%'/*END*/
|
310
|
+
/*IF ctx[:status] */AND h.status IN /*ctx[:status]*/(3, 4, 9) /* no-END at line:17 */
|
311
|
+
/*IF ctx[:ignore_status] */AND h.status NOT IN /*ctx[:ignore_status]*/(4, 9)/*END*/
|
312
|
+
/*END*/
|
313
|
+
|
314
|
+
/*IF ctx[:order_by] */ ORDER BY /*$ctx[:order_by]*/i.id /*$ctx[:order]*/ASC /*END*/
|
315
|
+
/*IF ctx[:limit] */ LIMIT /*ctx[:limit]*/10/*END*/
|
316
|
+
/*IF ctx[:offset] */ OFFSET /*ctx[:offset]*/0/*END*/
|
317
|
+
EOS
|
318
|
+
begin
|
319
|
+
TwoWaySQL::Template.parse(sql)
|
320
|
+
rescue Racc::ParseError => e
|
321
|
+
# unmatched END is at line 17 but value stack indicates open BEGIN statement
|
322
|
+
e.to_s.should match(/line:\[14\]/)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
|
239
329
|
end
|
data/spec/twowaysql_spec.rb
CHANGED
@@ -609,6 +609,23 @@ describe TwoWaySQL::Template do
|
|
609
609
|
end
|
610
610
|
|
611
611
|
|
612
|
+
describe "show line number of SQL when parse error has occurred" do
|
613
|
+
it "single line" do
|
614
|
+
begin
|
615
|
+
TwoWaySQL::Template.parse("SELECT * FROM emp/*hoge")
|
616
|
+
rescue Racc::ParseError => e
|
617
|
+
e.to_s.should match(/line:\[1\]/)
|
618
|
+
end
|
619
|
+
end
|
620
|
+
it "multiple lines" do
|
621
|
+
begin
|
622
|
+
TwoWaySQL::Template.parse("SELECT\n *\n FROM\n emp\n/*hoge")
|
623
|
+
rescue Racc::ParseError => e
|
624
|
+
e.to_s.should match(/line:\[5\]/)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
612
629
|
|
613
630
|
describe "when parsed from '/*BEGIN*/'" do
|
614
631
|
it "should cause parse error" do
|
data/tasks/deployment.rake
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
desc 'Release the website and new gem version'
|
2
|
-
task :deploy => [:check_version, :website, :release] do
|
2
|
+
task :deploy => [:check_version, :ditz_release, :website, :release] do
|
3
3
|
puts "Remember to create SVN tag:"
|
4
4
|
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
5
5
|
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
@@ -33,13 +33,12 @@ namespace :manifest do
|
|
33
33
|
|
34
34
|
# this task is inspired by http://d.hatena.ne.jp/bellbind/20070605/1180979599
|
35
35
|
glob_pattern = File.join("**", "*")
|
36
|
-
exclude_patterns = [
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
]
|
36
|
+
exclude_patterns = []
|
37
|
+
File.open('Manifest.skip') do |file|
|
38
|
+
while line = file.gets
|
39
|
+
exclude_patterns << Regexp.new(line.chomp)
|
40
|
+
end
|
41
|
+
end
|
43
42
|
|
44
43
|
files = Dir.glob(glob_pattern).delete_if do |fname|
|
45
44
|
File.directory?(fname) or
|
data/tasks/ditz.rake
CHANGED
@@ -3,3 +3,22 @@ task :ditz_report do
|
|
3
3
|
puts 'generate ditz report'
|
4
4
|
`ditz -i issues html website/issues`
|
5
5
|
end
|
6
|
+
|
7
|
+
desc 'ditz release'
|
8
|
+
task :ditz_release do
|
9
|
+
unless ENV['DITZ_REL']
|
10
|
+
puts 'Must pass a DITZ_REL=release_name'
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
release = ENV['DITZ_REL']
|
15
|
+
puts "ditz release #{release}"
|
16
|
+
`ditz -n -i issues release #{release}`
|
17
|
+
|
18
|
+
mv 'History.txt', 'History.txt.tmp'
|
19
|
+
puts "generate changelog for #{release}"
|
20
|
+
`ditz -i issues changelog #{release} > History.txt`
|
21
|
+
`echo '' >> History.txt`
|
22
|
+
`cat History.txt.tmp >> History.txt`
|
23
|
+
rm 'History.txt.tmp'
|
24
|
+
end
|
data/tasks/rcov.rake
ADDED
data/tasks/website.rake
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
desc 'Generate website files'
|
2
|
-
task :website_generate => [:ruby_env,:ditz_report] do
|
2
|
+
task :website_generate => [:ruby_env,:rcov_report,:ditz_report] do
|
3
3
|
(Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt|
|
4
4
|
sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
5
5
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twowaysql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takuto Wada
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-09-
|
12
|
+
date: 2008-09-30 00:00:00 +09:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -37,6 +37,7 @@ extra_rdoc_files:
|
|
37
37
|
files:
|
38
38
|
- History.txt
|
39
39
|
- License.txt
|
40
|
+
- Manifest.skip
|
40
41
|
- Manifest.txt
|
41
42
|
- README.txt
|
42
43
|
- Rakefile
|
@@ -47,6 +48,7 @@ files:
|
|
47
48
|
- issues/issue-25efcfc383f3b0f6c0e2730ae7c2975bb2b3de26.yaml
|
48
49
|
- issues/issue-279105dd0d9f03514d318f5eab5e99c4c2d47fda.yaml
|
49
50
|
- issues/issue-28cde89ed3eb306957edc90595b1d16bf43daf42.yaml
|
51
|
+
- issues/issue-2a172da16d3a59adc8448f2e36ec48b36951e02d.yaml
|
50
52
|
- issues/issue-39023ea09e17e2d64bcef03aa59cdfe38b78ad5b.yaml
|
51
53
|
- issues/issue-4bc308d55ae91f266e656162a4147d356de1166c.yaml
|
52
54
|
- issues/issue-5c973ef5bb074eacca0c6c84f7d27c4267773ea8.yaml
|
@@ -54,6 +56,7 @@ files:
|
|
54
56
|
- issues/issue-6daccddf089d11d42bf016897da98f70cf5ab46c.yaml
|
55
57
|
- issues/issue-897995fa10377eabdf597e8e7692f17087c76923.yaml
|
56
58
|
- issues/issue-901f65630639507c8b05b466790e9f22256c6450.yaml
|
59
|
+
- issues/issue-a185b4247f64c1104bc49ebf823b57b5de3d06f8.yaml
|
57
60
|
- issues/issue-bd38c1cdc965d73dd629a81db2de1bcdcf4b10b8.yaml
|
58
61
|
- issues/issue-dca4b19aa13de59838b33e03252bf824670a2d12.yaml
|
59
62
|
- issues/issue-f1bd40de5458397d9b142ea3e197e5264e0dcdbf.yaml
|
@@ -67,10 +70,6 @@ files:
|
|
67
70
|
- lib/twowaysql/parser.y
|
68
71
|
- lib/twowaysql/template.rb
|
69
72
|
- lib/twowaysql/version.rb
|
70
|
-
- script/console
|
71
|
-
- script/destroy
|
72
|
-
- script/generate
|
73
|
-
- script/txt2html
|
74
73
|
- setup.rb
|
75
74
|
- spec/large_sql_spec.rb
|
76
75
|
- spec/learning_regex_spec.rb
|
@@ -81,6 +80,7 @@ files:
|
|
81
80
|
- tasks/ditz.rake
|
82
81
|
- tasks/environment.rake
|
83
82
|
- tasks/racc.rake
|
83
|
+
- tasks/rcov.rake
|
84
84
|
- tasks/rspec.rake
|
85
85
|
- tasks/website.rake
|
86
86
|
has_rdoc: true
|
data/script/console
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# File: script/console
|
3
|
-
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
-
|
5
|
-
libs = " -r irb/completion"
|
6
|
-
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
-
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
-
libs << " -r #{File.dirname(__FILE__) + '/../lib/twowaysql.rb'}"
|
9
|
-
puts "Loading twowaysql gem"
|
10
|
-
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rubigen'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rubygems'
|
8
|
-
require 'rubigen'
|
9
|
-
end
|
10
|
-
require 'rubigen/scripts/destroy'
|
11
|
-
|
12
|
-
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
-
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
-
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rubigen'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rubygems'
|
8
|
-
require 'rubigen'
|
9
|
-
end
|
10
|
-
require 'rubigen/scripts/generate'
|
11
|
-
|
12
|
-
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
-
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
-
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/txt2html
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
GEM_NAME = 'twowaysql' # what ppl will type to install your gem
|
4
|
-
RUBYFORGE_PROJECT = 'twowaysql'
|
5
|
-
|
6
|
-
require 'rubygems'
|
7
|
-
begin
|
8
|
-
require 'newgem'
|
9
|
-
require 'rubyforge'
|
10
|
-
rescue LoadError
|
11
|
-
puts "\n\nGenerating the website requires the newgem RubyGem"
|
12
|
-
puts "Install: gem install newgem\n\n"
|
13
|
-
exit(1)
|
14
|
-
end
|
15
|
-
require 'redcloth'
|
16
|
-
require 'syntax/convertors/html'
|
17
|
-
require 'erb'
|
18
|
-
require File.dirname(__FILE__) + "/../lib/#{GEM_NAME}/version.rb"
|
19
|
-
|
20
|
-
version = TwoWaySQL::VERSION::STRING
|
21
|
-
download = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
22
|
-
|
23
|
-
def rubyforge_project_id
|
24
|
-
RubyForge.new.autoconfig["group_ids"][RUBYFORGE_PROJECT]
|
25
|
-
end
|
26
|
-
|
27
|
-
class Fixnum
|
28
|
-
def ordinal
|
29
|
-
# teens
|
30
|
-
return 'th' if (10..19).include?(self % 100)
|
31
|
-
# others
|
32
|
-
case self % 10
|
33
|
-
when 1: return 'st'
|
34
|
-
when 2: return 'nd'
|
35
|
-
when 3: return 'rd'
|
36
|
-
else return 'th'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
class Time
|
42
|
-
def pretty
|
43
|
-
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def convert_syntax(syntax, source)
|
48
|
-
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
49
|
-
end
|
50
|
-
|
51
|
-
if ARGV.length >= 1
|
52
|
-
src, template = ARGV
|
53
|
-
template ||= File.join(File.dirname(__FILE__), '/../website/template.html.erb')
|
54
|
-
else
|
55
|
-
puts("Usage: #{File.split($0).last} source.txt [template.html.erb] > output.html")
|
56
|
-
exit!
|
57
|
-
end
|
58
|
-
|
59
|
-
template = ERB.new(File.open(template).read)
|
60
|
-
|
61
|
-
title = nil
|
62
|
-
body = nil
|
63
|
-
File.open(src) do |fsrc|
|
64
|
-
title_text = fsrc.readline
|
65
|
-
body_text_template = fsrc.read
|
66
|
-
body_text = ERB.new(body_text_template).result(binding)
|
67
|
-
syntax_items = []
|
68
|
-
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
69
|
-
ident = syntax_items.length
|
70
|
-
element, syntax, source = $1, $2, $3
|
71
|
-
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
72
|
-
"syntax-temp-#{ident}"
|
73
|
-
}
|
74
|
-
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
75
|
-
body = RedCloth.new(body_text).to_html
|
76
|
-
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
77
|
-
end
|
78
|
-
stat = File.stat(src)
|
79
|
-
created = stat.ctime
|
80
|
-
modified = stat.mtime
|
81
|
-
|
82
|
-
$stdout << template.result(binding)
|