sqlpp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,280 @@
1
+ require 'test_helper'
2
+
3
+ class ParserTest < Minitest::Test
4
+ def test_it_should_parse_number_as_expression
5
+ expr = _parser("5").parse_expression
6
+ assert_instance_of SQLPP::AST::Atom, expr
7
+ assert_equal :lit, expr.type
8
+ assert_equal "5", expr.left
9
+ end
10
+
11
+ def test_it_should_parse_string_as_expression
12
+ expr = _parser("'hello'").parse_expression
13
+ assert_instance_of SQLPP::AST::Atom, expr
14
+ assert_equal :lit, expr.type
15
+ assert_equal "'hello'", expr.left
16
+ end
17
+
18
+ def test_it_should_parse_id_as_expression
19
+ expr = _parser("hello").parse_expression
20
+ assert_instance_of SQLPP::AST::Atom, expr
21
+ assert_equal :attr, expr.type
22
+ assert_equal "hello", expr.left
23
+ end
24
+
25
+ def test_it_should_parse_star_as_expression
26
+ expr = _parser("*").parse_expression
27
+ assert_instance_of SQLPP::AST::Atom, expr
28
+ assert_equal :lit, expr.type
29
+ assert_equal "*", expr.left
30
+ end
31
+
32
+ def test_it_should_parse_function_as_expression
33
+ expr = _parser("sum(donuts)").parse_expression
34
+ assert_instance_of SQLPP::AST::Atom, expr
35
+ assert_equal :func, expr.type
36
+ assert_equal "sum", expr.left
37
+ assert_equal 1, expr.right.count
38
+
39
+ assert_instance_of SQLPP::AST::Atom, expr.right[0]
40
+ assert_equal :attr, expr.right[0].type
41
+ assert_equal "donuts", expr.right[0].left
42
+ end
43
+
44
+ def test_it_should_parse_parenthesized_expression
45
+ expr = _parser("(donuts)").parse_expression
46
+ assert_instance_of SQLPP::AST::Parens, expr
47
+ assert_instance_of SQLPP::AST::Atom, expr.value
48
+ assert_equal :attr, expr.value.type
49
+ assert_equal "donuts", expr.value.left
50
+ end
51
+
52
+ def test_it_should_parse_qualified_references
53
+ expr = _parser("foo.bar").parse_expression
54
+ assert_instance_of SQLPP::AST::Atom, expr
55
+ assert_equal :attr, expr.type
56
+ assert_equal "foo", expr.left
57
+ assert_equal "bar", expr.right
58
+ end
59
+
60
+ def test_it_should_treat_null_as_literal
61
+ expr = _parser("NULL").parse_expression
62
+ assert_instance_of SQLPP::AST::Atom, expr
63
+ assert_equal :lit, expr.type
64
+ assert_equal "NULL", expr.left
65
+ end
66
+
67
+ def test_it_should_parse_case_with_root_expression
68
+ expr = _parser("case x when 5 then 1 end").parse_expression
69
+ assert_instance_of SQLPP::AST::Atom, expr
70
+ assert_equal :case, expr.type
71
+ assert_equal "x", expr.left.left
72
+ assert_equal 1, expr.right.length
73
+ assert_equal 2, expr.right[0].length
74
+ assert_equal "5", expr.right[0][0].left
75
+ assert_equal "1", expr.right[0][1].left
76
+ end
77
+
78
+ def test_it_should_parse_case_without_root_expression
79
+ expr = _parser("case when 5 then 1 end").parse_expression
80
+ assert_instance_of SQLPP::AST::Atom, expr
81
+ assert_equal :case, expr.type
82
+ assert_nil expr.left
83
+ assert_equal 1, expr.right.length
84
+ assert_equal 2, expr.right[0].length
85
+ assert_equal "5", expr.right[0][0].left
86
+ assert_equal "1", expr.right[0][1].left
87
+ end
88
+
89
+ def test_it_should_parse_case_with_else_expression
90
+ expr = _parser("case when 5 then 1 else 3 end").parse_expression
91
+ assert_instance_of SQLPP::AST::Atom, expr
92
+ assert_equal :case, expr.type
93
+ assert_nil expr.left
94
+ assert_equal 2, expr.right.length
95
+ assert_equal 2, expr.right[0].length
96
+ assert_equal "5", expr.right[0][0].left
97
+ assert_equal "1", expr.right[0][1].left
98
+ assert_equal "3", expr.right[1].left
99
+ end
100
+
101
+ def test_it_should_parse_unary_plus
102
+ expr = _parser("+x").parse_expression
103
+ assert_instance_of SQLPP::AST::Unary, expr
104
+ assert_equal "+", expr.op
105
+ assert_equal "x", expr.expr.left
106
+ end
107
+
108
+ def test_it_should_parse_unary_minus
109
+ expr = _parser("-x").parse_expression
110
+ assert_instance_of SQLPP::AST::Unary, expr
111
+ assert_equal "-", expr.op
112
+ assert_equal "x", expr.expr.left
113
+ end
114
+
115
+ def test_it_should_parse_arithmatic
116
+ %w(+ - * /).each do |op|
117
+ expr = _parser("x #{op} y").parse_expression
118
+ assert_instance_of SQLPP::AST::Expr, expr
119
+ assert_instance_of SQLPP::AST::Atom, expr.left
120
+ assert_equal op, expr.op
121
+ assert_instance_of SQLPP::AST::Atom, expr.right
122
+ end
123
+ end
124
+
125
+ def test_it_should_parse_between
126
+ expr = _parser("x between y and z").parse_expression
127
+ assert_instance_of SQLPP::AST::Expr, expr
128
+ assert_instance_of SQLPP::AST::Atom, expr.left
129
+ assert_equal :between, expr.op
130
+ assert_instance_of SQLPP::AST::Atom, expr.right
131
+ assert_equal :range, expr.right.type
132
+ assert_instance_of SQLPP::AST::Atom, expr.right.left
133
+ assert_instance_of SQLPP::AST::Atom, expr.right.right
134
+ end
135
+
136
+ def test_it_should_parse_like
137
+ expr = _parser("x like y").parse_expression
138
+ assert_instance_of SQLPP::AST::Expr, expr
139
+ assert_instance_of SQLPP::AST::Atom, expr.left
140
+ assert_equal :like, expr.op
141
+ assert_instance_of SQLPP::AST::Atom, expr.right
142
+ end
143
+
144
+ def test_it_should_parse_ilike
145
+ expr = _parser("x ilike y").parse_expression
146
+ assert_instance_of SQLPP::AST::Expr, expr
147
+ assert_instance_of SQLPP::AST::Atom, expr.left
148
+ assert_equal :ilike, expr.op
149
+ assert_instance_of SQLPP::AST::Atom, expr.right
150
+ end
151
+
152
+ def test_it_should_parse_in
153
+ expr = _parser("x in (1,2,3,4,5)").parse_expression
154
+ assert_instance_of SQLPP::AST::Expr, expr
155
+ assert_instance_of SQLPP::AST::Atom, expr.left
156
+ assert_equal :in, expr.op
157
+ assert_instance_of SQLPP::AST::Atom, expr.right
158
+ assert_equal :list, expr.right.type
159
+ assert_equal 5, expr.right.left.length
160
+ end
161
+
162
+ def test_it_should_parse_boolean_operations
163
+ %w(< <= <> != = >= >).each do |op|
164
+ expr = _parser("x #{op} y").parse_expression
165
+ assert_instance_of SQLPP::AST::Expr, expr
166
+ assert_instance_of SQLPP::AST::Atom, expr.left
167
+ assert_equal op, expr.op
168
+ assert_instance_of SQLPP::AST::Atom, expr.right
169
+ end
170
+ end
171
+
172
+ def test_it_should_parse_expr_operations
173
+ [:is, "is not", :and, :or].each do |op|
174
+ expr = _parser("x #{op} y").parse_expression
175
+ assert_instance_of SQLPP::AST::Expr, expr
176
+ assert_instance_of SQLPP::AST::Atom, expr.left
177
+ assert_equal op, expr.op
178
+ assert_instance_of SQLPP::AST::Atom, expr.right
179
+ end
180
+ end
181
+
182
+ def test_from_should_recognize_single_attr
183
+ from = _parser("x").parse_from
184
+ assert_instance_of SQLPP::AST::Atom, from
185
+ end
186
+
187
+ def test_from_should_recognize_alias
188
+ from = _parser("x y").parse_from
189
+ assert_instance_of SQLPP::AST::Alias, from
190
+ assert_equal "y", from.name
191
+ assert_instance_of SQLPP::AST::Atom, from.expr
192
+ end
193
+
194
+ def test_from_should_recognize_as
195
+ from = _parser("x AS y").parse_from
196
+ assert_instance_of SQLPP::AST::As, from
197
+ assert_equal "y", from.name
198
+ assert_instance_of SQLPP::AST::Atom, from.expr
199
+ end
200
+
201
+ def test_from_may_be_parenthesized
202
+ from = _parser("( x ) AS y").parse_from
203
+ assert_instance_of SQLPP::AST::As, from
204
+ assert_equal "y", from.name
205
+ assert_instance_of SQLPP::AST::Parens, from.expr
206
+ assert_instance_of SQLPP::AST::Atom, from.expr.value
207
+ end
208
+
209
+ def test_from_may_be_a_subselect
210
+ from = _parser("(select * from x) as y").parse_from
211
+ assert_instance_of SQLPP::AST::As, from
212
+ assert_instance_of SQLPP::AST::Parens, from.expr
213
+ assert_instance_of SQLPP::AST::Select, from.expr.value
214
+ end
215
+
216
+ def test_from_may_join_to_another_entity
217
+ [ "inner",
218
+ "left outer",
219
+ "right outer",
220
+ "full outer",
221
+ "cross"
222
+ ].each do |type|
223
+ from = _parser("x #{type} join y AS z ON x.id = z.id").parse_from
224
+ assert_instance_of SQLPP::AST::Join, from
225
+ assert_instance_of SQLPP::AST::Atom, from.left
226
+ assert_equal type, from.type
227
+ assert_instance_of SQLPP::AST::As, from.right
228
+ assert_instance_of SQLPP::AST::Expr, from.on
229
+ end
230
+ end
231
+
232
+ def test_chained_join_will_build_tree
233
+ from = _parser("x inner join y on x.id = y.id inner join z on x.id = z.id").parse_from
234
+ assert_instance_of SQLPP::AST::Join, from
235
+ assert_instance_of SQLPP::AST::Join, from.left
236
+ end
237
+
238
+ def test_accepts_select_with_projections
239
+ s = _parser("select x, y").parse_select
240
+ assert_instance_of SQLPP::AST::Select, s
241
+ assert_equal 2, s.projections.length
242
+ end
243
+
244
+ def test_accepts_select_with_froms
245
+ s = _parser("select from x, y").parse_select
246
+ assert_instance_of SQLPP::AST::Select, s
247
+ assert_equal 2, s.froms.length
248
+ end
249
+
250
+ def test_accepts_select_with_where
251
+ s = _parser("select from x where x > 5 and z < 2").parse_select
252
+ assert_instance_of SQLPP::AST::Select, s
253
+ assert_instance_of SQLPP::AST::Expr, s.wheres
254
+ end
255
+
256
+ def test_accepts_select_with_group_by
257
+ s = _parser("select * from x group by a, b").parse_select
258
+ assert_instance_of SQLPP::AST::Select, s
259
+ assert_equal 2, s.groups.length
260
+ end
261
+
262
+ def test_accepts_select_with_order_by
263
+ s = _parser("select * from x order by a ASC, b DESC NULLS LAST").parse_select
264
+ assert_instance_of SQLPP::AST::Select, s
265
+ assert_equal 2, s.orders.length
266
+ assert_instance_of SQLPP::AST::SortKey, s.orders[0]
267
+ assert_equal [:asc], s.orders[0].options
268
+ assert_instance_of SQLPP::AST::SortKey, s.orders[1]
269
+ assert_equal [:desc, "nulls last"], s.orders[1].options
270
+ end
271
+
272
+ def test_parse_should_recognize_select
273
+ s = _parser("select * from x").parse
274
+ assert_instance_of SQLPP::AST::Select, s
275
+ end
276
+
277
+ def _parser(string)
278
+ SQLPP::Parser.new(string)
279
+ end
280
+ end
@@ -0,0 +1,2 @@
1
+ require 'sqlpp'
2
+ require 'minitest/autorun'
@@ -0,0 +1,130 @@
1
+ require 'test_helper'
2
+
3
+ class TokenizerTest < Minitest::Test
4
+ def setup
5
+ @tokenizer = nil
6
+ end
7
+
8
+ def test_next_should_return_next_token_in_stream
9
+ _setup_tokenizer("select * from table")
10
+
11
+ assert_equal :key, tokenizer.next.type
12
+ assert_equal :space, tokenizer.next.type
13
+ assert_equal :punct, tokenizer.next.type
14
+ assert_equal :space, tokenizer.next.type
15
+ assert_equal :key, tokenizer.next.type
16
+ assert_equal :space, tokenizer.next.type
17
+ assert_equal :id, tokenizer.next.type
18
+ end
19
+
20
+ def test_peek_should_not_advance_token_pointer
21
+ _setup_tokenizer("select * from table")
22
+
23
+ assert_equal :key, tokenizer.peek.type
24
+ assert_equal :key, tokenizer.peek.type
25
+ end
26
+
27
+ def test_push_should_put_token_into_stream
28
+ _setup_tokenizer("select * from table")
29
+
30
+ tok = tokenizer.next
31
+ tokenizer.push tok
32
+
33
+ assert_equal :key, tokenizer.next.type
34
+ end
35
+
36
+ def test_it_should_recognize_keywords
37
+ SQLPP::Tokenizer::KEYWORDS.each do |word|
38
+ tok = _setup_tokenizer(word).next
39
+ assert_token tok, type: :key, text: word.downcase.to_sym
40
+ end
41
+ end
42
+
43
+ def test_it_should_recognize_identifiers
44
+ _setup_tokenizer "word word123 \"quoted word\" \"with \\\"escape\\\" word\""
45
+
46
+ assert_token _next, type: :id, text: "word"
47
+ _skip :space
48
+ assert_token _next, type: :id, text: "word123"
49
+ _skip :space
50
+ assert_token _next, type: :id, text: '"quoted word"'
51
+ _skip :space
52
+ assert_token _next, type: :id, text: '"with \"escape\" word"'
53
+ end
54
+
55
+ def test_it_should_recognize_number_literals
56
+ _setup_tokenizer "1 123 0.5 123.456"
57
+
58
+ assert_token _next, type: :lit, text: "1"; _skip :space
59
+ assert_token _next, type: :lit, text: "123"; _skip :space
60
+ assert_token _next, type: :lit, text: "0.5"; _skip :space
61
+ assert_token _next, type: :lit, text: "123.456"
62
+ end
63
+
64
+ def test_it_should_recognize_string_literals
65
+ _setup_tokenizer "'hello' 'quoted ''string'' here'"
66
+
67
+ assert_token _next, type: :lit, text: "'hello'"; _skip :space
68
+ assert_token _next, type: :lit, text: "'quoted ''string'' here'"
69
+ end
70
+
71
+ def test_it_should_recognize_whitespace
72
+ _setup_tokenizer " space\n "
73
+
74
+ assert_token _next, type: :space, text: " "; _skip :id
75
+ assert_token _next, type: :space, text: "\n "
76
+ end
77
+
78
+ def test_it_should_recognize_multichar_punctuation
79
+ _setup_tokenizer "<= <> != >="
80
+
81
+ assert_token _next, type: :punct, text: "<="; _skip :space
82
+ assert_token _next, type: :punct, text: "<>"; _skip :space
83
+ assert_token _next, type: :punct, text: "!="; _skip :space
84
+ assert_token _next, type: :punct, text: ">="
85
+ end
86
+
87
+ def test_it_should_recognize_punctuation
88
+ _setup_tokenizer "< > = ( ) . * , / + -"
89
+
90
+ assert_token _next, type: :punct, text: "<"; _skip :space
91
+ assert_token _next, type: :punct, text: ">"; _skip :space
92
+ assert_token _next, type: :punct, text: "="; _skip :space
93
+ assert_token _next, type: :punct, text: "("; _skip :space
94
+ assert_token _next, type: :punct, text: ")"; _skip :space
95
+ assert_token _next, type: :punct, text: "."; _skip :space
96
+ assert_token _next, type: :punct, text: "*"; _skip :space
97
+ assert_token _next, type: :punct, text: ","; _skip :space
98
+ assert_token _next, type: :punct, text: "/"; _skip :space
99
+ assert_token _next, type: :punct, text: "+"; _skip :space
100
+ assert_token _next, type: :punct, text: "-"
101
+ end
102
+
103
+ def test_it_should_recognize_end_of_file
104
+ _setup_tokenizer "done"
105
+
106
+ _skip :id
107
+ assert_token _next, type: :eof
108
+ assert_token _next, type: :eof
109
+ end
110
+
111
+ attr_reader :tokenizer
112
+
113
+ def _setup_tokenizer(string)
114
+ @tokenizer = SQLPP::Tokenizer.new(string)
115
+ end
116
+
117
+ def _next
118
+ tokenizer.next
119
+ end
120
+
121
+ def _skip(type)
122
+ assert_token _next, type: type
123
+ end
124
+
125
+ def assert_token(token, type: nil, text: nil, pos: nil)
126
+ assert_equal type, token.type if type
127
+ assert_equal text, token.text if text
128
+ assert_equal pos, token.pos if pos
129
+ end
130
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sqlpp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jamis Buck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A simplistic SQL parser and pretty-printer
42
+ email:
43
+ - jamis@jamisbuck.org
44
+ executables:
45
+ - sqlpp
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - MIT-LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - bin/sqlpp
53
+ - lib/sqlpp.rb
54
+ - lib/sqlpp/ast.rb
55
+ - lib/sqlpp/formatter.rb
56
+ - lib/sqlpp/parser.rb
57
+ - lib/sqlpp/tokenizer.rb
58
+ - lib/sqlpp/version.rb
59
+ - sqlpp.gemspec
60
+ - test/formatter_test.rb
61
+ - test/parser_test.rb
62
+ - test/test_helper.rb
63
+ - test/tokenizer_test.rb
64
+ homepage: http://github.com/jamis/sqlp
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.5.1
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: A simplistic SQL parser and pretty-printer
88
+ test_files:
89
+ - test/formatter_test.rb
90
+ - test/parser_test.rb
91
+ - test/test_helper.rb
92
+ - test/tokenizer_test.rb
93
+ has_rdoc: