activerecord 6.0.0.beta1 → 6.0.1.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 +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -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 +27 -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 +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- 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 +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- 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/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- 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 +3 -3
- 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_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- 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/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- 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 +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- 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 +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- 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 +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- 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.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -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 +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- 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 +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -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 +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -14,6 +14,8 @@ module ActiveRecord
|
|
14
14
|
# * change_column
|
15
15
|
# * change_column_default (must supply a :from and :to option)
|
16
16
|
# * change_column_null
|
17
|
+
# * change_column_comment (must supply a :from and :to option)
|
18
|
+
# * change_table_comment (must supply a :from and :to option)
|
17
19
|
# * create_join_table
|
18
20
|
# * create_table
|
19
21
|
# * disable_extension
|
@@ -35,7 +37,8 @@ module ActiveRecord
|
|
35
37
|
:change_column_default, :add_reference, :remove_reference, :transaction,
|
36
38
|
:drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
|
37
39
|
:change_column, :execute, :remove_columns, :change_column_null,
|
38
|
-
:add_foreign_key, :remove_foreign_key
|
40
|
+
:add_foreign_key, :remove_foreign_key,
|
41
|
+
:change_column_comment, :change_table_comment
|
39
42
|
]
|
40
43
|
include JoinTable
|
41
44
|
|
@@ -231,28 +234,39 @@ module ActiveRecord
|
|
231
234
|
end
|
232
235
|
|
233
236
|
def invert_remove_foreign_key(args)
|
234
|
-
|
237
|
+
options = args.extract_options!
|
238
|
+
from_table, to_table = args
|
235
239
|
|
236
|
-
to_table
|
237
|
-
options_or_to_table[:to_table]
|
238
|
-
else
|
239
|
-
options_or_to_table
|
240
|
-
end
|
241
|
-
|
242
|
-
remove_options = if options_or_to_table.is_a?(Hash)
|
243
|
-
options_or_to_table.except(:to_table)
|
244
|
-
else
|
245
|
-
options_or_nil
|
246
|
-
end
|
240
|
+
to_table ||= options.delete(:to_table)
|
247
241
|
|
248
242
|
raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil?
|
249
243
|
|
250
244
|
reversed_args = [from_table, to_table]
|
251
|
-
reversed_args <<
|
245
|
+
reversed_args << options unless options.empty?
|
252
246
|
|
253
247
|
[:add_foreign_key, reversed_args]
|
254
248
|
end
|
255
249
|
|
250
|
+
def invert_change_column_comment(args)
|
251
|
+
table, column, options = *args
|
252
|
+
|
253
|
+
unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
|
254
|
+
raise ActiveRecord::IrreversibleMigration, "change_column_comment is only reversible if given a :from and :to option."
|
255
|
+
end
|
256
|
+
|
257
|
+
[:change_column_comment, [table, column, from: options[:to], to: options[:from]]]
|
258
|
+
end
|
259
|
+
|
260
|
+
def invert_change_table_comment(args)
|
261
|
+
table, options = *args
|
262
|
+
|
263
|
+
unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
|
264
|
+
raise ActiveRecord::IrreversibleMigration, "change_table_comment is only reversible if given a :from and :to option."
|
265
|
+
end
|
266
|
+
|
267
|
+
[:change_table_comment, [table, from: options[:to], to: options[:from]]]
|
268
|
+
end
|
269
|
+
|
256
270
|
def respond_to_missing?(method, _)
|
257
271
|
super || delegate.respond_to?(method)
|
258
272
|
end
|
@@ -16,13 +16,65 @@ module ActiveRecord
|
|
16
16
|
V6_0 = Current
|
17
17
|
|
18
18
|
class V5_2 < V6_0
|
19
|
+
module TableDefinition
|
20
|
+
def timestamps(**options)
|
21
|
+
options[:precision] ||= nil
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
19
26
|
module CommandRecorder
|
20
27
|
def invert_transaction(args, &block)
|
21
28
|
[:transaction, args, block]
|
22
29
|
end
|
30
|
+
|
31
|
+
def invert_change_column_comment(args)
|
32
|
+
table_name, column_name, comment = args
|
33
|
+
[:change_column_comment, [table_name, column_name, from: comment, to: comment]]
|
34
|
+
end
|
35
|
+
|
36
|
+
def invert_change_table_comment(args)
|
37
|
+
table_name, comment = args
|
38
|
+
[:change_table_comment, [table_name, from: comment, to: comment]]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_table(table_name, **options)
|
43
|
+
if block_given?
|
44
|
+
super { |t| yield compatible_table_definition(t) }
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def change_table(table_name, **options)
|
51
|
+
if block_given?
|
52
|
+
super { |t| yield compatible_table_definition(t) }
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_join_table(table_1, table_2, **options)
|
59
|
+
if block_given?
|
60
|
+
super { |t| yield compatible_table_definition(t) }
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_timestamps(table_name, **options)
|
67
|
+
options[:precision] ||= nil
|
68
|
+
super
|
23
69
|
end
|
24
70
|
|
25
71
|
private
|
72
|
+
def compatible_table_definition(t)
|
73
|
+
class << t
|
74
|
+
prepend TableDefinition
|
75
|
+
end
|
76
|
+
t
|
77
|
+
end
|
26
78
|
|
27
79
|
def command_recorder
|
28
80
|
recorder = super
|
@@ -35,20 +87,18 @@ module ActiveRecord
|
|
35
87
|
|
36
88
|
class V5_1 < V5_2
|
37
89
|
def change_column(table_name, column_name, type, options = {})
|
38
|
-
if adapter_name == "PostgreSQL"
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
44
|
-
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
90
|
+
if connection.adapter_name == "PostgreSQL"
|
91
|
+
super(table_name, column_name, type, options.except(:default, :null, :comment))
|
92
|
+
connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
93
|
+
connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
94
|
+
connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
45
95
|
else
|
46
96
|
super
|
47
97
|
end
|
48
98
|
end
|
49
99
|
|
50
100
|
def create_table(table_name, options = {})
|
51
|
-
if adapter_name == "Mysql2"
|
101
|
+
if connection.adapter_name == "Mysql2"
|
52
102
|
super(table_name, options: "ENGINE=InnoDB", **options)
|
53
103
|
else
|
54
104
|
super
|
@@ -70,13 +120,13 @@ module ActiveRecord
|
|
70
120
|
end
|
71
121
|
|
72
122
|
def create_table(table_name, options = {})
|
73
|
-
if adapter_name == "PostgreSQL"
|
123
|
+
if connection.adapter_name == "PostgreSQL"
|
74
124
|
if options[:id] == :uuid && !options.key?(:default)
|
75
125
|
options[:default] = "uuid_generate_v4()"
|
76
126
|
end
|
77
127
|
end
|
78
128
|
|
79
|
-
unless adapter_name == "Mysql2" && options[:id] == :bigint
|
129
|
+
unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
|
80
130
|
if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
|
81
131
|
options[:default] = nil
|
82
132
|
end
|
@@ -89,35 +139,12 @@ module ActiveRecord
|
|
89
139
|
options[:id] = :integer
|
90
140
|
end
|
91
141
|
|
92
|
-
|
93
|
-
super do |t|
|
94
|
-
yield compatible_table_definition(t)
|
95
|
-
end
|
96
|
-
else
|
97
|
-
super
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def change_table(table_name, options = {})
|
102
|
-
if block_given?
|
103
|
-
super do |t|
|
104
|
-
yield compatible_table_definition(t)
|
105
|
-
end
|
106
|
-
else
|
107
|
-
super
|
108
|
-
end
|
142
|
+
super
|
109
143
|
end
|
110
144
|
|
111
145
|
def create_join_table(table_1, table_2, column_options: {}, **options)
|
112
146
|
column_options.reverse_merge!(type: :integer)
|
113
|
-
|
114
|
-
if block_given?
|
115
|
-
super do |t|
|
116
|
-
yield compatible_table_definition(t)
|
117
|
-
end
|
118
|
-
else
|
119
|
-
super
|
120
|
-
end
|
147
|
+
super
|
121
148
|
end
|
122
149
|
|
123
150
|
def add_column(table_name, column_name, type, options = {})
|
@@ -138,7 +165,7 @@ module ActiveRecord
|
|
138
165
|
class << t
|
139
166
|
prepend TableDefinition
|
140
167
|
end
|
141
|
-
|
168
|
+
super
|
142
169
|
end
|
143
170
|
end
|
144
171
|
|
@@ -156,33 +183,13 @@ module ActiveRecord
|
|
156
183
|
end
|
157
184
|
end
|
158
185
|
|
159
|
-
def
|
160
|
-
if block_given?
|
161
|
-
super do |t|
|
162
|
-
yield compatible_table_definition(t)
|
163
|
-
end
|
164
|
-
else
|
165
|
-
super
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def change_table(table_name, options = {})
|
170
|
-
if block_given?
|
171
|
-
super do |t|
|
172
|
-
yield compatible_table_definition(t)
|
173
|
-
end
|
174
|
-
else
|
175
|
-
super
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def add_reference(*, **options)
|
186
|
+
def add_reference(table_name, ref_name, **options)
|
180
187
|
options[:index] ||= false
|
181
188
|
super
|
182
189
|
end
|
183
190
|
alias :add_belongs_to :add_reference
|
184
191
|
|
185
|
-
def add_timestamps(
|
192
|
+
def add_timestamps(table_name, **options)
|
186
193
|
options[:null] = true if options[:null].nil?
|
187
194
|
super
|
188
195
|
end
|
@@ -193,7 +200,7 @@ module ActiveRecord
|
|
193
200
|
if options[:name].present?
|
194
201
|
options[:name].to_s
|
195
202
|
else
|
196
|
-
index_name(table_name, column: column_names)
|
203
|
+
connection.index_name(table_name, column: column_names)
|
197
204
|
end
|
198
205
|
super
|
199
206
|
end
|
@@ -213,15 +220,17 @@ module ActiveRecord
|
|
213
220
|
end
|
214
221
|
|
215
222
|
def index_name_for_remove(table_name, options = {})
|
216
|
-
index_name = index_name(table_name, options)
|
223
|
+
index_name = connection.index_name(table_name, options)
|
217
224
|
|
218
|
-
unless index_name_exists?(table_name, index_name)
|
225
|
+
unless connection.index_name_exists?(table_name, index_name)
|
219
226
|
if options.is_a?(Hash) && options.has_key?(:name)
|
220
227
|
options_without_column = options.dup
|
221
228
|
options_without_column.delete :column
|
222
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
229
|
+
index_name_without_column = connection.index_name(table_name, options_without_column)
|
223
230
|
|
224
|
-
|
231
|
+
if connection.index_name_exists?(table_name, index_name_without_column)
|
232
|
+
return index_name_without_column
|
233
|
+
end
|
225
234
|
end
|
226
235
|
|
227
236
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record/insert_all"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
# = Active Record \Persistence
|
5
7
|
module Persistence
|
@@ -55,6 +57,192 @@ module ActiveRecord
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
60
|
+
# Inserts a single record into the database in a single SQL INSERT
|
61
|
+
# statement. It does not instantiate any models nor does it trigger
|
62
|
+
# Active Record callbacks or validations. Though passed values
|
63
|
+
# go through Active Record's type casting and serialization.
|
64
|
+
#
|
65
|
+
# See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
|
66
|
+
def insert(attributes, returning: nil, unique_by: nil)
|
67
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
71
|
+
# statement. It does not instantiate any models nor does it trigger
|
72
|
+
# Active Record callbacks or validations. Though passed values
|
73
|
+
# go through Active Record's type casting and serialization.
|
74
|
+
#
|
75
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
76
|
+
# the attributes for a single row and must have the same keys.
|
77
|
+
#
|
78
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
79
|
+
# duplicate rows are skipped.
|
80
|
+
# Override with <tt>:unique_by</tt> (see below).
|
81
|
+
#
|
82
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
83
|
+
# <tt>:returning</tt> (see below).
|
84
|
+
#
|
85
|
+
# ==== Options
|
86
|
+
#
|
87
|
+
# [:returning]
|
88
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
89
|
+
# inserted records, which by default is the primary key.
|
90
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
91
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
|
+
# clause entirely.
|
93
|
+
#
|
94
|
+
# [:unique_by]
|
95
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
96
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
97
|
+
#
|
98
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
99
|
+
#
|
100
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
101
|
+
# row has an existing id, or is not unique by another unique index,
|
102
|
+
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
103
|
+
#
|
104
|
+
# Unique indexes can be identified by columns or name:
|
105
|
+
#
|
106
|
+
# unique_by: :isbn
|
107
|
+
# unique_by: %i[ author_id name ]
|
108
|
+
# unique_by: :index_books_on_isbn
|
109
|
+
#
|
110
|
+
# Because it relies on the index information from the database
|
111
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
112
|
+
# Active Record's schema_cache.
|
113
|
+
#
|
114
|
+
# ==== Example
|
115
|
+
#
|
116
|
+
# # Insert records and skip inserting any duplicates.
|
117
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
118
|
+
#
|
119
|
+
# Book.insert_all([
|
120
|
+
# { id: 1, title: "Rework", author: "David" },
|
121
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
122
|
+
# ])
|
123
|
+
def insert_all(attributes, returning: nil, unique_by: nil)
|
124
|
+
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
|
125
|
+
end
|
126
|
+
|
127
|
+
# Inserts a single record into the database in a single SQL INSERT
|
128
|
+
# statement. It does not instantiate any models nor does it trigger
|
129
|
+
# Active Record callbacks or validations. Though passed values
|
130
|
+
# go through Active Record's type casting and serialization.
|
131
|
+
#
|
132
|
+
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
|
133
|
+
def insert!(attributes, returning: nil)
|
134
|
+
insert_all!([ attributes ], returning: returning)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
138
|
+
# statement. It does not instantiate any models nor does it trigger
|
139
|
+
# Active Record callbacks or validations. Though passed values
|
140
|
+
# go through Active Record's type casting and serialization.
|
141
|
+
#
|
142
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
143
|
+
# the attributes for a single row and must have the same keys.
|
144
|
+
#
|
145
|
+
# Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
|
146
|
+
# unique index on the table. In that case, no rows are inserted.
|
147
|
+
#
|
148
|
+
# To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
|
149
|
+
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
150
|
+
#
|
151
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
152
|
+
# <tt>:returning</tt> (see below).
|
153
|
+
#
|
154
|
+
# ==== Options
|
155
|
+
#
|
156
|
+
# [:returning]
|
157
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
158
|
+
# inserted records, which by default is the primary key.
|
159
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
160
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
|
+
# clause entirely.
|
162
|
+
#
|
163
|
+
# ==== Examples
|
164
|
+
#
|
165
|
+
# # Insert multiple records
|
166
|
+
# Book.insert_all!([
|
167
|
+
# { title: "Rework", author: "David" },
|
168
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
169
|
+
# ])
|
170
|
+
#
|
171
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
172
|
+
# # does not have a unique id.
|
173
|
+
# Book.insert_all!([
|
174
|
+
# { id: 1, title: "Rework", author: "David" },
|
175
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
176
|
+
# ])
|
177
|
+
def insert_all!(attributes, returning: nil)
|
178
|
+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
179
|
+
end
|
180
|
+
|
181
|
+
# Updates or inserts (upserts) a single record into the database in a
|
182
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
183
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
184
|
+
# go through Active Record's type casting and serialization.
|
185
|
+
#
|
186
|
+
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
187
|
+
def upsert(attributes, returning: nil, unique_by: nil)
|
188
|
+
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Updates or inserts (upserts) multiple records into the database in a
|
192
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
193
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
194
|
+
# go through Active Record's type casting and serialization.
|
195
|
+
#
|
196
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
197
|
+
# the attributes for a single row and must have the same keys.
|
198
|
+
#
|
199
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
200
|
+
# <tt>:returning</tt> (see below).
|
201
|
+
#
|
202
|
+
# ==== Options
|
203
|
+
#
|
204
|
+
# [:returning]
|
205
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
206
|
+
# inserted records, which by default is the primary key.
|
207
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
208
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
|
+
# clause entirely.
|
210
|
+
#
|
211
|
+
# [:unique_by]
|
212
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
214
|
+
#
|
215
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
216
|
+
#
|
217
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
218
|
+
# row has an existing id, or is not unique by another unique index,
|
219
|
+
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
220
|
+
#
|
221
|
+
# Unique indexes can be identified by columns or name:
|
222
|
+
#
|
223
|
+
# unique_by: :isbn
|
224
|
+
# unique_by: %i[ author_id name ]
|
225
|
+
# unique_by: :index_books_on_isbn
|
226
|
+
#
|
227
|
+
# Because it relies on the index information from the database
|
228
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
229
|
+
# Active Record's schema_cache.
|
230
|
+
#
|
231
|
+
# ==== Examples
|
232
|
+
#
|
233
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
234
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
235
|
+
#
|
236
|
+
# Book.upsert_all([
|
237
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
238
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
239
|
+
# ], unique_by: :isbn)
|
240
|
+
#
|
241
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
242
|
+
def upsert_all(attributes, returning: nil, unique_by: nil)
|
243
|
+
InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
|
244
|
+
end
|
245
|
+
|
58
246
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
59
247
|
# the appropriate class. Accepts only keys as strings.
|
60
248
|
#
|
@@ -142,7 +330,7 @@ module ActiveRecord
|
|
142
330
|
end
|
143
331
|
end
|
144
332
|
|
145
|
-
# Deletes the row with a primary key matching the +id+ argument, using
|
333
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
146
334
|
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
147
335
|
# Record objects are not instantiated, so the object's callbacks are not
|
148
336
|
# executed, including any <tt>:dependent</tt> association options.
|
@@ -161,10 +349,11 @@ module ActiveRecord
|
|
161
349
|
# # Delete multiple rows
|
162
350
|
# Todo.delete([2,3,4])
|
163
351
|
def delete(id_or_array)
|
164
|
-
|
352
|
+
delete_by(primary_key => id_or_array)
|
165
353
|
end
|
166
354
|
|
167
355
|
def _insert_record(values) # :nodoc:
|
356
|
+
primary_key = self.primary_key
|
168
357
|
primary_key_value = nil
|
169
358
|
|
170
359
|
if primary_key && Hash === values
|
@@ -235,20 +424,20 @@ module ActiveRecord
|
|
235
424
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
236
425
|
# for the object doesn't exist in the database yet; otherwise, returns false.
|
237
426
|
def new_record?
|
238
|
-
sync_with_transaction_state
|
427
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
239
428
|
@new_record
|
240
429
|
end
|
241
430
|
|
242
431
|
# Returns true if this object has been destroyed, otherwise returns false.
|
243
432
|
def destroyed?
|
244
|
-
sync_with_transaction_state
|
433
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
245
434
|
@destroyed
|
246
435
|
end
|
247
436
|
|
248
437
|
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
249
438
|
# not destroyed, otherwise returns false.
|
250
439
|
def persisted?
|
251
|
-
sync_with_transaction_state
|
440
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
252
441
|
!(@new_record || @destroyed)
|
253
442
|
end
|
254
443
|
|
@@ -342,7 +531,6 @@ module ActiveRecord
|
|
342
531
|
def destroy
|
343
532
|
_raise_readonly_record_error if readonly?
|
344
533
|
destroy_associations
|
345
|
-
self.class.connection.add_transaction_record(self)
|
346
534
|
@_trigger_destroy_callback = if persisted?
|
347
535
|
destroy_row > 0
|
348
536
|
else
|
@@ -380,7 +568,6 @@ module ActiveRecord
|
|
380
568
|
became.send(:initialize)
|
381
569
|
became.instance_variable_set("@attributes", @attributes)
|
382
570
|
became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
|
383
|
-
became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
|
384
571
|
became.instance_variable_set("@new_record", new_record?)
|
385
572
|
became.instance_variable_set("@destroyed", destroyed?)
|
386
573
|
became.errors.copy!(errors)
|
@@ -436,7 +623,7 @@ module ActiveRecord
|
|
436
623
|
end
|
437
624
|
|
438
625
|
alias update_attributes update
|
439
|
-
deprecate :
|
626
|
+
deprecate update_attributes: "please, use update instead"
|
440
627
|
|
441
628
|
# Updates its receiver just like #update but calls #save! instead
|
442
629
|
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
@@ -450,7 +637,7 @@ module ActiveRecord
|
|
450
637
|
end
|
451
638
|
|
452
639
|
alias update_attributes! update!
|
453
|
-
deprecate
|
640
|
+
deprecate update_attributes!: "please, use update! instead"
|
454
641
|
|
455
642
|
# Equivalent to <code>update_columns(name => value)</code>.
|
456
643
|
def update_column(name, value)
|
@@ -477,8 +664,13 @@ module ActiveRecord
|
|
477
664
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
478
665
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
479
666
|
|
667
|
+
attributes = attributes.transform_keys do |key|
|
668
|
+
name = key.to_s
|
669
|
+
self.class.attribute_aliases[name] || name
|
670
|
+
end
|
671
|
+
|
480
672
|
attributes.each_key do |key|
|
481
|
-
verify_readonly_attribute(key
|
673
|
+
verify_readonly_attribute(key)
|
482
674
|
end
|
483
675
|
|
484
676
|
id_in_database = self.id_in_database
|
@@ -488,7 +680,7 @@ module ActiveRecord
|
|
488
680
|
|
489
681
|
affected_rows = self.class._update_record(
|
490
682
|
attributes,
|
491
|
-
|
683
|
+
@primary_key => id_in_database
|
492
684
|
)
|
493
685
|
|
494
686
|
affected_rows == 1
|
@@ -665,7 +857,9 @@ module ActiveRecord
|
|
665
857
|
end
|
666
858
|
|
667
859
|
attribute_names = timestamp_attributes_for_update_in_model
|
668
|
-
attribute_names |= names.map(&:to_s)
|
860
|
+
attribute_names |= names.map!(&:to_s).map! { |name|
|
861
|
+
self.class.attribute_aliases[name] || name
|
862
|
+
}
|
669
863
|
|
670
864
|
unless attribute_names.empty?
|
671
865
|
affected_rows = _touch_row(attribute_names, time)
|
@@ -686,15 +880,14 @@ module ActiveRecord
|
|
686
880
|
end
|
687
881
|
|
688
882
|
def _delete_row
|
689
|
-
self.class._delete_record(
|
883
|
+
self.class._delete_record(@primary_key => id_in_database)
|
690
884
|
end
|
691
885
|
|
692
886
|
def _touch_row(attribute_names, time)
|
693
887
|
time ||= current_time_from_proper_timezone
|
694
888
|
|
695
889
|
attribute_names.each do |attr_name|
|
696
|
-
|
697
|
-
clear_attribute_change(attr_name)
|
890
|
+
_write_attribute(attr_name, time)
|
698
891
|
end
|
699
892
|
|
700
893
|
_update_row(attribute_names, "touch")
|
@@ -703,14 +896,14 @@ module ActiveRecord
|
|
703
896
|
def _update_row(attribute_names, attempted_action = "update")
|
704
897
|
self.class._update_record(
|
705
898
|
attributes_with_values(attribute_names),
|
706
|
-
|
899
|
+
@primary_key => id_in_database
|
707
900
|
)
|
708
901
|
end
|
709
902
|
|
710
|
-
def create_or_update(
|
903
|
+
def create_or_update(**, &block)
|
711
904
|
_raise_readonly_record_error if readonly?
|
712
905
|
return false if destroyed?
|
713
|
-
result = new_record? ? _create_record(&block) : _update_record(
|
906
|
+
result = new_record? ? _create_record(&block) : _update_record(&block)
|
714
907
|
result != false
|
715
908
|
end
|
716
909
|
|
@@ -741,7 +934,7 @@ module ActiveRecord
|
|
741
934
|
attributes_with_values(attribute_names)
|
742
935
|
)
|
743
936
|
|
744
|
-
self.id ||= new_id if
|
937
|
+
self.id ||= new_id if @primary_key
|
745
938
|
|
746
939
|
@new_record = false
|
747
940
|
|