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.
@@ -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
@@ -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 if where
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 = self.parse_select_clause(tokens)
32
- select_node.from = self.parse_from_clause(tokens) if tokens.peek == SQLTree::Token::FROM
33
- select_node.where = self.parse_where_clause(tokens) if tokens.peek == SQLTree::Token::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
- # TODO: implement me
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)
@@ -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
@@ -15,10 +15,6 @@ module SQLTree::Node
15
15
  end
16
16
  end
17
17
 
18
- def to_tree
19
- @value
20
- end
21
-
22
18
  def ==(other)
23
19
  other.kind_of?(self.class) && other.value == self.value
24
20
  end
@@ -12,10 +12,6 @@ module SQLTree::Node
12
12
  quote_var(@name)
13
13
  end
14
14
 
15
- def to_tree
16
- @name.to_sym
17
- end
18
-
19
15
  def ==(other)
20
16
  other.name == self.name
21
17
  end
@@ -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 GOUP HAVING ORDER DISTINCT LEFT RIGHT INNER FULL OUTER NATURAL JOIN USING
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 a complex SQL query" do
18
- SQLTree['SELECT a.*, MD5( a.name ) AS checksum FROM table AS a , other WHERE other.timestamp > a.timestamp'].to_sql.should ==
19
- 'SELECT "a".*, MD5("a"."name") AS "checksum" FROM "table" AS "a", "other" WHERE ("other"."timestamp" > "a"."timestamp")'
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 = '0.0.1'
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.1
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