activerecord 6.1.7.6 → 7.0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1575 -1016
  3. data/README.rdoc +3 -3
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +20 -22
  17. data/lib/active_record/associations/collection_proxy.rb +15 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +76 -73
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
  93. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
  96. data/lib/active_record/connection_adapters.rb +6 -5
  97. data/lib/active_record/connection_handling.rb +49 -55
  98. data/lib/active_record/core.rb +123 -148
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  100. data/lib/active_record/database_configurations/database_config.rb +12 -9
  101. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  102. data/lib/active_record/database_configurations/url_config.rb +2 -2
  103. data/lib/active_record/database_configurations.rb +15 -32
  104. data/lib/active_record/delegated_type.rb +53 -12
  105. data/lib/active_record/destroy_association_async_job.rb +1 -1
  106. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  107. data/lib/active_record/dynamic_matchers.rb +1 -1
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  109. data/lib/active_record/encryption/cipher.rb +53 -0
  110. data/lib/active_record/encryption/config.rb +44 -0
  111. data/lib/active_record/encryption/configurable.rb +67 -0
  112. data/lib/active_record/encryption/context.rb +35 -0
  113. data/lib/active_record/encryption/contexts.rb +72 -0
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  116. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  118. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  119. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  120. data/lib/active_record/encryption/encryptor.rb +155 -0
  121. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  122. data/lib/active_record/encryption/errors.rb +15 -0
  123. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  124. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  125. data/lib/active_record/encryption/key.rb +28 -0
  126. data/lib/active_record/encryption/key_generator.rb +42 -0
  127. data/lib/active_record/encryption/key_provider.rb +46 -0
  128. data/lib/active_record/encryption/message.rb +33 -0
  129. data/lib/active_record/encryption/message_serializer.rb +90 -0
  130. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  131. data/lib/active_record/encryption/properties.rb +76 -0
  132. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  133. data/lib/active_record/encryption/scheme.rb +99 -0
  134. data/lib/active_record/encryption.rb +55 -0
  135. data/lib/active_record/enum.rb +50 -43
  136. data/lib/active_record/errors.rb +67 -4
  137. data/lib/active_record/explain_registry.rb +11 -6
  138. data/lib/active_record/explain_subscriber.rb +1 -1
  139. data/lib/active_record/fixture_set/file.rb +15 -1
  140. data/lib/active_record/fixture_set/table_row.rb +41 -6
  141. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  142. data/lib/active_record/fixtures.rb +20 -23
  143. data/lib/active_record/future_result.rb +139 -0
  144. data/lib/active_record/gem_version.rb +5 -5
  145. data/lib/active_record/inheritance.rb +55 -17
  146. data/lib/active_record/insert_all.rb +80 -14
  147. data/lib/active_record/integration.rb +4 -3
  148. data/lib/active_record/internal_metadata.rb +1 -5
  149. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  150. data/lib/active_record/locking/optimistic.rb +36 -21
  151. data/lib/active_record/locking/pessimistic.rb +10 -4
  152. data/lib/active_record/log_subscriber.rb +23 -7
  153. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  154. data/lib/active_record/middleware/database_selector.rb +18 -6
  155. data/lib/active_record/middleware/shard_selector.rb +60 -0
  156. data/lib/active_record/migration/command_recorder.rb +8 -9
  157. data/lib/active_record/migration/compatibility.rb +93 -46
  158. data/lib/active_record/migration/join_table.rb +1 -1
  159. data/lib/active_record/migration.rb +167 -87
  160. data/lib/active_record/model_schema.rb +58 -59
  161. data/lib/active_record/nested_attributes.rb +13 -12
  162. data/lib/active_record/no_touching.rb +3 -3
  163. data/lib/active_record/null_relation.rb +2 -6
  164. data/lib/active_record/persistence.rb +231 -61
  165. data/lib/active_record/query_cache.rb +2 -2
  166. data/lib/active_record/query_logs.rb +149 -0
  167. data/lib/active_record/querying.rb +16 -6
  168. data/lib/active_record/railtie.rb +136 -22
  169. data/lib/active_record/railties/controller_runtime.rb +4 -5
  170. data/lib/active_record/railties/databases.rake +78 -136
  171. data/lib/active_record/readonly_attributes.rb +11 -0
  172. data/lib/active_record/reflection.rb +80 -49
  173. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  174. data/lib/active_record/relation/batches.rb +6 -6
  175. data/lib/active_record/relation/calculations.rb +92 -60
  176. data/lib/active_record/relation/delegation.rb +7 -7
  177. data/lib/active_record/relation/finder_methods.rb +31 -35
  178. data/lib/active_record/relation/merger.rb +20 -13
  179. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  180. data/lib/active_record/relation/predicate_builder.rb +1 -6
  181. data/lib/active_record/relation/query_attribute.rb +28 -11
  182. data/lib/active_record/relation/query_methods.rb +304 -68
  183. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  184. data/lib/active_record/relation/spawn_methods.rb +2 -2
  185. data/lib/active_record/relation/where_clause.rb +10 -19
  186. data/lib/active_record/relation.rb +189 -88
  187. data/lib/active_record/result.rb +23 -11
  188. data/lib/active_record/runtime_registry.rb +9 -13
  189. data/lib/active_record/sanitization.rb +17 -12
  190. data/lib/active_record/schema.rb +38 -23
  191. data/lib/active_record/schema_dumper.rb +29 -19
  192. data/lib/active_record/schema_migration.rb +4 -4
  193. data/lib/active_record/scoping/default.rb +60 -13
  194. data/lib/active_record/scoping/named.rb +3 -11
  195. data/lib/active_record/scoping.rb +64 -34
  196. data/lib/active_record/serialization.rb +6 -1
  197. data/lib/active_record/signed_id.rb +3 -3
  198. data/lib/active_record/store.rb +2 -2
  199. data/lib/active_record/suppressor.rb +11 -15
  200. data/lib/active_record/table_metadata.rb +6 -2
  201. data/lib/active_record/tasks/database_tasks.rb +127 -60
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  204. data/lib/active_record/test_databases.rb +1 -1
  205. data/lib/active_record/test_fixtures.rb +9 -6
  206. data/lib/active_record/timestamp.rb +3 -4
  207. data/lib/active_record/transactions.rb +12 -17
  208. data/lib/active_record/translation.rb +3 -3
  209. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  210. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  211. data/lib/active_record/type/internal/timezone.rb +2 -2
  212. data/lib/active_record/type/serialized.rb +9 -5
  213. data/lib/active_record/type/type_map.rb +17 -20
  214. data/lib/active_record/type.rb +1 -2
  215. data/lib/active_record/validations/associated.rb +4 -4
  216. data/lib/active_record/validations/presence.rb +2 -2
  217. data/lib/active_record/validations/uniqueness.rb +4 -4
  218. data/lib/active_record/version.rb +1 -1
  219. data/lib/active_record.rb +225 -27
  220. data/lib/arel/attributes/attribute.rb +0 -8
  221. data/lib/arel/crud.rb +28 -22
  222. data/lib/arel/delete_manager.rb +18 -4
  223. data/lib/arel/filter_predications.rb +9 -0
  224. data/lib/arel/insert_manager.rb +2 -3
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/casted.rb +1 -1
  227. data/lib/arel/nodes/delete_statement.rb +12 -13
  228. data/lib/arel/nodes/filter.rb +10 -0
  229. data/lib/arel/nodes/function.rb +1 -0
  230. data/lib/arel/nodes/insert_statement.rb +2 -2
  231. data/lib/arel/nodes/select_core.rb +2 -2
  232. data/lib/arel/nodes/select_statement.rb +2 -2
  233. data/lib/arel/nodes/update_statement.rb +8 -3
  234. data/lib/arel/nodes.rb +1 -0
  235. data/lib/arel/predications.rb +11 -3
  236. data/lib/arel/select_manager.rb +10 -4
  237. data/lib/arel/table.rb +0 -1
  238. data/lib/arel/tree_manager.rb +0 -12
  239. data/lib/arel/update_manager.rb +18 -4
  240. data/lib/arel/visitors/dot.rb +80 -90
  241. data/lib/arel/visitors/mysql.rb +8 -2
  242. data/lib/arel/visitors/postgresql.rb +0 -10
  243. data/lib/arel/visitors/to_sql.rb +58 -2
  244. data/lib/arel.rb +2 -1
  245. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  247. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  248. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  249. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  250. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  251. metadata +55 -11
@@ -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)
@@ -174,7 +175,7 @@ module ActiveRecord
174
175
 
175
176
  # REFERENTIAL INTEGRITY ====================================
176
177
 
177
- def disable_referential_integrity #:nodoc:
178
+ def disable_referential_integrity # :nodoc:
178
179
  old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
179
180
 
180
181
  begin
@@ -185,54 +186,40 @@ module ActiveRecord
185
186
  end
186
187
  end
187
188
 
188
- # CONNECTION MANAGEMENT ====================================
189
-
190
- def clear_cache! # :nodoc:
191
- reload_type_map
192
- super
193
- end
194
-
195
189
  #--
196
190
  # DATABASE STATEMENTS ======================================
197
191
  #++
198
192
 
199
193
  # 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
194
+ def execute(sql, name = nil, async: false)
195
+ raw_execute(sql, name, async: async)
209
196
  end
210
197
 
211
198
  # Mysql2Adapter doesn't have to free a result after using it, but we use this method
212
199
  # to write stuff in an abstract way without concerning ourselves about whether it
213
200
  # needs to be explicitly freed or not.
214
- def execute_and_free(sql, name = nil) # :nodoc:
215
- yield execute(sql, name)
201
+ def execute_and_free(sql, name = nil, async: false) # :nodoc:
202
+ yield execute(sql, name, async: async)
216
203
  end
217
204
 
218
- def begin_db_transaction
205
+ def begin_db_transaction # :nodoc:
219
206
  execute("BEGIN", "TRANSACTION")
220
207
  end
221
208
 
222
- def begin_isolated_db_transaction(isolation)
209
+ def begin_isolated_db_transaction(isolation) # :nodoc:
223
210
  execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
224
211
  begin_db_transaction
225
212
  end
226
213
 
227
- def commit_db_transaction #:nodoc:
214
+ def commit_db_transaction # :nodoc:
228
215
  execute("COMMIT", "TRANSACTION")
229
216
  end
230
217
 
231
- def exec_rollback_db_transaction #:nodoc:
218
+ def exec_rollback_db_transaction # :nodoc:
232
219
  execute("ROLLBACK", "TRANSACTION")
233
220
  end
234
221
 
235
- def empty_insert_statement_value(primary_key = nil)
222
+ def empty_insert_statement_value(primary_key = nil) # :nodoc:
236
223
  "VALUES ()"
237
224
  end
238
225
 
@@ -270,7 +257,7 @@ module ActiveRecord
270
257
  #
271
258
  # Example:
272
259
  # drop_database('sebastian_development')
273
- def drop_database(name) #:nodoc:
260
+ def drop_database(name) # :nodoc:
274
261
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
275
262
  end
276
263
 
@@ -346,12 +333,12 @@ module ActiveRecord
346
333
  end
347
334
  end
348
335
 
349
- def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
336
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
350
337
  default = extract_new_default_value(default_or_changes)
351
338
  change_column table_name, column_name, nil, default: default
352
339
  end
353
340
 
354
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
341
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
355
342
  unless null || default.nil?
356
343
  execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
357
344
  end
@@ -364,16 +351,16 @@ module ActiveRecord
364
351
  change_column table_name, column_name, nil, comment: comment
365
352
  end
366
353
 
367
- def change_column(table_name, column_name, type, **options) #:nodoc:
354
+ def change_column(table_name, column_name, type, **options) # :nodoc:
368
355
  execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
369
356
  end
370
357
 
371
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
358
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
372
359
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
373
360
  rename_column_indexes(table_name, column_name, new_column_name)
374
361
  end
375
362
 
376
- def add_index(table_name, column_name, **options) #:nodoc:
363
+ def add_index(table_name, column_name, **options) # :nodoc:
377
364
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
378
365
 
379
366
  return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
@@ -411,7 +398,7 @@ module ActiveRecord
411
398
 
412
399
  fk_info.map do |row|
413
400
  options = {
414
- column: row["column"],
401
+ column: unquote_identifier(row["column"]),
415
402
  name: row["name"],
416
403
  primary_key: row["primary_key"]
417
404
  }
@@ -419,7 +406,7 @@ module ActiveRecord
419
406
  options[:on_update] = extract_foreign_key_action(row["on_update"])
420
407
  options[:on_delete] = extract_foreign_key_action(row["on_delete"])
421
408
 
422
- ForeignKeyDefinition.new(table_name, row["to_table"], options)
409
+ ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
423
410
  end
424
411
  end
425
412
 
@@ -427,7 +414,7 @@ module ActiveRecord
427
414
  if supports_check_constraints?
428
415
  scope = quoted_scope(table_name)
429
416
 
430
- chk_info = exec_query(<<~SQL, "SCHEMA")
417
+ sql = <<~SQL
431
418
  SELECT cc.constraint_name AS 'name',
432
419
  cc.check_clause AS 'expression'
433
420
  FROM information_schema.check_constraints cc
@@ -437,13 +424,17 @@ module ActiveRecord
437
424
  AND tc.table_name = #{scope[:name]}
438
425
  AND cc.constraint_schema = #{scope[:schema]}
439
426
  SQL
427
+ sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
428
+
429
+ chk_info = exec_query(sql, "SCHEMA")
440
430
 
441
431
  chk_info.map do |row|
442
432
  options = {
443
433
  name: row["name"]
444
434
  }
445
435
  expression = row["expression"]
446
- expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
436
+ expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
437
+ expression = strip_whitespace_characters(expression)
447
438
  CheckConstraintDefinition.new(table_name, expression, options)
448
439
  end
449
440
  else
@@ -548,8 +539,12 @@ module ActiveRecord
548
539
  sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
549
540
  elsif insert.update_duplicates?
550
541
  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(",")
542
+ if insert.raw_update_sql?
543
+ sql << insert.raw_update_sql
544
+ else
545
+ sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
546
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
547
+ end
553
548
  end
554
549
 
555
550
  sql
@@ -561,55 +556,87 @@ module ActiveRecord
561
556
  end
562
557
  end
563
558
 
564
- private
565
- def initialize_type_map(m = type_map)
566
- super
559
+ class << self
560
+ private
561
+ def initialize_type_map(m)
562
+ super
563
+
564
+ m.register_type(%r(char)i) do |sql_type|
565
+ limit = extract_limit(sql_type)
566
+ Type.lookup(:string, adapter: :mysql2, limit: limit)
567
+ end
567
568
 
568
- m.register_type(%r(char)i) do |sql_type|
569
- limit = extract_limit(sql_type)
570
- Type.lookup(:string, adapter: :mysql2, limit: limit)
569
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
570
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
571
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
572
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
573
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
574
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
575
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
576
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
577
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
578
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
579
+
580
+ register_integer_type m, %r(^bigint)i, limit: 8
581
+ register_integer_type m, %r(^int)i, limit: 4
582
+ register_integer_type m, %r(^mediumint)i, limit: 3
583
+ register_integer_type m, %r(^smallint)i, limit: 2
584
+ register_integer_type m, %r(^tinyint)i, limit: 1
585
+
586
+ m.alias_type %r(year)i, "integer"
587
+ m.alias_type %r(bit)i, "binary"
588
+
589
+ m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
590
+ m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
571
591
  end
572
592
 
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
-
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)
593
+ def register_integer_type(mapping, key, **options)
594
+ mapping.register_type(key) do |sql_type|
595
+ if /\bunsigned\b/.match?(sql_type)
596
+ Type::UnsignedInteger.new(**options)
597
+ else
598
+ Type::Integer.new(**options)
599
+ end
600
+ end
601
+ end
602
+
603
+ def extract_precision(sql_type)
604
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
605
+ super || 0
602
606
  else
603
- Type::Integer.new(**options)
607
+ super
604
608
  end
605
609
  end
610
+ end
611
+
612
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
613
+ TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
614
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
615
+ end
616
+
617
+ private
618
+ def strip_whitespace_characters(expression)
619
+ expression = expression.gsub(/\\n|\\\\/, "")
620
+ expression = expression.gsub(/\s{2,}/, " ")
621
+ expression
606
622
  end
607
623
 
608
- def extract_precision(sql_type)
609
- if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
610
- super || 0
611
- else
612
- super
624
+ def text_type?(type)
625
+ TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
626
+ end
627
+
628
+ def type_map
629
+ emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
630
+ end
631
+
632
+ def raw_execute(sql, name, async: false)
633
+ materialize_transactions
634
+ mark_transaction_written_if_write(sql)
635
+
636
+ log(sql, name, async: async) do
637
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
638
+ @connection.query(sql)
639
+ end
613
640
  end
614
641
  end
615
642
 
@@ -690,6 +717,14 @@ module ActiveRecord
690
717
  options[:comment] = column.comment
691
718
  end
692
719
 
720
+ unless options.key?(:collation)
721
+ options[:collation] = column.collation if text_type?(type)
722
+ end
723
+
724
+ unless options.key?(:auto_increment)
725
+ options[:auto_increment] = column.auto_increment?
726
+ end
727
+
693
728
  td = create_table_definition(table_name)
694
729
  cd = td.new_column_definition(column.name, type, **options)
695
730
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -780,14 +815,13 @@ module ActiveRecord
780
815
  end
781
816
 
782
817
  # Gather up all of the SET variables...
783
- variable_assignments = variables.map do |k, v|
818
+ variable_assignments = variables.filter_map do |k, v|
784
819
  if defaults.include?(v)
785
820
  "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
786
821
  elsif !v.nil?
787
822
  "@@SESSION.#{k} = #{quote(v)}"
788
823
  end
789
- # or else nil; compact to clear nils out
790
- end.compact.join(", ")
824
+ end.join(", ")
791
825
 
792
826
  # ...and send them all in one query
793
827
  execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
@@ -839,10 +873,6 @@ module ActiveRecord
839
873
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
840
874
  end
841
875
 
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
876
  ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
847
877
  Type::ImmutableString.new(true: "1", false: "0", **args)
848
878
  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
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  end
21
21
 
22
22
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
23
- :desc, :describe, :set, :show, :use
23
+ :desc, :describe, :set, :show, :use, :kill
24
24
  ) # :nodoc:
25
25
  private_constant :READ_QUERY
26
26
 
@@ -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,12 +78,30 @@ 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)
103
+ @connection.abandon_results!
90
104
  end
91
- @connection.abandon_results!
92
105
  end
93
106
 
94
107
  def default_insert_value(column)
@@ -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,12 +6,32 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
10
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
11
+
12
+ def quote_bound_value(value)
13
+ case value
14
+ when Rational
15
+ quote(value.to_f.to_s)
16
+ when Numeric, ActiveSupport::Duration
17
+ quote(value.to_s)
18
+ when BigDecimal
19
+ quote(value.to_s("F"))
20
+ when true
21
+ "'1'"
22
+ when false
23
+ "'0'"
24
+ else
25
+ quote(value)
26
+ end
27
+ end
28
+
9
29
  def quote_column_name(name)
10
- self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
30
+ QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
11
31
  end
12
32
 
13
33
  def quote_table_name(name)
14
- self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
34
+ QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
15
35
  end
16
36
 
17
37
  def unquoted_true
@@ -34,6 +54,34 @@ module ActiveRecord
34
54
  "x'#{value.hex}'"
35
55
  end
36
56
 
57
+ def unquote_identifier(identifier)
58
+ if identifier && identifier.start_with?("`")
59
+ identifier[1..-2]
60
+ else
61
+ identifier
62
+ end
63
+ end
64
+
65
+ # Override +type_cast+ we pass to mysql2 Date and Time objects instead
66
+ # of Strings since mysql2 is able to handle those classes more efficiently.
67
+ def type_cast(value) # :nodoc:
68
+ case value
69
+ when ActiveSupport::TimeWithZone
70
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
71
+ # we need to transform it to Time objects but we don't want to
72
+ # transform Time objects to themselves.
73
+ if ActiveRecord.default_timezone == :utc
74
+ value.getutc
75
+ else
76
+ value.getlocal
77
+ end
78
+ when Date, Time
79
+ value
80
+ else
81
+ super
82
+ end
83
+ end
84
+
37
85
  def column_name_matcher
38
86
  COLUMN_NAME
39
87
  end
@@ -69,27 +117,6 @@ module ActiveRecord
69
117
  /ix
70
118
 
71
119
  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
120
  end
94
121
  end
95
122
  end
@@ -90,7 +90,10 @@ module ActiveRecord
90
90
  end
91
91
 
92
92
  def integer_like_primary_key_type(type, options)
93
- options[:auto_increment] = true
93
+ unless options[:auto_increment] == false
94
+ options[:auto_increment] = true
95
+ end
96
+
94
97
  type
95
98
  end
96
99
  end
@@ -53,7 +53,13 @@ module ActiveRecord
53
53
  end
54
54
 
55
55
  def schema_precision(column)
56
- super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
56
+ if /\Atime(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
57
+ nil
58
+ elsif column.type == :datetime
59
+ column.precision == 0 ? "nil" : super
60
+ else
61
+ super
62
+ end
57
63
  end
58
64
 
59
65
  def schema_collation(column)
@@ -158,18 +158,37 @@ module ActiveRecord
158
158
  MySQL::TableDefinition.new(self, name, **options)
159
159
  end
160
160
 
161
+ def default_type(table_name, field_name)
162
+ match = create_table_info(table_name)&.match(/`#{field_name}` (.+) DEFAULT ('|\d+|[A-z]+)/)
163
+ default_pre = match[2] if match
164
+
165
+ if default_pre == "'"
166
+ :string
167
+ elsif default_pre&.match?(/^\d+$/)
168
+ :integer
169
+ elsif default_pre&.match?(/^[A-z]+$/)
170
+ :function
171
+ end
172
+ end
173
+
161
174
  def new_column_from_field(table_name, field)
175
+ field_name = field.fetch(:Field)
162
176
  type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
163
177
  default, default_function = field[:Default], nil
164
178
 
165
179
  if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
180
+ default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[:Extra])
166
181
  default, default_function = nil, default
167
182
  elsif type_metadata.extra == "DEFAULT_GENERATED"
168
183
  default = +"(#{default})" unless default.start_with?("(")
169
184
  default, default_function = nil, default
170
- elsif type_metadata.type == :text && default
185
+ elsif type_metadata.type == :text && default&.start_with?("'")
171
186
  # strip and unescape quotes
172
187
  default = default[1...-1].gsub("\\'", "'")
188
+ elsif default&.match?(/\A\d/)
189
+ # Its a number so we can skip the query to check if it is a function
190
+ elsif default && default_type(table_name, field_name) == :function
191
+ default, default_function = nil, default
173
192
  end
174
193
 
175
194
  MySQL::Column.new(