activerecord 6.0.0.beta2 → 6.0.2.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -2,18 +2,23 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Querying
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
QUERYING_METHODS = [
|
6
|
+
:find, :find_by, :find_by!, :take, :take!, :first, :first!, :last, :last!,
|
7
|
+
:second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
|
8
|
+
:forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
|
9
|
+
:exists?, :any?, :many?, :none?, :one?,
|
10
|
+
:first_or_create, :first_or_create!, :first_or_initialize,
|
11
|
+
:find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
|
12
|
+
:create_or_find_by, :create_or_find_by!,
|
13
|
+
:destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
14
|
+
:find_each, :find_in_batches, :in_batches,
|
15
|
+
:select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
16
|
+
:where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly, :extending, :or,
|
17
|
+
:having, :create_with, :distinct, :references, :none, :unscope, :optimizer_hints, :merge, :except, :only,
|
18
|
+
:count, :average, :minimum, :maximum, :sum, :calculate, :annotate,
|
19
|
+
:pluck, :pick, :ids
|
20
|
+
].freeze # :nodoc:
|
21
|
+
delegate(*QUERYING_METHODS, to: :all)
|
17
22
|
|
18
23
|
# Executes a custom SQL query against your database and returns all the results. The results will
|
19
24
|
# be returned as an array, with the requested columns encapsulated as attributes of the model you call
|
@@ -134,7 +134,6 @@ end_error
|
|
134
134
|
|
135
135
|
cache = YAML.load(File.read(filename))
|
136
136
|
if cache.version == current_version
|
137
|
-
connection.schema_cache = cache
|
138
137
|
connection_pool.schema_cache = cache.dup
|
139
138
|
else
|
140
139
|
warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{current_version}, but the one in the cache is #{cache.version}."
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "active_record"
|
4
4
|
|
5
|
+
databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
|
6
|
+
|
5
7
|
db_namespace = namespace :db do
|
6
8
|
desc "Set the environment value for the database"
|
7
9
|
task "environment:set" => :load_config do
|
@@ -23,7 +25,7 @@ db_namespace = namespace :db do
|
|
23
25
|
ActiveRecord::Tasks::DatabaseTasks.create_all
|
24
26
|
end
|
25
27
|
|
26
|
-
ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
|
28
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
27
29
|
desc "Create #{spec_name} database for current environment"
|
28
30
|
task spec_name => :load_config do
|
29
31
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
@@ -42,7 +44,7 @@ db_namespace = namespace :db do
|
|
42
44
|
ActiveRecord::Tasks::DatabaseTasks.drop_all
|
43
45
|
end
|
44
46
|
|
45
|
-
ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
|
47
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
46
48
|
desc "Drop #{spec_name} database for current environment"
|
47
49
|
task spec_name => [:load_config, :check_protected_environments] do
|
48
50
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
@@ -66,6 +68,11 @@ db_namespace = namespace :db do
|
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
71
|
+
# desc "Truncates tables of each database for current environment"
|
72
|
+
task truncate_all: [:load_config, :check_protected_environments] do
|
73
|
+
ActiveRecord::Tasks::DatabaseTasks.truncate_all
|
74
|
+
end
|
75
|
+
|
69
76
|
# desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:purge:all to purge all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
|
70
77
|
task purge: [:load_config, :check_protected_environments] do
|
71
78
|
ActiveRecord::Tasks::DatabaseTasks.purge_current
|
@@ -73,11 +80,14 @@ db_namespace = namespace :db do
|
|
73
80
|
|
74
81
|
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
|
75
82
|
task migrate: :load_config do
|
76
|
-
ActiveRecord::Base.
|
83
|
+
original_config = ActiveRecord::Base.connection_config
|
84
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
77
85
|
ActiveRecord::Base.establish_connection(db_config.config)
|
78
86
|
ActiveRecord::Tasks::DatabaseTasks.migrate
|
79
87
|
end
|
80
88
|
db_namespace["_dump"].invoke
|
89
|
+
ensure
|
90
|
+
ActiveRecord::Base.establish_connection(original_config)
|
81
91
|
end
|
82
92
|
|
83
93
|
# IMPORTANT: This task won't dump the schema if ActiveRecord::Base.dump_schema_after_migration is set to false
|
@@ -96,7 +106,7 @@ db_namespace = namespace :db do
|
|
96
106
|
end
|
97
107
|
|
98
108
|
namespace :migrate do
|
99
|
-
ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
|
109
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
100
110
|
desc "Migrate #{spec_name} database for current environment"
|
101
111
|
task spec_name => :load_config do
|
102
112
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
@@ -123,6 +133,8 @@ db_namespace = namespace :db do
|
|
123
133
|
|
124
134
|
# desc 'Runs the "up" for a given migration VERSION.'
|
125
135
|
task up: :load_config do
|
136
|
+
ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:up")
|
137
|
+
|
126
138
|
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
|
127
139
|
|
128
140
|
ActiveRecord::Tasks::DatabaseTasks.check_target_version
|
@@ -134,8 +146,29 @@ db_namespace = namespace :db do
|
|
134
146
|
db_namespace["_dump"].invoke
|
135
147
|
end
|
136
148
|
|
149
|
+
namespace :up do
|
150
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
151
|
+
task spec_name => :load_config do
|
152
|
+
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
|
153
|
+
|
154
|
+
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
155
|
+
|
156
|
+
ActiveRecord::Base.establish_connection(db_config.config)
|
157
|
+
ActiveRecord::Tasks::DatabaseTasks.check_target_version
|
158
|
+
ActiveRecord::Base.connection.migration_context.run(
|
159
|
+
:up,
|
160
|
+
ActiveRecord::Tasks::DatabaseTasks.target_version
|
161
|
+
)
|
162
|
+
|
163
|
+
db_namespace["_dump"].invoke
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
137
168
|
# desc 'Runs the "down" for a given migration VERSION.'
|
138
169
|
task down: :load_config do
|
170
|
+
ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:down")
|
171
|
+
|
139
172
|
raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty?
|
140
173
|
|
141
174
|
ActiveRecord::Tasks::DatabaseTasks.check_target_version
|
@@ -147,16 +180,35 @@ db_namespace = namespace :db do
|
|
147
180
|
db_namespace["_dump"].invoke
|
148
181
|
end
|
149
182
|
|
183
|
+
namespace :down do
|
184
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
185
|
+
task spec_name => :load_config do
|
186
|
+
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
|
187
|
+
|
188
|
+
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
189
|
+
|
190
|
+
ActiveRecord::Base.establish_connection(db_config.config)
|
191
|
+
ActiveRecord::Tasks::DatabaseTasks.check_target_version
|
192
|
+
ActiveRecord::Base.connection.migration_context.run(
|
193
|
+
:down,
|
194
|
+
ActiveRecord::Tasks::DatabaseTasks.target_version
|
195
|
+
)
|
196
|
+
|
197
|
+
db_namespace["_dump"].invoke
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
150
202
|
desc "Display status of migrations"
|
151
203
|
task status: :load_config do
|
152
|
-
ActiveRecord::Base.configurations.configs_for(env_name:
|
204
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
153
205
|
ActiveRecord::Base.establish_connection(db_config.config)
|
154
206
|
ActiveRecord::Tasks::DatabaseTasks.migrate_status
|
155
207
|
end
|
156
208
|
end
|
157
209
|
|
158
210
|
namespace :status do
|
159
|
-
ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
|
211
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
160
212
|
desc "Display status of migrations for #{spec_name} database"
|
161
213
|
task spec_name => :load_config do
|
162
214
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
@@ -203,7 +255,11 @@ db_namespace = namespace :db do
|
|
203
255
|
|
204
256
|
# desc "Raises an error if there are pending migrations"
|
205
257
|
task abort_if_pending_migrations: :load_config do
|
206
|
-
pending_migrations = ActiveRecord::Base.
|
258
|
+
pending_migrations = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).flat_map do |db_config|
|
259
|
+
ActiveRecord::Base.establish_connection(db_config.config)
|
260
|
+
|
261
|
+
ActiveRecord::Base.connection.migration_context.open.pending_migrations
|
262
|
+
end
|
207
263
|
|
208
264
|
if pending_migrations.any?
|
209
265
|
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
|
@@ -212,17 +268,74 @@ db_namespace = namespace :db do
|
|
212
268
|
end
|
213
269
|
abort %{Run `rails db:migrate` to update your database then try again.}
|
214
270
|
end
|
271
|
+
ensure
|
272
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Tasks::DatabaseTasks.env.to_sym)
|
273
|
+
end
|
274
|
+
|
275
|
+
namespace :abort_if_pending_migrations do
|
276
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
|
277
|
+
# desc "Raises an error if there are pending migrations for #{spec_name} database"
|
278
|
+
task spec_name => :load_config do
|
279
|
+
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
|
280
|
+
ActiveRecord::Base.establish_connection(db_config.config)
|
281
|
+
|
282
|
+
pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
|
283
|
+
|
284
|
+
if pending_migrations.any?
|
285
|
+
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
|
286
|
+
pending_migrations.each do |pending_migration|
|
287
|
+
puts " %4d %s" % [pending_migration.version, pending_migration.name]
|
288
|
+
end
|
289
|
+
abort %{Run `rails db:migrate:#{spec_name}` to update your database then try again.}
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
215
293
|
end
|
216
294
|
|
217
295
|
desc "Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)"
|
218
296
|
task setup: ["db:schema:load_if_ruby", "db:structure:load_if_sql", :seed]
|
219
297
|
|
298
|
+
desc "Runs setup if database does not exist, or runs migrations if it does"
|
299
|
+
task prepare: :load_config do
|
300
|
+
seed = false
|
301
|
+
|
302
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
303
|
+
ActiveRecord::Base.establish_connection(db_config.config)
|
304
|
+
|
305
|
+
# Skipped when no database
|
306
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
307
|
+
if ActiveRecord::Base.dump_schema_after_migration
|
308
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name)
|
309
|
+
end
|
310
|
+
|
311
|
+
rescue ActiveRecord::NoDatabaseError
|
312
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, db_config.spec_name)
|
313
|
+
ActiveRecord::Tasks::DatabaseTasks.load_schema(
|
314
|
+
db_config.config,
|
315
|
+
ActiveRecord::Base.schema_format,
|
316
|
+
nil,
|
317
|
+
db_config.env_name,
|
318
|
+
db_config.spec_name
|
319
|
+
)
|
320
|
+
|
321
|
+
seed = true
|
322
|
+
end
|
323
|
+
|
324
|
+
ActiveRecord::Base.establish_connection
|
325
|
+
ActiveRecord::Tasks::DatabaseTasks.load_seed if seed
|
326
|
+
end
|
327
|
+
|
220
328
|
desc "Loads the seed data from db/seeds.rb"
|
221
329
|
task seed: :load_config do
|
222
330
|
db_namespace["abort_if_pending_migrations"].invoke
|
223
331
|
ActiveRecord::Tasks::DatabaseTasks.load_seed
|
224
332
|
end
|
225
333
|
|
334
|
+
namespace :seed do
|
335
|
+
desc "Truncates tables of each database for current environment and loads the seeds"
|
336
|
+
task replant: [:load_config, :truncate_all, :seed]
|
337
|
+
end
|
338
|
+
|
226
339
|
namespace :fixtures do
|
227
340
|
desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
228
341
|
task load: :load_config do
|
@@ -274,13 +387,9 @@ db_namespace = namespace :db do
|
|
274
387
|
namespace :schema do
|
275
388
|
desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record"
|
276
389
|
task dump: :load_config do
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
File.open(filename, "w:utf-8") do |file|
|
281
|
-
ActiveRecord::Base.establish_connection(db_config.config)
|
282
|
-
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
283
|
-
end
|
390
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
391
|
+
ActiveRecord::Base.establish_connection(db_config.config)
|
392
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :ruby, db_config.spec_name)
|
284
393
|
end
|
285
394
|
|
286
395
|
db_namespace["schema:dump"].reenable
|
@@ -298,7 +407,7 @@ db_namespace = namespace :db do
|
|
298
407
|
namespace :cache do
|
299
408
|
desc "Creates a db/schema_cache.yml file."
|
300
409
|
task dump: :load_config do
|
301
|
-
ActiveRecord::Base.configurations.configs_for(env_name:
|
410
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
302
411
|
ActiveRecord::Base.establish_connection(db_config.config)
|
303
412
|
filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config.spec_name)
|
304
413
|
ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(
|
@@ -310,7 +419,7 @@ db_namespace = namespace :db do
|
|
310
419
|
|
311
420
|
desc "Clears a db/schema_cache.yml file."
|
312
421
|
task clear: :load_config do
|
313
|
-
ActiveRecord::Base.configurations.configs_for(env_name:
|
422
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
314
423
|
filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config.spec_name)
|
315
424
|
rm_f filename, verbose: false
|
316
425
|
end
|
@@ -321,16 +430,9 @@ db_namespace = namespace :db do
|
|
321
430
|
namespace :structure do
|
322
431
|
desc "Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql"
|
323
432
|
task dump: :load_config do
|
324
|
-
ActiveRecord::Base.configurations.configs_for(env_name:
|
433
|
+
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
325
434
|
ActiveRecord::Base.establish_connection(db_config.config)
|
326
|
-
|
327
|
-
ActiveRecord::Tasks::DatabaseTasks.structure_dump(db_config.config, filename)
|
328
|
-
if ActiveRecord::SchemaMigration.table_exists?
|
329
|
-
File.open(filename, "a") do |f|
|
330
|
-
f.puts ActiveRecord::Base.connection.dump_schema_information
|
331
|
-
f.print "\n"
|
332
|
-
end
|
333
|
-
end
|
435
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :sql, db_config.spec_name)
|
334
436
|
end
|
335
437
|
|
336
438
|
db_namespace["structure:dump"].reenable
|
@@ -21,12 +21,12 @@ module ActiveRecord
|
|
21
21
|
|
22
22
|
def add_reflection(ar, name, reflection)
|
23
23
|
ar.clear_reflections_cache
|
24
|
-
name = name.to_s
|
24
|
+
name = -name.to_s
|
25
25
|
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
26
26
|
end
|
27
27
|
|
28
28
|
def add_aggregate_reflection(ar, name, reflection)
|
29
|
-
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
29
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
@@ -477,7 +477,7 @@ module ActiveRecord
|
|
477
477
|
def check_preloadable!
|
478
478
|
return unless scope
|
479
479
|
|
480
|
-
|
480
|
+
unless scope.arity == 0
|
481
481
|
raise ArgumentError, <<-MSG.squish
|
482
482
|
The association scope '#{name}' is instance dependent (the scope
|
483
483
|
block takes an argument). Preloading instance dependent scopes is
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class Relation
|
6
6
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
7
7
|
:order, :joins, :left_outer_joins, :references,
|
8
|
-
:extending, :unscope]
|
8
|
+
:extending, :unscope, :optimizer_hints, :annotate]
|
9
9
|
|
10
10
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
11
11
|
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
@@ -197,6 +197,10 @@ module ActiveRecord
|
|
197
197
|
# if a DELETE between those two statements is run by another client. But for most applications,
|
198
198
|
# that's a significantly less likely condition to hit.
|
199
199
|
# * It relies on exception handling to handle control flow, which may be marginally slower.
|
200
|
+
# * The primary key may auto-increment on each create, even if it fails. This can accelerate
|
201
|
+
# the problem of running out of integers, if the underlying table is still stuck on a primary
|
202
|
+
# key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
|
203
|
+
# to this problem).
|
200
204
|
#
|
201
205
|
# This method will return a record if all given attributes are covered by unique constraints
|
202
206
|
# (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
|
@@ -287,31 +291,99 @@ module ActiveRecord
|
|
287
291
|
limit_value ? records.many? : size > 1
|
288
292
|
end
|
289
293
|
|
290
|
-
# Returns a cache key that can be used to identify
|
291
|
-
#
|
292
|
-
# the number of records matched by the query and a timestamp of the last
|
293
|
-
# updated record. When a new record comes to match the query, or any of
|
294
|
-
# the existing records is updated or deleted, the cache key changes.
|
294
|
+
# Returns a stable cache key that can be used to identify this query.
|
295
|
+
# The cache key is built with a fingerprint of the SQL query.
|
295
296
|
#
|
296
|
-
#
|
297
|
-
#
|
297
|
+
# Product.where("name like ?", "%Cosmic Encounter%").cache_key
|
298
|
+
# # => "products/query-1850ab3d302391b85b8693e941286659"
|
298
299
|
#
|
299
|
-
# If
|
300
|
-
#
|
300
|
+
# If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
|
301
|
+
# in Rails 6.0 and earlier, the cache key will also include a version.
|
301
302
|
#
|
302
|
-
#
|
303
|
+
# ActiveRecord::Base.collection_cache_versioning = false
|
304
|
+
# Product.where("name like ?", "%Cosmic Encounter%").cache_key
|
305
|
+
# # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
|
303
306
|
#
|
304
307
|
# You can also pass a custom timestamp column to fetch the timestamp of the
|
305
308
|
# last updated record.
|
306
309
|
#
|
307
310
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
308
|
-
#
|
309
|
-
# You can customize the strategy to generate the key on a per model basis
|
310
|
-
# overriding ActiveRecord::Base#collection_cache_key.
|
311
311
|
def cache_key(timestamp_column = :updated_at)
|
312
312
|
@cache_keys ||= {}
|
313
|
-
@cache_keys[timestamp_column] ||=
|
313
|
+
@cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
|
314
|
+
end
|
315
|
+
|
316
|
+
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
|
317
|
+
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
318
|
+
key = "#{klass.model_name.cache_key}/query-#{query_signature}"
|
319
|
+
|
320
|
+
if cache_version(timestamp_column)
|
321
|
+
key
|
322
|
+
else
|
323
|
+
"#{key}-#{compute_cache_version(timestamp_column)}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
private :compute_cache_key
|
327
|
+
|
328
|
+
# Returns a cache version that can be used together with the cache key to form
|
329
|
+
# a recyclable caching scheme. The cache version is built with the number of records
|
330
|
+
# matching the query, and the timestamp of the last updated record. When a new record
|
331
|
+
# comes to match the query, or any of the existing records is updated or deleted,
|
332
|
+
# the cache version changes.
|
333
|
+
#
|
334
|
+
# If the collection is loaded, the method will iterate through the records
|
335
|
+
# to generate the timestamp, otherwise it will trigger one SQL query like:
|
336
|
+
#
|
337
|
+
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
338
|
+
def cache_version(timestamp_column = :updated_at)
|
339
|
+
if collection_cache_versioning
|
340
|
+
@cache_versions ||= {}
|
341
|
+
@cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def compute_cache_version(timestamp_column) # :nodoc:
|
346
|
+
if loaded? || distinct_value
|
347
|
+
size = records.size
|
348
|
+
if size > 0
|
349
|
+
timestamp = max_by(×tamp_column)._read_attribute(timestamp_column)
|
350
|
+
end
|
351
|
+
else
|
352
|
+
collection = eager_loading? ? apply_join_dependency : self
|
353
|
+
|
354
|
+
column = connection.visitor.compile(arel_attribute(timestamp_column))
|
355
|
+
select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
|
356
|
+
|
357
|
+
if collection.has_limit_or_offset?
|
358
|
+
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
359
|
+
subquery_alias = "subquery_for_cache_key"
|
360
|
+
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
361
|
+
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
362
|
+
else
|
363
|
+
query = collection.unscope(:order)
|
364
|
+
query.select_values = [select_values % column]
|
365
|
+
arel = query.arel
|
366
|
+
end
|
367
|
+
|
368
|
+
result = connection.select_one(arel, nil)
|
369
|
+
|
370
|
+
if result
|
371
|
+
column_type = klass.type_for_attribute(timestamp_column)
|
372
|
+
timestamp = column_type.deserialize(result["timestamp"])
|
373
|
+
size = result["size"]
|
374
|
+
else
|
375
|
+
timestamp = nil
|
376
|
+
size = 0
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
if timestamp
|
381
|
+
"#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
|
382
|
+
else
|
383
|
+
"#{size}"
|
384
|
+
end
|
314
385
|
end
|
386
|
+
private :compute_cache_version
|
315
387
|
|
316
388
|
# Scope all queries to the current scope.
|
317
389
|
#
|
@@ -338,6 +410,8 @@ module ActiveRecord
|
|
338
410
|
# trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
|
339
411
|
# Active Record's normal type casting and serialization.
|
340
412
|
#
|
413
|
+
# Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
|
414
|
+
#
|
341
415
|
# ==== Parameters
|
342
416
|
#
|
343
417
|
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
|
@@ -412,10 +486,10 @@ module ActiveRecord
|
|
412
486
|
update_all updates
|
413
487
|
end
|
414
488
|
|
415
|
-
# Touches all records in the current relation without instantiating records first with the updated_at
|
489
|
+
# Touches all records in the current relation without instantiating records first with the +updated_at+/+updated_on+ attributes
|
416
490
|
# set to the current time or the time specified.
|
417
491
|
# This method can be passed attribute names and an optional time argument.
|
418
|
-
# If attribute names are passed, they are updated along with updated_at
|
492
|
+
# If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
|
419
493
|
# If no time argument is passed, the current time is used as default.
|
420
494
|
#
|
421
495
|
# === Examples
|
@@ -479,8 +553,8 @@ module ActiveRecord
|
|
479
553
|
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
|
480
554
|
def delete_all
|
481
555
|
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
|
482
|
-
value =
|
483
|
-
|
556
|
+
value = @values[method]
|
557
|
+
method == :distinct ? value : value&.any?
|
484
558
|
end
|
485
559
|
if invalid_methods.any?
|
486
560
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
@@ -555,6 +629,7 @@ module ActiveRecord
|
|
555
629
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
556
630
|
@records = [].freeze
|
557
631
|
@offsets = {}
|
632
|
+
@take = nil
|
558
633
|
self
|
559
634
|
end
|
560
635
|
|
@@ -670,6 +745,10 @@ module ActiveRecord
|
|
670
745
|
@loaded = true
|
671
746
|
end
|
672
747
|
|
748
|
+
def null_relation? # :nodoc:
|
749
|
+
is_a?(NullRelation)
|
750
|
+
end
|
751
|
+
|
673
752
|
private
|
674
753
|
def already_in_scope?
|
675
754
|
@delegate_to_klass && begin
|
@@ -719,7 +798,7 @@ module ActiveRecord
|
|
719
798
|
@records =
|
720
799
|
if eager_loading?
|
721
800
|
apply_join_dependency do |relation, join_dependency|
|
722
|
-
if
|
801
|
+
if relation.null_relation?
|
723
802
|
[]
|
724
803
|
else
|
725
804
|
relation = join_dependency.apply_column_aliases(relation)
|