sql_tree 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ module SQLTree::Node
2
+
3
+ class Assignment < Base
4
+
5
+ attr_accessor :field, :expression
6
+
7
+ def initialize(field, expression = nil)
8
+ @field, @expression = field, expression
9
+ end
10
+
11
+ def to_sql
12
+ "#{quote_var(field)} = #{expression.to_sql}"
13
+ end
14
+
15
+ def self.parse(tokens)
16
+ assignment = self.new(SQLTree::Node::Variable.parse(tokens).name)
17
+ tokens.consume(SQLTree::Token::EQ)
18
+ assignment.expression = SQLTree::Node::Expression.parse(tokens)
19
+ assignment
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module SQLTree::Node
2
+
3
+ class DeleteQuery < Base
4
+
5
+ attr_accessor :table, :where
6
+
7
+ def initialize(table, where = nil)
8
+ @table, @where = table, where
9
+ end
10
+
11
+ def to_sql
12
+ sql = "DELETE FROM #{self.quote_var(table)}"
13
+ sql << " WHERE #{where.to_sql}" if self.where
14
+ sql
15
+ end
16
+
17
+ def self.parse(tokens)
18
+ tokens.consume(SQLTree::Token::DELETE)
19
+ tokens.consume(SQLTree::Token::FROM)
20
+ delete_query = self.new(SQLTree::Node::Variable.parse(tokens).name)
21
+ if tokens.peek == SQLTree::Token::WHERE
22
+ tokens.consume(SQLTree::Token::WHERE)
23
+ delete_query.where = SQLTree::Node::Expression.parse(tokens)
24
+ end
25
+ return delete_query
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ module SQLTree::Node
2
+
3
+ class InsertQuery < Base
4
+
5
+ attr_accessor :table, :fields, :values
6
+
7
+ def initialize(table, fields = nil, values = [])
8
+ @table, @fields, @values = table, fields, values
9
+ end
10
+
11
+ def to_sql
12
+ sql = "INSERT INTO #{self.quote_var(table)} "
13
+ sql << '(' + fields.map { |f| f.to_sql }.join(', ') + ') ' if fields
14
+ sql << 'VALUES (' + values.map { |v| v.to_sql }.join(', ') + ')'
15
+ sql
16
+ end
17
+
18
+ def self.parse_field_list(tokens)
19
+ tokens.consume(SQLTree::Token::LPAREN)
20
+ fields = [SQLTree::Node::Variable.parse(tokens)]
21
+ while tokens.peek == SQLTree::Token::COMMA
22
+ tokens.consume(SQLTree::Token::COMMA)
23
+ fields << SQLTree::Node::Variable.parse(tokens)
24
+ end
25
+ tokens.consume(SQLTree::Token::RPAREN)
26
+ return fields
27
+ end
28
+
29
+ def self.parse_value_list(tokens)
30
+ tokens.consume(SQLTree::Token::VALUES)
31
+ tokens.consume(SQLTree::Token::LPAREN)
32
+ values = [SQLTree::Node::Expression.parse(tokens)]
33
+ while tokens.peek == SQLTree::Token::COMMA
34
+ tokens.consume(SQLTree::Token::COMMA)
35
+ values << SQLTree::Node::Expression.parse(tokens)
36
+ end
37
+ tokens.consume(SQLTree::Token::RPAREN)
38
+ return values
39
+ end
40
+
41
+ def self.parse(tokens)
42
+ tokens.consume(SQLTree::Token::INSERT)
43
+ tokens.consume(SQLTree::Token::INTO)
44
+ insert_query = self.new(SQLTree::Node::Variable.parse(tokens).name)
45
+
46
+ insert_query.fields = self.parse_field_list(tokens) if tokens.peek == SQLTree::Token::LPAREN
47
+ insert_query.values = self.parse_value_list(tokens)
48
+ return insert_query
49
+ end
50
+ end
51
+ end
@@ -13,7 +13,7 @@ module SQLTree::Node
13
13
  raise "At least one SELECT expression is required" if self.select.empty?
14
14
  sql = (self.distinct) ? "SELECT DISTINCT " : "SELECT "
15
15
  sql << select.map { |s| s.to_sql }.join(', ')
16
- sql << " FROM " << from.map { |f| f.to_sql }.join(', ')
16
+ sql << " FROM " << from.map { |f| f.to_sql }.join(', ') if from
17
17
  sql << " WHERE " << where.to_sql if where
18
18
  sql << " GROUP BY " << group_by.map { |g| g.to_sql }.join(', ') if group_by
19
19
  sql << " ORDER BY " << order_by.map { |o| o.to_sql }.join(', ') if order_by
@@ -0,0 +1,36 @@
1
+ module SQLTree::Node
2
+
3
+ class UpdateQuery < Base
4
+
5
+ attr_accessor :table, :updates, :where
6
+
7
+ def initialize(table, updates = [], where = nil)
8
+ @table, @updates, @where = table, updates, where
9
+ end
10
+
11
+ def to_sql
12
+ sql = "UPDATE #{self.quote_var(table)} SET "
13
+ sql << updates.map { |u| u.to_sql }.join(', ')
14
+ sql << " WHERE " << where.to_sql if self.where
15
+ sql
16
+ end
17
+
18
+ def self.parse(tokens)
19
+ tokens.consume(SQLTree::Token::UPDATE)
20
+ update_query = self.new(SQLTree::Node::Variable.parse(tokens).name)
21
+ tokens.consume(SQLTree::Token::SET)
22
+ update_query.updates = [SQLTree::Node::Assignment.parse(tokens)]
23
+ while tokens.peek == SQLTree::Token::COMMA
24
+ tokens.consume(SQLTree::Token::COMMA)
25
+ update_query.updates << SQLTree::Node::Assignment.parse(tokens)
26
+ end
27
+
28
+ if tokens.peek == SQLTree::Token::WHERE
29
+ tokens.consume(SQLTree::Token::WHERE)
30
+ update_query.where = SQLTree::Node::Expression.parse(tokens)
31
+ end
32
+
33
+ update_query
34
+ end
35
+ end
36
+ end
@@ -13,7 +13,7 @@ module SQLTree::Node
13
13
  end
14
14
 
15
15
  def ==(other)
16
- other.name == self.name
16
+ other.class == self.class && other.name == self.name
17
17
  end
18
18
 
19
19
  def self.parse(tokens)
@@ -57,6 +57,9 @@ class SQLTree::Parser
57
57
  def parse!
58
58
  case self.peek
59
59
  when SQLTree::Token::SELECT then SQLTree::Node::SelectQuery.parse(self)
60
+ when SQLTree::Token::INSERT then SQLTree::Node::InsertQuery.parse(self)
61
+ when SQLTree::Token::DELETE then SQLTree::Node::DeleteQuery.parse(self)
62
+ when SQLTree::Token::UPDATE then SQLTree::Node::UpdateQuery.parse(self)
60
63
  else raise UnexpectedToken.new(self.peek)
61
64
  end
62
65
  end
@@ -112,7 +112,7 @@ class SQLTree::Token
112
112
 
113
113
  # A list of all the SQL reserverd keywords.
114
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}
115
+ AND OR NOT AS ON IS NULL BY LIKE ILIKE BETWEEN IN ASC DESC INSERT INTO VALUES DELETE UPDATE SET}
116
116
 
117
117
  # Create a token for all the reserved keywords in SQL
118
118
  KEYWORDS.each { |kw| const_set(kw, Class.new(SQLTree::Token::Keyword).new(kw)) }
@@ -2,6 +2,10 @@ require "#{File.dirname(__FILE__)}/../spec_helper"
2
2
 
3
3
  describe SQLTree, 'parsing and generating SQL' do
4
4
 
5
+ it "should parse an generate q query without FROM" do
6
+ SQLTree['SELECT 1'].to_sql.should == 'SELECT 1'
7
+ end
8
+
5
9
  it "should parse and generate SQL fo a simple list query" do
6
10
  SQLTree["SELECT * FROM table"].to_sql.should == 'SELECT * FROM "table"'
7
11
  end
@@ -38,4 +42,34 @@ describe SQLTree, 'parsing and generating SQL' do
38
42
  SQLTree['SELECT SUM( field1 ) FROM t GROUP BY field1, MD5( field2 ) HAVING SUM( field1 ) > 10'].to_sql.should ==
39
43
  'SELECT SUM("field1") FROM "t" GROUP BY "field1", MD5("field2") HAVING (SUM("field1") > 10)'
40
44
  end
45
+
46
+ it "should parse and generate an INSERT query with field list" do
47
+ SQLTree['INSERT INTO table ( field1, field2) VALUES (1, 2)'].to_sql.should ==
48
+ 'INSERT INTO "table" ("field1", "field2") VALUES (1, 2)'
49
+ end
50
+
51
+ it "should parse and generate an INSERT query without field list" do
52
+ SQLTree['INSERT INTO table VALUES (1, 2)'].to_sql.should ==
53
+ 'INSERT INTO "table" VALUES (1, 2)'
54
+ end
55
+
56
+ it "should parse and generate an DELETE query without WHERE clause" do
57
+ SQLTree['DELETE FROM table'].to_sql.should ==
58
+ 'DELETE FROM "table"'
59
+ end
60
+
61
+ it "should parse and generate an DELETE query with WHERE clause" do
62
+ SQLTree['DELETE FROM table WHERE 1 = 1'].to_sql.should ==
63
+ 'DELETE FROM "table" WHERE (1 = 1)'
64
+ end
65
+
66
+ it "should parse and generate an UPDATE query without WHERE clause" do
67
+ SQLTree['UPDATE table SET field1 = 1, field2 = 2'].to_sql.should ==
68
+ 'UPDATE "table" SET "field1" = 1, "field2" = 2'
69
+ end
70
+
71
+ it "should parse and generate an UPDATE query with WHERE clause" do
72
+ SQLTree['UPDATE table SET field1 = 123 WHERE id = 17'].to_sql.should ==
73
+ 'UPDATE "table" SET "field1" = 123 WHERE ("id" = 17)'
74
+ end
41
75
  end
@@ -0,0 +1,16 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ describe SQLTree::Node::DeleteQuery do
4
+
5
+ it "should parse a delete query without WHERE clause correctly" do
6
+ delete = SQLTree::Node::DeleteQuery["DELETE FROM table"]
7
+ delete.table.should == 'table'
8
+ delete.where.should be_nil
9
+ end
10
+
11
+ it "should parse a delete query without WHERE clause correctly" do
12
+ delete = SQLTree::Node::DeleteQuery["DELETE FROM table WHERE 1 = 1"]
13
+ delete.table.should == 'table'
14
+ delete.where.should be_kind_of(SQLTree::Node::Expression)
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ describe SQLTree::Node::InsertQuery do
4
+
5
+ it "should parse an insert query without field list correctly" do
6
+ insert = SQLTree::Node::InsertQuery["INSERT INTO table VALUES (1, 'two', 3+4, MD5('$ecret'))"]
7
+ insert.table.should == 'table'
8
+ insert.fields.should be_nil
9
+ insert.values.should have(4).items
10
+ insert.values[0].should == SQLTree::Node::Value.new(1)
11
+ insert.values[1].should == SQLTree::Node::Value.new('two')
12
+ insert.values[2].should be_kind_of(SQLTree::Node::ArithmeticExpression)
13
+ insert.values[3].should be_kind_of(SQLTree::Node::FunctionExpression)
14
+ end
15
+
16
+ it "should parse an insert query with field list" do
17
+ insert = SQLTree::Node::InsertQuery['INSERT INTO table ("field1", "field2") VALUES (1, 2)']
18
+ insert.table.should == 'table'
19
+ insert.fields.should have(2).items
20
+ insert.fields[0].should == SQLTree::Node::Variable.new('field1')
21
+ insert.fields[1].should == SQLTree::Node::Variable.new('field2')
22
+ insert.values.should have(2).items
23
+ end
24
+ end
@@ -1,5 +1,29 @@
1
1
  require "#{File.dirname(__FILE__)}/../spec_helper"
2
2
 
3
+ describe SQLTree::Node::SelectQuery do
4
+
5
+ it "should parse a query without FROM, WHERE, ORDER, GROUP or HAVING clause" do
6
+ tree = SQLTree::Node::SelectQuery['SELECT 1']
7
+ tree.select.first.expression.value.should == 1
8
+ tree.from.should be_nil
9
+ tree.where.should be_nil
10
+ tree.group_by.should be_nil
11
+ tree.having.should be_nil
12
+ tree.order_by.should be_nil
13
+ end
14
+
15
+ it "should parse a query with all clauses" do
16
+ tree = SQLTree::Node::SelectQuery['SELECT 1 AS static, field FROM table1 AS t1, table2 LEFT JOIN table3 t3 ON (t1.id = t2.id)
17
+ WHERE t1.field = 1234 GROUP BY t2.group_field HAVING SUM(t2.group_field) > 100 ORDER BY t.timestamp DESC']
18
+
19
+ tree.select.length.should == 2
20
+ tree.from.length.should == 2
21
+ tree.where.should be_kind_of(SQLTree::Node::ComparisonExpression)
22
+ tree.group_by.first.should be_kind_of(SQLTree::Node::Field)
23
+ tree.having.should be_kind_of(SQLTree::Node::ComparisonExpression)
24
+ end
25
+ end
26
+
3
27
  describe SQLTree::Node::Source do
4
28
 
5
29
  it "should parse the table name correctly" do
@@ -0,0 +1,22 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ describe SQLTree::Node::UpdateQuery do
4
+
5
+ it "should parse a delete query without WHERE clause correctly" do
6
+ update = SQLTree::Node::UpdateQuery["UPDATE table SET field1 = 1, field2 = 5 - 3"]
7
+ update.table.should == 'table'
8
+ update.updates.should have(2).items
9
+ update.updates[0].field.should == 'field1'
10
+ update.updates[0].expression.should == SQLTree::Node::Value.new(1)
11
+ update.updates[1].field.should == 'field2'
12
+ update.updates[1].expression.should be_kind_of(SQLTree::Node::ArithmeticExpression)
13
+ update.where.should be_nil
14
+ end
15
+
16
+ it "should parse a delete query without WHERE clause correctly" do
17
+ update = SQLTree::Node::UpdateQuery["UPDATE table SET field = 1 WHERE id = 17"]
18
+ update.table.should == 'table'
19
+ update.updates.should have(1).item
20
+ update.where.should be_kind_of(SQLTree::Node::Expression)
21
+ end
22
+ 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.3"
6
+ s.version = "0.1.0"
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 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
- 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)
25
+ s.files = %w(spec/unit/select_query_spec.rb spec/unit/insert_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 lib/sql_tree/parser.rb sql_tree.gemspec spec/unit/tokenizer_spec.rb spec/unit/expression_node_spec.rb spec/unit/delete_query_spec.rb lib/sql_tree/node/select_expression.rb lib/sql_tree/node/assignment.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/insert_query.rb lib/sql_tree/node/field.rb Rakefile tasks/github-gem.rake spec/unit/update_query_spec.rb spec/integration/parse_and_generate_spec.rb 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/node/delete_query.rb lib/sql_tree.rb lib/sql_tree/node/update_query.rb)
26
+ s.test_files = %w(spec/unit/select_query_spec.rb spec/unit/insert_query_spec.rb spec/unit/tokenizer_spec.rb spec/unit/expression_node_spec.rb spec/unit/delete_query_spec.rb spec/unit/leaf_node_spec.rb spec/unit/update_query_spec.rb spec/integration/parse_and_generate_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.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
@@ -23,6 +23,7 @@ extra_rdoc_files:
23
23
  - README.rdoc
24
24
  files:
25
25
  - spec/unit/select_query_spec.rb
26
+ - spec/unit/insert_query_spec.rb
26
27
  - spec/spec_helper.rb
27
28
  - lib/sql_tree/tokenizer.rb
28
29
  - lib/sql_tree/node/variable.rb
@@ -31,26 +32,32 @@ files:
31
32
  - lib/sql_tree/node/ordering.rb
32
33
  - LICENSE
33
34
  - spec/lib/matchers.rb
34
- - spec/integration/full_queries_spec.rb
35
35
  - lib/sql_tree/parser.rb
36
36
  - sql_tree.gemspec
37
37
  - spec/unit/tokenizer_spec.rb
38
38
  - spec/unit/expression_node_spec.rb
39
+ - spec/unit/delete_query_spec.rb
39
40
  - lib/sql_tree/node/select_expression.rb
41
+ - lib/sql_tree/node/assignment.rb
40
42
  - spec/unit/leaf_node_spec.rb
41
43
  - lib/sql_tree/token.rb
42
44
  - lib/sql_tree/node/table_reference.rb
43
45
  - lib/sql_tree/node/source.rb
46
+ - lib/sql_tree/node/insert_query.rb
44
47
  - lib/sql_tree/node/field.rb
45
48
  - Rakefile
46
49
  - tasks/github-gem.rake
50
+ - spec/unit/update_query_spec.rb
51
+ - spec/integration/parse_and_generate_spec.rb
47
52
  - lib/sql_tree/node/select_query.rb
48
53
  - lib/sql_tree/node.rb
49
54
  - README.rdoc
50
55
  - spec/integration/api_spec.rb
51
56
  - lib/sql_tree/node/value.rb
52
57
  - lib/sql_tree/node/expression.rb
58
+ - lib/sql_tree/node/delete_query.rb
53
59
  - lib/sql_tree.rb
60
+ - lib/sql_tree/node/update_query.rb
54
61
  has_rdoc: true
55
62
  homepage: http://wiki.github.com/wvanbergen/sql_tree
56
63
  licenses: []
@@ -86,8 +93,11 @@ specification_version: 3
86
93
  summary: A pure Ruby library to represent SQL queries with a syntax tree for inspection and modification.
87
94
  test_files:
88
95
  - spec/unit/select_query_spec.rb
89
- - spec/integration/full_queries_spec.rb
96
+ - spec/unit/insert_query_spec.rb
90
97
  - spec/unit/tokenizer_spec.rb
91
98
  - spec/unit/expression_node_spec.rb
99
+ - spec/unit/delete_query_spec.rb
92
100
  - spec/unit/leaf_node_spec.rb
101
+ - spec/unit/update_query_spec.rb
102
+ - spec/integration/parse_and_generate_spec.rb
93
103
  - spec/integration/api_spec.rb