activerecord 6.1.7 → 7.0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1615 -1001
  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 +52 -73
  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 +82 -53
  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 -141
  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 +306 -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(