sql_tree 0.1.0 → 0.1.1

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.
@@ -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