arel_toolkit 0.4.0 → 0.4.5
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.
- checksums.yaml +4 -4
- data/.github/workflows/coverage.yml +48 -0
- data/.github/workflows/test.yml +65 -0
- data/.gitignore +6 -1
- data/Appraisals +4 -0
- data/CHANGELOG.md +87 -7
- data/Gemfile.lock +50 -39
- data/Guardfile +4 -0
- data/README.md +25 -11
- data/Rakefile +11 -1
- data/arel_toolkit.gemspec +10 -5
- data/benchmark.rb +54 -0
- data/ext/pg_result_init/extconf.rb +52 -0
- data/ext/pg_result_init/pg_result_init.c +138 -0
- data/ext/pg_result_init/pg_result_init.h +6 -0
- data/gemfiles/active_record_6.gemfile +7 -0
- data/gemfiles/active_record_6.gemfile.lock +210 -0
- data/gemfiles/arel_gems.gemfile.lock +28 -18
- data/gemfiles/default.gemfile.lock +30 -20
- data/lib/arel/enhance.rb +1 -0
- data/lib/arel/enhance/context_enhancer/arel_table.rb +18 -1
- data/lib/arel/enhance/node.rb +71 -28
- data/lib/arel/enhance/query.rb +2 -0
- data/lib/arel/enhance/query_methods.rb +23 -0
- data/lib/arel/enhance/visitor.rb +19 -3
- data/lib/arel/extensions.rb +8 -2
- data/lib/arel/extensions/active_model_attribute_with_cast_value.rb +22 -0
- data/lib/arel/extensions/active_record_relation_query_attribute.rb +22 -0
- data/lib/arel/extensions/active_record_type_caster_connection.rb +7 -0
- data/lib/arel/extensions/attributes_attribute.rb +47 -0
- data/lib/arel/extensions/bind_param.rb +15 -0
- data/lib/arel/extensions/coalesce.rb +17 -3
- data/lib/arel/extensions/delete_statement.rb +20 -15
- data/lib/arel/extensions/exists.rb +59 -0
- data/lib/arel/extensions/function.rb +3 -2
- data/lib/arel/extensions/greatest.rb +17 -3
- data/lib/arel/extensions/infer.rb +1 -1
- data/lib/arel/extensions/insert_statement.rb +3 -3
- data/lib/arel/extensions/least.rb +17 -3
- data/lib/arel/extensions/node.rb +10 -0
- data/lib/arel/extensions/range_function.rb +10 -2
- data/lib/arel/extensions/select_core.rb +22 -7
- data/lib/arel/extensions/top.rb +8 -0
- data/lib/arel/extensions/tree_manager.rb +5 -0
- data/lib/arel/extensions/update_statement.rb +9 -23
- data/lib/arel/middleware.rb +5 -1
- data/lib/arel/middleware/active_record_extension.rb +13 -0
- data/lib/arel/middleware/cache_accessor.rb +35 -0
- data/lib/arel/middleware/chain.rb +110 -31
- data/lib/arel/middleware/database_executor.rb +77 -0
- data/lib/arel/middleware/no_op_cache.rb +9 -0
- data/lib/arel/middleware/postgresql_adapter.rb +41 -5
- data/lib/arel/middleware/railtie.rb +6 -2
- data/lib/arel/middleware/result.rb +170 -0
- data/lib/arel/middleware/to_sql_executor.rb +15 -0
- data/lib/arel/middleware/to_sql_middleware.rb +33 -0
- data/lib/arel/sql_to_arel/pg_query_visitor.rb +34 -33
- data/lib/arel/sql_to_arel/result.rb +19 -2
- data/lib/arel/transformer.rb +2 -1
- data/lib/arel/transformer/prefix_schema_name.rb +183 -0
- data/lib/arel/transformer/remove_active_record_info.rb +2 -4
- data/lib/arel/transformer/replace_table_with_subquery.rb +31 -0
- data/lib/arel_toolkit.rb +7 -1
- data/lib/arel_toolkit/version.rb +1 -1
- metadata +101 -37
- data/.travis.yml +0 -34
- data/lib/arel/extensions/generate_series.rb +0 -9
- data/lib/arel/extensions/rank.rb +0 -9
- data/lib/arel/transformer/add_schema_to_table.rb +0 -26
data/lib/arel/enhance/query.rb
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module Enhance
|
|
3
|
+
module QueryMethods
|
|
4
|
+
class QueryMethod
|
|
5
|
+
attr_reader :subject
|
|
6
|
+
|
|
7
|
+
def initialize(subject)
|
|
8
|
+
@subject = subject
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Ancestors < QueryMethod
|
|
13
|
+
def matches?(other)
|
|
14
|
+
other <= subject
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.in_ancestors?(object)
|
|
19
|
+
Ancestors.new(object)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/arel/enhance/visitor.rb
CHANGED
|
@@ -40,8 +40,10 @@ module Arel
|
|
|
40
40
|
end
|
|
41
41
|
alias visit_Arel_Nodes_And nary
|
|
42
42
|
|
|
43
|
-
def visit_Hash(
|
|
44
|
-
|
|
43
|
+
def visit_Hash(object)
|
|
44
|
+
object.each do |key, child|
|
|
45
|
+
process_node(child, Arel::Enhance::PathNode.new([:[], key], key))
|
|
46
|
+
end
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
def visit_Array(object)
|
|
@@ -61,9 +63,23 @@ module Arel
|
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
# rubocop:disable Metrics/AbcSize
|
|
67
|
+
# arel/lib/arel/visitors/visitor.rb:29
|
|
64
68
|
def visit(object)
|
|
65
|
-
|
|
69
|
+
dispatch_method = dispatch[object.class]
|
|
70
|
+
send dispatch_method, object
|
|
71
|
+
rescue NoMethodError => e
|
|
72
|
+
raise e if respond_to?(dispatch_method, true)
|
|
73
|
+
|
|
74
|
+
superklass = object.class.ancestors.find do |klass|
|
|
75
|
+
respond_to?(dispatch[klass], true)
|
|
76
|
+
end
|
|
77
|
+
raise(TypeError, "Cannot visit #{object.class}") unless superklass
|
|
78
|
+
|
|
79
|
+
dispatch[object.class] = dispatch[superklass]
|
|
80
|
+
retry
|
|
66
81
|
end
|
|
82
|
+
# rubocop:enable Metrics/AbcSize
|
|
67
83
|
|
|
68
84
|
def current_node
|
|
69
85
|
@node_stack.last
|
data/lib/arel/extensions.rb
CHANGED
|
@@ -21,6 +21,7 @@ require 'arel/extensions/lateral'
|
|
|
21
21
|
require 'arel/extensions/range_function'
|
|
22
22
|
require 'arel/extensions/with_ordinality'
|
|
23
23
|
require 'arel/extensions/table'
|
|
24
|
+
require 'arel/extensions/attributes_attribute'
|
|
24
25
|
require 'arel/extensions/row'
|
|
25
26
|
require 'arel/extensions/ordering'
|
|
26
27
|
require 'arel/extensions/all'
|
|
@@ -63,8 +64,6 @@ require 'arel/extensions/current_of_expression'
|
|
|
63
64
|
require 'arel/extensions/delete_statement'
|
|
64
65
|
require 'arel/extensions/least'
|
|
65
66
|
require 'arel/extensions/greatest'
|
|
66
|
-
require 'arel/extensions/generate_series'
|
|
67
|
-
require 'arel/extensions/rank'
|
|
68
67
|
require 'arel/extensions/coalesce'
|
|
69
68
|
require 'arel/extensions/not_equal'
|
|
70
69
|
require 'arel/extensions/equality'
|
|
@@ -110,6 +109,13 @@ require 'arel/extensions/to_sql'
|
|
|
110
109
|
require 'arel/extensions/prepare'
|
|
111
110
|
require 'arel/extensions/dealocate'
|
|
112
111
|
require 'arel/extensions/active_record_type_caster_map'
|
|
112
|
+
require 'arel/extensions/active_record_type_caster_connection'
|
|
113
|
+
require 'arel/extensions/active_record_relation_query_attribute'
|
|
114
|
+
require 'arel/extensions/active_model_attribute_with_cast_value'
|
|
115
|
+
require 'arel/extensions/exists'
|
|
116
|
+
require 'arel/extensions/bind_param'
|
|
117
|
+
require 'arel/extensions/node'
|
|
118
|
+
require 'arel/extensions/top'
|
|
113
119
|
|
|
114
120
|
module Arel
|
|
115
121
|
module Extensions
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
4
|
+
module Arel
|
|
5
|
+
module Visitors
|
|
6
|
+
class Dot
|
|
7
|
+
def visit_ActiveModel_Attribute_WithCastValue(o)
|
|
8
|
+
visit_edge o, 'name'
|
|
9
|
+
visit_edge o, 'value_before_type_cast'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ToSql
|
|
14
|
+
def visit_ActiveModel_Attribute_WithCastValue(_o, collector)
|
|
15
|
+
collector
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# rubocop:enable Naming/MethodName
|
|
22
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
4
|
+
module Arel
|
|
5
|
+
module Visitors
|
|
6
|
+
class Dot
|
|
7
|
+
def visit_ActiveRecord_Relation_QueryAttribute(o)
|
|
8
|
+
visit_edge o, 'name'
|
|
9
|
+
visit_edge o, 'value_before_type_cast'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ToSql
|
|
14
|
+
def visit_ActiveRecord_Relation_QueryAttribute(_o, collector)
|
|
15
|
+
collector
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# rubocop:enable Naming/MethodName
|
|
22
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
4
|
+
module Arel
|
|
5
|
+
module Attributes
|
|
6
|
+
class Attribute
|
|
7
|
+
module AttributeExtension
|
|
8
|
+
# postgres only: https://www.postgresql.org/docs/10/ddl-schemas.html
|
|
9
|
+
attr_accessor :schema_name
|
|
10
|
+
attr_accessor :database
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
prepend AttributeExtension
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Visitors
|
|
18
|
+
class ToSql
|
|
19
|
+
module AttributesAttributeExtension
|
|
20
|
+
def visit_Arel_Attributes_Attribute(o, collector)
|
|
21
|
+
collector << "#{quote_table_name(o.database)}." if o.database
|
|
22
|
+
collector << "#{quote_table_name(o.schema_name)}." if o.schema_name
|
|
23
|
+
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
prepend AttributesAttributeExtension
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Dot
|
|
32
|
+
module AttributesAttributeExtension
|
|
33
|
+
def visit_Arel_Attributes_Attribute(o)
|
|
34
|
+
super
|
|
35
|
+
|
|
36
|
+
visit_edge o, 'schema_name'
|
|
37
|
+
visit_edge o, 'database'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
prepend AttributesAttributeExtension
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# rubocop:enable Naming/MethodName
|
|
47
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
4
|
+
module Arel
|
|
5
|
+
module Visitors
|
|
6
|
+
class Dot
|
|
7
|
+
def visit_Arel_Nodes_BindParam(o)
|
|
8
|
+
visit_edge o, 'value'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# rubocop:enable Naming/MethodName
|
|
15
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
1
4
|
module Arel
|
|
2
5
|
module Nodes
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
# https://www.postgresql.org/docs/10/functions-conditional.html
|
|
7
|
+
class Coalesce < Arel::Nodes::Unary
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Visitors
|
|
12
|
+
class ToSql
|
|
13
|
+
def visit_Arel_Nodes_Coalesce(o, collector)
|
|
14
|
+
collector << 'COALESCE('
|
|
15
|
+
collector = inject_join(o.expr, collector, ', ')
|
|
16
|
+
collector << ')'
|
|
6
17
|
end
|
|
7
18
|
end
|
|
8
19
|
end
|
|
9
20
|
end
|
|
21
|
+
|
|
22
|
+
# rubocop:enable Naming/MethodName
|
|
23
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -9,11 +9,14 @@ module Arel
|
|
|
9
9
|
attr_accessor :using
|
|
10
10
|
attr_accessor :with
|
|
11
11
|
attr_accessor :returning
|
|
12
|
+
attr_accessor :orders
|
|
12
13
|
|
|
13
14
|
def initialize(relation = nil, wheres = [])
|
|
14
15
|
super
|
|
15
16
|
|
|
16
17
|
@returning = []
|
|
18
|
+
@orders = []
|
|
19
|
+
@using = []
|
|
17
20
|
end
|
|
18
21
|
end
|
|
19
22
|
|
|
@@ -27,27 +30,29 @@ module Arel
|
|
|
27
30
|
def visit_Arel_Nodes_DeleteStatement(o, collector)
|
|
28
31
|
if o.with
|
|
29
32
|
collector = visit o.with, collector
|
|
30
|
-
collector <<
|
|
33
|
+
collector << ' '
|
|
31
34
|
end
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if o.using
|
|
37
|
-
collector << ' USING '
|
|
38
|
-
collector = inject_join o.using, collector, ', '
|
|
39
|
-
end
|
|
36
|
+
if Gem.loaded_specs['activerecord'].version >= Gem::Version.new('6.0.0')
|
|
37
|
+
o = prepare_delete_statement(o)
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
if has_join_sources?(o)
|
|
40
|
+
collector << 'DELETE '
|
|
41
|
+
visit o.relation.left, collector
|
|
42
|
+
collector << ' FROM '
|
|
43
|
+
else
|
|
44
|
+
collector << 'DELETE FROM '
|
|
45
|
+
end
|
|
46
|
+
else
|
|
47
|
+
collector << 'DELETE FROM '
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
collector << ' RETURNING '
|
|
48
|
-
collector = inject_join o.returning, collector, ', '
|
|
49
|
-
end
|
|
50
|
+
collector = visit o.relation, collector
|
|
50
51
|
|
|
52
|
+
collect_nodes_for o.using, collector, ' USING ', ', '
|
|
53
|
+
collect_nodes_for o.wheres, collector, ' WHERE ', ' AND '
|
|
54
|
+
collect_nodes_for o.returning, collector, ' RETURNING ', ', '
|
|
55
|
+
collect_nodes_for o.orders, collector, ' ORDER BY '
|
|
51
56
|
maybe_visit o.limit, collector
|
|
52
57
|
end
|
|
53
58
|
# rubocop:enable Metrics/AbcSize
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
4
|
+
module Arel
|
|
5
|
+
module Nodes
|
|
6
|
+
# This is a copy of https://github.com/rails/arel/blob/v9.0.0/lib/arel/nodes/function.rb
|
|
7
|
+
# Only difference is the superclass, because EXISTS is not a function but a subquery expression.
|
|
8
|
+
# Semantic meaning is important when transforming the Arel using the enhanced AST,
|
|
9
|
+
# because EXISTS cannot be processed as a function. For example it does not have a schema
|
|
10
|
+
# like a normal function.
|
|
11
|
+
#
|
|
12
|
+
# To change the superclass we're removing the existing Exists class `Arel::Nodes::Exists`
|
|
13
|
+
# and recreating it extending from `Arel::Nodes::Unary`.
|
|
14
|
+
remove_const(:Exists)
|
|
15
|
+
|
|
16
|
+
# https://www.postgresql.org/docs/10/functions-subquery.html
|
|
17
|
+
class Exists < Arel::Nodes::Unary
|
|
18
|
+
include Arel::Predications
|
|
19
|
+
include Arel::WindowPredications
|
|
20
|
+
include Arel::OrderPredications
|
|
21
|
+
attr_accessor :expressions, :alias, :distinct
|
|
22
|
+
|
|
23
|
+
def initialize(expr, aliaz = nil)
|
|
24
|
+
@expressions = expr
|
|
25
|
+
@alias = aliaz && SqlLiteral.new(aliaz)
|
|
26
|
+
@distinct = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def as(aliaz)
|
|
30
|
+
self.alias = SqlLiteral.new(aliaz)
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def hash
|
|
35
|
+
[@expressions, @alias, @distinct].hash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def eql?(other)
|
|
39
|
+
self.class == other.class &&
|
|
40
|
+
expressions == other.expressions &&
|
|
41
|
+
self.alias == other.alias &&
|
|
42
|
+
distinct == other.distinct
|
|
43
|
+
end
|
|
44
|
+
alias == eql?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module Visitors
|
|
49
|
+
class Dot
|
|
50
|
+
def visit_Arel_Nodes_Exists(o)
|
|
51
|
+
visit_edge o, 'expressions'
|
|
52
|
+
visit_edge o, 'alias'
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# rubocop:enable Naming/MethodName
|
|
59
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -46,7 +46,7 @@ module Arel
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
if o.orders.any?
|
|
49
|
-
collector <<
|
|
49
|
+
collector << ' ' unless o.within_group
|
|
50
50
|
collector << 'ORDER BY '
|
|
51
51
|
collector = inject_join o.orders, collector, ', '
|
|
52
52
|
end
|
|
@@ -80,13 +80,14 @@ module Arel
|
|
|
80
80
|
visit_edge o, 'filter'
|
|
81
81
|
visit_edge o, 'within_group'
|
|
82
82
|
visit_edge o, 'variardic'
|
|
83
|
+
visit_edge o, 'schema_name'
|
|
83
84
|
end
|
|
84
85
|
|
|
85
|
-
alias visit_Arel_Nodes_Exists function
|
|
86
86
|
alias visit_Arel_Nodes_Min function
|
|
87
87
|
alias visit_Arel_Nodes_Max function
|
|
88
88
|
alias visit_Arel_Nodes_Avg function
|
|
89
89
|
alias visit_Arel_Nodes_Sum function
|
|
90
|
+
alias visit_Arel_Nodes_Count function
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
prepend FunctionExtension
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
# rubocop:disable Naming/MethodName
|
|
2
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
|
3
|
+
|
|
1
4
|
module Arel
|
|
2
5
|
module Nodes
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
# https://www.postgresql.org/docs/10/functions-conditional.html
|
|
7
|
+
class Greatest < Arel::Nodes::Unary
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Visitors
|
|
12
|
+
class ToSql
|
|
13
|
+
def visit_Arel_Nodes_Greatest(o, collector)
|
|
14
|
+
collector << 'GREATEST('
|
|
15
|
+
collector = inject_join(o.expr, collector, ', ')
|
|
16
|
+
collector << ')'
|
|
6
17
|
end
|
|
7
18
|
end
|
|
8
19
|
end
|
|
9
20
|
end
|
|
21
|
+
|
|
22
|
+
# rubocop:enable Naming/MethodName
|
|
23
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
|
@@ -5,7 +5,7 @@ module Arel
|
|
|
5
5
|
module Nodes
|
|
6
6
|
class InsertStatement
|
|
7
7
|
# https://www.postgresql.org/docs/9.5/sql-insert.html
|
|
8
|
-
module
|
|
8
|
+
module InsertStatementExtension
|
|
9
9
|
attr_accessor :with
|
|
10
10
|
attr_accessor :conflict
|
|
11
11
|
attr_accessor :override
|
|
@@ -18,7 +18,7 @@ module Arel
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
prepend(
|
|
21
|
+
prepend(InsertStatementExtension)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -30,7 +30,7 @@ module Arel
|
|
|
30
30
|
def visit_Arel_Nodes_InsertStatement(o, collector)
|
|
31
31
|
if o.with
|
|
32
32
|
collector = visit o.with, collector
|
|
33
|
-
collector <<
|
|
33
|
+
collector << ' '
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
collector << 'INSERT INTO '
|