sqlpp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24d6f928aab73c2d65cf5eefc714dfac7f441641
4
+ data.tar.gz: df2f51ed9d6aef74270f7dfb64322154d94dc0b3
5
+ SHA512:
6
+ metadata.gz: dda5183a4e28c854494b164f6a725a2e35f788c1def25f256693f557cb9cdf2a95f49915d8ceb2d6c4a3491b81634f1253c30989cd913047b90597f42e39366a
7
+ data.tar.gz: 980e4630ec6de08556260f31ac361366b14a250f3ee8af89d77cbbc96c734e2a4d3426d3e0e283feb9f1e3f690d2d2e71898e6fb1e17545c142c45efd24cb6c1
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Jamis Buck
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # SQLPP
2
+
3
+ SQLPP is a simplistic SQL parser and pretty-printer.
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ require 'sqlpp'
9
+
10
+ sql = "..."
11
+ ast = SQLPP::Parser.parse(sql)
12
+
13
+ puts SQLPP::Formatter.new.format(ast)
14
+ ```
15
+
16
+ Or, you can use the included `bin/sqlpp` script to format SQL via STDIN:
17
+
18
+ ```sh
19
+ $ sqlpp < query.sql
20
+ ```
21
+
22
+ ## Output
23
+
24
+ The formatter is not particularly sophisticated, and is optimized primarily for displaying queries with deeply nested subselects. The major query components (`FROM`, `WHERE`, `GROUP BY`, and `ORDER BY`) are printed on separate lines, with subselects indented.
25
+
26
+ ```sql
27
+ SELECT a, b, sum(c)
28
+ FROM (
29
+ SELECT d, e, f
30
+ FROM (
31
+ SELECT g, h, i
32
+ FROM table
33
+ WHERE id IN (1, 2, 3)
34
+ ) a
35
+ WHERE a.e = 5
36
+ OR a.e = 7
37
+ ) b
38
+ WHERE b.c > 5
39
+ GROUP BY a, b
40
+ ORDER BY a ASC, b DESC
41
+ ```
42
+
43
+ ## Caveats
44
+
45
+ This implementation is far, far, far from complete. It currently accepts only `SELECT` statements, and even then will only recognize a subset of the valid SQL syntax. That said, it should be a pretty big subset. It's done well enough for what I've needed it for.
46
+
47
+ If, however, you find that it doesn't recognize some syntax that you need, pull requests would be appreciated!
48
+
49
+ ## License
50
+
51
+ MIT. See `MIT-LICENSE`.
52
+
53
+ ## Author
54
+
55
+ Jamis Buck (jamis@jamisbuck.org)
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ task default: :test
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
data/bin/sqlpp ADDED
@@ -0,0 +1,8 @@
1
+ #!/bin/sh ruby
2
+
3
+ require 'sqlpp'
4
+
5
+ sql = STDIN.read
6
+
7
+ ast = SQLPP::Parser.parse(sql)
8
+ puts SQLPP::Formatter.new.format(ast)
data/lib/sqlpp/ast.rb ADDED
@@ -0,0 +1,30 @@
1
+ module SQLPP
2
+ module AST
3
+ class Select < Struct.new(:projections, :froms, :wheres, :groups, :orders)
4
+ end
5
+
6
+ class Expr < Struct.new(:left, :op, :right)
7
+ end
8
+
9
+ class Unary < Struct.new(:op, :expr)
10
+ end
11
+
12
+ class Atom < Struct.new(:type, :left, :right)
13
+ end
14
+
15
+ class Parens < Struct.new(:value)
16
+ end
17
+
18
+ class As < Struct.new(:name, :expr)
19
+ end
20
+
21
+ class Alias < Struct.new(:name, :expr)
22
+ end
23
+
24
+ class Join < Struct.new(:type, :left, :right, :on)
25
+ end
26
+
27
+ class SortKey < Struct.new(:key, :options)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,163 @@
1
+ module SQLPP
2
+ class Formatter
3
+ def initialize
4
+ @indent = nil
5
+ @state = nil
6
+ end
7
+
8
+ def format(node)
9
+ name = node.class.to_s.split(/::/).last
10
+ send(:"_format_#{name}", node)
11
+ end
12
+
13
+ def _format_Select(node)
14
+ output = ""
15
+
16
+ if @indent.nil?
17
+ @indent = 0
18
+ else
19
+ @indent += 2
20
+ output << "\n"
21
+ end
22
+
23
+ output << "#{_indent}SELECT "
24
+ output << node.projections.map { |c| format(c) }.join(", ")
25
+ output << "\n"
26
+
27
+ if node.froms
28
+ output << "#{_indent}FROM "
29
+ output << node.froms.map { |c| format(c) }.join(", ")
30
+ output << "\n"
31
+ end
32
+
33
+ if node.wheres
34
+ save, @state = @state, :where
35
+ output << "#{_indent}WHERE "
36
+ output << format(node.wheres)
37
+ output << "\n"
38
+ @state = save
39
+ end
40
+
41
+ if node.groups
42
+ output << "#{_indent}GROUP BY "
43
+ output << node.groups.map { |c| format(c) }.join(", ")
44
+ output << "\n"
45
+ end
46
+
47
+ if node.orders
48
+ output << "#{_indent}ORDER BY "
49
+ output << node.orders.map { |c| format(c) }.join(", ")
50
+ output << "\n"
51
+ end
52
+
53
+ @indent -= 2
54
+ @indent = nil if @indent < 0
55
+
56
+ output << _indent
57
+ end
58
+
59
+ def _format_Expr(node)
60
+ output = format(node.left)
61
+ if node.op
62
+ op = node.op.to_s.upcase
63
+
64
+ if @state == :where && %w(AND OR).include?(op)
65
+ output << "\n#{_indent}"
66
+ else
67
+ output << " "
68
+ end
69
+
70
+ output << op << " "
71
+ output << format(node.right)
72
+ end
73
+ output
74
+ end
75
+
76
+ def _format_Unary(node)
77
+ op = node.op.to_s.upcase
78
+ output = op
79
+ output << " " if op =~ /\w/
80
+ output << format(node.expr)
81
+ end
82
+
83
+ def _format_Atom(node)
84
+ output = ""
85
+
86
+ case node.type
87
+ when :range
88
+ output << format(node.left) << " AND " << format(node.right)
89
+ when :list
90
+ output << "(" << node.left.map { |c| format(c) }.join(", ") << ")"
91
+ when :func
92
+ output << format(node.left) << "("
93
+ output << node.right.map { |c| format(c) }.join(", ")
94
+ output << ")"
95
+ when :lit
96
+ output << node.left
97
+ when :attr
98
+ output << node.left
99
+ output << "." << node.right if node.right
100
+ when :case
101
+ output << "CASE "
102
+ output << format(node.left) << " " if node.left
103
+ node.right.each do |child|
104
+ if child.is_a?(Array)
105
+ output << "WHEN " << format(child[0]) << " "
106
+ output << "THEN " << format(child[1]) << " "
107
+ else
108
+ output << "ELSE " << format(child) << " "
109
+ end
110
+ end
111
+ output << "END"
112
+ else
113
+ raise ArgumentError, "unknown atom type #{node.type.inspect}"
114
+ end
115
+
116
+ output
117
+ end
118
+
119
+ def _format_Parens(node)
120
+ "(" + format(node.value) + ")"
121
+ end
122
+
123
+ def _format_As(node)
124
+ format(node.expr) + " AS " + format(node.name)
125
+ end
126
+
127
+ def _format_Alias(node)
128
+ format(node.expr) + " " + format(node.name)
129
+ end
130
+
131
+ def _format_Join(node)
132
+ output = ""
133
+
134
+ output << format(node.left)
135
+ output << "\n#{_indent}"
136
+ output << node.type.upcase << " JOIN "
137
+ output << format(node.right)
138
+ output << "\n#{_indent}ON " << format(node.on) if node.on
139
+
140
+ output
141
+ end
142
+
143
+ def _format_SortKey(node)
144
+ output = ""
145
+ output << format(node.key)
146
+
147
+ if node.options.any?
148
+ output << " "
149
+ output << node.options.map { |opt| opt.upcase }.join(", ")
150
+ end
151
+
152
+ output
153
+ end
154
+
155
+ def _format_String(string)
156
+ string
157
+ end
158
+
159
+ def _indent
160
+ " " * (@indent || 0)
161
+ end
162
+ end
163
+ end