activerecord 6.0.0.beta1 → 6.0.0
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 +455 -9
- data/README.rdoc +3 -1
- data/lib/active_record/associations/association.rb +18 -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 +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/join_association.rb +21 -7
- data/lib/active_record/associations/join_dependency.rb +10 -9
- 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/associations/preloader.rb +11 -6
- data/lib/active_record/associations.rb +3 -2
- 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/attribute_methods.rb +3 -53
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +15 -5
- 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 +124 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -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 +108 -39
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- 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 +5 -1
- 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 +91 -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 +118 -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 +69 -118
- data/lib/active_record/connection_handling.rb +32 -16
- data/lib/active_record/core.rb +27 -20
- 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/database_configurations.rb +99 -50
- 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 +1 -1
- 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/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/migration.rb +62 -44
- 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/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +13 -13
- 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 +217 -68
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/relation.rb +184 -35
- 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 +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/scoping.rb +6 -7
- 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 +55 -45
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -1
- data/lib/arel/insert_manager.rb +3 -3
- 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/nodes.rb +2 -1
- 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/arel.rb +7 -0
- 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 +17 -13
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -4,17 +4,18 @@ module ActiveRecord
|
|
4
4
|
class TableMetadata # :nodoc:
|
5
5
|
delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
|
6
6
|
|
7
|
-
def initialize(klass, arel_table, association = nil)
|
7
|
+
def initialize(klass, arel_table, association = nil, types = klass)
|
8
8
|
@klass = klass
|
9
|
+
@types = types
|
9
10
|
@arel_table = arel_table
|
10
11
|
@association = association
|
11
12
|
end
|
12
13
|
|
13
14
|
def resolve_column_aliases(hash)
|
14
15
|
new_hash = hash.dup
|
15
|
-
hash.
|
16
|
-
if
|
17
|
-
new_hash[
|
16
|
+
hash.each_key do |key|
|
17
|
+
if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
|
18
|
+
new_hash[new_key] = new_hash.delete(key)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
new_hash
|
@@ -29,11 +30,7 @@ module ActiveRecord
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def type(column_name)
|
32
|
-
|
33
|
-
klass.type_for_attribute(column_name)
|
34
|
-
else
|
35
|
-
Type.default_value
|
36
|
-
end
|
33
|
+
types.type_for_attribute(column_name)
|
37
34
|
end
|
38
35
|
|
39
36
|
def has_column?(column_name)
|
@@ -52,13 +49,12 @@ module ActiveRecord
|
|
52
49
|
elsif association && !association.polymorphic?
|
53
50
|
association_klass = association.klass
|
54
51
|
arel_table = association_klass.arel_table.alias(table_name)
|
52
|
+
TableMetadata.new(association_klass, arel_table, association)
|
55
53
|
else
|
56
54
|
type_caster = TypeCaster::Connection.new(klass, table_name)
|
57
|
-
association_klass = nil
|
58
55
|
arel_table = Arel::Table.new(table_name, type_caster: type_caster)
|
56
|
+
TableMetadata.new(nil, arel_table, association, type_caster)
|
59
57
|
end
|
60
|
-
|
61
|
-
TableMetadata.new(association_klass, arel_table, association)
|
62
58
|
end
|
63
59
|
|
64
60
|
def polymorphic_association?
|
@@ -74,6 +70,6 @@ module ActiveRecord
|
|
74
70
|
end
|
75
71
|
|
76
72
|
private
|
77
|
-
attr_reader :klass, :arel_table, :association
|
73
|
+
attr_reader :klass, :types, :arel_table, :association
|
78
74
|
end
|
79
75
|
end
|
@@ -141,8 +141,21 @@ module ActiveRecord
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
def
|
145
|
-
|
144
|
+
def setup_initial_database_yaml
|
145
|
+
return {} unless defined?(Rails)
|
146
|
+
|
147
|
+
begin
|
148
|
+
Rails.application.config.load_database_yaml
|
149
|
+
rescue
|
150
|
+
$stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
|
151
|
+
|
152
|
+
{}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def for_each(databases)
|
157
|
+
return {} unless defined?(Rails)
|
158
|
+
|
146
159
|
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
|
147
160
|
|
148
161
|
# if this is a single database application we don't want tasks for each primary database
|
@@ -153,8 +166,22 @@ module ActiveRecord
|
|
153
166
|
end
|
154
167
|
end
|
155
168
|
|
156
|
-
def
|
157
|
-
|
169
|
+
def raise_for_multi_db(environment = env, command:)
|
170
|
+
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
|
171
|
+
|
172
|
+
if db_configs.count > 1
|
173
|
+
dbs_list = []
|
174
|
+
|
175
|
+
db_configs.each do |db|
|
176
|
+
dbs_list << "#{command}:#{db.spec_name}"
|
177
|
+
end
|
178
|
+
|
179
|
+
raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def create_current(environment = env, spec_name = nil)
|
184
|
+
each_current_configuration(environment, spec_name) { |configuration|
|
158
185
|
create configuration
|
159
186
|
}
|
160
187
|
ActiveRecord::Base.establish_connection(environment.to_sym)
|
@@ -182,6 +209,26 @@ module ActiveRecord
|
|
182
209
|
}
|
183
210
|
end
|
184
211
|
|
212
|
+
def truncate_tables(configuration)
|
213
|
+
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
|
214
|
+
conn = ActiveRecord::Base.connection
|
215
|
+
table_names = conn.tables
|
216
|
+
table_names -= [
|
217
|
+
conn.schema_migration.table_name,
|
218
|
+
InternalMetadata.table_name
|
219
|
+
]
|
220
|
+
|
221
|
+
ActiveRecord::Base.connection.truncate_tables(*table_names)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
private :truncate_tables
|
225
|
+
|
226
|
+
def truncate_all(environment = env)
|
227
|
+
ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
|
228
|
+
truncate_tables db_config.config
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
185
232
|
def migrate
|
186
233
|
check_target_version
|
187
234
|
|
@@ -198,7 +245,7 @@ module ActiveRecord
|
|
198
245
|
end
|
199
246
|
|
200
247
|
def migrate_status
|
201
|
-
unless ActiveRecord::
|
248
|
+
unless ActiveRecord::Base.connection.schema_migration.table_exists?
|
202
249
|
Kernel.abort "Schema migrations table does not exist yet."
|
203
250
|
end
|
204
251
|
|
@@ -286,10 +333,60 @@ module ActiveRecord
|
|
286
333
|
end
|
287
334
|
ActiveRecord::InternalMetadata.create_table
|
288
335
|
ActiveRecord::InternalMetadata[:environment] = environment
|
336
|
+
ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
|
289
337
|
ensure
|
290
338
|
Migration.verbose = verbose_was
|
291
339
|
end
|
292
340
|
|
341
|
+
def schema_up_to_date?(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary")
|
342
|
+
file ||= dump_filename(spec_name, format)
|
343
|
+
|
344
|
+
return true unless File.exist?(file)
|
345
|
+
|
346
|
+
ActiveRecord::Base.establish_connection(configuration)
|
347
|
+
return false unless ActiveRecord::InternalMetadata.table_exists?
|
348
|
+
ActiveRecord::InternalMetadata[:schema_sha1] == schema_sha1(file)
|
349
|
+
end
|
350
|
+
|
351
|
+
def reconstruct_from_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
|
352
|
+
file ||= dump_filename(spec_name, format)
|
353
|
+
|
354
|
+
check_schema_file(file)
|
355
|
+
|
356
|
+
ActiveRecord::Base.establish_connection(configuration)
|
357
|
+
|
358
|
+
if schema_up_to_date?(configuration, format, file, environment, spec_name)
|
359
|
+
truncate_tables(configuration)
|
360
|
+
else
|
361
|
+
purge(configuration)
|
362
|
+
load_schema(configuration, format, file, environment, spec_name)
|
363
|
+
end
|
364
|
+
rescue ActiveRecord::NoDatabaseError
|
365
|
+
create(configuration)
|
366
|
+
load_schema(configuration, format, file, environment, spec_name)
|
367
|
+
end
|
368
|
+
|
369
|
+
def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
|
370
|
+
require "active_record/schema_dumper"
|
371
|
+
filename = dump_filename(spec_name, format)
|
372
|
+
connection = ActiveRecord::Base.connection
|
373
|
+
|
374
|
+
case format
|
375
|
+
when :ruby
|
376
|
+
File.open(filename, "w:utf-8") do |file|
|
377
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
378
|
+
end
|
379
|
+
when :sql
|
380
|
+
structure_dump(configuration, filename)
|
381
|
+
if connection.schema_migration.table_exists?
|
382
|
+
File.open(filename, "a") do |f|
|
383
|
+
f.puts connection.dump_schema_information
|
384
|
+
f.print "\n"
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
293
390
|
def schema_file(format = ActiveRecord::Base.schema_format)
|
294
391
|
File.join(db_dir, schema_file_type(format))
|
295
392
|
end
|
@@ -371,12 +468,14 @@ module ActiveRecord
|
|
371
468
|
task.is_a?(String) ? task.constantize : task
|
372
469
|
end
|
373
470
|
|
374
|
-
def each_current_configuration(environment)
|
471
|
+
def each_current_configuration(environment, spec_name = nil)
|
375
472
|
environments = [environment]
|
376
473
|
environments << "test" if environment == "development"
|
377
474
|
|
378
475
|
environments.each do |env|
|
379
476
|
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
477
|
+
next if spec_name && spec_name != db_config.spec_name
|
478
|
+
|
380
479
|
yield db_config.config, db_config.spec_name, env
|
381
480
|
end
|
382
481
|
end
|
@@ -398,6 +497,10 @@ module ActiveRecord
|
|
398
497
|
def local_database?(configuration)
|
399
498
|
configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
|
400
499
|
end
|
500
|
+
|
501
|
+
def schema_sha1(file)
|
502
|
+
Digest::SHA1.hexdigest(File.read(file))
|
503
|
+
end
|
401
504
|
end
|
402
505
|
end
|
403
506
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Tasks # :nodoc:
|
5
5
|
class MySQLDatabaseTasks # :nodoc:
|
6
|
+
ER_DB_CREATE_EXISTS = 1007
|
7
|
+
|
6
8
|
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
7
9
|
|
8
10
|
def initialize(configuration)
|
@@ -14,7 +16,7 @@ module ActiveRecord
|
|
14
16
|
connection.create_database configuration["database"], creation_options
|
15
17
|
establish_connection configuration
|
16
18
|
rescue ActiveRecord::StatementInvalid => error
|
17
|
-
if error.
|
19
|
+
if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
|
18
20
|
raise DatabaseAlreadyExists
|
19
21
|
else
|
20
22
|
raise
|
@@ -8,31 +8,16 @@ module ActiveRecord
|
|
8
8
|
create_and_load_schema(i, env_name: Rails.env)
|
9
9
|
end
|
10
10
|
|
11
|
-
ActiveSupport::Testing::Parallelization.run_cleanup_hook do
|
12
|
-
drop(env_name: Rails.env)
|
13
|
-
end
|
14
|
-
|
15
11
|
def self.create_and_load_schema(i, env_name:)
|
16
12
|
old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
|
17
13
|
|
18
14
|
ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
|
19
15
|
db_config.config["database"] += "-#{i}"
|
20
|
-
ActiveRecord::Tasks::DatabaseTasks.
|
21
|
-
ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
|
16
|
+
ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
|
22
17
|
end
|
23
18
|
ensure
|
24
19
|
ActiveRecord::Base.establish_connection(Rails.env.to_sym)
|
25
20
|
ENV["VERBOSE"] = old
|
26
21
|
end
|
27
|
-
|
28
|
-
def self.drop(env_name:)
|
29
|
-
old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
|
30
|
-
|
31
|
-
ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
|
32
|
-
ActiveRecord::Tasks::DatabaseTasks.drop(db_config.config)
|
33
|
-
end
|
34
|
-
ensure
|
35
|
-
ENV["VERBOSE"] = old
|
36
|
-
end
|
37
22
|
end
|
38
23
|
end
|
@@ -122,7 +122,7 @@ module ActiveRecord
|
|
122
122
|
# Begin transactions for connections already established
|
123
123
|
@fixture_connections = enlist_fixture_connections
|
124
124
|
@fixture_connections.each do |connection|
|
125
|
-
connection.begin_transaction joinable: false
|
125
|
+
connection.begin_transaction joinable: false, _lazy: false
|
126
126
|
connection.pool.lock_thread = true if lock_threads
|
127
127
|
end
|
128
128
|
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
end
|
139
139
|
|
140
140
|
if connection && !@fixture_connections.include?(connection)
|
141
|
-
connection.begin_transaction joinable: false
|
141
|
+
connection.begin_transaction joinable: false, _lazy: false
|
142
142
|
connection.pool.lock_thread = true if lock_threads
|
143
143
|
@fixture_connections << connection
|
144
144
|
end
|
@@ -59,19 +59,26 @@ module ActiveRecord
|
|
59
59
|
attribute_names.index_with(time || current_time_from_proper_timezone)
|
60
60
|
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
timestamp_attributes_for_create
|
65
|
-
|
62
|
+
def timestamp_attributes_for_create_in_model
|
63
|
+
@timestamp_attributes_for_create_in_model ||=
|
64
|
+
(timestamp_attributes_for_create & column_names).freeze
|
65
|
+
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
def timestamp_attributes_for_update_in_model
|
68
|
+
@timestamp_attributes_for_update_in_model ||=
|
69
|
+
(timestamp_attributes_for_update & column_names).freeze
|
70
|
+
end
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
def all_timestamp_attributes_in_model
|
73
|
+
@all_timestamp_attributes_in_model ||=
|
74
|
+
(timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
|
75
|
+
end
|
74
76
|
|
77
|
+
def current_time_from_proper_timezone
|
78
|
+
default_timezone == :utc ? Time.now.utc : Time.now
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
75
82
|
def timestamp_attributes_for_create
|
76
83
|
["created_at", "created_on"]
|
77
84
|
end
|
@@ -80,8 +87,11 @@ module ActiveRecord
|
|
80
87
|
["updated_at", "updated_on"]
|
81
88
|
end
|
82
89
|
|
83
|
-
def
|
84
|
-
|
90
|
+
def reload_schema_from_cache
|
91
|
+
@timestamp_attributes_for_create_in_model = nil
|
92
|
+
@timestamp_attributes_for_update_in_model = nil
|
93
|
+
@all_timestamp_attributes_in_model = nil
|
94
|
+
super
|
85
95
|
end
|
86
96
|
end
|
87
97
|
|
@@ -101,8 +111,8 @@ module ActiveRecord
|
|
101
111
|
super
|
102
112
|
end
|
103
113
|
|
104
|
-
def _update_record
|
105
|
-
if
|
114
|
+
def _update_record
|
115
|
+
if @_touch_record && should_record_timestamps?
|
106
116
|
current_time = current_time_from_proper_timezone
|
107
117
|
|
108
118
|
timestamp_attributes_for_update_in_model.each do |column|
|
@@ -110,7 +120,13 @@ module ActiveRecord
|
|
110
120
|
_write_attribute(column, current_time)
|
111
121
|
end
|
112
122
|
end
|
113
|
-
|
123
|
+
|
124
|
+
super
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_or_update(touch: true, **)
|
128
|
+
@_touch_record = touch
|
129
|
+
super
|
114
130
|
end
|
115
131
|
|
116
132
|
def should_record_timestamps?
|
@@ -118,19 +134,19 @@ module ActiveRecord
|
|
118
134
|
end
|
119
135
|
|
120
136
|
def timestamp_attributes_for_create_in_model
|
121
|
-
self.class.
|
137
|
+
self.class.timestamp_attributes_for_create_in_model
|
122
138
|
end
|
123
139
|
|
124
140
|
def timestamp_attributes_for_update_in_model
|
125
|
-
self.class.
|
141
|
+
self.class.timestamp_attributes_for_update_in_model
|
126
142
|
end
|
127
143
|
|
128
144
|
def all_timestamp_attributes_in_model
|
129
|
-
self.class.
|
145
|
+
self.class.all_timestamp_attributes_in_model
|
130
146
|
end
|
131
147
|
|
132
148
|
def current_time_from_proper_timezone
|
133
|
-
self.class.
|
149
|
+
self.class.current_time_from_proper_timezone
|
134
150
|
end
|
135
151
|
|
136
152
|
def max_updated_column_timestamp
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# = Active Record Touch Later
|
5
|
-
module TouchLater
|
5
|
+
module TouchLater # :nodoc:
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
@@ -22,7 +22,8 @@ module ActiveRecord
|
|
22
22
|
@_touch_time = current_time_from_proper_timezone
|
23
23
|
|
24
24
|
surreptitiously_touch @_defer_touch_attrs
|
25
|
-
|
25
|
+
add_to_transaction
|
26
|
+
@_new_record_before_last_commit ||= false
|
26
27
|
|
27
28
|
# touch the parents as we are not calling the after_save callbacks
|
28
29
|
self.class.reflect_on_all_associations(:belongs_to).each do |r|
|
@@ -48,6 +49,7 @@ module ActiveRecord
|
|
48
49
|
|
49
50
|
def touch_deferred_attributes
|
50
51
|
if has_defer_touch_attrs? && persisted?
|
52
|
+
@_skip_dirty_tracking = true
|
51
53
|
touch(*@_defer_touch_attrs, time: @_touch_time)
|
52
54
|
@_defer_touch_attrs, @_touch_time = nil, nil
|
53
55
|
end
|
@@ -234,6 +234,12 @@ module ActiveRecord
|
|
234
234
|
set_callback(:commit, :after, *args, &block)
|
235
235
|
end
|
236
236
|
|
237
|
+
# Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
|
238
|
+
def after_save_commit(*args, &block)
|
239
|
+
set_options_for_callbacks!(args, on: [ :create, :update ])
|
240
|
+
set_callback(:commit, :after, *args, &block)
|
241
|
+
end
|
242
|
+
|
237
243
|
# Shortcut for <tt>after_commit :hook, on: :create</tt>.
|
238
244
|
def after_create_commit(*args, &block)
|
239
245
|
set_options_for_callbacks!(args, on: :create)
|
@@ -327,7 +333,7 @@ module ActiveRecord
|
|
327
333
|
# Ensure that it is not called if the object was never persisted (failed create),
|
328
334
|
# but call it after the commit of a destroyed object.
|
329
335
|
def committed!(should_run_callbacks: true) #:nodoc:
|
330
|
-
if should_run_callbacks
|
336
|
+
if should_run_callbacks
|
331
337
|
@_committed_already_called = true
|
332
338
|
_run_commit_without_transaction_enrollment_callbacks
|
333
339
|
_run_commit_callbacks
|
@@ -349,18 +355,6 @@ module ActiveRecord
|
|
349
355
|
clear_transaction_record_state
|
350
356
|
end
|
351
357
|
|
352
|
-
# Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
|
353
|
-
# can be called.
|
354
|
-
def add_to_transaction
|
355
|
-
if has_transactional_callbacks?
|
356
|
-
self.class.connection.add_transaction_record(self)
|
357
|
-
else
|
358
|
-
sync_with_transaction_state
|
359
|
-
set_transaction_state(self.class.connection.transaction_state)
|
360
|
-
end
|
361
|
-
remember_transaction_record_state
|
362
|
-
end
|
363
|
-
|
364
358
|
# Executes +method+ within a transaction and captures its return value as a
|
365
359
|
# status flag. If the status is true the transaction is committed, otherwise
|
366
360
|
# a ROLLBACK is issued. In any case the status flag is returned.
|
@@ -370,29 +364,40 @@ module ActiveRecord
|
|
370
364
|
def with_transaction_returning_status
|
371
365
|
status = nil
|
372
366
|
self.class.transaction do
|
373
|
-
|
367
|
+
if has_transactional_callbacks?
|
368
|
+
add_to_transaction
|
369
|
+
else
|
370
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
371
|
+
@transaction_state = self.class.connection.transaction_state
|
372
|
+
end
|
373
|
+
remember_transaction_record_state
|
374
|
+
|
374
375
|
status = yield
|
375
376
|
raise ActiveRecord::Rollback unless status
|
376
377
|
end
|
377
378
|
status
|
378
379
|
end
|
379
380
|
|
381
|
+
def trigger_transactional_callbacks? # :nodoc:
|
382
|
+
(@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
|
383
|
+
_trigger_destroy_callback && destroyed?
|
384
|
+
end
|
385
|
+
|
380
386
|
private
|
381
387
|
attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
|
382
388
|
|
383
389
|
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
384
390
|
def remember_transaction_record_state
|
385
|
-
@_start_transaction_state
|
391
|
+
@_start_transaction_state ||= {
|
386
392
|
id: id,
|
387
393
|
new_record: @new_record,
|
388
394
|
destroyed: @destroyed,
|
395
|
+
attributes: @attributes,
|
389
396
|
frozen?: frozen?,
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
end
|
397
|
+
level: 0
|
398
|
+
}
|
399
|
+
@_start_transaction_state[:level] += 1
|
394
400
|
|
395
|
-
def remember_new_record_before_last_commit
|
396
401
|
if _committed_already_called
|
397
402
|
@_new_record_before_last_commit = false
|
398
403
|
else
|
@@ -402,27 +407,32 @@ module ActiveRecord
|
|
402
407
|
|
403
408
|
# Clear the new record state and id of a record.
|
404
409
|
def clear_transaction_record_state
|
405
|
-
|
410
|
+
return unless @_start_transaction_state
|
411
|
+
@_start_transaction_state[:level] -= 1
|
406
412
|
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
|
407
413
|
end
|
408
414
|
|
409
415
|
# Force to clear the transaction record state.
|
410
416
|
def force_clear_transaction_record_state
|
411
|
-
@_start_transaction_state
|
417
|
+
@_start_transaction_state = nil
|
418
|
+
@transaction_state = nil
|
412
419
|
end
|
413
420
|
|
414
421
|
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
415
|
-
def restore_transaction_record_state(
|
416
|
-
|
417
|
-
|
418
|
-
if transaction_level < 1 || force
|
419
|
-
restore_state = @_start_transaction_state
|
420
|
-
thaw
|
422
|
+
def restore_transaction_record_state(force_restore_state = false)
|
423
|
+
if restore_state = @_start_transaction_state
|
424
|
+
if force_restore_state || restore_state[:level] <= 1
|
421
425
|
@new_record = restore_state[:new_record]
|
422
426
|
@destroyed = restore_state[:destroyed]
|
423
|
-
|
424
|
-
|
425
|
-
|
427
|
+
@attributes = restore_state[:attributes].map do |attr|
|
428
|
+
value = @attributes.fetch_value(attr.name)
|
429
|
+
attr = attr.with_value_from_user(value) if attr.value != value
|
430
|
+
attr
|
431
|
+
end
|
432
|
+
@mutations_from_database = nil
|
433
|
+
@mutations_before_last_save = nil
|
434
|
+
if @attributes.fetch_value(@primary_key) != restore_state[:id]
|
435
|
+
@attributes.write_from_user(@primary_key, restore_state[:id])
|
426
436
|
end
|
427
437
|
freeze if restore_state[:frozen?]
|
428
438
|
end
|
@@ -443,8 +453,10 @@ module ActiveRecord
|
|
443
453
|
end
|
444
454
|
end
|
445
455
|
|
446
|
-
|
447
|
-
|
456
|
+
# Add the record to the current transaction so that the #after_rollback and #after_commit
|
457
|
+
# callbacks can be called.
|
458
|
+
def add_to_transaction
|
459
|
+
self.class.connection.add_transaction_record(self)
|
448
460
|
end
|
449
461
|
|
450
462
|
def has_transactional_callbacks?
|
@@ -464,19 +476,17 @@ module ActiveRecord
|
|
464
476
|
# This method checks to see if the ActiveRecord object's state reflects
|
465
477
|
# the TransactionState, and rolls back or commits the Active Record object
|
466
478
|
# as appropriate.
|
467
|
-
#
|
468
|
-
# Since Active Record objects can be inside multiple transactions, this
|
469
|
-
# method recursively goes through the parent of the TransactionState and
|
470
|
-
# checks if the Active Record object reflects the state of the object.
|
471
479
|
def sync_with_transaction_state
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
+
if transaction_state = @transaction_state
|
481
|
+
if transaction_state.fully_committed?
|
482
|
+
force_clear_transaction_record_state
|
483
|
+
elsif transaction_state.committed?
|
484
|
+
clear_transaction_record_state
|
485
|
+
elsif transaction_state.rolledback?
|
486
|
+
force_restore_state = transaction_state.fully_rolledback?
|
487
|
+
restore_transaction_record_state(force_restore_state)
|
488
|
+
clear_transaction_record_state
|
489
|
+
end
|
480
490
|
end
|
481
491
|
end
|
482
492
|
end
|
@@ -8,21 +8,27 @@ module ActiveRecord
|
|
8
8
|
@table_name = table_name
|
9
9
|
end
|
10
10
|
|
11
|
-
def type_cast_for_database(
|
11
|
+
def type_cast_for_database(attr_name, value)
|
12
12
|
return value if value.is_a?(Arel::Nodes::BindParam)
|
13
|
-
|
14
|
-
|
13
|
+
type = type_for_attribute(attr_name)
|
14
|
+
type.serialize(value)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
delegate :connection, to: :@klass
|
17
|
+
def type_for_attribute(attr_name)
|
18
|
+
schema_cache = connection.schema_cache
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
20
|
+
if schema_cache.data_source_exists?(table_name)
|
21
|
+
column = schema_cache.columns_hash(table_name)[attr_name.to_s]
|
22
|
+
type = connection.lookup_cast_type_from_column(column) if column
|
25
23
|
end
|
24
|
+
|
25
|
+
type || Type.default_value
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate :connection, to: :@klass, private: true
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :table_name
|
26
32
|
end
|
27
33
|
end
|
28
34
|
end
|
@@ -12,7 +12,7 @@ module ActiveRecord
|
|
12
12
|
raise ArgumentError, "#{options[:scope]} is not supported format for :scope option. " \
|
13
13
|
"Pass a symbol or an array of symbols instead: `scope: :user_id`"
|
14
14
|
end
|
15
|
-
super
|
15
|
+
super
|
16
16
|
@klass = options[:class]
|
17
17
|
end
|
18
18
|
|
@@ -25,7 +25,7 @@ module ActiveRecord
|
|
25
25
|
if finder_class.primary_key
|
26
26
|
relation = relation.where.not(finder_class.primary_key => record.id_in_database)
|
27
27
|
else
|
28
|
-
raise UnknownPrimaryKey.new(finder_class, "
|
28
|
+
raise UnknownPrimaryKey.new(finder_class, "Cannot validate uniqueness for persisted record without primary key.")
|
29
29
|
end
|
30
30
|
end
|
31
31
|
relation = scope_relation(record, relation)
|
@@ -60,8 +60,8 @@ module ActiveRecord
|
|
60
60
|
comparison = relation.bind_attribute(attribute, value) do |attr, bind|
|
61
61
|
return relation.none! if bind.unboundable?
|
62
62
|
|
63
|
-
if bind.nil?
|
64
|
-
|
63
|
+
if !options.key?(:case_sensitive) || bind.nil?
|
64
|
+
klass.connection.default_uniqueness_comparison(attr, bind, klass)
|
65
65
|
elsif options[:case_sensitive]
|
66
66
|
klass.connection.case_sensitive_comparison(attr, bind)
|
67
67
|
else
|
@@ -40,6 +40,7 @@ module ActiveRecord
|
|
40
40
|
include ActiveModel::Validations
|
41
41
|
|
42
42
|
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
|
43
|
+
# The validation context can be changed by passing <tt>context: context</tt>.
|
43
44
|
# The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
|
44
45
|
# with this when the validations module is mixed in, which it is by default.
|
45
46
|
def save(options = {})
|