sql_tree 0.0.1 → 0.0.2
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/lib/sql_tree/node/expression.rb +0 -24
- data/lib/sql_tree/node/field.rb +3 -4
- data/lib/sql_tree/node/ordering.rb +25 -0
- data/lib/sql_tree/node/select_query.rb +14 -6
- data/lib/sql_tree/node/source.rb +1 -1
- data/lib/sql_tree/node/value.rb +0 -4
- data/lib/sql_tree/node/variable.rb +0 -4
- data/lib/sql_tree/token.rb +6 -2
- data/spec/integration/full_queries_spec.rb +18 -3
- data/spec/lib/matchers.rb +0 -28
- data/spec/unit/select_query_spec.rb +29 -0
- data/sql_tree.gemspec +2 -2
- metadata +2 -1
@@ -49,10 +49,6 @@ module SQLTree::Node
|
|
49
49
|
"NOT(#{@expression.to_sql})"
|
50
50
|
end
|
51
51
|
|
52
|
-
def to_tree
|
53
|
-
[:not, expression.to_tree]
|
54
|
-
end
|
55
|
-
|
56
52
|
def ==(other)
|
57
53
|
other.kind_of?(self.class) && other.expression == self.expression
|
58
54
|
end
|
@@ -75,10 +71,6 @@ module SQLTree::Node
|
|
75
71
|
"(" + @expressions.map { |e| e.to_sql }.join(" #{@operator.to_s.upcase} ") + ")"
|
76
72
|
end
|
77
73
|
|
78
|
-
def to_tree
|
79
|
-
[@operator] + @expressions.map { |e| e.to_tree }
|
80
|
-
end
|
81
|
-
|
82
74
|
def ==(other)
|
83
75
|
self.operator == other.operator && self.expressions == other.expressions
|
84
76
|
end
|
@@ -105,10 +97,6 @@ module SQLTree::Node
|
|
105
97
|
"(#{@lhs.to_sql} #{@operator} #{@rhs.to_sql})"
|
106
98
|
end
|
107
99
|
|
108
|
-
def to_tree
|
109
|
-
[SQLTree::Token::OPERATORS_HASH[@operator], @lhs.to_tree, @rhs.to_tree]
|
110
|
-
end
|
111
|
-
|
112
100
|
def self.parse_comparison_operator(tokens)
|
113
101
|
operator_token = tokens.next
|
114
102
|
if SQLTree::Token::IS === operator_token
|
@@ -151,10 +139,6 @@ module SQLTree::Node
|
|
151
139
|
@items = items
|
152
140
|
end
|
153
141
|
|
154
|
-
def to_tree
|
155
|
-
items.map { |i| i.to_tree }
|
156
|
-
end
|
157
|
-
|
158
142
|
def to_sql
|
159
143
|
"(#{items.map {|i| i.to_sql}.join(', ')})"
|
160
144
|
end
|
@@ -184,10 +168,6 @@ module SQLTree::Node
|
|
184
168
|
"#{@function}(" + @arguments.map { |e| e.to_sql }.join(', ') + ")"
|
185
169
|
end
|
186
170
|
|
187
|
-
def to_tree
|
188
|
-
[@function.to_sym] + @arguments.map { |e| e.to_tree }
|
189
|
-
end
|
190
|
-
|
191
171
|
def self.parse(tokens)
|
192
172
|
expr = self.new(tokens.next.literal)
|
193
173
|
tokens.consume(SQLTree::Token::LPAREN)
|
@@ -213,10 +193,6 @@ module SQLTree::Node
|
|
213
193
|
"(#{@lhs.to_sql} #{@operator} #{@rhs.to_sql})"
|
214
194
|
end
|
215
195
|
|
216
|
-
def to_tree
|
217
|
-
[SQLTree::Token::OPERATORS_HASH[@operator], @lhs.to_tree, @rhs.to_tree]
|
218
|
-
end
|
219
|
-
|
220
196
|
def self.parse(tokens)
|
221
197
|
self.parse_primary(tokens)
|
222
198
|
end
|
data/lib/sql_tree/node/field.rb
CHANGED
@@ -4,6 +4,9 @@ module SQLTree::Node
|
|
4
4
|
|
5
5
|
attr_accessor :name, :table
|
6
6
|
|
7
|
+
alias :field :name
|
8
|
+
alias :field= :name=
|
9
|
+
|
7
10
|
def initialize(name, table = nil)
|
8
11
|
@name = name
|
9
12
|
@table = table
|
@@ -17,10 +20,6 @@ module SQLTree::Node
|
|
17
20
|
def to_sql
|
18
21
|
@table.nil? ? quote_var(@name) : quote_var(@table) + '.' + quote_var(@name)
|
19
22
|
end
|
20
|
-
|
21
|
-
def to_tree
|
22
|
-
to_sql.to_sym
|
23
|
-
end
|
24
23
|
|
25
24
|
def ==(other)
|
26
25
|
other.name == self.name && other.table == self.table
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SQLTree::Node
|
2
|
+
|
3
|
+
class Ordering < Base
|
4
|
+
|
5
|
+
attr_accessor :expression, :direction
|
6
|
+
|
7
|
+
def initialize(expression, direction = nil)
|
8
|
+
@expression, @direction = expression, direction
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_sql
|
12
|
+
sql = expression.to_sql
|
13
|
+
sql << " #{direction.to_s.upcase}" if direction
|
14
|
+
sql
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse(tokens)
|
18
|
+
ordering = self.new(SQLTree::Node::Expression.parse(tokens))
|
19
|
+
if tokens.peek && tokens.peek.direction?
|
20
|
+
ordering.direction = tokens.next.literal.downcase.to_sym
|
21
|
+
end
|
22
|
+
return ordering
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -14,7 +14,8 @@ module SQLTree::Node
|
|
14
14
|
sql = (self.distinct) ? "SELECT DISTINCT " : "SELECT "
|
15
15
|
sql << select.map { |s| s.to_sql }.join(', ')
|
16
16
|
sql << " FROM " << from.map { |f| f.to_sql }.join(', ')
|
17
|
-
sql << " WHERE " << where.to_sql
|
17
|
+
sql << " WHERE " << where.to_sql if where
|
18
|
+
sql << " ORDER BY " << order_by.map { |o| o.to_sql }.join(', ') if order_by
|
18
19
|
return sql
|
19
20
|
end
|
20
21
|
|
@@ -28,10 +29,10 @@ module SQLTree::Node
|
|
28
29
|
select_node.distinct = true
|
29
30
|
end
|
30
31
|
|
31
|
-
select_node.select
|
32
|
-
select_node.from
|
33
|
-
select_node.where
|
34
|
-
|
32
|
+
select_node.select = self.parse_select_clause(tokens)
|
33
|
+
select_node.from = self.parse_from_clause(tokens) if tokens.peek == SQLTree::Token::FROM
|
34
|
+
select_node.where = self.parse_where_clause(tokens) if tokens.peek == SQLTree::Token::WHERE
|
35
|
+
select_node.order_by = self.parse_order_clause(tokens) if tokens.peek == SQLTree::Token::ORDER
|
35
36
|
return select_node
|
36
37
|
end
|
37
38
|
|
@@ -68,7 +69,14 @@ module SQLTree::Node
|
|
68
69
|
end
|
69
70
|
|
70
71
|
def self.parse_order_clause(tokens)
|
71
|
-
|
72
|
+
tokens.consume(SQLTree::Token::ORDER)
|
73
|
+
tokens.consume(SQLTree::Token::BY)
|
74
|
+
exprs = [SQLTree::Node::Ordering.parse(tokens)]
|
75
|
+
while tokens.peek == SQLTree::Token::COMMA
|
76
|
+
tokens.consume(SQLTree::Token::COMMA)
|
77
|
+
exprs << SQLTree::Node::Ordering.parse(tokens)
|
78
|
+
end
|
79
|
+
return exprs
|
72
80
|
end
|
73
81
|
|
74
82
|
def self.parse_limit_clause(tokens)
|
data/lib/sql_tree/node/source.rb
CHANGED
@@ -29,7 +29,7 @@ module SQLTree::Node
|
|
29
29
|
def self.parse(tokens)
|
30
30
|
source = self.new(SQLTree::Node::TableReference.parse(tokens))
|
31
31
|
while tokens.peek && tokens.peek.join?
|
32
|
-
source.joins << Join.parse(tokens)
|
32
|
+
source.joins << SQLTree::Node::Join.parse(tokens)
|
33
33
|
end
|
34
34
|
return source
|
35
35
|
end
|
data/lib/sql_tree/node/value.rb
CHANGED
data/lib/sql_tree/token.rb
CHANGED
@@ -36,6 +36,10 @@ class SQLTree::Token
|
|
36
36
|
SQLTree::Token::FULL].include?(self)
|
37
37
|
end
|
38
38
|
|
39
|
+
def direction?
|
40
|
+
[SQLTree::Token::ASC, SQLTree::Token::DESC].include?(self)
|
41
|
+
end
|
42
|
+
|
39
43
|
###################################################################
|
40
44
|
# DYNAMIC TOKEN TYPES
|
41
45
|
###################################################################
|
@@ -107,8 +111,8 @@ class SQLTree::Token
|
|
107
111
|
COMMA = Class.new(SQLTree::Token).new(',')
|
108
112
|
|
109
113
|
# A list of all the SQL reserverd keywords.
|
110
|
-
KEYWORDS = %w{SELECT FROM WHERE
|
111
|
-
AND OR NOT AS ON IS NULL BY LIKE ILIKE BETWEEN IN}
|
114
|
+
KEYWORDS = %w{SELECT FROM WHERE GROUP HAVING ORDER DISTINCT LEFT RIGHT INNER FULL OUTER NATURAL JOIN USING
|
115
|
+
AND OR NOT AS ON IS NULL BY LIKE ILIKE BETWEEN IN ASC DESC}
|
112
116
|
|
113
117
|
# Create a token for all the reserved keywords in SQL
|
114
118
|
KEYWORDS.each { |kw| const_set(kw, Class.new(SQLTree::Token::Keyword).new(kw)) }
|
@@ -14,8 +14,23 @@ describe SQLTree, 'parsing and generating SQL' do
|
|
14
14
|
SQLTree["SELECT a.* FROM table AS a"].to_sql.should == 'SELECT "a".* FROM "table" AS "a"'
|
15
15
|
end
|
16
16
|
|
17
|
-
it "parse and generate
|
18
|
-
SQLTree[
|
19
|
-
|
17
|
+
it "should parse and generate an ORDER BY clause" do
|
18
|
+
SQLTree["SELECT * FROM table ORDER BY field1, field2"].to_sql.should ==
|
19
|
+
'SELECT * FROM "table" ORDER BY "field1", "field2"'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should parse and generate an expression in the SELECT clause" do
|
23
|
+
SQLTree['SELECT MD5( a) AS a, b > 0 AS test FROM table'].to_sql.should ==
|
24
|
+
'SELECT MD5("a") AS "a", ("b" > 0) AS "test" FROM "table"'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should parse and generate a complex FROM clause" do
|
28
|
+
SQLTree['SELECT * FROM a LEFT JOIN b ON ( a.id = b.a_id), c AS d'].to_sql.should ==
|
29
|
+
'SELECT * FROM "a" LEFT JOIN "b" ON ("a"."id" = "b"."a_id"), "c" AS "d"'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should parse and generate a WHERE clause" do
|
33
|
+
SQLTree['SELECT * FROM t WHERE ( field > 4 OR NOW() > timestamp) AND other_field IS NOT NULL'].to_sql.should ==
|
34
|
+
'SELECT * FROM "t" WHERE ((("field" > 4) OR (NOW() > "timestamp")) AND ("other_field" IS NOT NULL))'
|
20
35
|
end
|
21
36
|
end
|
data/spec/lib/matchers.rb
CHANGED
@@ -1,31 +1,3 @@
|
|
1
|
-
class ParseAs
|
2
|
-
|
3
|
-
def initialize(expected_tree)
|
4
|
-
@expected_tree = expected_tree
|
5
|
-
end
|
6
|
-
|
7
|
-
def matches?(found_tree)
|
8
|
-
@found_tree = found_tree.to_tree
|
9
|
-
return @found_tree == @expected_tree
|
10
|
-
end
|
11
|
-
|
12
|
-
def description
|
13
|
-
"expected to parse to #{@expected_tree.inspect}"
|
14
|
-
end
|
15
|
-
|
16
|
-
def failure_message
|
17
|
-
" #{@expected_tree.inspect} expected, but found #{@found_tree.inspect}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def negative_failure_message
|
21
|
-
" expected not to be tokenized to #{@expected_tree.inspect}"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def parse_as(tree)
|
26
|
-
ParseAs.new(tree)
|
27
|
-
end
|
28
|
-
|
29
1
|
class TokenizeTo
|
30
2
|
|
31
3
|
def initialize(expected_tokens)
|
@@ -50,3 +50,32 @@ describe SQLTree::Node::Join do
|
|
50
50
|
SQLTree::Node::Join['LEFT JOIN table t ON other.field = table.field'].table_alias.should == 't'
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
describe SQLTree::Node::Ordering do
|
55
|
+
it "should parse an ordering with direction" do
|
56
|
+
ordering = SQLTree::Node::Ordering["table.field ASC"]
|
57
|
+
ordering.expression.table.should == 'table'
|
58
|
+
ordering.expression.name.should == 'field'
|
59
|
+
ordering.direction.should == :asc
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should parse an ordering without direction" do
|
63
|
+
ordering = SQLTree::Node::Ordering["table.field"]
|
64
|
+
ordering.expression.table.should == 'table'
|
65
|
+
ordering.expression.name.should == 'field'
|
66
|
+
ordering.direction.should be_nil
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should parse an ordering without direction" do
|
70
|
+
ordering = SQLTree::Node::Ordering["MD5(3 + 6) DESC"]
|
71
|
+
ordering.expression.should be_kind_of(SQLTree::Node::FunctionExpression)
|
72
|
+
ordering.direction.should == :desc
|
73
|
+
end
|
74
|
+
|
75
|
+
it "shoulde parse multiple orderings" do
|
76
|
+
tree = SQLTree['SELECT * FROM table ORDER BY field1 ASC, field2 DESC']
|
77
|
+
tree.order_by.should have(2).items
|
78
|
+
tree.order_by[0].direction.should == :asc
|
79
|
+
tree.order_by[1].direction.should == :desc
|
80
|
+
end
|
81
|
+
end
|
data/sql_tree.gemspec
CHANGED
@@ -3,7 +3,7 @@ Gem::Specification.new do |s|
|
|
3
3
|
|
4
4
|
# Do not modify the version and date values by hand, because this will
|
5
5
|
# automatically by them gem release script.
|
6
|
-
s.version =
|
6
|
+
s.version = "0.0.2"
|
7
7
|
s.date = "2009-10-09"
|
8
8
|
|
9
9
|
s.summary = "A pure Ruby library to represent SQL queries with a syntax tree for inspection and modification."
|
@@ -22,6 +22,6 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
# Do not modify the files and test_files values by hand, because this will
|
24
24
|
# automatically by them gem release script.
|
25
|
-
s.files = %w(spec/unit/select_query_spec.rb spec/spec_helper.rb lib/sql_tree/tokenizer.rb lib/sql_tree/node/variable.rb lib/sql_tree/node/join.rb .gitignore LICENSE spec/lib/matchers.rb spec/integration/full_queries_spec.rb lib/sql_tree/parser.rb sql_tree.gemspec spec/unit/tokenizer_spec.rb spec/unit/expression_node_spec.rb lib/sql_tree/node/select_expression.rb spec/unit/leaf_node_spec.rb lib/sql_tree/token.rb lib/sql_tree/node/table_reference.rb lib/sql_tree/node/source.rb lib/sql_tree/node/field.rb Rakefile tasks/github-gem.rake lib/sql_tree/node/select_query.rb lib/sql_tree/node.rb README.rdoc spec/integration/api_spec.rb lib/sql_tree/node/value.rb lib/sql_tree/node/expression.rb lib/sql_tree.rb)
|
25
|
+
s.files = %w(spec/unit/select_query_spec.rb spec/spec_helper.rb lib/sql_tree/tokenizer.rb lib/sql_tree/node/variable.rb lib/sql_tree/node/join.rb .gitignore lib/sql_tree/node/ordering.rb LICENSE spec/lib/matchers.rb spec/integration/full_queries_spec.rb lib/sql_tree/parser.rb sql_tree.gemspec spec/unit/tokenizer_spec.rb spec/unit/expression_node_spec.rb lib/sql_tree/node/select_expression.rb spec/unit/leaf_node_spec.rb lib/sql_tree/token.rb lib/sql_tree/node/table_reference.rb lib/sql_tree/node/source.rb lib/sql_tree/node/field.rb Rakefile tasks/github-gem.rake lib/sql_tree/node/select_query.rb lib/sql_tree/node.rb README.rdoc spec/integration/api_spec.rb lib/sql_tree/node/value.rb lib/sql_tree/node/expression.rb lib/sql_tree.rb)
|
26
26
|
s.test_files = %w(spec/unit/select_query_spec.rb spec/integration/full_queries_spec.rb spec/unit/tokenizer_spec.rb spec/unit/expression_node_spec.rb spec/unit/leaf_node_spec.rb spec/integration/api_spec.rb)
|
27
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sql_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- lib/sql_tree/node/variable.rb
|
29
29
|
- lib/sql_tree/node/join.rb
|
30
30
|
- .gitignore
|
31
|
+
- lib/sql_tree/node/ordering.rb
|
31
32
|
- LICENSE
|
32
33
|
- spec/lib/matchers.rb
|
33
34
|
- spec/integration/full_queries_spec.rb
|