sqlpp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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: