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.

Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +455 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record/associations/association.rb +18 -1
  5. data/lib/active_record/associations/builder/association.rb +14 -18
  6. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  7. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  9. data/lib/active_record/associations/builder/has_many.rb +2 -0
  10. data/lib/active_record/associations/builder/has_one.rb +35 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  12. data/lib/active_record/associations/collection_association.rb +5 -6
  13. data/lib/active_record/associations/collection_proxy.rb +13 -42
  14. data/lib/active_record/associations/has_many_association.rb +1 -9
  15. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  16. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  17. data/lib/active_record/associations/join_dependency.rb +10 -9
  18. data/lib/active_record/associations/preloader/association.rb +37 -34
  19. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  20. data/lib/active_record/associations/preloader.rb +11 -6
  21. data/lib/active_record/associations.rb +3 -2
  22. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  23. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  24. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  25. data/lib/active_record/attribute_methods/query.rb +2 -3
  26. data/lib/active_record/attribute_methods/read.rb +3 -9
  27. data/lib/active_record/attribute_methods/write.rb +6 -12
  28. data/lib/active_record/attribute_methods.rb +3 -53
  29. data/lib/active_record/attributes.rb +13 -0
  30. data/lib/active_record/autosave_association.rb +15 -5
  31. data/lib/active_record/base.rb +0 -1
  32. data/lib/active_record/callbacks.rb +3 -3
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +124 -23
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -5
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  40. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  42. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  43. data/lib/active_record/connection_adapters/abstract_adapter.rb +108 -39
  44. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
  45. data/lib/active_record/connection_adapters/column.rb +17 -13
  46. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  47. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  48. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  49. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  50. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  51. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  52. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  53. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  55. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  56. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  58. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  59. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  60. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  61. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  62. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  63. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  64. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  65. data/lib/active_record/connection_adapters/postgresql_adapter.rb +91 -24
  66. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  67. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  68. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  69. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +69 -118
  72. data/lib/active_record/connection_handling.rb +32 -16
  73. data/lib/active_record/core.rb +27 -20
  74. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  75. data/lib/active_record/database_configurations/url_config.rb +21 -16
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/dynamic_matchers.rb +1 -1
  78. data/lib/active_record/enum.rb +15 -0
  79. data/lib/active_record/errors.rb +18 -13
  80. data/lib/active_record/fixtures.rb +11 -6
  81. data/lib/active_record/gem_version.rb +1 -1
  82. data/lib/active_record/inheritance.rb +1 -1
  83. data/lib/active_record/insert_all.rb +179 -0
  84. data/lib/active_record/integration.rb +13 -1
  85. data/lib/active_record/internal_metadata.rb +5 -1
  86. data/lib/active_record/locking/optimistic.rb +3 -4
  87. data/lib/active_record/log_subscriber.rb +1 -1
  88. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  89. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/migration/command_recorder.rb +28 -14
  92. data/lib/active_record/migration/compatibility.rb +72 -63
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/persistence.rb +212 -19
  95. data/lib/active_record/querying.rb +18 -14
  96. data/lib/active_record/railtie.rb +9 -1
  97. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  98. data/lib/active_record/railties/databases.rake +124 -25
  99. data/lib/active_record/reflection.rb +18 -32
  100. data/lib/active_record/relation/calculations.rb +40 -44
  101. data/lib/active_record/relation/delegation.rb +23 -31
  102. data/lib/active_record/relation/finder_methods.rb +13 -13
  103. data/lib/active_record/relation/merger.rb +11 -16
  104. data/lib/active_record/relation/query_attribute.rb +5 -3
  105. data/lib/active_record/relation/query_methods.rb +217 -68
  106. data/lib/active_record/relation/spawn_methods.rb +1 -1
  107. data/lib/active_record/relation/where_clause.rb +10 -10
  108. data/lib/active_record/relation.rb +184 -35
  109. data/lib/active_record/sanitization.rb +33 -4
  110. data/lib/active_record/schema.rb +1 -1
  111. data/lib/active_record/schema_dumper.rb +10 -1
  112. data/lib/active_record/schema_migration.rb +1 -1
  113. data/lib/active_record/scoping/default.rb +7 -15
  114. data/lib/active_record/scoping/named.rb +10 -2
  115. data/lib/active_record/scoping.rb +6 -7
  116. data/lib/active_record/statement_cache.rb +2 -2
  117. data/lib/active_record/store.rb +48 -0
  118. data/lib/active_record/table_metadata.rb +9 -13
  119. data/lib/active_record/tasks/database_tasks.rb +109 -6
  120. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  121. data/lib/active_record/test_databases.rb +1 -16
  122. data/lib/active_record/test_fixtures.rb +2 -2
  123. data/lib/active_record/timestamp.rb +35 -19
  124. data/lib/active_record/touch_later.rb +4 -2
  125. data/lib/active_record/transactions.rb +55 -45
  126. data/lib/active_record/type_caster/connection.rb +16 -10
  127. data/lib/active_record/validations/uniqueness.rb +4 -4
  128. data/lib/active_record/validations.rb +1 -0
  129. data/lib/active_record.rb +7 -1
  130. data/lib/arel/insert_manager.rb +3 -3
  131. data/lib/arel/nodes/and.rb +1 -1
  132. data/lib/arel/nodes/case.rb +1 -1
  133. data/lib/arel/nodes/comment.rb +29 -0
  134. data/lib/arel/nodes/select_core.rb +16 -12
  135. data/lib/arel/nodes/unary.rb +1 -0
  136. data/lib/arel/nodes/values_list.rb +2 -17
  137. data/lib/arel/nodes.rb +2 -1
  138. data/lib/arel/select_manager.rb +10 -10
  139. data/lib/arel/visitors/depth_first.rb +7 -2
  140. data/lib/arel/visitors/dot.rb +7 -2
  141. data/lib/arel/visitors/ibm_db.rb +13 -0
  142. data/lib/arel/visitors/informix.rb +6 -0
  143. data/lib/arel/visitors/mssql.rb +15 -1
  144. data/lib/arel/visitors/oracle12.rb +4 -5
  145. data/lib/arel/visitors/postgresql.rb +4 -10
  146. data/lib/arel/visitors/to_sql.rb +107 -131
  147. data/lib/arel/visitors/visitor.rb +9 -5
  148. data/lib/arel.rb +7 -0
  149. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  150. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  151. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  152. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  153. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  154. metadata +17 -13
  155. data/lib/active_record/collection_cache_key.rb +0 -53
  156. data/lib/arel/nodes/values.rb +0 -16
@@ -7,8 +7,10 @@ require "active_record/database_configurations/url_config"
7
7
  module ActiveRecord
8
8
  # ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
9
9
  # objects (either a HashConfig or UrlConfig) that are constructed from the
10
- # application's database configuration hash or url string.
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
 
@@ -17,22 +19,22 @@ module ActiveRecord
17
19
  end
18
20
 
19
21
  # Collects the configs for the environment and optionally the specification
20
- # name passed in. To include replica configurations pass `include_replicas: true`.
22
+ # name passed in. To include replica configurations pass <tt>include_replicas: true</tt>.
21
23
  #
22
24
  # If a spec name is provided a single DatabaseConfig object will be
23
25
  # returned, otherwise an array of DatabaseConfig objects will be
24
26
  # returned that corresponds with the environment and type requested.
25
27
  #
26
- # Options:
28
+ # ==== Options
27
29
  #
28
- # <tt>env_name:</tt> The environment name. Defaults to nil which will collect
29
- # configs for all environments.
30
- # <tt>spec_name:</tt> The specification name (ie primary, animals, etc.). Defaults
31
- # to +nil+.
32
- # <tt>include_replicas:</tt> Determines whether to include replicas in
33
- # the returned list. Most of the time we're only iterating over the write
34
- # connection (i.e. migrations don't need to run for the write and read connection).
35
- # Defaults to +false+.
30
+ # * <tt>env_name:</tt> The environment name. Defaults to +nil+ which will collect
31
+ # configs for all environments.
32
+ # * <tt>spec_name:</tt> The specification name (i.e. primary, animals, etc.). Defaults
33
+ # to +nil+.
34
+ # * <tt>include_replicas:</tt> Determines whether to include replicas in
35
+ # the returned list. Most of the time we're only iterating over the write
36
+ # connection (i.e. migrations don't need to run for the write and read connection).
37
+ # Defaults to +false+.
36
38
  def configs_for(env_name: nil, spec_name: nil, include_replicas: false)
37
39
  configs = env_with_configs(env_name)
38
40
 
@@ -53,7 +55,7 @@ module ActiveRecord
53
55
 
54
56
  # Returns the config hash that corresponds with the environment
55
57
  #
56
- # If the application has multiple databases `default_hash` will
58
+ # If the application has multiple databases +default_hash+ will
57
59
  # return the first config hash for the environment.
58
60
  #
59
61
  # { database: "my_db", adapter: "mysql2" }
@@ -65,7 +67,7 @@ module ActiveRecord
65
67
 
66
68
  # Returns a single DatabaseConfig object based on the requested environment.
67
69
  #
68
- # If the application has multiple databases `find_db_config` will return
70
+ # If the application has multiple databases +find_db_config+ will return
69
71
  # the first DatabaseConfig for the environment.
70
72
  def find_db_config(env)
71
73
  configurations.find do |db_config|
@@ -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
@@ -102,83 +117,117 @@ module ActiveRecord
102
117
 
103
118
  def build_configs(configs)
104
119
  return configs.configurations if configs.is_a?(DatabaseConfigurations)
120
+ return configs if configs.is_a?(Array)
105
121
 
106
- build_db_config = configs.each_pair.flat_map do |env_name, config|
107
- walk_configs(env_name.to_s, "primary", config)
108
- end.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
109
129
 
110
- if url = ENV["DATABASE_URL"]
111
- build_url_config(url, build_db_config)
112
- else
113
- 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", {})
114
134
  end
135
+
136
+ merge_db_environment_variables(current_env, db_configs.compact)
115
137
  end
116
138
 
117
- def walk_configs(env_name, spec_name, config)
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)
142
+ end
143
+ end
144
+
145
+ def build_db_config_from_raw_config(env_name, spec_name, config)
118
146
  case config
119
147
  when String
120
148
  build_db_config_from_string(env_name, spec_name, config)
121
149
  when Hash
122
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."
123
153
  end
124
154
  end
125
155
 
126
156
  def build_db_config_from_string(env_name, spec_name, config)
127
157
  url = config
128
158
  uri = URI.parse(url)
129
- if uri.try(:scheme)
159
+ if uri.scheme
130
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."
131
163
  end
132
- rescue URI::InvalidURIError
133
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
134
164
  end
135
165
 
136
166
  def build_db_config_from_hash(env_name, spec_name, config)
137
- if url = config["url"]
167
+ if config.has_key?("url")
168
+ url = config["url"]
138
169
  config_without_url = config.dup
139
170
  config_without_url.delete "url"
171
+
140
172
  ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
141
- elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
142
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
143
173
  else
144
- config.each_pair.map do |sub_spec_name, sub_config|
145
- walk_configs(env_name, sub_spec_name, sub_config)
146
- end
174
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
147
175
  end
148
176
  end
149
177
 
150
- def build_url_config(url, configs)
151
- 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
152
181
 
153
- if original_config = configs.find(&:for_current_env?)
154
- if original_config.url_config?
155
- configs
156
- else
157
- configs.map do |config|
158
- ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, config.spec_name, url, config.config)
159
- end
160
- end
161
- else
162
- 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
163
184
  end
164
185
  end
165
186
 
166
- def method_missing(method, *args, &blk)
167
- if Hash.method_defined?(method)
168
- ActiveSupport::Deprecation.warn \
169
- "Returning a hash from ActiveRecord::Base.configurations is deprecated. Therefore calling `#{method}` on the hash is also deprecated. Please switch to using the `configs_for` method instead to collect and iterate over database configurations."
170
- end
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
171
200
 
201
+ def method_missing(method, *args, &blk)
172
202
  case method
173
- when :each, :first
174
- configurations.send(method, *args, &blk)
175
203
  when :fetch
204
+ throw_getter_deprecation(method)
176
205
  configs_for(env_name: args.first)
177
206
  when :values
207
+ throw_getter_deprecation(method)
178
208
  configurations.map(&:config)
209
+ when :[]=
210
+ throw_setter_deprecation(method)
211
+
212
+ env_name = args[0]
213
+ config = args[1]
214
+
215
+ remaining_configs = configurations.reject { |db_config| db_config.env_name == env_name }
216
+ new_config = build_configs(env_name => config)
217
+ new_configs = remaining_configs + new_config
218
+
219
+ ActiveRecord::Base.configurations = new_configs
179
220
  else
180
- super
221
+ raise NotImplementedError, "`ActiveRecord::Base.configurations` in Rails 6 now returns an object instead of a hash. The `#{method}` method is not supported. Please use `configs_for` or consult the documentation for supported methods."
181
222
  end
182
223
  end
224
+
225
+ def throw_setter_deprecation(method)
226
+ ActiveSupport::Deprecation.warn("Setting `ActiveRecord::Base.configurations` with `#{method}` is deprecated. Use `ActiveRecord::Base.configurations=` directly to set the configurations instead.")
227
+ end
228
+
229
+ def throw_getter_deprecation(method)
230
+ ActiveSupport::Deprecation.warn("`ActiveRecord::Base.configurations` no longer returns a hash. Methods that act on the hash like `#{method}` are deprecated and will be removed in Rails 6.1. Use the `configs_for` method to collect and iterate over the database configurations.")
231
+ end
183
232
  end
184
233
  end
@@ -53,7 +53,7 @@ module ActiveRecord
53
53
  @model = model
54
54
  @name = name.to_s
55
55
  @attribute_names = @name.match(self.class.pattern)[1].split("_and_")
56
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
56
+ @attribute_names.map! { |name| @model.attribute_aliases[name] || name }
57
57
  end
58
58
 
59
59
  def valid?
@@ -31,7 +31,9 @@ module ActiveRecord
31
31
  # as well. With the above example:
32
32
  #
33
33
  # Conversation.active
34
+ # Conversation.not_active
34
35
  # Conversation.archived
36
+ # Conversation.not_archived
35
37
  #
36
38
  # Of course, you can also query them directly if the scopes don't fit your
37
39
  # needs:
@@ -196,9 +198,15 @@ module ActiveRecord
196
198
  define_method("#{value_method_name}!") { update!(attr => value) }
197
199
 
198
200
  # scope :active, -> { where(status: 0) }
201
+ # scope :not_active, -> { where.not(status: 0) }
199
202
  if enum_scopes != false
203
+ klass.send(:detect_negative_condition!, value_method_name)
204
+
200
205
  klass.send(:detect_enum_conflict!, name, value_method_name, true)
201
206
  klass.scope value_method_name, -> { where(attr => value) }
207
+
208
+ klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
209
+ klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
202
210
  end
203
211
  end
204
212
  end
@@ -255,5 +263,12 @@ module ActiveRecord
255
263
  source: source
256
264
  }
257
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
258
273
  end
259
274
  end
@@ -68,7 +68,7 @@ module ActiveRecord
68
68
 
69
69
  # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
70
70
  # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
71
- # methods when a record is invalid and can not be saved.
71
+ # methods when a record is invalid and cannot be saved.
72
72
  class RecordNotSaved < ActiveRecordError
73
73
  attr_reader :record
74
74
 
@@ -126,16 +126,26 @@ module ActiveRecord
126
126
 
127
127
  # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
128
128
  class MismatchedForeignKey < StatementInvalid
129
- def initialize(adapter = nil, message: nil, sql: nil, binds: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
130
- @adapter = adapter
129
+ def initialize(
130
+ message: nil,
131
+ sql: nil,
132
+ binds: nil,
133
+ table: nil,
134
+ foreign_key: nil,
135
+ target_table: nil,
136
+ primary_key: nil,
137
+ primary_key_column: nil
138
+ )
131
139
  if table
132
- msg = +<<~EOM
133
- Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
134
- This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
135
- To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
140
+ type = primary_key_column.bigint? ? :bigint : primary_key_column.type
141
+ msg = <<~EOM.squish
142
+ Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
143
+ which has type `#{primary_key_column.sql_type}`.
144
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
145
+ (For example `t.#{type} :#{foreign_key}`).
136
146
  EOM
137
147
  else
138
- msg = +<<~EOM
148
+ msg = <<~EOM.squish
139
149
  There is a mismatch between the foreign key and primary key column types.
140
150
  Verify that the foreign key column type and the primary key of the associated table match types.
141
151
  EOM
@@ -145,11 +155,6 @@ module ActiveRecord
145
155
  end
146
156
  super(msg, sql: sql, binds: binds)
147
157
  end
148
-
149
- private
150
- def column_type(table, column)
151
- @adapter.columns(table).detect { |c| c.name == column }.sql_type
152
- end
153
158
  end
154
159
 
155
160
  # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
@@ -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 = "beta1"
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -249,7 +249,7 @@ module ActiveRecord
249
249
  sti_column = arel_attribute(inheritance_column, table)
250
250
  sti_names = ([self] + descendants).map(&:sti_name)
251
251
 
252
- sti_column.in(sti_names)
252
+ predicate_builder.build(sti_column, sti_names)
253
253
  end
254
254
 
255
255
  # Detect the subclass from the inheritance column of attrs. If the inheritance column value
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class InsertAll # :nodoc:
5
+ attr_reader :model, :connection, :inserts, :keys
6
+ attr_reader :on_duplicate, :returning, :unique_by
7
+
8
+ def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
9
+ raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
10
+
11
+ @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s).to_set
12
+ @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
13
+
14
+ @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
15
+ @returning = false if @returning == []
16
+
17
+ @unique_by = find_unique_index_for(unique_by) if unique_by
18
+ @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
19
+
20
+ ensure_valid_options_for_connection!
21
+ end
22
+
23
+ def execute
24
+ message = +"#{model} "
25
+ message << "Bulk " if inserts.many?
26
+ message << (on_duplicate == :update ? "Upsert" : "Insert")
27
+ connection.exec_query to_sql, message
28
+ end
29
+
30
+ def updatable_columns
31
+ keys - readonly_columns - unique_by_columns
32
+ end
33
+
34
+ def primary_keys
35
+ Array(model.primary_key)
36
+ end
37
+
38
+
39
+ def skip_duplicates?
40
+ on_duplicate == :skip
41
+ end
42
+
43
+ def update_duplicates?
44
+ on_duplicate == :update
45
+ end
46
+
47
+ def map_key_with_value
48
+ inserts.map do |attributes|
49
+ attributes = attributes.stringify_keys
50
+ verify_attributes(attributes)
51
+
52
+ keys.map do |key|
53
+ yield key, attributes[key]
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+ def find_unique_index_for(unique_by)
60
+ match = Array(unique_by).map(&:to_s)
61
+
62
+ if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
63
+ index
64
+ else
65
+ raise ArgumentError, "No unique index found for #{unique_by}"
66
+ end
67
+ end
68
+
69
+ def unique_indexes
70
+ connection.schema_cache.indexes(model.table_name).select(&:unique)
71
+ end
72
+
73
+
74
+ def ensure_valid_options_for_connection!
75
+ if returning && !connection.supports_insert_returning?
76
+ raise ArgumentError, "#{connection.class} does not support :returning"
77
+ end
78
+
79
+ if skip_duplicates? && !connection.supports_insert_on_duplicate_skip?
80
+ raise ArgumentError, "#{connection.class} does not support skipping duplicates"
81
+ end
82
+
83
+ if update_duplicates? && !connection.supports_insert_on_duplicate_update?
84
+ raise ArgumentError, "#{connection.class} does not support upsert"
85
+ end
86
+
87
+ if unique_by && !connection.supports_insert_conflict_target?
88
+ raise ArgumentError, "#{connection.class} does not support :unique_by"
89
+ end
90
+ end
91
+
92
+
93
+ def to_sql
94
+ connection.build_insert_sql(ActiveRecord::InsertAll::Builder.new(self))
95
+ end
96
+
97
+
98
+ def readonly_columns
99
+ primary_keys + model.readonly_attributes.to_a
100
+ end
101
+
102
+ def unique_by_columns
103
+ Array(unique_by&.columns)
104
+ end
105
+
106
+
107
+ def verify_attributes(attributes)
108
+ if keys != attributes.keys.to_set
109
+ raise ArgumentError, "All objects being inserted must have the same keys"
110
+ end
111
+ end
112
+
113
+ class Builder # :nodoc:
114
+ attr_reader :model
115
+
116
+ delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
117
+
118
+ def initialize(insert_all)
119
+ @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
120
+ end
121
+
122
+ def into
123
+ "INTO #{model.quoted_table_name}(#{columns_list})"
124
+ end
125
+
126
+ def values_list
127
+ types = extract_types_from_columns_on(model.table_name, keys: keys)
128
+
129
+ values_list = insert_all.map_key_with_value do |key, value|
130
+ connection.with_yaml_fallback(types[key].serialize(value))
131
+ end
132
+
133
+ Arel::InsertManager.new.create_values_list(values_list).to_sql
134
+ end
135
+
136
+ def returning
137
+ format_columns(insert_all.returning) if insert_all.returning
138
+ end
139
+
140
+ def conflict_target
141
+ if index = insert_all.unique_by
142
+ sql = +"(#{format_columns(index.columns)})"
143
+ sql << " WHERE #{index.where}" if index.where
144
+ sql
145
+ elsif update_duplicates?
146
+ "(#{format_columns(insert_all.primary_keys)})"
147
+ end
148
+ end
149
+
150
+ def updatable_columns
151
+ quote_columns(insert_all.updatable_columns)
152
+ end
153
+
154
+ private
155
+ attr_reader :connection, :insert_all
156
+
157
+ def columns_list
158
+ format_columns(insert_all.keys)
159
+ end
160
+
161
+ def extract_types_from_columns_on(table_name, keys:)
162
+ columns = connection.schema_cache.columns_hash(table_name)
163
+
164
+ unknown_column = (keys - columns.keys).first
165
+ raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column
166
+
167
+ keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h
168
+ end
169
+
170
+ def format_columns(columns)
171
+ quote_columns(columns).join(",")
172
+ end
173
+
174
+ def quote_columns(columns)
175
+ columns.map(&connection.method(:quote_column_name))
176
+ end
177
+ end
178
+ end
179
+ end
@@ -22,6 +22,14 @@ module ActiveRecord
22
22
  #
23
23
  # This is +true+, by default on Rails 5.2 and above.
24
24
  class_attribute :cache_versioning, instance_writer: false, default: false
25
+
26
+ ##
27
+ # :singleton-method:
28
+ # Indicates whether to use a stable #cache_key method that is accompanied
29
+ # by a changing version in the #cache_version method on collections.
30
+ #
31
+ # This is +false+, by default until Rails 6.1.
32
+ class_attribute :collection_cache_versioning, instance_writer: false, default: false
25
33
  end
26
34
 
27
35
  # Returns a +String+, which Action Pack uses for constructing a URL to this
@@ -152,6 +160,10 @@ module ActiveRecord
152
160
  end
153
161
  end
154
162
  end
163
+
164
+ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
165
+ collection.send(:compute_cache_key, timestamp_column)
166
+ end
155
167
  end
156
168
 
157
169
  private
@@ -180,7 +192,7 @@ module ActiveRecord
180
192
  # raw_timestamp_to_cache_version(timestamp)
181
193
  # # => "20181015200215266505"
182
194
  #
183
- # Postgres truncates trailing zeros,
195
+ # PostgreSQL truncates trailing zeros,
184
196
  # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
185
197
  # to account for this we pad the output with zeros
186
198
  def raw_timestamp_to_cache_version(timestamp)
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  def table_name
20
- "#{table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
20
+ "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
21
21
  end
22
22
 
23
23
  def []=(key, value)
@@ -44,6 +44,10 @@ module ActiveRecord
44
44
  end
45
45
  end
46
46
  end
47
+
48
+ def drop_table
49
+ connection.drop_table table_name, if_exists: true
50
+ end
47
51
  end
48
52
  end
49
53
  end
@@ -71,9 +71,8 @@ module ActiveRecord
71
71
  end
72
72
 
73
73
  def _touch_row(attribute_names, time)
74
+ @_touch_attr_names << self.class.locking_column if locking_enabled?
74
75
  super
75
- ensure
76
- clear_attribute_change(self.class.locking_column) if locking_enabled?
77
76
  end
78
77
 
79
78
  def _update_row(attribute_names, attempted_action = "update")
@@ -88,7 +87,7 @@ module ActiveRecord
88
87
 
89
88
  affected_rows = self.class._update_record(
90
89
  attributes_with_values(attribute_names),
91
- self.class.primary_key => id_in_database,
90
+ @primary_key => id_in_database,
92
91
  locking_column => previous_lock_value
93
92
  )
94
93
 
@@ -111,7 +110,7 @@ module ActiveRecord
111
110
  locking_column = self.class.locking_column
112
111
 
113
112
  affected_rows = self.class._delete_record(
114
- self.class.primary_key => id_in_database,
113
+ @primary_key => id_in_database,
115
114
  locking_column => read_attribute_before_type_cast(locking_column)
116
115
  )
117
116