activerecord 6.1.7.6 → 7.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 (238) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1055 -1180
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +18 -19
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/join_dependency.rb +6 -2
  25. data/lib/active_record/associations/preloader/association.rb +186 -52
  26. data/lib/active_record/associations/preloader/batch.rb +48 -0
  27. data/lib/active_record/associations/preloader/branch.rb +147 -0
  28. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  29. data/lib/active_record/associations/preloader.rb +39 -113
  30. data/lib/active_record/associations/singular_association.rb +8 -2
  31. data/lib/active_record/associations/through_association.rb +3 -3
  32. data/lib/active_record/associations.rb +90 -82
  33. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  34. data/lib/active_record/attribute_assignment.rb +1 -1
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  37. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  38. data/lib/active_record/attribute_methods/query.rb +2 -2
  39. data/lib/active_record/attribute_methods/read.rb +7 -5
  40. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  42. data/lib/active_record/attribute_methods/write.rb +7 -10
  43. data/lib/active_record/attribute_methods.rb +13 -14
  44. data/lib/active_record/attributes.rb +24 -35
  45. data/lib/active_record/autosave_association.rb +6 -21
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -2
  48. data/lib/active_record/coders/yaml_column.rb +2 -14
  49. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  53. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  54. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  55. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/quoting.rb +43 -82
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -13
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  67. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  70. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  73. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -76
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
  85. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  86. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  87. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  88. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  89. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  90. data/lib/active_record/connection_adapters.rb +6 -5
  91. data/lib/active_record/connection_handling.rb +47 -53
  92. data/lib/active_record/core.rb +121 -146
  93. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  94. data/lib/active_record/database_configurations/database_config.rb +12 -9
  95. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  96. data/lib/active_record/database_configurations/url_config.rb +2 -2
  97. data/lib/active_record/database_configurations.rb +15 -32
  98. data/lib/active_record/delegated_type.rb +52 -11
  99. data/lib/active_record/destroy_association_async_job.rb +1 -1
  100. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  103. data/lib/active_record/encryption/cipher.rb +53 -0
  104. data/lib/active_record/encryption/config.rb +44 -0
  105. data/lib/active_record/encryption/configurable.rb +61 -0
  106. data/lib/active_record/encryption/context.rb +35 -0
  107. data/lib/active_record/encryption/contexts.rb +72 -0
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  109. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  110. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  111. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  112. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  113. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  114. data/lib/active_record/encryption/encryptor.rb +155 -0
  115. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  116. data/lib/active_record/encryption/errors.rb +15 -0
  117. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  118. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  119. data/lib/active_record/encryption/key.rb +28 -0
  120. data/lib/active_record/encryption/key_generator.rb +42 -0
  121. data/lib/active_record/encryption/key_provider.rb +46 -0
  122. data/lib/active_record/encryption/message.rb +33 -0
  123. data/lib/active_record/encryption/message_serializer.rb +90 -0
  124. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  125. data/lib/active_record/encryption/properties.rb +76 -0
  126. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  127. data/lib/active_record/encryption/scheme.rb +99 -0
  128. data/lib/active_record/encryption.rb +55 -0
  129. data/lib/active_record/enum.rb +49 -42
  130. data/lib/active_record/errors.rb +67 -4
  131. data/lib/active_record/explain_registry.rb +11 -6
  132. data/lib/active_record/fixture_set/file.rb +15 -1
  133. data/lib/active_record/fixture_set/table_row.rb +41 -6
  134. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  135. data/lib/active_record/fixtures.rb +17 -20
  136. data/lib/active_record/future_result.rb +139 -0
  137. data/lib/active_record/gem_version.rb +4 -4
  138. data/lib/active_record/inheritance.rb +55 -17
  139. data/lib/active_record/insert_all.rb +80 -14
  140. data/lib/active_record/integration.rb +4 -3
  141. data/lib/active_record/internal_metadata.rb +1 -5
  142. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  143. data/lib/active_record/locking/optimistic.rb +10 -9
  144. data/lib/active_record/locking/pessimistic.rb +9 -3
  145. data/lib/active_record/log_subscriber.rb +14 -3
  146. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  147. data/lib/active_record/middleware/database_selector.rb +8 -3
  148. data/lib/active_record/middleware/shard_selector.rb +60 -0
  149. data/lib/active_record/migration/command_recorder.rb +4 -4
  150. data/lib/active_record/migration/compatibility.rb +89 -10
  151. data/lib/active_record/migration/join_table.rb +1 -1
  152. data/lib/active_record/migration.rb +110 -80
  153. data/lib/active_record/model_schema.rb +45 -58
  154. data/lib/active_record/nested_attributes.rb +13 -12
  155. data/lib/active_record/no_touching.rb +3 -3
  156. data/lib/active_record/null_relation.rb +2 -6
  157. data/lib/active_record/persistence.rb +219 -52
  158. data/lib/active_record/query_cache.rb +2 -2
  159. data/lib/active_record/query_logs.rb +138 -0
  160. data/lib/active_record/querying.rb +15 -5
  161. data/lib/active_record/railtie.rb +127 -17
  162. data/lib/active_record/railties/controller_runtime.rb +1 -1
  163. data/lib/active_record/railties/databases.rake +66 -129
  164. data/lib/active_record/readonly_attributes.rb +11 -0
  165. data/lib/active_record/reflection.rb +67 -50
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  167. data/lib/active_record/relation/batches.rb +3 -3
  168. data/lib/active_record/relation/calculations.rb +40 -36
  169. data/lib/active_record/relation/delegation.rb +6 -6
  170. data/lib/active_record/relation/finder_methods.rb +31 -35
  171. data/lib/active_record/relation/merger.rb +20 -13
  172. data/lib/active_record/relation/predicate_builder.rb +1 -6
  173. data/lib/active_record/relation/query_attribute.rb +5 -11
  174. data/lib/active_record/relation/query_methods.rb +235 -63
  175. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  176. data/lib/active_record/relation/spawn_methods.rb +2 -2
  177. data/lib/active_record/relation/where_clause.rb +10 -19
  178. data/lib/active_record/relation.rb +169 -84
  179. data/lib/active_record/result.rb +17 -7
  180. data/lib/active_record/runtime_registry.rb +9 -13
  181. data/lib/active_record/sanitization.rb +11 -7
  182. data/lib/active_record/schema_dumper.rb +10 -3
  183. data/lib/active_record/schema_migration.rb +4 -4
  184. data/lib/active_record/scoping/default.rb +61 -12
  185. data/lib/active_record/scoping/named.rb +3 -11
  186. data/lib/active_record/scoping.rb +64 -34
  187. data/lib/active_record/serialization.rb +1 -1
  188. data/lib/active_record/signed_id.rb +1 -1
  189. data/lib/active_record/store.rb +1 -6
  190. data/lib/active_record/suppressor.rb +11 -15
  191. data/lib/active_record/tasks/database_tasks.rb +116 -58
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  194. data/lib/active_record/test_databases.rb +1 -1
  195. data/lib/active_record/test_fixtures.rb +9 -13
  196. data/lib/active_record/timestamp.rb +3 -4
  197. data/lib/active_record/transactions.rb +9 -14
  198. data/lib/active_record/translation.rb +2 -2
  199. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  200. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  201. data/lib/active_record/type/internal/timezone.rb +2 -2
  202. data/lib/active_record/type/serialized.rb +1 -1
  203. data/lib/active_record/type/type_map.rb +17 -20
  204. data/lib/active_record/type.rb +1 -2
  205. data/lib/active_record/validations/associated.rb +1 -1
  206. data/lib/active_record/validations/uniqueness.rb +1 -1
  207. data/lib/active_record.rb +204 -28
  208. data/lib/arel/attributes/attribute.rb +0 -8
  209. data/lib/arel/crud.rb +28 -22
  210. data/lib/arel/delete_manager.rb +18 -4
  211. data/lib/arel/filter_predications.rb +9 -0
  212. data/lib/arel/insert_manager.rb +2 -3
  213. data/lib/arel/nodes/casted.rb +1 -1
  214. data/lib/arel/nodes/delete_statement.rb +12 -13
  215. data/lib/arel/nodes/filter.rb +10 -0
  216. data/lib/arel/nodes/function.rb +1 -0
  217. data/lib/arel/nodes/insert_statement.rb +2 -2
  218. data/lib/arel/nodes/select_core.rb +2 -2
  219. data/lib/arel/nodes/select_statement.rb +2 -2
  220. data/lib/arel/nodes/update_statement.rb +8 -3
  221. data/lib/arel/nodes.rb +1 -0
  222. data/lib/arel/predications.rb +11 -3
  223. data/lib/arel/select_manager.rb +10 -4
  224. data/lib/arel/table.rb +0 -1
  225. data/lib/arel/tree_manager.rb +0 -12
  226. data/lib/arel/update_manager.rb +18 -4
  227. data/lib/arel/visitors/dot.rb +80 -90
  228. data/lib/arel/visitors/mysql.rb +8 -2
  229. data/lib/arel/visitors/postgresql.rb +0 -10
  230. data/lib/arel/visitors/to_sql.rb +58 -2
  231. data/lib/arel.rb +2 -1
  232. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  233. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  234. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  235. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  236. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  237. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  238. metadata +58 -14
@@ -31,6 +31,7 @@ module ActiveRecord
31
31
  string: { name: "varchar", limit: 255 },
32
32
  text: { name: "text" },
33
33
  integer: { name: "int", limit: 4 },
34
+ bigint: { name: "bigint" },
34
35
  float: { name: "float", limit: 24 },
35
36
  decimal: { name: "decimal" },
36
37
  datetime: { name: "datetime" },
@@ -54,7 +55,7 @@ module ActiveRecord
54
55
  super(connection, logger, config)
55
56
  end
56
57
 
57
- def get_database_version #:nodoc:
58
+ def get_database_version # :nodoc:
58
59
  full_version_string = get_full_version
59
60
  version_string = version_string(full_version_string)
60
61
  Version.new(version_string, full_version_string)
@@ -137,6 +138,11 @@ module ActiveRecord
137
138
  true
138
139
  end
139
140
 
141
+ def field_ordered_value(column, values) # :nodoc:
142
+ field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse])
143
+ Arel::Nodes::Descending.new(field)
144
+ end
145
+
140
146
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
141
147
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
142
148
  end
@@ -174,7 +180,7 @@ module ActiveRecord
174
180
 
175
181
  # REFERENTIAL INTEGRITY ====================================
176
182
 
177
- def disable_referential_integrity #:nodoc:
183
+ def disable_referential_integrity # :nodoc:
178
184
  old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
179
185
 
180
186
  begin
@@ -185,54 +191,40 @@ module ActiveRecord
185
191
  end
186
192
  end
187
193
 
188
- # CONNECTION MANAGEMENT ====================================
189
-
190
- def clear_cache! # :nodoc:
191
- reload_type_map
192
- super
193
- end
194
-
195
194
  #--
196
195
  # DATABASE STATEMENTS ======================================
197
196
  #++
198
197
 
199
198
  # Executes the SQL statement in the context of this connection.
200
- def execute(sql, name = nil)
201
- materialize_transactions
202
- mark_transaction_written_if_write(sql)
203
-
204
- log(sql, name) do
205
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
206
- @connection.query(sql)
207
- end
208
- end
199
+ def execute(sql, name = nil, async: false)
200
+ raw_execute(sql, name, async)
209
201
  end
210
202
 
211
203
  # Mysql2Adapter doesn't have to free a result after using it, but we use this method
212
204
  # to write stuff in an abstract way without concerning ourselves about whether it
213
205
  # needs to be explicitly freed or not.
214
- def execute_and_free(sql, name = nil) # :nodoc:
215
- yield execute(sql, name)
206
+ def execute_and_free(sql, name = nil, async: false) # :nodoc:
207
+ yield execute(sql, name, async: async)
216
208
  end
217
209
 
218
- def begin_db_transaction
210
+ def begin_db_transaction # :nodoc:
219
211
  execute("BEGIN", "TRANSACTION")
220
212
  end
221
213
 
222
- def begin_isolated_db_transaction(isolation)
214
+ def begin_isolated_db_transaction(isolation) # :nodoc:
223
215
  execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
224
216
  begin_db_transaction
225
217
  end
226
218
 
227
- def commit_db_transaction #:nodoc:
219
+ def commit_db_transaction # :nodoc:
228
220
  execute("COMMIT", "TRANSACTION")
229
221
  end
230
222
 
231
- def exec_rollback_db_transaction #:nodoc:
223
+ def exec_rollback_db_transaction # :nodoc:
232
224
  execute("ROLLBACK", "TRANSACTION")
233
225
  end
234
226
 
235
- def empty_insert_statement_value(primary_key = nil)
227
+ def empty_insert_statement_value(primary_key = nil) # :nodoc:
236
228
  "VALUES ()"
237
229
  end
238
230
 
@@ -270,7 +262,7 @@ module ActiveRecord
270
262
  #
271
263
  # Example:
272
264
  # drop_database('sebastian_development')
273
- def drop_database(name) #:nodoc:
265
+ def drop_database(name) # :nodoc:
274
266
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
275
267
  end
276
268
 
@@ -346,12 +338,12 @@ module ActiveRecord
346
338
  end
347
339
  end
348
340
 
349
- def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
341
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
350
342
  default = extract_new_default_value(default_or_changes)
351
343
  change_column table_name, column_name, nil, default: default
352
344
  end
353
345
 
354
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
346
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
355
347
  unless null || default.nil?
356
348
  execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
357
349
  end
@@ -364,16 +356,16 @@ module ActiveRecord
364
356
  change_column table_name, column_name, nil, comment: comment
365
357
  end
366
358
 
367
- def change_column(table_name, column_name, type, **options) #:nodoc:
359
+ def change_column(table_name, column_name, type, **options) # :nodoc:
368
360
  execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
369
361
  end
370
362
 
371
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
363
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
372
364
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
373
365
  rename_column_indexes(table_name, column_name, new_column_name)
374
366
  end
375
367
 
376
- def add_index(table_name, column_name, **options) #:nodoc:
368
+ def add_index(table_name, column_name, **options) # :nodoc:
377
369
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
378
370
 
379
371
  return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
@@ -427,7 +419,7 @@ module ActiveRecord
427
419
  if supports_check_constraints?
428
420
  scope = quoted_scope(table_name)
429
421
 
430
- chk_info = exec_query(<<~SQL, "SCHEMA")
422
+ sql = <<~SQL
431
423
  SELECT cc.constraint_name AS 'name',
432
424
  cc.check_clause AS 'expression'
433
425
  FROM information_schema.check_constraints cc
@@ -437,6 +429,9 @@ module ActiveRecord
437
429
  AND tc.table_name = #{scope[:name]}
438
430
  AND cc.constraint_schema = #{scope[:schema]}
439
431
  SQL
432
+ sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
433
+
434
+ chk_info = exec_query(sql, "SCHEMA")
440
435
 
441
436
  chk_info.map do |row|
442
437
  options = {
@@ -548,8 +543,12 @@ module ActiveRecord
548
543
  sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
549
544
  elsif insert.update_duplicates?
550
545
  sql << " ON DUPLICATE KEY UPDATE "
551
- sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
552
- sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
546
+ if insert.raw_update_sql?
547
+ sql << insert.raw_update_sql
548
+ else
549
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
550
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
551
+ end
553
552
  end
554
553
 
555
554
  sql
@@ -561,55 +560,77 @@ module ActiveRecord
561
560
  end
562
561
  end
563
562
 
564
- private
565
- def initialize_type_map(m = type_map)
566
- super
563
+ class << self
564
+ private
565
+ def initialize_type_map(m)
566
+ super
567
567
 
568
- m.register_type(%r(char)i) do |sql_type|
569
- limit = extract_limit(sql_type)
570
- Type.lookup(:string, adapter: :mysql2, limit: limit)
568
+ m.register_type(%r(char)i) do |sql_type|
569
+ limit = extract_limit(sql_type)
570
+ Type.lookup(:string, adapter: :mysql2, limit: limit)
571
+ end
572
+
573
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
574
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
575
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
576
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
577
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
578
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
579
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
580
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
581
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
582
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
583
+
584
+ register_integer_type m, %r(^bigint)i, limit: 8
585
+ register_integer_type m, %r(^int)i, limit: 4
586
+ register_integer_type m, %r(^mediumint)i, limit: 3
587
+ register_integer_type m, %r(^smallint)i, limit: 2
588
+ register_integer_type m, %r(^tinyint)i, limit: 1
589
+
590
+ m.alias_type %r(year)i, "integer"
591
+ m.alias_type %r(bit)i, "binary"
592
+
593
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
594
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
571
595
  end
572
596
 
573
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
574
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
575
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
576
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
577
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
578
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
579
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
580
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
581
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
582
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
583
-
584
- register_integer_type m, %r(^bigint)i, limit: 8
585
- register_integer_type m, %r(^int)i, limit: 4
586
- register_integer_type m, %r(^mediumint)i, limit: 3
587
- register_integer_type m, %r(^smallint)i, limit: 2
588
- register_integer_type m, %r(^tinyint)i, limit: 1
589
-
590
- m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
591
- m.alias_type %r(year)i, "integer"
592
- m.alias_type %r(bit)i, "binary"
593
-
594
- m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
595
- m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
596
- end
597
+ def register_integer_type(mapping, key, **options)
598
+ mapping.register_type(key) do |sql_type|
599
+ if /\bunsigned\b/.match?(sql_type)
600
+ Type::UnsignedInteger.new(**options)
601
+ else
602
+ Type::Integer.new(**options)
603
+ end
604
+ end
605
+ end
597
606
 
598
- def register_integer_type(mapping, key, **options)
599
- mapping.register_type(key) do |sql_type|
600
- if /\bunsigned\b/.match?(sql_type)
601
- Type::UnsignedInteger.new(**options)
607
+ def extract_precision(sql_type)
608
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
609
+ super || 0
602
610
  else
603
- Type::Integer.new(**options)
611
+ super
604
612
  end
605
613
  end
614
+ end
615
+
616
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
617
+ TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
618
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
619
+ end
620
+
621
+ private
622
+ def type_map
623
+ emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
606
624
  end
607
625
 
608
- def extract_precision(sql_type)
609
- if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
610
- super || 0
611
- else
612
- super
626
+ def raw_execute(sql, name, async: false)
627
+ materialize_transactions
628
+ mark_transaction_written_if_write(sql)
629
+
630
+ log(sql, name, async: async) do
631
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
632
+ @connection.query(sql)
633
+ end
613
634
  end
614
635
  end
615
636
 
@@ -780,14 +801,13 @@ module ActiveRecord
780
801
  end
781
802
 
782
803
  # Gather up all of the SET variables...
783
- variable_assignments = variables.map do |k, v|
804
+ variable_assignments = variables.filter_map do |k, v|
784
805
  if defaults.include?(v)
785
806
  "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
786
807
  elsif !v.nil?
787
808
  "@@SESSION.#{k} = #{quote(v)}"
788
809
  end
789
- # or else nil; compact to clear nils out
790
- end.compact.join(", ")
810
+ end.join(", ")
791
811
 
792
812
  # ...and send them all in one query
793
813
  execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
@@ -839,10 +859,6 @@ module ActiveRecord
839
859
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
840
860
  end
841
861
 
842
- # Alias MysqlString to work Mashal.load(File.read("legacy_record.dump")).
843
- # TODO: Remove the constant alias once Rails 6.1 has released.
844
- MysqlString = Type::String # :nodoc:
845
-
846
862
  ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
847
863
  Type::ImmutableString.new(true: "1", false: "0", **args)
848
864
  end
@@ -87,6 +87,10 @@ module ActiveRecord
87
87
  comment.hash
88
88
  end
89
89
 
90
+ def virtual?
91
+ false
92
+ end
93
+
90
94
  private
91
95
  def deduplicated
92
96
  @name = -name
@@ -32,29 +32,24 @@ module ActiveRecord
32
32
 
33
33
  def explain(arel, binds = [])
34
34
  sql = "EXPLAIN #{to_sql(arel, binds)}"
35
- start = Concurrent.monotonic_time
35
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
36
36
  result = exec_query(sql, "EXPLAIN", binds)
37
- elapsed = Concurrent.monotonic_time - start
37
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
38
38
 
39
39
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
40
40
  end
41
41
 
42
42
  # Executes the SQL statement in the context of this connection.
43
- def execute(sql, name = nil)
44
- if preventing_writes? && write_query?(sql)
45
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
46
- end
47
-
48
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
49
- # made since we established the connection
50
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
43
+ def execute(sql, name = nil, async: false)
44
+ sql = transform_query(sql)
45
+ check_if_write_query(sql)
51
46
 
52
- super
47
+ raw_execute(sql, name, async: async)
53
48
  end
54
49
 
55
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
50
+ def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
56
51
  if without_prepared_statement?(binds)
57
- execute_and_free(sql, name) do |result|
52
+ execute_and_free(sql, name, async: async) do |result|
58
53
  if result
59
54
  build_result(columns: result.fields, rows: result.to_a)
60
55
  else
@@ -62,7 +57,7 @@ module ActiveRecord
62
57
  end
63
58
  end
64
59
  else
65
- exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
60
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
66
61
  if result
67
62
  build_result(columns: result.fields, rows: result.to_a)
68
63
  else
@@ -72,7 +67,7 @@ module ActiveRecord
72
67
  end
73
68
  end
74
69
 
75
- def exec_delete(sql, name = nil, binds = [])
70
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
76
71
  if without_prepared_statement?(binds)
77
72
  @lock.synchronize do
78
73
  execute_and_free(sql, name) { @connection.affected_rows }
@@ -83,10 +78,28 @@ module ActiveRecord
83
78
  end
84
79
  alias :exec_update :exec_delete
85
80
 
81
+ # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
82
+ # https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
83
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
84
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
85
+
86
+ def high_precision_current_timestamp
87
+ HIGH_PRECISION_CURRENT_TIMESTAMP
88
+ end
89
+
86
90
  private
91
+ def raw_execute(sql, name, async: false)
92
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
93
+ # made since we established the connection
94
+ @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
95
+
96
+ super
97
+ end
98
+
87
99
  def execute_batch(statements, name = nil)
100
+ statements = statements.map { |sql| transform_query(sql) }
88
101
  combine_multi_statements(statements).each do |statement|
89
- execute(statement, name)
102
+ raw_execute(statement, name)
90
103
  end
91
104
  @connection.abandon_results!
92
105
  end
@@ -150,21 +163,20 @@ module ActiveRecord
150
163
  @max_allowed_packet ||= show_variable("max_allowed_packet")
151
164
  end
152
165
 
153
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
154
- if preventing_writes? && write_query?(sql)
155
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
156
- end
166
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
167
+ sql = transform_query(sql)
168
+ check_if_write_query(sql)
157
169
 
158
170
  materialize_transactions
159
171
  mark_transaction_written_if_write(sql)
160
172
 
161
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
173
+ # make sure we carry over any changes to ActiveRecord.default_timezone that have been
162
174
  # made since we established the connection
163
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
175
+ @connection.query_options[:database_timezone] = ActiveRecord.default_timezone
164
176
 
165
177
  type_casted_binds = type_casted_binds(binds)
166
178
 
167
- log(sql, name, binds, type_casted_binds) do
179
+ log(sql, name, binds, type_casted_binds, async: async) do
168
180
  if cache_stmt
169
181
  stmt = @statements[sql] ||= @connection.prepare(sql)
170
182
  else
@@ -6,6 +6,21 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
+ def quote_bound_value(value)
10
+ case value
11
+ when Numeric
12
+ quote(value.to_s)
13
+ when BigDecimal
14
+ quote(value.to_s("F"))
15
+ when true
16
+ "'1'"
17
+ when false
18
+ "'0'"
19
+ else
20
+ quote(value)
21
+ end
22
+ end
23
+
9
24
  def quote_column_name(name)
10
25
  self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
11
26
  end
@@ -34,6 +49,26 @@ module ActiveRecord
34
49
  "x'#{value.hex}'"
35
50
  end
36
51
 
52
+ # Override +type_cast+ we pass to mysql2 Date and Time objects instead
53
+ # of Strings since mysql2 is able to handle those classes more efficiently.
54
+ def type_cast(value) # :nodoc:
55
+ case value
56
+ when ActiveSupport::TimeWithZone
57
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
58
+ # we need to transform it to Time objects but we don't want to
59
+ # transform Time objects to themselves.
60
+ if ActiveRecord.default_timezone == :utc
61
+ value.getutc
62
+ else
63
+ value.getlocal
64
+ end
65
+ when Date, Time
66
+ value
67
+ else
68
+ super
69
+ end
70
+ end
71
+
37
72
  def column_name_matcher
38
73
  COLUMN_NAME
39
74
  end
@@ -69,27 +104,6 @@ module ActiveRecord
69
104
  /ix
70
105
 
71
106
  private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
72
-
73
- private
74
- # Override +_type_cast+ we pass to mysql2 Date and Time objects instead
75
- # of Strings since mysql2 is able to handle those classes more efficiently.
76
- def _type_cast(value)
77
- case value
78
- when ActiveSupport::TimeWithZone
79
- # We need to check explicitly for ActiveSupport::TimeWithZone because
80
- # we need to transform it to Time objects but we don't want to
81
- # transform Time objects to themselves.
82
- if ActiveRecord::Base.default_timezone == :utc
83
- value.getutc
84
- else
85
- value.getlocal
86
- end
87
- when Date, Time
88
- value
89
- else
90
- super
91
- end
92
- end
93
107
  end
94
108
  end
95
109
  end
@@ -30,7 +30,11 @@ module ActiveRecord
30
30
 
31
31
  module ConnectionAdapters
32
32
  class Mysql2Adapter < AbstractMysqlAdapter
33
- ER_BAD_DB_ERROR = 1049
33
+ ER_BAD_DB_ERROR = 1049
34
+ ER_ACCESS_DENIED_ERROR = 1045
35
+ ER_CONN_HOST_ERROR = 2003
36
+ ER_UNKNOWN_HOST_ERROR = 2005
37
+
34
38
  ADAPTER_NAME = "Mysql2"
35
39
 
36
40
  include MySQL::DatabaseStatements
@@ -40,7 +44,11 @@ module ActiveRecord
40
44
  Mysql2::Client.new(config)
41
45
  rescue Mysql2::Error => error
42
46
  if error.error_number == ConnectionAdapters::Mysql2Adapter::ER_BAD_DB_ERROR
43
- raise ActiveRecord::NoDatabaseError
47
+ raise ActiveRecord::NoDatabaseError.db_error(config[:database])
48
+ elsif error.error_number == ConnectionAdapters::Mysql2Adapter::ER_ACCESS_DENIED_ERROR
49
+ raise ActiveRecord::DatabaseConnectionError.username_error(config[:username])
50
+ elsif [ConnectionAdapters::Mysql2Adapter::ER_CONN_HOST_ERROR, ConnectionAdapters::Mysql2Adapter::ER_UNKNOWN_HOST_ERROR].include?(error.error_number)
51
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(config[:host])
44
52
  else
45
53
  raise ActiveRecord::ConnectionNotEstablished, error.message
46
54
  end
@@ -81,11 +89,9 @@ module ActiveRecord
81
89
 
82
90
  # HELPER METHODS ===========================================
83
91
 
84
- def each_hash(result) # :nodoc:
92
+ def each_hash(result, &block) # :nodoc:
85
93
  if block_given?
86
- result.each(as: :hash, symbolize_keys: true) do |row|
87
- yield row
88
- end
94
+ result.each(as: :hash, symbolize_keys: true, &block)
89
95
  else
90
96
  to_enum(:each_hash, result)
91
97
  end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  class PoolConfig # :nodoc:
6
6
  include Mutex_m
7
7
 
8
- attr_reader :db_config, :connection_klass
8
+ attr_reader :db_config, :connection_class, :role, :shard
9
9
  attr_accessor :schema_cache
10
10
 
11
11
  INSTANCES = ObjectSpace::WeakMap.new
@@ -17,21 +17,21 @@ module ActiveRecord
17
17
  end
18
18
  end
19
19
 
20
- def initialize(connection_klass, db_config)
20
+ def initialize(connection_class, db_config, role, shard)
21
21
  super()
22
- @connection_klass = connection_klass
22
+ @connection_class = connection_class
23
23
  @db_config = db_config
24
+ @role = role
25
+ @shard = shard
24
26
  @pool = nil
25
27
  INSTANCES[self] = self
26
28
  end
27
29
 
28
30
  def connection_specification_name
29
- if connection_klass.is_a?(String)
30
- connection_klass
31
- elsif connection_klass.primary_class?
31
+ if connection_class.primary_class?
32
32
  "ActiveRecord::Base"
33
33
  else
34
- connection_klass.name
34
+ connection_class.name
35
35
  end
36
36
  end
37
37
 
@@ -1,25 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/object/blank"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module PostgreSQL
6
8
  class Column < ConnectionAdapters::Column # :nodoc:
7
9
  delegate :oid, :fmod, to: :sql_type_metadata
8
10
 
9
- def initialize(*, serial: nil, **)
11
+ def initialize(*, serial: nil, generated: nil, **)
10
12
  super
11
13
  @serial = serial
14
+ @generated = generated
12
15
  end
13
16
 
14
17
  def serial?
15
18
  @serial
16
19
  end
17
20
 
21
+ def virtual?
22
+ # We assume every generated column is virtual, no matter the concrete type
23
+ @generated.present?
24
+ end
25
+
26
+ def has_default?
27
+ super && !virtual?
28
+ end
29
+
18
30
  def array
19
31
  sql_type_metadata.sql_type.end_with?("[]")
20
32
  end
21
33
  alias :array? :array
22
34
 
35
+ def enum?
36
+ type == :enum
37
+ end
38
+
23
39
  def sql_type
24
40
  super.delete_suffix("[]")
25
41
  end