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.
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