activerecord 6.1.6 → 7.0.4

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 (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -975
  3. data/README.rdoc +1 -1
  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 +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -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 +49 -13
  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 +124 -95
  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 +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  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 +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +10 -2
  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 +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  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 +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +124 -134
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +7 -2
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +16 -9
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +217 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. 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)
@@ -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.map { |value| Arel::Nodes.build_quoted(value) }])
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: 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
+
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
567
572
 
568
- m.register_type(%r(char)i) do |sql_type|
569
- limit = extract_limit(sql_type)
570
- Type.lookup(:string, adapter: :mysql2, limit: limit)
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
 
@@ -690,6 +711,14 @@ module ActiveRecord
690
711
  options[:comment] = column.comment
691
712
  end
692
713
 
714
+ unless options.key?(:collation)
715
+ options[:collation] = column.collation
716
+ end
717
+
718
+ unless options.key?(:auto_increment)
719
+ options[:auto_increment] = column.auto_increment?
720
+ end
721
+
693
722
  td = create_table_definition(table_name)
694
723
  cd = td.new_column_definition(column.name, type, **options)
695
724
  schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -780,14 +809,13 @@ module ActiveRecord
780
809
  end
781
810
 
782
811
  # Gather up all of the SET variables...
783
- variable_assignments = variables.map do |k, v|
812
+ variable_assignments = variables.filter_map do |k, v|
784
813
  if defaults.include?(v)
785
814
  "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
786
815
  elsif !v.nil?
787
816
  "@@SESSION.#{k} = #{quote(v)}"
788
817
  end
789
- # or else nil; compact to clear nils out
790
- end.compact.join(", ")
818
+ end.join(", ")
791
819
 
792
820
  # ...and send them all in one query
793
821
  execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
@@ -839,10 +867,6 @@ module ActiveRecord
839
867
  full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
840
868
  end
841
869
 
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
870
  ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
847
871
  Type::ImmutableString.new(true: "1", false: "0", **args)
848
872
  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,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,6 +6,23 @@ 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 Rational
12
+ quote(value.to_f.to_s)
13
+ when Numeric, ActiveSupport::Duration
14
+ quote(value.to_s)
15
+ when BigDecimal
16
+ quote(value.to_s("F"))
17
+ when true
18
+ "'1'"
19
+ when false
20
+ "'0'"
21
+ else
22
+ quote(value)
23
+ end
24
+ end
25
+
9
26
  def quote_column_name(name)
10
27
  self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
11
28
  end
@@ -34,6 +51,26 @@ module ActiveRecord
34
51
  "x'#{value.hex}'"
35
52
  end
36
53
 
54
+ # Override +type_cast+ we pass to mysql2 Date and Time objects instead
55
+ # of Strings since mysql2 is able to handle those classes more efficiently.
56
+ def type_cast(value) # :nodoc:
57
+ case value
58
+ when ActiveSupport::TimeWithZone
59
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
60
+ # we need to transform it to Time objects but we don't want to
61
+ # transform Time objects to themselves.
62
+ if ActiveRecord.default_timezone == :utc
63
+ value.getutc
64
+ else
65
+ value.getlocal
66
+ end
67
+ when Date, Time
68
+ value
69
+ else
70
+ super
71
+ end
72
+ end
73
+
37
74
  def column_name_matcher
38
75
  COLUMN_NAME
39
76
  end
@@ -69,27 +106,6 @@ module ActiveRecord
69
106
  /ix
70
107
 
71
108
  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
109
  end
94
110
  end
95
111
  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(
@@ -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