activerecord 6.0.0.beta3 → 6.0.0.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 +286 -6
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/associations/association.rb +1 -1
- 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 +3 -13
- 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_proxy.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/preloader.rb +11 -6
- data/lib/active_record/associations/preloader/association.rb +32 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +4 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +42 -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 +13 -3
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
- data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
- 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 +57 -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 +118 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
- data/lib/active_record/connection_handling.rb +17 -10
- data/lib/active_record/core.rb +15 -20
- data/lib/active_record/database_configurations.rb +14 -14
- 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 +6 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +180 -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/migration.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railties/databases.rake +68 -6
- data/lib/active_record/reflection.rb +2 -2
- data/lib/active_record/relation.rb +98 -20
- data/lib/active_record/relation/calculations.rb +39 -39
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +3 -9
- data/lib/active_record/relation/merger.rb +7 -16
- data/lib/active_record/relation/query_methods.rb +153 -38
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +3 -2
- data/lib/active_record/schema_dumper.rb +5 -0
- 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 +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +3 -3
- data/lib/active_record/tasks/database_tasks.rb +36 -1
- data/lib/active_record/touch_later.rb +2 -2
- data/lib/active_record/transactions.rb +52 -41
- data/lib/active_record/validations/uniqueness.rb +3 -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 +6 -1
- 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 +87 -108
- 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 +12 -11
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -4,9 +4,10 @@ require "benchmark"
|
|
4
4
|
require "set"
|
5
5
|
require "zlib"
|
6
6
|
require "active_support/core_ext/module/attribute_accessors"
|
7
|
+
require "active_support/actionable_error"
|
7
8
|
|
8
9
|
module ActiveRecord
|
9
|
-
class MigrationError < ActiveRecordError#:nodoc:
|
10
|
+
class MigrationError < ActiveRecordError #:nodoc:
|
10
11
|
def initialize(message = nil)
|
11
12
|
message = "\n\n#{message}\n\n" if message
|
12
13
|
super
|
@@ -87,7 +88,7 @@ module ActiveRecord
|
|
87
88
|
class IrreversibleMigration < MigrationError
|
88
89
|
end
|
89
90
|
|
90
|
-
class DuplicateMigrationVersionError < MigrationError#:nodoc:
|
91
|
+
class DuplicateMigrationVersionError < MigrationError #:nodoc:
|
91
92
|
def initialize(version = nil)
|
92
93
|
if version
|
93
94
|
super("Multiple migrations have the version number #{version}.")
|
@@ -97,7 +98,7 @@ module ActiveRecord
|
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
100
|
-
class DuplicateMigrationNameError < MigrationError#:nodoc:
|
101
|
+
class DuplicateMigrationNameError < MigrationError #:nodoc:
|
101
102
|
def initialize(name = nil)
|
102
103
|
if name
|
103
104
|
super("Multiple migrations have the name #{name}.")
|
@@ -117,7 +118,7 @@ module ActiveRecord
|
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
120
|
-
class IllegalMigrationNameError < MigrationError#:nodoc:
|
121
|
+
class IllegalMigrationNameError < MigrationError #:nodoc:
|
121
122
|
def initialize(name = nil)
|
122
123
|
if name
|
123
124
|
super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
|
@@ -127,7 +128,13 @@ module ActiveRecord
|
|
127
128
|
end
|
128
129
|
end
|
129
130
|
|
130
|
-
class PendingMigrationError < MigrationError#:nodoc:
|
131
|
+
class PendingMigrationError < MigrationError #:nodoc:
|
132
|
+
include ActiveSupport::ActionableError
|
133
|
+
|
134
|
+
action "Run pending migrations" do
|
135
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
136
|
+
end
|
137
|
+
|
131
138
|
def initialize(message = nil)
|
132
139
|
if !message && defined?(Rails.env)
|
133
140
|
super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
|
@@ -308,7 +315,7 @@ module ActiveRecord
|
|
308
315
|
# named +column_name+ from the table called +table_name+.
|
309
316
|
# * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
|
310
317
|
# columns from the table definition.
|
311
|
-
# * <tt>remove_foreign_key(from_table,
|
318
|
+
# * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
|
312
319
|
# given foreign key from the table called +table_name+.
|
313
320
|
# * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
|
314
321
|
# specified by +column_names+.
|
@@ -520,10 +527,10 @@ module ActiveRecord
|
|
520
527
|
autoload :Compatibility, "active_record/migration/compatibility"
|
521
528
|
|
522
529
|
# This must be defined before the inherited hook, below
|
523
|
-
class Current < Migration
|
530
|
+
class Current < Migration #:nodoc:
|
524
531
|
end
|
525
532
|
|
526
|
-
def self.inherited(subclass)
|
533
|
+
def self.inherited(subclass) #:nodoc:
|
527
534
|
super
|
528
535
|
if subclass.superclass == Migration
|
529
536
|
raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
|
@@ -541,7 +548,7 @@ module ActiveRecord
|
|
541
548
|
ActiveRecord::VERSION::STRING.to_f
|
542
549
|
end
|
543
550
|
|
544
|
-
MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/
|
551
|
+
MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
|
545
552
|
|
546
553
|
# This class is used to verify that all migrations have been run before
|
547
554
|
# loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
|
@@ -568,10 +575,10 @@ module ActiveRecord
|
|
568
575
|
end
|
569
576
|
|
570
577
|
class << self
|
571
|
-
attr_accessor :delegate
|
572
|
-
attr_accessor :disable_ddl_transaction
|
578
|
+
attr_accessor :delegate #:nodoc:
|
579
|
+
attr_accessor :disable_ddl_transaction #:nodoc:
|
573
580
|
|
574
|
-
def nearest_delegate
|
581
|
+
def nearest_delegate #:nodoc:
|
575
582
|
delegate || superclass.nearest_delegate
|
576
583
|
end
|
577
584
|
|
@@ -595,13 +602,13 @@ module ActiveRecord
|
|
595
602
|
end
|
596
603
|
end
|
597
604
|
|
598
|
-
def maintain_test_schema!
|
605
|
+
def maintain_test_schema! #:nodoc:
|
599
606
|
if ActiveRecord::Base.maintain_test_schema
|
600
607
|
suppress_messages { load_schema_if_pending! }
|
601
608
|
end
|
602
609
|
end
|
603
610
|
|
604
|
-
def method_missing(name, *args, &block)
|
611
|
+
def method_missing(name, *args, &block) #:nodoc:
|
605
612
|
nearest_delegate.send(name, *args, &block)
|
606
613
|
end
|
607
614
|
|
@@ -618,7 +625,7 @@ module ActiveRecord
|
|
618
625
|
end
|
619
626
|
end
|
620
627
|
|
621
|
-
def disable_ddl_transaction
|
628
|
+
def disable_ddl_transaction #:nodoc:
|
622
629
|
self.class.disable_ddl_transaction
|
623
630
|
end
|
624
631
|
|
@@ -693,7 +700,7 @@ module ActiveRecord
|
|
693
700
|
connection.respond_to?(:reverting) && connection.reverting
|
694
701
|
end
|
695
702
|
|
696
|
-
ReversibleBlockHelper = Struct.new(:reverting) do
|
703
|
+
ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
|
697
704
|
def up
|
698
705
|
yield unless reverting
|
699
706
|
end
|
@@ -1006,7 +1013,7 @@ module ActiveRecord
|
|
1006
1013
|
end
|
1007
1014
|
end
|
1008
1015
|
|
1009
|
-
class MigrationContext
|
1016
|
+
class MigrationContext #:nodoc:
|
1010
1017
|
attr_reader :migrations_paths
|
1011
1018
|
|
1012
1019
|
def initialize(migrations_paths)
|
@@ -1165,7 +1172,7 @@ module ActiveRecord
|
|
1165
1172
|
end
|
1166
1173
|
end
|
1167
1174
|
|
1168
|
-
class Migrator
|
1175
|
+
class Migrator #:nodoc:
|
1169
1176
|
class << self
|
1170
1177
|
attr_accessor :migrations_paths
|
1171
1178
|
|
@@ -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
|
@@ -27,6 +27,16 @@ module ActiveRecord
|
|
27
27
|
def invert_transaction(args, &block)
|
28
28
|
[:transaction, args, block]
|
29
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
|
30
40
|
end
|
31
41
|
|
32
42
|
def create_table(table_name, **options)
|
@@ -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
|
#
|
@@ -165,6 +353,7 @@ module ActiveRecord
|
|
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)
|
@@ -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,7 +896,7 @@ 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
|
|
@@ -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
|
|