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.
- data/.gitignore +2 -1
- data/README.rdoc +6 -5
- data/lib/sql_tree.rb +11 -3
- data/lib/sql_tree/node.rb +75 -4
- data/lib/sql_tree/node/delete_query.rb +19 -6
- data/lib/sql_tree/node/expression.rb +431 -163
- data/lib/sql_tree/node/insert_query.rb +13 -17
- data/lib/sql_tree/node/join.rb +8 -12
- data/lib/sql_tree/node/ordering.rb +4 -3
- data/lib/sql_tree/node/select_declaration.rb +62 -0
- data/lib/sql_tree/node/select_query.rb +27 -46
- data/lib/sql_tree/node/source.rb +5 -8
- data/lib/sql_tree/node/table_reference.rb +7 -10
- data/lib/sql_tree/node/update_query.rb +73 -12
- data/lib/sql_tree/parser.rb +70 -14
- data/lib/sql_tree/token.rb +48 -22
- data/lib/sql_tree/tokenizer.rb +23 -16
- data/spec/integration/parse_and_generate_spec.rb +8 -0
- data/spec/lib/matchers.rb +3 -2
- data/spec/unit/delete_query_spec.rb +2 -2
- data/spec/unit/expression_node_spec.rb +24 -23
- data/spec/unit/insert_query_spec.rb +8 -8
- data/spec/unit/leaf_node_spec.rb +17 -17
- data/spec/unit/select_query_spec.rb +4 -4
- data/spec/unit/tokenizer_spec.rb +17 -21
- data/spec/unit/update_query_spec.rb +6 -6
- data/sql_tree.gemspec +3 -3
- metadata +4 -8
- data/lib/sql_tree/node/assignment.rb +0 -22
- data/lib/sql_tree/node/field.rb +0 -49
- data/lib/sql_tree/node/select_expression.rb +0 -45
- data/lib/sql_tree/node/value.rb +0 -33
- data/lib/sql_tree/node/variable.rb +0 -31
@@ -2,26 +2,26 @@ module SQLTree::Node
|
|
2
2
|
|
3
3
|
class InsertQuery < Base
|
4
4
|
|
5
|
-
|
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 #{
|
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 =
|
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 =
|
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::
|
40
|
+
insert_query = self.new(SQLTree::Node::TableReference.parse(tokens))
|
45
41
|
|
46
|
-
insert_query.fields = self.parse_field_list(tokens) if
|
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
|
data/lib/sql_tree/node/join.rb
CHANGED
@@ -2,16 +2,18 @@ module SQLTree::Node
|
|
2
2
|
|
3
3
|
class Join < Base
|
4
4
|
|
5
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
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 =
|
35
|
-
select_node.from = self.parse_from_clause(tokens) if
|
36
|
-
select_node.where = self.parse_where_clause(tokens) if
|
37
|
-
if
|
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
|
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)
|
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
|
-
|
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
|
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
|
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)
|
data/lib/sql_tree/node/source.rb
CHANGED
@@ -2,7 +2,8 @@ module SQLTree::Node
|
|
2
2
|
|
3
3
|
class Source < Base
|
4
4
|
|
5
|
-
|
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
|
-
|
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::
|
19
|
+
if SQLTree::Token::Identifier === tokens.next
|
23
20
|
table_reference = self.new(tokens.current.literal)
|
24
|
-
if
|
25
|
-
tokens.consume(SQLTree::Token::AS) if
|
26
|
-
table_reference.table_alias =
|
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
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
sql
|
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::
|
44
|
+
update_query = self.new(SQLTree::Node::TableReference.parse(tokens))
|
21
45
|
tokens.consume(SQLTree::Token::SET)
|
22
|
-
update_query.updates =
|
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
|
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
|