activerecord 6.0.0.rc2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97bc72165e9eb1685576780f439be5be8894b7c589cc890d8c6340b8125de0a4
4
- data.tar.gz: 74d58cd2f64976ef8faa42a02e94e6d9c9356fce3f0f2c219f7377a09712e7a4
3
+ metadata.gz: ed17e948626d108075b2951ac71abbac3075ef55b055abe64e2b352e91755fd9
4
+ data.tar.gz: 2c031642e021041f9fdf278888b580fd446879008a23f12d33c8b33aafbddc59
5
5
  SHA512:
6
- metadata.gz: 5306d17e736a64c87fc0115299b055c58343df4d18a53d532b09e570d6cc799abf84b4149deef9c03f56563ed23ee563cf57d03fc6c65529e9a6aa0324ca1614
7
- data.tar.gz: 6fe8094ad7c0d0433c23691e372417124bddf17659c4bcb64b0bca5af0513d2c9b8599cfe0c244d28649529ab64b99c4a74b638bdf5ab9b4932c276a287d1e78
6
+ metadata.gz: 6b208a5cbee77c2a057649b762a92ec4ca5be3b8fbd1b2123caf485579abbc602bcb2aee455a503b74f33efa9ce364ab644dc8958b9e1edd94fee3397bf17cc8
7
+ data.tar.gz: 751ecf72279d478887665f6a3efc5ad366b70284fb3f4ef6d2f3b072faf7c325be660116a7d3ac9427ac44dcf6f8c345e145921692d0465e24a70f1c2ca3b1bd
@@ -1,3 +1,28 @@
1
+ ## Rails 6.0.0 (August 16, 2019) ##
2
+
3
+ * Preserve user supplied joins order as much as possible.
4
+
5
+ Fixes #36761, #34328, #24281, #12953.
6
+
7
+ *Ryuta Kamizono*
8
+
9
+ * Make the DATABASE_URL env variable only affect the primary connection. Add new env variables for multiple databases.
10
+
11
+ *John Crepezzi*, *Eileen Uchitelle*
12
+
13
+ * Add a warning for enum elements with 'not_' prefix.
14
+
15
+ class Foo
16
+ enum status: [:sent, :not_sent]
17
+ end
18
+
19
+ *Edu Depetris*
20
+
21
+ * Make currency symbols optional for money column type in PostgreSQL
22
+
23
+ *Joel Schneider*
24
+
25
+
1
26
  ## Rails 6.0.0.rc2 (July 22, 2019) ##
2
27
 
3
28
  * Add database_exists? method to connection adapters to check if a database exists.
@@ -386,7 +411,7 @@
386
411
  `GET` and `HEAD` requests will read from the replica unless there was
387
412
  a write in the last 2 seconds, otherwise they will read from the primary.
388
413
  Non-get requests will always write to the primary. The middleware accepts
389
- an argument for a Resolver class and a Operations class where you are able
414
+ an argument for a Resolver class and an Operations class where you are able
390
415
  to change how the auto-switcher works to be most beneficial for your
391
416
  application.
392
417
 
@@ -820,7 +845,7 @@
820
845
  *Darwin Wu*
821
846
 
822
847
  * Configuration item `config.filter_parameters` could also filter out
823
- sensitive values of database columns when call `#inspect`.
848
+ sensitive values of database columns when calling `#inspect`.
824
849
  We also added `ActiveRecord::Base::filter_attributes`/`=` in order to
825
850
  specify sensitive attributes to specific model.
826
851
 
@@ -63,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
63
63
 
64
64
  def middle_reflection(join_model)
65
65
  middle_name = [lhs_model.name.downcase.pluralize,
66
- association_name].join("_").gsub("::", "_").to_sym
66
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
67
67
  middle_options = middle_options join_model
68
68
 
69
69
  HasMany.create_reflection(lhs_model,
@@ -304,7 +304,7 @@ module ActiveRecord
304
304
  def validate_single_association(reflection)
305
305
  association = association_instance_get(reflection.name)
306
306
  record = association && association.reader
307
- association_valid?(reflection, record) if record
307
+ association_valid?(reflection, record) if record && record.changed_for_autosave?
308
308
  end
309
309
 
310
310
  # Validate the associated records if <tt>:validate</tt> or
@@ -1005,28 +1005,33 @@ module ActiveRecord
1005
1005
  end
1006
1006
  end
1007
1007
 
1008
- attr_reader :prevent_writes
1009
-
1010
1008
  def initialize
1011
1009
  # These caches are keyed by spec.name (ConnectionSpecification#name).
1012
1010
  @owner_to_pool = ConnectionHandler.create_owner_to_pool
1013
- @prevent_writes = false
1014
1011
 
1015
1012
  # Backup finalizer: if the forked child never needed a pool, the above
1016
1013
  # early discard has not occurred
1017
1014
  ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
1018
1015
  end
1019
1016
 
1017
+ def prevent_writes # :nodoc:
1018
+ Thread.current[:prevent_writes]
1019
+ end
1020
+
1021
+ def prevent_writes=(prevent_writes) # :nodoc:
1022
+ Thread.current[:prevent_writes] = prevent_writes
1023
+ end
1024
+
1020
1025
  # Prevent writing to the database regardless of role.
1021
1026
  #
1022
1027
  # In some cases you may want to prevent writes to the database
1023
1028
  # even if you are on a database that can write. `while_preventing_writes`
1024
1029
  # will prevent writes to the database for the duration of the block.
1025
- def while_preventing_writes
1026
- original, @prevent_writes = @prevent_writes, true
1030
+ def while_preventing_writes(enabled = true)
1031
+ original, self.prevent_writes = self.prevent_writes, enabled
1027
1032
  yield
1028
1033
  ensure
1029
- @prevent_writes = original
1034
+ self.prevent_writes = original
1030
1035
  end
1031
1036
 
1032
1037
  def connection_pool_list
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  def index_exists?(table_name, column_name, options = {})
102
102
  column_names = Array(column_name).map(&:to_s)
103
103
  checks = []
104
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
105
105
  checks << lambda { |i| i.unique } if options[:unique]
106
106
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
107
 
@@ -11,6 +11,7 @@ require "arel/collectors/bind"
11
11
  require "arel/collectors/composite"
12
12
  require "arel/collectors/sql_string"
13
13
  require "arel/collectors/substitute_binds"
14
+ require "concurrent/atomic/thread_local_var"
14
15
 
15
16
  module ActiveRecord
16
17
  module ConnectionAdapters # :nodoc:
@@ -78,7 +79,7 @@ module ActiveRecord
78
79
  SIMPLE_INT = /\A\d+\z/
79
80
 
80
81
  attr_accessor :pool
81
- attr_reader :visitor, :owner, :logger, :lock, :prepared_statements
82
+ attr_reader :visitor, :owner, :logger, :lock
82
83
  alias :in_use? :owner
83
84
 
84
85
  set_callback :checkin, :after, :enable_lazy_transactions!
@@ -129,10 +130,10 @@ module ActiveRecord
129
130
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
130
131
 
131
132
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
132
- @prepared_statements = true
133
+ @prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
133
134
  @visitor.extend(DetermineIfPreparableVisitor)
134
135
  else
135
- @prepared_statements = false
136
+ @prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
136
137
  end
137
138
 
138
139
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
@@ -175,6 +176,10 @@ module ActiveRecord
175
176
  end
176
177
  end
177
178
 
179
+ def prepared_statements
180
+ @prepared_statement_status.value
181
+ end
182
+
178
183
  class Version
179
184
  include Comparable
180
185
 
@@ -259,10 +264,7 @@ module ActiveRecord
259
264
  end
260
265
 
261
266
  def unprepared_statement
262
- old_prepared_statements, @prepared_statements = @prepared_statements, false
263
- yield
264
- ensure
265
- @prepared_statements = old_prepared_statements
267
+ @prepared_statement_status.bind(false) { yield }
266
268
  end
267
269
 
268
270
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -222,7 +222,7 @@ module ActiveRecord
222
222
  when Hash
223
223
  resolve_hash_connection config_or_env
224
224
  else
225
- resolve_connection config_or_env
225
+ raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config_or_env.inspect}"
226
226
  end
227
227
  end
228
228
 
@@ -39,8 +39,8 @@ module ActiveRecord
39
39
  include MySQL::DatabaseStatements
40
40
 
41
41
  def initialize(connection, logger, connection_options, config)
42
- super
43
- @prepared_statements = false unless config.key?(:prepared_statements)
42
+ superclass_config = config.reverse_merge(prepared_statements: false)
43
+ super(connection, logger, connection_options, superclass_config)
44
44
  configure_connection
45
45
  end
46
46
 
@@ -26,9 +26,9 @@ module ActiveRecord
26
26
 
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
- when /^-?\D+[\d,]+\.\d{2}$/ # (1)
29
+ when /^-?\D*[\d,]+\.\d{2}$/ # (1)
30
30
  value.gsub!(/[^-\d.]/, "")
31
- when /^-?\D+[\d.]+,\d{2}$/ # (2)
31
+ when /^-?\D*[\d.]+,\d{2}$/ # (2)
32
32
  value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
33
33
  end
34
34
 
@@ -9,6 +9,8 @@ module ActiveRecord
9
9
  # objects (either a HashConfig or UrlConfig) that are constructed from the
10
10
  # application's database configuration hash or URL string.
11
11
  class DatabaseConfigurations
12
+ class InvalidConfigurationError < StandardError; end
13
+
12
14
  attr_reader :configurations
13
15
  delegate :any?, to: :configurations
14
16
 
@@ -91,6 +93,19 @@ module ActiveRecord
91
93
  end
92
94
  alias :blank? :empty?
93
95
 
96
+ def each
97
+ throw_getter_deprecation(:each)
98
+ configurations.each { |config|
99
+ yield [config.env_name, config.config]
100
+ }
101
+ end
102
+
103
+ def first
104
+ throw_getter_deprecation(:first)
105
+ config = configurations.first
106
+ [config.env_name, config.config]
107
+ end
108
+
94
109
  private
95
110
  def env_with_configs(env = nil)
96
111
  if env
@@ -104,34 +119,48 @@ module ActiveRecord
104
119
  return configs.configurations if configs.is_a?(DatabaseConfigurations)
105
120
  return configs if configs.is_a?(Array)
106
121
 
107
- build_db_config = configs.each_pair.flat_map do |env_name, config|
108
- walk_configs(env_name.to_s, "primary", config)
109
- end.flatten.compact
122
+ db_configs = configs.flat_map do |env_name, config|
123
+ if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
124
+ walk_configs(env_name.to_s, config)
125
+ else
126
+ build_db_config_from_raw_config(env_name.to_s, "primary", config)
127
+ end
128
+ end
110
129
 
111
- if url = ENV["DATABASE_URL"]
112
- build_url_config(url, build_db_config)
113
- else
114
- build_db_config
130
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
131
+
132
+ unless db_configs.find(&:for_current_env?)
133
+ db_configs << environment_url_config(current_env, "primary", {})
134
+ end
135
+
136
+ merge_db_environment_variables(current_env, db_configs.compact)
137
+ end
138
+
139
+ def walk_configs(env_name, config)
140
+ config.map do |spec_name, sub_config|
141
+ build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
115
142
  end
116
143
  end
117
144
 
118
- def walk_configs(env_name, spec_name, config)
145
+ def build_db_config_from_raw_config(env_name, spec_name, config)
119
146
  case config
120
147
  when String
121
148
  build_db_config_from_string(env_name, spec_name, config)
122
149
  when Hash
123
150
  build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
151
+ else
152
+ raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
124
153
  end
125
154
  end
126
155
 
127
156
  def build_db_config_from_string(env_name, spec_name, config)
128
157
  url = config
129
158
  uri = URI.parse(url)
130
- if uri.try(:scheme)
159
+ if uri.scheme
131
160
  ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
161
+ else
162
+ raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
132
163
  end
133
- rescue URI::InvalidURIError
134
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
135
164
  end
136
165
 
137
166
  def build_db_config_from_hash(env_name, spec_name, config)
@@ -141,36 +170,36 @@ module ActiveRecord
141
170
  config_without_url.delete "url"
142
171
 
143
172
  ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
144
- elsif config["database"] || config["adapter"] || ENV["DATABASE_URL"]
145
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
146
173
  else
147
- config.each_pair.map do |sub_spec_name, sub_config|
148
- walk_configs(env_name, sub_spec_name, sub_config)
149
- end
174
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
150
175
  end
151
176
  end
152
177
 
153
- def build_url_config(url, configs)
154
- env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
178
+ def merge_db_environment_variables(current_env, configs)
179
+ configs.map do |config|
180
+ next config if config.url_config? || config.env_name != current_env
155
181
 
156
- if configs.find(&:for_current_env?)
157
- configs.map do |config|
158
- if config.url_config?
159
- config
160
- else
161
- ActiveRecord::DatabaseConfigurations::UrlConfig.new(config.env_name, config.spec_name, url, config.config)
162
- end
163
- end
164
- else
165
- configs + [ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, "primary", url)]
182
+ url_config = environment_url_config(current_env, config.spec_name, config.config)
183
+ url_config || config
166
184
  end
167
185
  end
168
186
 
187
+ def environment_url_config(env, spec_name, config)
188
+ url = environment_value_for(spec_name)
189
+ return unless url
190
+
191
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
192
+ end
193
+
194
+ def environment_value_for(spec_name)
195
+ spec_env_key = "#{spec_name.upcase}_DATABASE_URL"
196
+ url = ENV[spec_env_key]
197
+ url ||= ENV["DATABASE_URL"] if spec_name == "primary"
198
+ url
199
+ end
200
+
169
201
  def method_missing(method, *args, &blk)
170
202
  case method
171
- when :each, :first
172
- throw_getter_deprecation(method)
173
- configurations.send(method, *args, &blk)
174
203
  when :fetch
175
204
  throw_getter_deprecation(method)
176
205
  configs_for(env_name: args.first)
@@ -200,6 +200,8 @@ module ActiveRecord
200
200
  # scope :active, -> { where(status: 0) }
201
201
  # scope :not_active, -> { where.not(status: 0) }
202
202
  if enum_scopes != false
203
+ klass.send(:detect_negative_condition!, value_method_name)
204
+
203
205
  klass.send(:detect_enum_conflict!, name, value_method_name, true)
204
206
  klass.scope value_method_name, -> { where(attr => value) }
205
207
 
@@ -261,5 +263,12 @@ module ActiveRecord
261
263
  source: source
262
264
  }
263
265
  end
266
+
267
+ def detect_negative_condition!(method_name)
268
+ if method_name.start_with?("not_") && logger
269
+ logger.warn "An enum element in #{self.name} uses the prefix 'not_'." \
270
+ " This will cause a conflict with auto generated negative scopes."
271
+ end
272
+ end
264
273
  end
265
274
  end
@@ -531,15 +531,15 @@ module ActiveRecord
531
531
  end
532
532
  end
533
533
 
534
- def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
534
+ def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
535
535
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
536
536
  class_names = ClassCache.new class_names, config
537
537
 
538
538
  # FIXME: Apparently JK uses this.
539
- connection = block_given? ? yield : ActiveRecord::Base.connection
539
+ connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
540
540
 
541
541
  fixture_files_to_read = fixture_set_names.reject do |fs_name|
542
- fixture_is_cached?(connection, fs_name)
542
+ fixture_is_cached?(connection.call, fs_name)
543
543
  end
544
544
 
545
545
  if fixture_files_to_read.any?
@@ -549,9 +549,9 @@ module ActiveRecord
549
549
  class_names,
550
550
  connection,
551
551
  )
552
- cache_fixtures(connection, fixtures_map)
552
+ cache_fixtures(connection.call, fixtures_map)
553
553
  end
554
- cached_fixtures(connection, fixture_set_names)
554
+ cached_fixtures(connection.call, fixture_set_names)
555
555
  end
556
556
 
557
557
  # Returns a consistent, platform-independent identifier for +label+.
@@ -591,7 +591,11 @@ module ActiveRecord
591
591
 
592
592
  def insert(fixture_sets, connection) # :nodoc:
593
593
  fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
594
- fixture_set.model_class&.connection || connection
594
+ if fixture_set.model_class
595
+ fixture_set.model_class.connection
596
+ else
597
+ connection.call
598
+ end
595
599
  end
596
600
 
597
601
  fixture_sets_by_connection.each do |conn, set|
@@ -602,6 +606,7 @@ module ActiveRecord
602
606
  table_rows_for_connection[table].unshift(*rows)
603
607
  end
604
608
  end
609
+
605
610
  conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
606
611
 
607
612
  # Cap primary key sequences to max(pk).
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  MAJOR = 6
11
11
  MINOR = 0
12
12
  TINY = 0
13
- PRE = "rc2"
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -110,8 +110,7 @@ module ActiveRecord
110
110
  end
111
111
  end
112
112
 
113
-
114
- class Builder
113
+ class Builder # :nodoc:
115
114
  attr_reader :model
116
115
 
117
116
  delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
 
48
48
  def read_from_primary(&blk)
49
49
  ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
50
- ActiveRecord::Base.connection_handler.while_preventing_writes do
50
+ ActiveRecord::Base.connection_handler.while_preventing_writes(true) do
51
51
  instrumenter.instrument("database_selector.active_record.read_from_primary") do
52
52
  yield
53
53
  end
@@ -65,10 +65,12 @@ module ActiveRecord
65
65
 
66
66
  def write_to_primary(&blk)
67
67
  ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
68
- instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
69
- yield
70
- ensure
71
- context.update_last_write_timestamp
68
+ ActiveRecord::Base.connection_handler.while_preventing_writes(false) do
69
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
70
+ yield
71
+ ensure
72
+ context.update_last_write_timestamp
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -588,18 +588,26 @@ module ActiveRecord
588
588
  end
589
589
 
590
590
  def load_schema_if_pending!
591
- if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?
591
+ current_config = Base.connection_config
592
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
593
+
594
+ needs_update = !all_configs.all? do |db_config|
595
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ActiveRecord::Base.schema_format, nil, Rails.env, db_config.spec_name)
596
+ end
597
+
598
+ if needs_update
592
599
  # Roundtrip to Rake to allow plugins to hook into database initialization.
593
600
  root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
594
601
  FileUtils.cd(root) do
595
- current_config = Base.connection_config
596
602
  Base.clear_all_connections!
597
603
  system("bin/rails db:test:prepare")
598
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
599
- Base.establish_connection(current_config)
600
604
  end
601
- check_pending!
602
605
  end
606
+
607
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
608
+ Base.establish_connection(current_config)
609
+
610
+ check_pending!
603
611
  end
604
612
 
605
613
  def maintain_test_schema! #:nodoc:
@@ -265,6 +265,8 @@ db_namespace = namespace :db do
265
265
  end
266
266
  abort %{Run `rails db:migrate` to update your database then try again.}
267
267
  end
268
+ ensure
269
+ ActiveRecord::Base.establish_connection(ActiveRecord::Tasks::DatabaseTasks.env.to_sym)
268
270
  end
269
271
 
270
272
  namespace :abort_if_pending_migrations do
@@ -297,10 +299,11 @@ db_namespace = namespace :db do
297
299
  ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
298
300
  ActiveRecord::Base.establish_connection(db_config.config)
299
301
 
300
- ActiveRecord::Tasks::DatabaseTasks.migrate
301
-
302
302
  # Skipped when no database
303
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name)
303
+ ActiveRecord::Tasks::DatabaseTasks.migrate
304
+ if ActiveRecord::Base.dump_schema_after_migration
305
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name)
306
+ end
304
307
 
305
308
  rescue ActiveRecord::NoDatabaseError
306
309
  ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, db_config.spec_name)
@@ -321,7 +321,7 @@ module ActiveRecord
321
321
  }
322
322
  group_columns = group_aliases.zip(group_fields)
323
323
 
324
- aggregate_alias = column_alias_for("#{operation}_#{column_name.to_s.downcase}")
324
+ aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
325
325
 
326
326
  select_values = [
327
327
  operation_over_aggregate_column(
@@ -374,8 +374,6 @@ module ActiveRecord
374
374
  # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
375
375
  # column_alias_for("count(*)") # => "count_all"
376
376
  def column_alias_for(field)
377
- return field if field.match?(/\A\w{,#{connection.table_alias_length}}\z/)
378
-
379
377
  column_alias = +field
380
378
  column_alias.gsub!(/\*/, "all")
381
379
  column_alias.gsub!(/\W+/, " ")
@@ -372,7 +372,7 @@ module ActiveRecord
372
372
 
373
373
  # Same as #reorder but operates on relation in-place instead of copying.
374
374
  def reorder!(*args) # :nodoc:
375
- preprocess_order_args(args)
375
+ preprocess_order_args(args) unless args.all?(&:blank?)
376
376
 
377
377
  self.reordering_value = true
378
378
  self.order_values = args
@@ -1095,26 +1095,44 @@ module ActiveRecord
1095
1095
  end
1096
1096
 
1097
1097
  def build_left_outer_joins(manager, outer_joins, aliases)
1098
- buckets = { association_join: valid_association_list(outer_joins) }
1098
+ buckets = Hash.new { |h, k| h[k] = [] }
1099
+ buckets[:association_join] = valid_association_list(outer_joins)
1099
1100
  build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
1100
1101
  end
1101
1102
 
1102
1103
  def build_joins(manager, joins, aliases)
1104
+ buckets = Hash.new { |h, k| h[k] = [] }
1105
+
1103
1106
  unless left_outer_joins_values.empty?
1104
1107
  left_joins = valid_association_list(left_outer_joins_values.flatten)
1105
- joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
1108
+ buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
1109
+ end
1110
+
1111
+ joins.map! do |join|
1112
+ if join.is_a?(String)
1113
+ table.create_string_join(Arel.sql(join.strip)) unless join.blank?
1114
+ else
1115
+ join
1116
+ end
1117
+ end.delete_if(&:blank?).uniq!
1118
+
1119
+ while joins.first.is_a?(Arel::Nodes::Join)
1120
+ join_node = joins.shift
1121
+ if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
1122
+ buckets[:join_node] << join_node
1123
+ else
1124
+ buckets[:leading_join] << join_node
1125
+ end
1106
1126
  end
1107
1127
 
1108
- buckets = joins.group_by do |join|
1128
+ joins.each do |join|
1109
1129
  case join
1110
- when String
1111
- :string_join
1112
1130
  when Hash, Symbol, Array
1113
- :association_join
1131
+ buckets[:association_join] << join
1114
1132
  when ActiveRecord::Associations::JoinDependency
1115
- :stashed_join
1133
+ buckets[:stashed_join] << join
1116
1134
  when Arel::Nodes::Join
1117
- :join_node
1135
+ buckets[:join_node] << join
1118
1136
  else
1119
1137
  raise "unknown class: %s" % join.class.name
1120
1138
  end
@@ -1124,25 +1142,21 @@ module ActiveRecord
1124
1142
  end
1125
1143
 
1126
1144
  def build_join_query(manager, buckets, join_type, aliases)
1127
- buckets.default = []
1128
-
1129
1145
  association_joins = buckets[:association_join]
1130
1146
  stashed_joins = buckets[:stashed_join]
1131
- join_nodes = buckets[:join_node].tap(&:uniq!)
1132
- string_joins = buckets[:string_join].delete_if(&:blank?).map!(&:strip).tap(&:uniq!)
1133
-
1134
- string_joins.map! { |join| table.create_string_join(Arel.sql(join)) }
1147
+ leading_joins = buckets[:leading_join]
1148
+ join_nodes = buckets[:join_node]
1135
1149
 
1136
1150
  join_sources = manager.join_sources
1137
- join_sources.concat(join_nodes) unless join_nodes.empty?
1151
+ join_sources.concat(leading_joins) unless leading_joins.empty?
1138
1152
 
1139
1153
  unless association_joins.empty? && stashed_joins.empty?
1140
- alias_tracker = alias_tracker(join_nodes + string_joins, aliases)
1154
+ alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
1141
1155
  join_dependency = construct_join_dependency(association_joins, join_type)
1142
1156
  join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
1143
1157
  end
1144
1158
 
1145
- join_sources.concat(string_joins) unless string_joins.empty?
1159
+ join_sources.concat(join_nodes) unless join_nodes.empty?
1146
1160
  end
1147
1161
 
1148
1162
  def build_select(arel)
@@ -154,6 +154,8 @@ module ActiveRecord
154
154
  end
155
155
 
156
156
  def for_each(databases)
157
+ return {} unless defined?(Rails)
158
+
157
159
  database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
158
160
 
159
161
  # if this is a single database application we don't want tasks for each primary database
@@ -331,10 +333,39 @@ module ActiveRecord
331
333
  end
332
334
  ActiveRecord::InternalMetadata.create_table
333
335
  ActiveRecord::InternalMetadata[:environment] = environment
336
+ ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
334
337
  ensure
335
338
  Migration.verbose = verbose_was
336
339
  end
337
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
+
338
369
  def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
339
370
  require "active_record/schema_dumper"
340
371
  filename = dump_filename(spec_name, format)
@@ -466,6 +497,10 @@ module ActiveRecord
466
497
  def local_database?(configuration)
467
498
  configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
468
499
  end
500
+
501
+ def schema_sha1(file)
502
+ Digest::SHA1.hexdigest(File.read(file))
503
+ end
469
504
  end
470
505
  end
471
506
  end
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  connection.create_database configuration["database"], creation_options
17
17
  establish_connection configuration
18
18
  rescue ActiveRecord::StatementInvalid => error
19
- if error.cause.error_number == ER_DB_CREATE_EXISTS
19
+ if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
20
20
  raise DatabaseAlreadyExists
21
21
  else
22
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.create(db_config.config)
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
@@ -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 = {})
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.rc2
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-22 00:00:00.000000000 Z
11
+ date: 2019-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0.rc2
19
+ version: 6.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 6.0.0.rc2
26
+ version: 6.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 6.0.0.rc2
33
+ version: 6.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 6.0.0.rc2
40
+ version: 6.0.0
41
41
  description: Databases on Rails. Build a persistent domain model by mapping database
42
42
  tables to Ruby classes. Strong conventions for associations, validations, aggregations,
43
43
  migrations, and testing come baked-in.
@@ -389,8 +389,8 @@ homepage: https://rubyonrails.org
389
389
  licenses:
390
390
  - MIT
391
391
  metadata:
392
- source_code_uri: https://github.com/rails/rails/tree/v6.0.0.rc2/activerecord
393
- changelog_uri: https://github.com/rails/rails/blob/v6.0.0.rc2/activerecord/CHANGELOG.md
392
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.0/activerecord
393
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.0/activerecord/CHANGELOG.md
394
394
  post_install_message:
395
395
  rdoc_options:
396
396
  - "--main"
@@ -404,9 +404,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
404
404
  version: 2.5.0
405
405
  required_rubygems_version: !ruby/object:Gem::Requirement
406
406
  requirements:
407
- - - ">"
407
+ - - ">="
408
408
  - !ruby/object:Gem::Version
409
- version: 1.3.1
409
+ version: '0'
410
410
  requirements: []
411
411
  rubygems_version: 3.0.1
412
412
  signing_key: