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