twowaysql 0.3.0 → 0.4.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.
- 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)
|