activerecord 6.1.4 → 7.0.0.rc1

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