sql_tree 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,26 +2,26 @@ module SQLTree::Node
2
2
 
3
3
  class InsertQuery < Base
4
4
 
5
- attr_accessor :table, :fields, :values
5
+ child :table
6
+
7
+ child :fields
8
+
9
+ child :values
6
10
 
7
11
  def initialize(table, fields = nil, values = [])
8
12
  @table, @fields, @values = table, fields, values
9
13
  end
10
14
 
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
+ def to_sql(options = {})
16
+ sql = "INSERT INTO #{table.to_sql(options)} "
17
+ sql << '(' + fields.map { |f| f.to_sql(options) }.join(', ') + ') ' if fields
18
+ sql << 'VALUES (' + values.map { |v| v.to_sql(options) }.join(', ') + ')'
15
19
  sql
16
20
  end
17
21
 
18
22
  def self.parse_field_list(tokens)
19
23
  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
24
+ fields = parse_list(tokens, SQLTree::Node::Expression::Field)
25
25
  tokens.consume(SQLTree::Token::RPAREN)
26
26
  return fields
27
27
  end
@@ -29,11 +29,7 @@ module SQLTree::Node
29
29
  def self.parse_value_list(tokens)
30
30
  tokens.consume(SQLTree::Token::VALUES)
31
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
32
+ values = parse_list(tokens)
37
33
  tokens.consume(SQLTree::Token::RPAREN)
38
34
  return values
39
35
  end
@@ -41,9 +37,9 @@ module SQLTree::Node
41
37
  def self.parse(tokens)
42
38
  tokens.consume(SQLTree::Token::INSERT)
43
39
  tokens.consume(SQLTree::Token::INTO)
44
- insert_query = self.new(SQLTree::Node::Variable.parse(tokens).name)
40
+ insert_query = self.new(SQLTree::Node::TableReference.parse(tokens))
45
41
 
46
- insert_query.fields = self.parse_field_list(tokens) if tokens.peek == SQLTree::Token::LPAREN
42
+ insert_query.fields = self.parse_field_list(tokens) if SQLTree::Token::LPAREN === tokens.peek
47
43
  insert_query.values = self.parse_value_list(tokens)
48
44
  return insert_query
49
45
  end
@@ -2,16 +2,18 @@ module SQLTree::Node
2
2
 
3
3
  class Join < Base
4
4
 
5
- attr_accessor :join_type, :table_reference, :join_expression
5
+ leaf :join_type
6
+ child :table_reference
7
+ child :join_expression
6
8
 
7
9
  def initialize(values = {})
8
10
  values.each { |key, value| self.send(:"#{key}=", value) }
9
11
  end
10
12
 
11
- def to_sql
13
+ def to_sql(options = {})
12
14
  join_sql = join_type ? "#{join_type.to_s.upcase} " : ""
13
- join_sql << "JOIN #{table_reference.to_sql} "
14
- join_sql << "ON #{join_expression.to_sql}"
15
+ join_sql << "JOIN #{table_reference.to_sql(options)} "
16
+ join_sql << "ON #{join_expression.to_sql(options)}"
15
17
  join_sql
16
18
  end
17
19
 
@@ -26,14 +28,13 @@ module SQLTree::Node
26
28
  def self.parse(tokens)
27
29
  join = self.new
28
30
 
29
- if tokens.peek == SQLTree::Token::FULL
31
+ if SQLTree::Token::FULL === tokens.peek
30
32
  join.join_type = :outer
31
33
  tokens.consume(SQLTree::Token::FULL, SQLTree::Token::OUTER)
32
- elsif [SQLTree::Token::OUTER, SQLTree::Token::INNER, SQLTree::Token::LEFT, SQLTree::Token::RIGHT].include?(tokens.peek)
34
+ elsif [SQLTree::Token::OUTER, SQLTree::Token::INNER, SQLTree::Token::LEFT, SQLTree::Token::RIGHT].include?(tokens.peek.class)
33
35
  join.join_type = tokens.next.literal.downcase.to_sym
34
36
  end
35
37
 
36
-
37
38
  tokens.consume(SQLTree::Token::JOIN)
38
39
  join.table_reference = SQLTree::Node::TableReference.parse(tokens)
39
40
  tokens.consume(SQLTree::Token::ON)
@@ -41,10 +42,5 @@ module SQLTree::Node
41
42
 
42
43
  return join
43
44
  end
44
-
45
- def ==(other)
46
- other.table = self.table && other.table_alias == self.table_alias &&
47
- other.join_type == self.join_type && other.join_expression == self.join_expression
48
- end
49
45
  end
50
46
  end
@@ -2,14 +2,15 @@ module SQLTree::Node
2
2
 
3
3
  class Ordering < Base
4
4
 
5
- attr_accessor :expression, :direction
5
+ child :expression
6
+ leaf :direction
6
7
 
7
8
  def initialize(expression, direction = nil)
8
9
  @expression, @direction = expression, direction
9
10
  end
10
11
 
11
- def to_sql
12
- sql = expression.to_sql
12
+ def to_sql(options = {})
13
+ sql = expression.to_sql(options)
13
14
  sql << " #{direction.to_s.upcase}" if direction
14
15
  sql
15
16
  end
@@ -0,0 +1,62 @@
1
+ module SQLTree::Node
2
+
3
+ class SelectDeclaration < Base
4
+
5
+ child :expression
6
+ leaf :variable
7
+
8
+ def to_sql(options = {})
9
+ sql = @expression.to_sql(options)
10
+ sql << " AS " << quote_var(@variable) if @variable
11
+ return sql
12
+ end
13
+
14
+ def self.parse(tokens)
15
+
16
+ if SQLTree::Token::MULTIPLY === tokens.peek
17
+
18
+ # "SELECT * FROM ..."
19
+ tokens.consume(SQLTree::Token::MULTIPLY)
20
+ return SQLTree::Node::ALL_FIELDS
21
+
22
+ elsif SQLTree::Token::Identifier === tokens.peek(1) &&
23
+ SQLTree::Token::DOT === tokens.peek(2) &&
24
+ SQLTree::Token::MULTIPLY === tokens.peek(3)
25
+
26
+ # "SELECT table.* FROM ..."
27
+ table = tokens.next.literal
28
+ tokens.consume(SQLTree::Token::DOT, SQLTree::Token::MULTIPLY)
29
+ return SQLTree::Node::AllFieldsDeclaration.new(table)
30
+
31
+ else
32
+
33
+ expression = SQLTree::Node::Expression.parse(tokens)
34
+ expr = self.new(:expression => expression)
35
+ if SQLTree::Token::AS === tokens.peek
36
+ tokens.consume(SQLTree::Token::AS)
37
+ if SQLTree::Token::Identifier === tokens.peek
38
+ expr.variable = tokens.next.literal
39
+ else
40
+ raise SQLTree::Parser::UnexpectedToken.new(tokens.peek)
41
+ end
42
+ end
43
+ return expr
44
+ end
45
+ end
46
+ end
47
+
48
+ class AllFieldsDeclaration < Base
49
+
50
+ leaf :table
51
+
52
+ def initialize(table = nil)
53
+ @table = table
54
+ end
55
+
56
+ def to_sql(options = {})
57
+ table ? "#{quote_var(table)}.*" : '*'
58
+ end
59
+ end
60
+
61
+ ALL_FIELDS = AllFieldsDeclaration.new
62
+ end
@@ -2,22 +2,29 @@ module SQLTree::Node
2
2
 
3
3
  class SelectQuery < Base
4
4
 
5
- attr_accessor :distinct, :select, :from, :where, :group_by, :having, :order_by, :limit
5
+ leaf :distinct
6
+ child :select
7
+ child :from
8
+ child :where
9
+ child :group_by
10
+ child :having
11
+ child :order_by
12
+ child :limit
6
13
 
7
14
  def initialize
8
15
  @distinct = false
9
16
  @select = []
10
17
  end
11
18
 
12
- def to_sql
19
+ def to_sql(options = {})
13
20
  raise "At least one SELECT expression is required" if self.select.empty?
14
21
  sql = (self.distinct) ? "SELECT DISTINCT " : "SELECT "
15
- sql << select.map { |s| s.to_sql }.join(', ')
16
- sql << " FROM " << from.map { |f| f.to_sql }.join(', ') if from
17
- sql << " WHERE " << where.to_sql if where
18
- sql << " GROUP BY " << group_by.map { |g| g.to_sql }.join(', ') if group_by
19
- sql << " ORDER BY " << order_by.map { |o| o.to_sql }.join(', ') if order_by
20
- sql << " HAVING " << having.to_sql if having
22
+ sql << select.map { |s| s.to_sql(options) }.join(', ')
23
+ sql << " FROM " << from.map { |f| f.to_sql(options) }.join(', ') if from
24
+ sql << " WHERE " << where.to_sql(options) if where
25
+ sql << " GROUP BY " << group_by.map { |g| g.to_sql(options) }.join(', ') if group_by
26
+ sql << " ORDER BY " << order_by.map { |o| o.to_sql(options) }.join(', ') if order_by
27
+ sql << " HAVING " << having.to_sql(options) if having
21
28
  return sql
22
29
  end
23
30
 
@@ -26,39 +33,25 @@ module SQLTree::Node
26
33
  select_node = self.new
27
34
  tokens.consume(SQLTree::Token::SELECT)
28
35
 
29
- if tokens.peek == SQLTree::Token::DISTINCT
36
+ if SQLTree::Token::DISTINCT === tokens.peek
30
37
  tokens.consume(SQLTree::Token::DISTINCT)
31
38
  select_node.distinct = true
32
39
  end
33
40
 
34
- select_node.select = self.parse_select_clause(tokens)
35
- select_node.from = self.parse_from_clause(tokens) if tokens.peek == SQLTree::Token::FROM
36
- select_node.where = self.parse_where_clause(tokens) if tokens.peek == SQLTree::Token::WHERE
37
- if tokens.peek == SQLTree::Token::GROUP
41
+ select_node.select = parse_list(tokens, SQLTree::Node::SelectDeclaration)
42
+ select_node.from = self.parse_from_clause(tokens) if SQLTree::Token::FROM === tokens.peek
43
+ select_node.where = self.parse_where_clause(tokens) if SQLTree::Token::WHERE === tokens.peek
44
+ if SQLTree::Token::GROUP === tokens.peek
38
45
  select_node.group_by = self.parse_group_clause(tokens)
39
- select_node.having = self.parse_having_clause(tokens) if tokens.peek == SQLTree::Token::HAVING
46
+ select_node.having = self.parse_having_clause(tokens) if SQLTree::Token::HAVING === tokens.peek
40
47
  end
41
- select_node.order_by = self.parse_order_clause(tokens) if tokens.peek == SQLTree::Token::ORDER
48
+ select_node.order_by = self.parse_order_clause(tokens) if SQLTree::Token::ORDER === tokens.peek
42
49
  return select_node
43
50
  end
44
51
 
45
- def self.parse_select_clause(tokens)
46
- expressions = [SQLTree::Node::SelectExpression.parse(tokens)]
47
- while tokens.peek == SQLTree::Token::COMMA
48
- tokens.consume(SQLTree::Token::COMMA)
49
- expressions << SQLTree::Node::SelectExpression.parse(tokens)
50
- end
51
- return expressions
52
- end
53
-
54
52
  def self.parse_from_clause(tokens)
55
53
  tokens.consume(SQLTree::Token::FROM)
56
- sources = [SQLTree::Node::Source.parse(tokens)]
57
- while tokens.peek == SQLTree::Token::COMMA
58
- tokens.consume(SQLTree::Token::COMMA)
59
- sources << SQLTree::Node::Source.parse(tokens)
60
- end
61
- return sources
54
+ parse_list(tokens, SQLTree::Node::Source)
62
55
  end
63
56
 
64
57
  def self.parse_where_clause(tokens)
@@ -67,14 +60,8 @@ module SQLTree::Node
67
60
  end
68
61
 
69
62
  def self.parse_group_clause(tokens)
70
- tokens.consume(SQLTree::Token::GROUP)
71
- tokens.consume(SQLTree::Token::BY)
72
- exprs = [SQLTree::Node::Expression.parse(tokens)]
73
- while tokens.peek == SQLTree::Token::COMMA
74
- tokens.consume(SQLTree::Token::COMMA)
75
- exprs << SQLTree::Node::Expression.parse(tokens)
76
- end
77
- return exprs
63
+ tokens.consume(SQLTree::Token::GROUP, SQLTree::Token::BY)
64
+ parse_list(tokens)
78
65
  end
79
66
 
80
67
  def self.parse_having_clause(tokens)
@@ -83,14 +70,8 @@ module SQLTree::Node
83
70
  end
84
71
 
85
72
  def self.parse_order_clause(tokens)
86
- tokens.consume(SQLTree::Token::ORDER)
87
- tokens.consume(SQLTree::Token::BY)
88
- exprs = [SQLTree::Node::Ordering.parse(tokens)]
89
- while tokens.peek == SQLTree::Token::COMMA
90
- tokens.consume(SQLTree::Token::COMMA)
91
- exprs << SQLTree::Node::Ordering.parse(tokens)
92
- end
93
- return exprs
73
+ tokens.consume(SQLTree::Token::ORDER, SQLTree::Token::BY)
74
+ parse_list(tokens, SQLTree::Node::Ordering)
94
75
  end
95
76
 
96
77
  def self.parse_limit_clause(tokens)
@@ -2,7 +2,8 @@ module SQLTree::Node
2
2
 
3
3
  class Source < Base
4
4
 
5
- attr_accessor :table_reference, :joins
5
+ child :table_reference
6
+ child :joins
6
7
 
7
8
  def initialize(table_reference, joins = [])
8
9
  @table_reference, @joins = table_reference, joins
@@ -16,16 +17,12 @@ module SQLTree::Node
16
17
  table_reference.table_alias
17
18
  end
18
19
 
19
- def to_sql
20
- sql = table_reference.to_sql
21
- sql << ' ' << joins.map { |j| j.to_sql }.join(' ') if joins.any?
20
+ def to_sql(options = {})
21
+ sql = table_reference.to_sql(options)
22
+ sql << ' ' << joins.map { |j| j.to_sql(options) }.join(' ') if joins.any?
22
23
  return sql
23
24
  end
24
25
 
25
- def ==(other)
26
- other.table_reference = self.table_reference && other.joins == self.joins
27
- end
28
-
29
26
  def self.parse(tokens)
30
27
  source = self.new(SQLTree::Node::TableReference.parse(tokens))
31
28
  while tokens.peek && tokens.peek.join?
@@ -2,28 +2,25 @@ module SQLTree::Node
2
2
 
3
3
  class TableReference < Base
4
4
 
5
- attr_accessor :table, :table_alias
5
+ leaf :table
6
+ leaf :table_alias
6
7
 
7
8
  def initialize(table, table_alias = nil)
8
9
  @table, @table_alias = table, table_alias
9
10
  end
10
11
 
11
- def to_sql
12
+ def to_sql(options = {})
12
13
  sql = quote_var(table)
13
14
  sql << " AS " << quote_var(table_alias) if table_alias
14
15
  return sql
15
16
  end
16
17
 
17
- def ==(other)
18
- other.table = self.table && other.table_alias == self.table_alias
19
- end
20
-
21
18
  def self.parse(tokens)
22
- if SQLTree::Token::Variable === tokens.next
19
+ if SQLTree::Token::Identifier === tokens.next
23
20
  table_reference = self.new(tokens.current.literal)
24
- if tokens.peek == SQLTree::Token::AS || SQLTree::Token::Variable === tokens.peek
25
- tokens.consume(SQLTree::Token::AS) if tokens.peek == SQLTree::Token::AS
26
- table_reference.table_alias = SQLTree::Node::Variable.parse(tokens).name
21
+ if SQLTree::Token::AS === tokens.peek || SQLTree::Token::Identifier === tokens.peek
22
+ tokens.consume(SQLTree::Token::AS) if SQLTree::Token::AS === tokens.peek
23
+ table_reference.table_alias = tokens.next.literal
27
24
  end
28
25
  return table_reference
29
26
  else
@@ -1,36 +1,97 @@
1
1
  module SQLTree::Node
2
2
 
3
+ # The UpdateQuery class represents an SQL +UPDATE+ query.
4
+ #
5
+ # This root node has three children: +table+, +updates+ and +where+.
3
6
  class UpdateQuery < Base
4
7
 
5
- attr_accessor :table, :updates, :where
8
+ # The table ({SQLTree::Node::TableReference}) to update.
9
+ child :table
10
+
11
+ # The updates to do in the table. This is an array of
12
+ # {SQLTree::Node::UpdateQuery::Assignment} instances.
13
+ child :updates
14
+
15
+ # The {SQLTree::Node::Expression} instance that restricts the records that
16
+ # should be updated.
17
+ child :where
6
18
 
7
19
  def initialize(table, updates = [], where = nil)
8
20
  @table, @updates, @where = table, updates, where
9
21
  end
10
22
 
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
23
+ # Generates the SQL UPDATE query.
24
+ # @return [String] The SQL update query
25
+ def to_sql(options = {})
26
+ sql = "UPDATE #{table.to_sql(options)} SET "
27
+ sql << updates.map { |u| u.to_sql(options) }.join(', ')
28
+ sql << " WHERE " << where.to_sql(options) if self.where
15
29
  sql
16
30
  end
17
31
 
32
+ # Parses an SQL UPDATE query. Syntax:
33
+ #
34
+ # UpdateQuery -> UPDATE TableReference
35
+ # SET Assignment (COMMA Assignment)*
36
+ # (WHERE Expression)?
37
+ #
38
+ # @param [SQLTree::Parser] tokens The token stream to parse from.
39
+ # @return [SQLTree::Node::UpdateQuery] The parsed UpdateQuery instance.
40
+ # @raise [SQLTree::Parser::UnexpectedToken] if an unexpected token is
41
+ # encountered during parsing.
18
42
  def self.parse(tokens)
19
43
  tokens.consume(SQLTree::Token::UPDATE)
20
- update_query = self.new(SQLTree::Node::Variable.parse(tokens).name)
44
+ update_query = self.new(SQLTree::Node::TableReference.parse(tokens))
21
45
  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
46
+ update_query.updates = parse_list(tokens, SQLTree::Node::UpdateQuery::Assignment)
27
47
 
28
- if tokens.peek == SQLTree::Token::WHERE
48
+ if SQLTree::Token::WHERE === tokens.peek
29
49
  tokens.consume(SQLTree::Token::WHERE)
30
50
  update_query.where = SQLTree::Node::Expression.parse(tokens)
31
51
  end
32
52
 
33
53
  update_query
34
54
  end
55
+
56
+ # The Assignment node is used to represent the assignment of a new
57
+ # value to a field in an +UPDATE+ query.
58
+ #
59
+ # This node has two children: <tt>field</tt> and <tt>expression</tt>.
60
+ class Assignment < Base
61
+
62
+ # The field ({SQLTree::Node::Expression::Field}) to update.
63
+ child :field
64
+
65
+ # A {SQLTree::Node::Expression} instance that is used to
66
+ # update the field with.
67
+ child :expression
68
+
69
+ # Initializes a new assignment node.
70
+ def initialize(field, expression = nil)
71
+ @field, @expression = field, expression
72
+ end
73
+
74
+ # Generates an SQL fragment for this node.
75
+ # @return [String] An SQL fragment that can be embedded in the SET
76
+ # clause of on SQL UPDATE query.
77
+ def to_sql(options = {})
78
+ "#{field.to_sql(options)} = #{expression.to_sql(options)}"
79
+ end
80
+
81
+ # Parses an Assignment node from a stream of tokens. Syntax:
82
+ #
83
+ # Assignment -> <identifier> EQ Expression
84
+ #
85
+ # @param [SQLTree::Parser] tokens The token stream to parse from.
86
+ # @return [SQLTree::Node::Assignment] The parsed assignment instance.
87
+ # @raise [SQLTree::Parser::UnexpectedToken] if an unexpected token is
88
+ # encountered during parsing.
89
+ def self.parse(tokens)
90
+ assignment = self.new(SQLTree::Node::Expression::Field.parse(tokens))
91
+ tokens.consume(SQLTree::Token::EQ)
92
+ assignment.expression = SQLTree::Node::Expression.parse(tokens)
93
+ assignment
94
+ end
95
+ end
35
96
  end
36
97
  end