sql_tree 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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