activerecord 6.0.0.beta2 → 6.0.2.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
data/lib/arel/insert_manager.rb
CHANGED
@@ -33,13 +33,13 @@ module Arel # :nodoc: all
|
|
33
33
|
@ast.columns << column
|
34
34
|
values << value
|
35
35
|
end
|
36
|
-
@ast.values = create_values
|
36
|
+
@ast.values = create_values(values)
|
37
37
|
end
|
38
38
|
self
|
39
39
|
end
|
40
40
|
|
41
|
-
def create_values(values
|
42
|
-
Nodes::
|
41
|
+
def create_values(values)
|
42
|
+
Nodes::ValuesList.new([values])
|
43
43
|
end
|
44
44
|
|
45
45
|
def create_values_list(rows)
|
data/lib/arel/nodes.rb
CHANGED
@@ -45,7 +45,6 @@ require "arel/nodes/and"
|
|
45
45
|
require "arel/nodes/function"
|
46
46
|
require "arel/nodes/count"
|
47
47
|
require "arel/nodes/extract"
|
48
|
-
require "arel/nodes/values"
|
49
48
|
require "arel/nodes/values_list"
|
50
49
|
require "arel/nodes/named_function"
|
51
50
|
|
@@ -62,6 +61,8 @@ require "arel/nodes/outer_join"
|
|
62
61
|
require "arel/nodes/right_outer_join"
|
63
62
|
require "arel/nodes/string_join"
|
64
63
|
|
64
|
+
require "arel/nodes/comment"
|
65
|
+
|
65
66
|
require "arel/nodes/sql_literal"
|
66
67
|
|
67
68
|
require "arel/nodes/casted"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Nodes
|
5
|
+
class Comment < Arel::Nodes::Node
|
6
|
+
attr_reader :values
|
7
|
+
|
8
|
+
def initialize(values)
|
9
|
+
super()
|
10
|
+
@values = values
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize_copy(other)
|
14
|
+
super
|
15
|
+
@values = @values.clone
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash
|
19
|
+
[@values].hash
|
20
|
+
end
|
21
|
+
|
22
|
+
def eql?(other)
|
23
|
+
self.class == other.class &&
|
24
|
+
self.values == other.values
|
25
|
+
end
|
26
|
+
alias :== :eql?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,20 +3,22 @@
|
|
3
3
|
module Arel # :nodoc: all
|
4
4
|
module Nodes
|
5
5
|
class SelectCore < Arel::Nodes::Node
|
6
|
-
attr_accessor :projections, :wheres, :groups, :windows
|
7
|
-
attr_accessor :havings, :source, :set_quantifier
|
6
|
+
attr_accessor :projections, :wheres, :groups, :windows, :comment
|
7
|
+
attr_accessor :havings, :source, :set_quantifier, :optimizer_hints
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
super()
|
11
|
-
@source
|
11
|
+
@source = JoinSource.new nil
|
12
12
|
|
13
13
|
# https://ronsavage.github.io/SQL/sql-92.bnf.html#set%20quantifier
|
14
|
-
@set_quantifier
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@
|
19
|
-
@
|
14
|
+
@set_quantifier = nil
|
15
|
+
@optimizer_hints = nil
|
16
|
+
@projections = []
|
17
|
+
@wheres = []
|
18
|
+
@groups = []
|
19
|
+
@havings = []
|
20
|
+
@windows = []
|
21
|
+
@comment = nil
|
20
22
|
end
|
21
23
|
|
22
24
|
def from
|
@@ -42,8 +44,8 @@ module Arel # :nodoc: all
|
|
42
44
|
|
43
45
|
def hash
|
44
46
|
[
|
45
|
-
@source, @set_quantifier, @projections,
|
46
|
-
@wheres, @groups, @havings, @windows
|
47
|
+
@source, @set_quantifier, @projections, @optimizer_hints,
|
48
|
+
@wheres, @groups, @havings, @windows, @comment
|
47
49
|
].hash
|
48
50
|
end
|
49
51
|
|
@@ -51,11 +53,13 @@ module Arel # :nodoc: all
|
|
51
53
|
self.class == other.class &&
|
52
54
|
self.source == other.source &&
|
53
55
|
self.set_quantifier == other.set_quantifier &&
|
56
|
+
self.optimizer_hints == other.optimizer_hints &&
|
54
57
|
self.projections == other.projections &&
|
55
58
|
self.wheres == other.wheres &&
|
56
59
|
self.groups == other.groups &&
|
57
60
|
self.havings == other.havings &&
|
58
|
-
self.windows == other.windows
|
61
|
+
self.windows == other.windows &&
|
62
|
+
self.comment == other.comment
|
59
63
|
end
|
60
64
|
alias :== :eql?
|
61
65
|
end
|
data/lib/arel/nodes/unary.rb
CHANGED
@@ -2,23 +2,8 @@
|
|
2
2
|
|
3
3
|
module Arel # :nodoc: all
|
4
4
|
module Nodes
|
5
|
-
class ValuesList <
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(rows)
|
9
|
-
@rows = rows
|
10
|
-
super()
|
11
|
-
end
|
12
|
-
|
13
|
-
def hash
|
14
|
-
@rows.hash
|
15
|
-
end
|
16
|
-
|
17
|
-
def eql?(other)
|
18
|
-
self.class == other.class &&
|
19
|
-
self.rows == other.rows
|
20
|
-
end
|
21
|
-
alias :== :eql?
|
5
|
+
class ValuesList < Unary
|
6
|
+
alias :rows :expr
|
22
7
|
end
|
23
8
|
end
|
24
9
|
end
|
data/lib/arel/select_manager.rb
CHANGED
@@ -146,6 +146,13 @@ module Arel # :nodoc: all
|
|
146
146
|
@ctx.projections = projections
|
147
147
|
end
|
148
148
|
|
149
|
+
def optimizer_hints(*hints)
|
150
|
+
unless hints.empty?
|
151
|
+
@ctx.optimizer_hints = Arel::Nodes::OptimizerHints.new(hints)
|
152
|
+
end
|
153
|
+
self
|
154
|
+
end
|
155
|
+
|
149
156
|
def distinct(value = true)
|
150
157
|
if value
|
151
158
|
@ctx.set_quantifier = Arel::Nodes::Distinct.new
|
@@ -237,16 +244,9 @@ module Arel # :nodoc: all
|
|
237
244
|
@ctx.source
|
238
245
|
end
|
239
246
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
end
|
244
|
-
|
245
|
-
def method_missing(name, *args)
|
246
|
-
name = name.to_s
|
247
|
-
return data[name] if data.key?(name)
|
248
|
-
super
|
249
|
-
end
|
247
|
+
def comment(*values)
|
248
|
+
@ctx.comment = Nodes::Comment.new(values)
|
249
|
+
self
|
250
250
|
end
|
251
251
|
|
252
252
|
private
|
@@ -10,7 +10,7 @@ module Arel # :nodoc: all
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
-
def visit(o)
|
13
|
+
def visit(o, _ = nil)
|
14
14
|
super
|
15
15
|
@block.call o
|
16
16
|
end
|
@@ -35,6 +35,8 @@ module Arel # :nodoc: all
|
|
35
35
|
alias :visit_Arel_Nodes_Ascending :unary
|
36
36
|
alias :visit_Arel_Nodes_Descending :unary
|
37
37
|
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
|
38
|
+
alias :visit_Arel_Nodes_OptimizerHints :unary
|
39
|
+
alias :visit_Arel_Nodes_ValuesList :unary
|
38
40
|
|
39
41
|
def function(o)
|
40
42
|
visit o.expressions
|
@@ -102,7 +104,6 @@ module Arel # :nodoc: all
|
|
102
104
|
alias :visit_Arel_Nodes_Regexp :binary
|
103
105
|
alias :visit_Arel_Nodes_RightOuterJoin :binary
|
104
106
|
alias :visit_Arel_Nodes_TableAlias :binary
|
105
|
-
alias :visit_Arel_Nodes_Values :binary
|
106
107
|
alias :visit_Arel_Nodes_When :binary
|
107
108
|
|
108
109
|
def visit_Arel_Nodes_StringJoin(o)
|
@@ -180,6 +181,10 @@ module Arel # :nodoc: all
|
|
180
181
|
visit o.limit
|
181
182
|
end
|
182
183
|
|
184
|
+
def visit_Arel_Nodes_Comment(o)
|
185
|
+
visit o.values
|
186
|
+
end
|
187
|
+
|
183
188
|
def visit_Array(o)
|
184
189
|
o.each { |i| visit i }
|
185
190
|
end
|
data/lib/arel/visitors/dot.rb
CHANGED
@@ -46,8 +46,8 @@ module Arel # :nodoc: all
|
|
46
46
|
visit_edge o, "distinct"
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
50
|
-
visit_edge o, "
|
49
|
+
def visit_Arel_Nodes_ValuesList(o)
|
50
|
+
visit_edge o, "rows"
|
51
51
|
end
|
52
52
|
|
53
53
|
def visit_Arel_Nodes_StringJoin(o)
|
@@ -82,6 +82,7 @@ module Arel # :nodoc: all
|
|
82
82
|
alias :visit_Arel_Nodes_Offset :unary
|
83
83
|
alias :visit_Arel_Nodes_On :unary
|
84
84
|
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
|
85
|
+
alias :visit_Arel_Nodes_OptimizerHints :unary
|
85
86
|
alias :visit_Arel_Nodes_Preceding :unary
|
86
87
|
alias :visit_Arel_Nodes_Following :unary
|
87
88
|
alias :visit_Arel_Nodes_Rows :unary
|
@@ -233,6 +234,10 @@ module Arel # :nodoc: all
|
|
233
234
|
end
|
234
235
|
alias :visit_Set :visit_Array
|
235
236
|
|
237
|
+
def visit_Arel_Nodes_Comment(o)
|
238
|
+
visit_edge(o, "values")
|
239
|
+
end
|
240
|
+
|
236
241
|
def visit_edge(o, method)
|
237
242
|
edge(method) { visit o.send(method) }
|
238
243
|
end
|
data/lib/arel/visitors/ibm_db.rb
CHANGED
@@ -4,6 +4,15 @@ module Arel # :nodoc: all
|
|
4
4
|
module Visitors
|
5
5
|
class IBM_DB < Arel::Visitors::ToSql
|
6
6
|
private
|
7
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
8
|
+
collector = super
|
9
|
+
maybe_visit o.optimizer_hints, collector
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
13
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join
|
14
|
+
collector << "/* <OPTGUIDELINES>#{hints}</OPTGUIDELINES> */"
|
15
|
+
end
|
7
16
|
|
8
17
|
def visit_Arel_Nodes_Limit(o, collector)
|
9
18
|
collector << "FETCH FIRST "
|
@@ -16,6 +25,10 @@ module Arel # :nodoc: all
|
|
16
25
|
collector = visit [o.left, o.right, 0, 1], collector
|
17
26
|
collector << ")"
|
18
27
|
end
|
28
|
+
|
29
|
+
def collect_optimizer_hints(o, collector)
|
30
|
+
collector
|
31
|
+
end
|
19
32
|
end
|
20
33
|
end
|
21
34
|
end
|
@@ -42,10 +42,16 @@ module Arel # :nodoc: all
|
|
42
42
|
collector
|
43
43
|
end
|
44
44
|
|
45
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
46
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
|
47
|
+
collector << "/*+ #{hints} */"
|
48
|
+
end
|
49
|
+
|
45
50
|
def visit_Arel_Nodes_Offset(o, collector)
|
46
51
|
collector << "SKIP "
|
47
52
|
visit o.expr, collector
|
48
53
|
end
|
54
|
+
|
49
55
|
def visit_Arel_Nodes_Limit(o, collector)
|
50
56
|
collector << "FIRST "
|
51
57
|
visit o.expr, collector
|
data/lib/arel/visitors/mssql.rb
CHANGED
@@ -76,6 +76,16 @@ module Arel # :nodoc: all
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
80
|
+
collector = super
|
81
|
+
maybe_visit o.optimizer_hints, collector
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
85
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
|
86
|
+
collector << "OPTION (#{hints})"
|
87
|
+
end
|
88
|
+
|
79
89
|
def get_offset_limit_clause(o)
|
80
90
|
first_row = o.offset ? o.offset.expr.to_i + 1 : 1
|
81
91
|
last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil
|
@@ -97,12 +107,16 @@ module Arel # :nodoc: all
|
|
97
107
|
collector = visit o.relation, collector
|
98
108
|
if o.wheres.any?
|
99
109
|
collector << " WHERE "
|
100
|
-
inject_join o.wheres, collector, AND
|
110
|
+
inject_join o.wheres, collector, " AND "
|
101
111
|
else
|
102
112
|
collector
|
103
113
|
end
|
104
114
|
end
|
105
115
|
|
116
|
+
def collect_optimizer_hints(o, collector)
|
117
|
+
collector
|
118
|
+
end
|
119
|
+
|
106
120
|
def determine_order_by(orders, x)
|
107
121
|
if orders.any?
|
108
122
|
orders
|
@@ -8,11 +8,10 @@ module Arel # :nodoc: all
|
|
8
8
|
def visit_Arel_Nodes_SelectStatement(o, collector)
|
9
9
|
# Oracle does not allow LIMIT clause with select for update
|
10
10
|
if o.limit && o.lock
|
11
|
-
raise ArgumentError,
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
MSG
|
11
|
+
raise ArgumentError, <<~MSG
|
12
|
+
Combination of limit and lock is not supported. Because generated SQL statements
|
13
|
+
`SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.
|
14
|
+
MSG
|
16
15
|
end
|
17
16
|
super
|
18
17
|
end
|
@@ -3,11 +3,6 @@
|
|
3
3
|
module Arel # :nodoc: all
|
4
4
|
module Visitors
|
5
5
|
class PostgreSQL < Arel::Visitors::ToSql
|
6
|
-
CUBE = "CUBE"
|
7
|
-
ROLLUP = "ROLLUP"
|
8
|
-
GROUPING_SETS = "GROUPING SETS"
|
9
|
-
LATERAL = "LATERAL"
|
10
|
-
|
11
6
|
private
|
12
7
|
|
13
8
|
def visit_Arel_Nodes_Matches(o, collector)
|
@@ -57,23 +52,22 @@ module Arel # :nodoc: all
|
|
57
52
|
end
|
58
53
|
|
59
54
|
def visit_Arel_Nodes_Cube(o, collector)
|
60
|
-
collector << CUBE
|
55
|
+
collector << "CUBE"
|
61
56
|
grouping_array_or_grouping_element o, collector
|
62
57
|
end
|
63
58
|
|
64
59
|
def visit_Arel_Nodes_RollUp(o, collector)
|
65
|
-
collector << ROLLUP
|
60
|
+
collector << "ROLLUP"
|
66
61
|
grouping_array_or_grouping_element o, collector
|
67
62
|
end
|
68
63
|
|
69
64
|
def visit_Arel_Nodes_GroupingSet(o, collector)
|
70
|
-
collector <<
|
65
|
+
collector << "GROUPING SETS"
|
71
66
|
grouping_array_or_grouping_element o, collector
|
72
67
|
end
|
73
68
|
|
74
69
|
def visit_Arel_Nodes_Lateral(o, collector)
|
75
|
-
collector << LATERAL
|
76
|
-
collector << SPACE
|
70
|
+
collector << "LATERAL "
|
77
71
|
grouping_parentheses o, collector
|
78
72
|
end
|
79
73
|
|
data/lib/arel/visitors/to_sql.rb
CHANGED
@@ -9,59 +9,6 @@ module Arel # :nodoc: all
|
|
9
9
|
end
|
10
10
|
|
11
11
|
class ToSql < Arel::Visitors::Visitor
|
12
|
-
##
|
13
|
-
# This is some roflscale crazy stuff. I'm roflscaling this because
|
14
|
-
# building SQL queries is a hotspot. I will explain the roflscale so that
|
15
|
-
# others will not rm this code.
|
16
|
-
#
|
17
|
-
# In YARV, string literals in a method body will get duped when the byte
|
18
|
-
# code is executed. Let's take a look:
|
19
|
-
#
|
20
|
-
# > puts RubyVM::InstructionSequence.new('def foo; "bar"; end').disasm
|
21
|
-
#
|
22
|
-
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>=====
|
23
|
-
# 0000 trace 8
|
24
|
-
# 0002 trace 1
|
25
|
-
# 0004 putstring "bar"
|
26
|
-
# 0006 trace 16
|
27
|
-
# 0008 leave
|
28
|
-
#
|
29
|
-
# The `putstring` bytecode will dup the string and push it on the stack.
|
30
|
-
# In many cases in our SQL visitor, that string is never mutated, so there
|
31
|
-
# is no need to dup the literal.
|
32
|
-
#
|
33
|
-
# If we change to a constant lookup, the string will not be duped, and we
|
34
|
-
# can reduce the objects in our system:
|
35
|
-
#
|
36
|
-
# > puts RubyVM::InstructionSequence.new('BAR = "bar"; def foo; BAR; end').disasm
|
37
|
-
#
|
38
|
-
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>========
|
39
|
-
# 0000 trace 8
|
40
|
-
# 0002 trace 1
|
41
|
-
# 0004 getinlinecache 11, <ic:0>
|
42
|
-
# 0007 getconstant :BAR
|
43
|
-
# 0009 setinlinecache <ic:0>
|
44
|
-
# 0011 trace 16
|
45
|
-
# 0013 leave
|
46
|
-
#
|
47
|
-
# `getconstant` should be a hash lookup, and no object is duped when the
|
48
|
-
# value of the constant is pushed on the stack. Hence the crazy
|
49
|
-
# constants below.
|
50
|
-
#
|
51
|
-
# `matches` and `doesNotMatch` operate case-insensitively via Visitor subclasses
|
52
|
-
# specialized for specific databases when necessary.
|
53
|
-
#
|
54
|
-
|
55
|
-
WHERE = " WHERE " # :nodoc:
|
56
|
-
SPACE = " " # :nodoc:
|
57
|
-
COMMA = ", " # :nodoc:
|
58
|
-
GROUP_BY = " GROUP BY " # :nodoc:
|
59
|
-
ORDER_BY = " ORDER BY " # :nodoc:
|
60
|
-
WINDOW = " WINDOW " # :nodoc:
|
61
|
-
AND = " AND " # :nodoc:
|
62
|
-
|
63
|
-
DISTINCT = "DISTINCT" # :nodoc:
|
64
|
-
|
65
12
|
def initialize(connection)
|
66
13
|
super()
|
67
14
|
@connection = connection
|
@@ -105,10 +52,14 @@ module Arel # :nodoc: all
|
|
105
52
|
def visit_Arel_Nodes_InsertStatement(o, collector)
|
106
53
|
collector << "INSERT INTO "
|
107
54
|
collector = visit o.relation, collector
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
55
|
+
|
56
|
+
unless o.columns.empty?
|
57
|
+
collector << " ("
|
58
|
+
o.columns.each_with_index do |x, i|
|
59
|
+
collector << ", " unless i == 0
|
60
|
+
collector << quote_column_name(x.name)
|
61
|
+
end
|
62
|
+
collector << ")"
|
112
63
|
end
|
113
64
|
|
114
65
|
if o.values
|
@@ -150,48 +101,27 @@ module Arel # :nodoc: all
|
|
150
101
|
def visit_Arel_Nodes_ValuesList(o, collector)
|
151
102
|
collector << "VALUES "
|
152
103
|
|
153
|
-
|
154
|
-
|
104
|
+
o.rows.each_with_index do |row, i|
|
105
|
+
collector << ", " unless i == 0
|
155
106
|
collector << "("
|
156
|
-
row_len = row.length - 1
|
157
107
|
row.each_with_index do |value, k|
|
108
|
+
collector << ", " unless k == 0
|
158
109
|
case value
|
159
110
|
when Nodes::SqlLiteral, Nodes::BindParam
|
160
111
|
collector = visit(value, collector)
|
161
112
|
else
|
162
|
-
collector << quote(value)
|
113
|
+
collector << quote(value).to_s
|
163
114
|
end
|
164
|
-
collector << COMMA unless k == row_len
|
165
115
|
end
|
166
116
|
collector << ")"
|
167
|
-
|
168
|
-
}
|
117
|
+
end
|
169
118
|
collector
|
170
119
|
end
|
171
120
|
|
172
|
-
def visit_Arel_Nodes_Values(o, collector)
|
173
|
-
collector << "VALUES ("
|
174
|
-
|
175
|
-
len = o.expressions.length - 1
|
176
|
-
o.expressions.each_with_index { |value, i|
|
177
|
-
case value
|
178
|
-
when Nodes::SqlLiteral, Nodes::BindParam
|
179
|
-
collector = visit value, collector
|
180
|
-
else
|
181
|
-
collector << quote(value).to_s
|
182
|
-
end
|
183
|
-
unless i == len
|
184
|
-
collector << COMMA
|
185
|
-
end
|
186
|
-
}
|
187
|
-
|
188
|
-
collector << ")"
|
189
|
-
end
|
190
|
-
|
191
121
|
def visit_Arel_Nodes_SelectStatement(o, collector)
|
192
122
|
if o.with
|
193
123
|
collector = visit o.with, collector
|
194
|
-
collector <<
|
124
|
+
collector << " "
|
195
125
|
end
|
196
126
|
|
197
127
|
collector = o.cores.inject(collector) { |c, x|
|
@@ -199,12 +129,11 @@ module Arel # :nodoc: all
|
|
199
129
|
}
|
200
130
|
|
201
131
|
unless o.orders.empty?
|
202
|
-
collector <<
|
203
|
-
|
204
|
-
|
132
|
+
collector << " ORDER BY "
|
133
|
+
o.orders.each_with_index do |x, i|
|
134
|
+
collector << ", " unless i == 0
|
205
135
|
collector = visit(x, collector)
|
206
|
-
|
207
|
-
}
|
136
|
+
end
|
208
137
|
end
|
209
138
|
|
210
139
|
visit_Arel_Nodes_SelectOptions(o, collector)
|
@@ -219,24 +148,34 @@ module Arel # :nodoc: all
|
|
219
148
|
def visit_Arel_Nodes_SelectCore(o, collector)
|
220
149
|
collector << "SELECT"
|
221
150
|
|
151
|
+
collector = collect_optimizer_hints(o, collector)
|
222
152
|
collector = maybe_visit o.set_quantifier, collector
|
223
153
|
|
224
|
-
collect_nodes_for o.projections, collector,
|
154
|
+
collect_nodes_for o.projections, collector, " "
|
225
155
|
|
226
156
|
if o.source && !o.source.empty?
|
227
157
|
collector << " FROM "
|
228
158
|
collector = visit o.source, collector
|
229
159
|
end
|
230
160
|
|
231
|
-
collect_nodes_for o.wheres, collector, WHERE, AND
|
232
|
-
collect_nodes_for o.groups, collector,
|
233
|
-
collect_nodes_for o.havings, collector, " HAVING ", AND
|
234
|
-
collect_nodes_for o.windows, collector, WINDOW
|
161
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
162
|
+
collect_nodes_for o.groups, collector, " GROUP BY "
|
163
|
+
collect_nodes_for o.havings, collector, " HAVING ", " AND "
|
164
|
+
collect_nodes_for o.windows, collector, " WINDOW "
|
235
165
|
|
236
|
-
collector
|
166
|
+
maybe_visit o.comment, collector
|
167
|
+
end
|
168
|
+
|
169
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
170
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(" ")
|
171
|
+
collector << "/*+ #{hints} */"
|
172
|
+
end
|
173
|
+
|
174
|
+
def visit_Arel_Nodes_Comment(o, collector)
|
175
|
+
collector << o.values.map { |v| "/* #{sanitize_as_sql_comment(v)} */" }.join(" ")
|
237
176
|
end
|
238
177
|
|
239
|
-
def collect_nodes_for(nodes, collector, spacer, connector =
|
178
|
+
def collect_nodes_for(nodes, collector, spacer, connector = ", ")
|
240
179
|
unless nodes.empty?
|
241
180
|
collector << spacer
|
242
181
|
inject_join nodes, collector, connector
|
@@ -248,7 +187,7 @@ module Arel # :nodoc: all
|
|
248
187
|
end
|
249
188
|
|
250
189
|
def visit_Arel_Nodes_Distinct(o, collector)
|
251
|
-
collector << DISTINCT
|
190
|
+
collector << "DISTINCT"
|
252
191
|
end
|
253
192
|
|
254
193
|
def visit_Arel_Nodes_DistinctOn(o, collector)
|
@@ -257,12 +196,12 @@ module Arel # :nodoc: all
|
|
257
196
|
|
258
197
|
def visit_Arel_Nodes_With(o, collector)
|
259
198
|
collector << "WITH "
|
260
|
-
inject_join o.children, collector,
|
199
|
+
inject_join o.children, collector, ", "
|
261
200
|
end
|
262
201
|
|
263
202
|
def visit_Arel_Nodes_WithRecursive(o, collector)
|
264
203
|
collector << "WITH RECURSIVE "
|
265
|
-
inject_join o.children, collector,
|
204
|
+
inject_join o.children, collector, ", "
|
266
205
|
end
|
267
206
|
|
268
207
|
def visit_Arel_Nodes_Union(o, collector)
|
@@ -295,13 +234,13 @@ module Arel # :nodoc: all
|
|
295
234
|
collect_nodes_for o.partitions, collector, "PARTITION BY "
|
296
235
|
|
297
236
|
if o.orders.any?
|
298
|
-
collector <<
|
237
|
+
collector << " " if o.partitions.any?
|
299
238
|
collector << "ORDER BY "
|
300
239
|
collector = inject_join o.orders, collector, ", "
|
301
240
|
end
|
302
241
|
|
303
242
|
if o.framing
|
304
|
-
collector <<
|
243
|
+
collector << " " if o.partitions.any? || o.orders.any?
|
305
244
|
collector = visit o.framing, collector
|
306
245
|
end
|
307
246
|
|
@@ -506,8 +445,8 @@ module Arel # :nodoc: all
|
|
506
445
|
collector = visit o.left, collector
|
507
446
|
end
|
508
447
|
if o.right.any?
|
509
|
-
collector <<
|
510
|
-
collector = inject_join o.right, collector,
|
448
|
+
collector << " " if o.left
|
449
|
+
collector = inject_join o.right, collector, " "
|
511
450
|
end
|
512
451
|
collector
|
513
452
|
end
|
@@ -527,7 +466,7 @@ module Arel # :nodoc: all
|
|
527
466
|
def visit_Arel_Nodes_FullOuterJoin(o, collector)
|
528
467
|
collector << "FULL OUTER JOIN "
|
529
468
|
collector = visit o.left, collector
|
530
|
-
collector <<
|
469
|
+
collector << " "
|
531
470
|
visit o.right, collector
|
532
471
|
end
|
533
472
|
|
@@ -541,7 +480,7 @@ module Arel # :nodoc: all
|
|
541
480
|
def visit_Arel_Nodes_RightOuterJoin(o, collector)
|
542
481
|
collector << "RIGHT OUTER JOIN "
|
543
482
|
collector = visit o.left, collector
|
544
|
-
collector <<
|
483
|
+
collector << " "
|
545
484
|
visit o.right, collector
|
546
485
|
end
|
547
486
|
|
@@ -549,7 +488,7 @@ module Arel # :nodoc: all
|
|
549
488
|
collector << "INNER JOIN "
|
550
489
|
collector = visit o.left, collector
|
551
490
|
if o.right
|
552
|
-
collector <<
|
491
|
+
collector << " "
|
553
492
|
visit(o.right, collector)
|
554
493
|
else
|
555
494
|
collector
|
@@ -568,41 +507,73 @@ module Arel # :nodoc: all
|
|
568
507
|
|
569
508
|
def visit_Arel_Table(o, collector)
|
570
509
|
if o.table_alias
|
571
|
-
collector <<
|
510
|
+
collector << quote_table_name(o.name) << " " << quote_table_name(o.table_alias)
|
572
511
|
else
|
573
512
|
collector << quote_table_name(o.name)
|
574
513
|
end
|
575
514
|
end
|
576
515
|
|
577
516
|
def visit_Arel_Nodes_In(o, collector)
|
578
|
-
|
517
|
+
unless Array === o.right
|
518
|
+
return collect_in_clause(o.left, o.right, collector)
|
519
|
+
end
|
520
|
+
|
521
|
+
unless o.right.empty?
|
579
522
|
o.right.delete_if { |value| unboundable?(value) }
|
580
523
|
end
|
581
524
|
|
582
|
-
|
583
|
-
|
525
|
+
return collector << "1=0" if o.right.empty?
|
526
|
+
|
527
|
+
in_clause_length = @connection.in_clause_length
|
528
|
+
|
529
|
+
if !in_clause_length || o.right.length <= in_clause_length
|
530
|
+
collect_in_clause(o.left, o.right, collector)
|
584
531
|
else
|
585
|
-
collector
|
586
|
-
|
587
|
-
|
532
|
+
collector << "("
|
533
|
+
o.right.each_slice(in_clause_length).each_with_index do |right, i|
|
534
|
+
collector << " OR " unless i == 0
|
535
|
+
collect_in_clause(o.left, right, collector)
|
536
|
+
end
|
537
|
+
collector << ")"
|
588
538
|
end
|
589
539
|
end
|
590
540
|
|
541
|
+
def collect_in_clause(left, right, collector)
|
542
|
+
collector = visit left, collector
|
543
|
+
collector << " IN ("
|
544
|
+
visit(right, collector) << ")"
|
545
|
+
end
|
546
|
+
|
591
547
|
def visit_Arel_Nodes_NotIn(o, collector)
|
592
|
-
|
548
|
+
unless Array === o.right
|
549
|
+
return collect_not_in_clause(o.left, o.right, collector)
|
550
|
+
end
|
551
|
+
|
552
|
+
unless o.right.empty?
|
593
553
|
o.right.delete_if { |value| unboundable?(value) }
|
594
554
|
end
|
595
555
|
|
596
|
-
|
597
|
-
|
556
|
+
return collector << "1=1" if o.right.empty?
|
557
|
+
|
558
|
+
in_clause_length = @connection.in_clause_length
|
559
|
+
|
560
|
+
if !in_clause_length || o.right.length <= in_clause_length
|
561
|
+
collect_not_in_clause(o.left, o.right, collector)
|
598
562
|
else
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
563
|
+
o.right.each_slice(in_clause_length).each_with_index do |right, i|
|
564
|
+
collector << " AND " unless i == 0
|
565
|
+
collect_not_in_clause(o.left, right, collector)
|
566
|
+
end
|
567
|
+
collector
|
603
568
|
end
|
604
569
|
end
|
605
570
|
|
571
|
+
def collect_not_in_clause(left, right, collector)
|
572
|
+
collector = visit left, collector
|
573
|
+
collector << " NOT IN ("
|
574
|
+
visit(right, collector) << ")"
|
575
|
+
end
|
576
|
+
|
606
577
|
def visit_Arel_Nodes_And(o, collector)
|
607
578
|
inject_join o.children, collector, " AND "
|
608
579
|
end
|
@@ -712,13 +683,12 @@ module Arel # :nodoc: all
|
|
712
683
|
end
|
713
684
|
|
714
685
|
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
|
715
|
-
collector <<
|
716
|
-
collector
|
686
|
+
collector << quote_column_name(o.name)
|
717
687
|
end
|
718
688
|
|
719
689
|
def visit_Arel_Attributes_Attribute(o, collector)
|
720
690
|
join_name = o.relation.table_alias || o.relation.name
|
721
|
-
collector <<
|
691
|
+
collector << quote_table_name(join_name) << "." << quote_column_name(o.name)
|
722
692
|
end
|
723
693
|
alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
|
724
694
|
alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
|
@@ -799,6 +769,15 @@ module Arel # :nodoc: all
|
|
799
769
|
@connection.quote_column_name(name)
|
800
770
|
end
|
801
771
|
|
772
|
+
def sanitize_as_sql_comment(value)
|
773
|
+
return value if Arel::Nodes::SqlLiteral === value
|
774
|
+
@connection.sanitize_as_sql_comment(value)
|
775
|
+
end
|
776
|
+
|
777
|
+
def collect_optimizer_hints(o, collector)
|
778
|
+
maybe_visit o.optimizer_hints, collector
|
779
|
+
end
|
780
|
+
|
802
781
|
def maybe_visit(thing, collector)
|
803
782
|
return collector unless thing
|
804
783
|
collector << " "
|
@@ -806,14 +785,11 @@ module Arel # :nodoc: all
|
|
806
785
|
end
|
807
786
|
|
808
787
|
def inject_join(list, collector, join_str)
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
visit(x, c) << join_str
|
815
|
-
end
|
816
|
-
}
|
788
|
+
list.each_with_index do |x, i|
|
789
|
+
collector << join_str unless i == 0
|
790
|
+
collector = visit(x, collector)
|
791
|
+
end
|
792
|
+
collector
|
817
793
|
end
|
818
794
|
|
819
795
|
def unboundable?(value)
|