activerecord 6.1.7 → 7.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActiveRecord
4
4
  ###
5
+ # = Active Record \Result
6
+ #
5
7
  # This class encapsulates a result returned from calling
6
8
  # {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
7
9
  # on any database connection adapter. For example:
@@ -36,6 +38,14 @@ module ActiveRecord
36
38
 
37
39
  attr_reader :columns, :rows, :column_types
38
40
 
41
+ def self.empty(async: false) # :nodoc:
42
+ if async
43
+ EMPTY_ASYNC
44
+ else
45
+ EMPTY
46
+ end
47
+ end
48
+
39
49
  def initialize(columns, rows, column_types = {})
40
50
  @columns = columns
41
51
  @rows = rows
@@ -57,19 +67,14 @@ module ActiveRecord
57
67
  # row as parameter.
58
68
  #
59
69
  # Returns an +Enumerator+ if no block is given.
60
- def each
70
+ def each(&block)
61
71
  if block_given?
62
- hash_rows.each { |row| yield row }
72
+ hash_rows.each(&block)
63
73
  else
64
74
  hash_rows.to_enum { @rows.size }
65
75
  end
66
76
  end
67
77
 
68
- alias :map! :map
69
- alias :collect! :map
70
- deprecate "map!": :map
71
- deprecate "collect!": :map
72
-
73
78
  # Returns true if there are no records, otherwise false.
74
79
  def empty?
75
80
  rows.empty?
@@ -91,6 +96,14 @@ module ActiveRecord
91
96
  n ? hash_rows.last(n) : hash_rows.last
92
97
  end
93
98
 
99
+ def result # :nodoc:
100
+ self
101
+ end
102
+
103
+ def cancel # :nodoc:
104
+ self
105
+ end
106
+
94
107
  def cast_values(type_overrides = {}) # :nodoc:
95
108
  if columns.one?
96
109
  # Separated to avoid allocating an array per row
@@ -98,7 +111,7 @@ module ActiveRecord
98
111
  type = if type_overrides.is_a?(Array)
99
112
  type_overrides.first
100
113
  else
101
- column_type(columns.first, type_overrides)
114
+ column_type(columns.first, 0, type_overrides)
102
115
  end
103
116
 
104
117
  rows.map do |(value)|
@@ -108,7 +121,7 @@ module ActiveRecord
108
121
  types = if type_overrides.is_a?(Array)
109
122
  type_overrides
110
123
  else
111
- columns.map { |name| column_type(name, type_overrides) }
124
+ columns.map.with_index { |name, i| column_type(name, i, type_overrides) }
112
125
  end
113
126
 
114
127
  rows.map do |values|
@@ -124,10 +137,17 @@ module ActiveRecord
124
137
  @hash_rows = nil
125
138
  end
126
139
 
140
+ def freeze # :nodoc:
141
+ hash_rows.freeze
142
+ super
143
+ end
144
+
127
145
  private
128
- def column_type(name, type_overrides = {})
146
+ def column_type(name, index, type_overrides)
129
147
  type_overrides.fetch(name) do
130
- column_types.fetch(name, Type.default_value)
148
+ column_types.fetch(index) do
149
+ column_types.fetch(name, Type.default_value)
150
+ end
131
151
  end
132
152
  end
133
153
 
@@ -171,5 +191,11 @@ module ActiveRecord
171
191
  }
172
192
  end
173
193
  end
194
+
195
+ EMPTY = new([].freeze, [].freeze, {}.freeze).freeze
196
+ private_constant :EMPTY
197
+
198
+ EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
199
+ private_constant :EMPTY_ASYNC
174
200
  end
175
201
  end
@@ -1,24 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/per_thread_registry"
4
-
5
3
  module ActiveRecord
6
4
  # This is a thread locals registry for Active Record. For example:
7
5
  #
8
- # ActiveRecord::RuntimeRegistry.connection_handler
9
- #
10
- # returns the connection handler local to the current thread.
6
+ # ActiveRecord::RuntimeRegistry.sql_runtime
11
7
  #
12
- # See the documentation of ActiveSupport::PerThreadRegistry
13
- # for further details.
14
- class RuntimeRegistry # :nodoc:
15
- extend ActiveSupport::PerThreadRegistry
8
+ # returns the connection handler local to the current unit of execution (either thread of fiber).
9
+ module RuntimeRegistry # :nodoc:
10
+ extend self
11
+
12
+ def sql_runtime
13
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ||= 0.0
14
+ end
16
15
 
17
- attr_accessor :sql_runtime
16
+ def sql_runtime=(runtime)
17
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
18
+ end
19
+
20
+ def async_sql_runtime
21
+ ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] ||= 0.0
22
+ end
18
23
 
19
- [:sql_runtime].each do |val|
20
- class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
21
- class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
24
+ def async_sql_runtime=(runtime)
25
+ ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] = runtime
22
26
  end
27
+
28
+ def reset
29
+ rt, self.sql_runtime = sql_runtime, 0.0
30
+ self.async_sql_runtime = 0.0
31
+ rt
32
+ end
33
+ end
34
+ end
35
+
36
+ ActiveSupport::Notifications.monotonic_subscribe("sql.active_record") do |name, start, finish, id, payload|
37
+ runtime = (finish - start) * 1_000.0
38
+
39
+ if payload[:async]
40
+ ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait])
23
41
  end
42
+ ActiveRecord::RuntimeRegistry.sql_runtime += runtime
24
43
  end
@@ -5,8 +5,8 @@ module ActiveRecord
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
- # Accepts an array or string of SQL conditions and sanitizes
9
- # them into a valid SQL fragment for a WHERE clause.
8
+ # Accepts an array of SQL conditions and sanitizes them into a valid
9
+ # SQL fragment for a WHERE clause.
10
10
  #
11
11
  # sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
12
12
  # # => "name='foo''bar' and group_id=4"
@@ -17,8 +17,19 @@ module ActiveRecord
17
17
  # sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
18
18
  # # => "name='foo''bar' and group_id='4'"
19
19
  #
20
+ # This method will NOT sanitize a SQL string since it won't contain
21
+ # any conditions in it and will return the string as is.
22
+ #
20
23
  # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
21
24
  # # => "name='foo''bar' and group_id='4'"
25
+ #
26
+ # Note that this sanitization method is not schema-aware, hence won't do any type casting
27
+ # and will directly use the database adapter's +quote+ method.
28
+ # For MySQL specifically this means that numeric parameters will be quoted as strings
29
+ # to prevent query manipulation attacks.
30
+ #
31
+ # sanitize_sql_for_conditions(["role = ?", 0])
32
+ # # => "role = '0'"
22
33
  def sanitize_sql_for_conditions(condition)
23
34
  return nil if condition.blank?
24
35
 
@@ -29,8 +40,8 @@ module ActiveRecord
29
40
  end
30
41
  alias :sanitize_sql :sanitize_sql_for_conditions
31
42
 
32
- # Accepts an array, hash, or string of SQL conditions and sanitizes
33
- # them into a valid SQL fragment for a SET clause.
43
+ # Accepts an array or hash of SQL conditions and sanitizes them into
44
+ # a valid SQL fragment for a SET clause.
34
45
  #
35
46
  # sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
36
47
  # # => "name=NULL and group_id=4"
@@ -41,8 +52,19 @@ module ActiveRecord
41
52
  # Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
42
53
  # # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
43
54
  #
55
+ # This method will NOT sanitize a SQL string since it won't contain
56
+ # any conditions in it and will return the string as is.
57
+ #
44
58
  # sanitize_sql_for_assignment("name=NULL and group_id='4'")
45
59
  # # => "name=NULL and group_id='4'"
60
+ #
61
+ # Note that this sanitization method is not schema-aware, hence won't do any type casting
62
+ # and will directly use the database adapter's +quote+ method.
63
+ # For MySQL specifically this means that numeric parameters will be quoted as strings
64
+ # to prevent query manipulation attacks.
65
+ #
66
+ # sanitize_sql_for_assignment(["role = ?", 0])
67
+ # # => "role = '0'"
46
68
  def sanitize_sql_for_assignment(assignments, default_table_name = table_name)
47
69
  case assignments
48
70
  when Array; sanitize_sql_array(assignments)
@@ -54,7 +76,7 @@ module ActiveRecord
54
76
  # Accepts an array, or string of SQL conditions and sanitizes
55
77
  # them into a valid SQL fragment for an ORDER clause.
56
78
  #
57
- # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
79
+ # sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
58
80
  # # => "field(id, 1,3,2)"
59
81
  #
60
82
  # sanitize_sql_for_order("id ASC")
@@ -92,26 +114,32 @@ module ActiveRecord
92
114
  end
93
115
 
94
116
  # Sanitizes a +string+ so that it is safe to use within an SQL
95
- # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
117
+ # LIKE statement. This method uses +escape_character+ to escape all
118
+ # occurrences of itself, "_" and "%".
96
119
  #
97
- # sanitize_sql_like("100%")
98
- # # => "100\\%"
120
+ # sanitize_sql_like("100% true!")
121
+ # # => "100\\% true!"
99
122
  #
100
123
  # sanitize_sql_like("snake_cased_string")
101
124
  # # => "snake\\_cased\\_string"
102
125
  #
103
- # sanitize_sql_like("100%", "!")
104
- # # => "100!%"
126
+ # sanitize_sql_like("100% true!", "!")
127
+ # # => "100!% true!!"
105
128
  #
106
129
  # sanitize_sql_like("snake_cased_string", "!")
107
130
  # # => "snake!_cased!_string"
108
131
  def sanitize_sql_like(string, escape_character = "\\")
109
- pattern = Regexp.union(escape_character, "%", "_")
110
- string.gsub(pattern) { |x| [escape_character, x].join }
132
+ if string.include?(escape_character) && escape_character != "%" && escape_character != "_"
133
+ string = string.gsub(escape_character, '\0\0')
134
+ end
135
+
136
+ string.gsub(/(?=[%_])/, escape_character)
111
137
  end
112
138
 
113
139
  # Accepts an array of conditions. The array has each value
114
- # sanitized and interpolated into the SQL statement.
140
+ # sanitized and interpolated into the SQL statement. If using named bind
141
+ # variables in SQL statements where a colon is required verbatim use a
142
+ # backslash to escape.
115
143
  #
116
144
  # sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
117
145
  # # => "name='foo''bar' and group_id=4"
@@ -119,8 +147,19 @@ module ActiveRecord
119
147
  # sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
120
148
  # # => "name='foo''bar' and group_id=4"
121
149
  #
150
+ # sanitize_sql_array(["TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12\\:MI\\:SS')", date: "foo"])
151
+ # # => "TO_TIMESTAMP('foo', 'YYYY/MM/DD HH12:MI:SS')"
152
+ #
122
153
  # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
123
154
  # # => "name='foo''bar' and group_id='4'"
155
+ #
156
+ # Note that this sanitization method is not schema-aware, hence won't do any type casting
157
+ # and will directly use the database adapter's +quote+ method.
158
+ # For MySQL specifically this means that numeric parameters will be quoted as strings
159
+ # to prevent query manipulation attacks.
160
+ #
161
+ # sanitize_sql_array(["role = ?", 0])
162
+ # # => "role = '0'"
124
163
  def sanitize_sql_array(ary)
125
164
  statement, *values = ary
126
165
  if values.first.is_a?(Hash) && /:\w+/.match?(statement)
@@ -137,14 +176,18 @@ module ActiveRecord
137
176
  def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
138
177
  unexpected = nil
139
178
  args.each do |arg|
140
- next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
179
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
141
180
  (unexpected ||= []) << arg
142
181
  end
143
182
 
144
183
  if unexpected
145
184
  raise(ActiveRecord::UnknownAttributeReference,
146
- "Query method called with non-attribute argument(s): " +
147
- unexpected.map(&:inspect).join(", ")
185
+ "Dangerous query method (method whose arguments are used as raw " \
186
+ "SQL) called with non-attribute argument(s): " \
187
+ "#{unexpected.map(&:inspect).join(", ")}." \
188
+ "This method should not be called with user-provided values, such as request " \
189
+ "parameters or model attributes. Known-safe values can be passed " \
190
+ "by wrapping them in Arel.sql()."
148
191
  )
149
192
  end
150
193
  end
@@ -168,9 +211,11 @@ module ActiveRecord
168
211
  end
169
212
 
170
213
  def replace_named_bind_variables(statement, bind_vars)
171
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
214
+ statement.gsub(/([:\\]?):([a-zA-Z]\w*)/) do |match|
172
215
  if $1 == ":" # skip postgresql casts
173
216
  match # return the whole match
217
+ elsif $1 == "\\" # escaped literal colon
218
+ match[1..-1] # return match with escaping backlash char removed
174
219
  elsif bind_vars.include?(match = $2.to_sym)
175
220
  replace_bind_variable(bind_vars[match])
176
221
  else
@@ -183,13 +228,13 @@ module ActiveRecord
183
228
  if value.respond_to?(:map) && !value.acts_like?(:string)
184
229
  values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
185
230
  if values.empty?
186
- c.quote(nil)
231
+ c.quote(c.cast_bound_value(nil))
187
232
  else
188
- values.map! { |v| c.quote(v) }.join(",")
233
+ values.map! { |v| c.quote(c.cast_bound_value(v)) }.join(",")
189
234
  end
190
235
  else
191
236
  value = value.id_for_database if value.respond_to?(:id_for_database)
192
- c.quote(value)
237
+ c.quote(c.cast_bound_value(value))
193
238
  end
194
239
  end
195
240
 
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  #
11
11
  # Usage:
12
12
  #
13
- # ActiveRecord::Schema.define do
13
+ # ActiveRecord::Schema[7.0].define do
14
14
  # create_table :authors do |t|
15
15
  # t.string :name, null: false
16
16
  # end
@@ -30,32 +30,46 @@ module ActiveRecord
30
30
  # ActiveRecord::Schema is only supported by database adapters that also
31
31
  # support migrations, the two features being very similar.
32
32
  class Schema < Migration::Current
33
- # Eval the given block. All methods available to the current connection
34
- # adapter are available within the block, so you can easily use the
35
- # database definition DSL to build up your schema (
36
- # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
37
- # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
38
- #
39
- # The +info+ hash is optional, and if given is used to define metadata
40
- # about the current schema (currently, only the schema's version):
41
- #
42
- # ActiveRecord::Schema.define(version: 2038_01_19_000001) do
43
- # ...
44
- # end
45
- def self.define(info = {}, &block)
46
- new.define(info, &block)
47
- end
33
+ module Definition
34
+ extend ActiveSupport::Concern
35
+
36
+ module ClassMethods
37
+ # Eval the given block. All methods available to the current connection
38
+ # adapter are available within the block, so you can easily use the
39
+ # database definition DSL to build up your schema (
40
+ # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
41
+ # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
42
+ #
43
+ # The +info+ hash is optional, and if given is used to define metadata
44
+ # about the current schema (currently, only the schema's version):
45
+ #
46
+ # ActiveRecord::Schema[7.0].define(version: 2038_01_19_000001) do
47
+ # ...
48
+ # end
49
+ def define(info = {}, &block)
50
+ new.define(info, &block)
51
+ end
52
+ end
48
53
 
49
- def define(info, &block) # :nodoc:
50
- instance_eval(&block)
54
+ def define(info, &block) # :nodoc:
55
+ instance_eval(&block)
51
56
 
52
- if info[:version].present?
53
57
  connection.schema_migration.create_table
54
- connection.assume_migrated_upto_version(info[:version])
58
+ if info[:version].present?
59
+ connection.assume_migrated_upto_version(info[:version])
60
+ end
61
+
62
+ connection.internal_metadata.create_table_and_set_flags(connection.migration_context.current_environment)
55
63
  end
64
+ end
65
+
66
+ include Definition
56
67
 
57
- ActiveRecord::InternalMetadata.create_table
58
- ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
68
+ def self.[](version)
69
+ @class_for_version ||= {}
70
+ @class_for_version[version] ||= Class.new(Migration::Compatibility.find(version)) do
71
+ include Definition
72
+ end
59
73
  end
60
74
  end
61
75
  end
@@ -7,14 +7,13 @@ module ActiveRecord
7
7
  #
8
8
  # This class is used to dump the database schema for some connection to some
9
9
  # output format (i.e., ActiveRecord::Schema).
10
- class SchemaDumper #:nodoc:
10
+ class SchemaDumper # :nodoc:
11
11
  private_class_method :new
12
12
 
13
13
  ##
14
14
  # :singleton-method:
15
15
  # A list of tables which should not be dumped to the schema.
16
- # Acceptable values are strings as well as regexp if ActiveRecord::Base.schema_format == :ruby.
17
- # Only strings are accepted if ActiveRecord::Base.schema_format == :sql.
16
+ # Acceptable values are strings and regexps.
18
17
  cattr_accessor :ignore_tables, default: []
19
18
 
20
19
  ##
@@ -29,6 +28,18 @@ module ActiveRecord
29
28
  # should not be dumped to db/schema.rb.
30
29
  cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/
31
30
 
31
+ ##
32
+ # :singleton-method:
33
+ # Specify a custom regular expression matching exclusion constraints which name
34
+ # should not be dumped to db/schema.rb.
35
+ cattr_accessor :excl_ignore_pattern, default: /^excl_rails_[0-9a-f]{10}$/
36
+
37
+ ##
38
+ # :singleton-method:
39
+ # Specify a custom regular expression matching unique constraints which name
40
+ # should not be dumped to db/schema.rb.
41
+ cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/
42
+
32
43
  class << self
33
44
  def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
34
45
  connection.create_schema_dumper(generate_options(config)).dump(stream)
@@ -46,7 +57,9 @@ module ActiveRecord
46
57
 
47
58
  def dump(stream)
48
59
  header(stream)
60
+ schemas(stream)
49
61
  extensions(stream)
62
+ types(stream)
50
63
  tables(stream)
51
64
  trailer(stream)
52
65
  stream
@@ -59,6 +72,11 @@ module ActiveRecord
59
72
  @connection = connection
60
73
  @version = connection.migration_context.current_version rescue nil
61
74
  @options = options
75
+ @ignore_tables = [
76
+ ActiveRecord::Base.schema_migrations_table_name,
77
+ ActiveRecord::Base.internal_metadata_table_name,
78
+ self.class.ignore_tables
79
+ ].flatten
62
80
  end
63
81
 
64
82
  # turns 20170404131909 into "2017_04_04_131909"
@@ -73,22 +91,21 @@ module ActiveRecord
73
91
  end
74
92
 
75
93
  def header(stream)
76
- stream.puts <<HEADER
77
- # This file is auto-generated from the current state of the database. Instead
78
- # of editing this file, please use the migrations feature of Active Record to
79
- # incrementally modify your database, and then regenerate this schema definition.
80
- #
81
- # This file is the source Rails uses to define your schema when running `bin/rails
82
- # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
83
- # be faster and is potentially less error prone than running all of your
84
- # migrations from scratch. Old migrations may fail to apply correctly if those
85
- # migrations use external dependencies or application code.
86
- #
87
- # It's strongly recommended that you check this file into your version control system.
88
-
89
- ActiveRecord::Schema.define(#{define_params}) do
90
-
91
- HEADER
94
+ stream.puts <<~HEADER
95
+ # This file is auto-generated from the current state of the database. Instead
96
+ # of editing this file, please use the migrations feature of Active Record to
97
+ # incrementally modify your database, and then regenerate this schema definition.
98
+ #
99
+ # This file is the source Rails uses to define your schema when running `bin/rails
100
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
101
+ # be faster and is potentially less error prone than running all of your
102
+ # migrations from scratch. Old migrations may fail to apply correctly if those
103
+ # migrations use external dependencies or application code.
104
+ #
105
+ # It's strongly recommended that you check this file into your version control system.
106
+
107
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
108
+ HEADER
92
109
  end
93
110
 
94
111
  def trailer(stream)
@@ -99,6 +116,14 @@ HEADER
99
116
  def extensions(stream)
100
117
  end
101
118
 
119
+ # (enum) types are only supported by PostgreSQL
120
+ def types(stream)
121
+ end
122
+
123
+ # schemas are only supported by PostgreSQL
124
+ def schemas(stream)
125
+ end
126
+
102
127
  def tables(stream)
103
128
  sorted_tables = @connection.tables.sort
104
129
 
@@ -107,7 +132,7 @@ HEADER
107
132
  end
108
133
 
109
134
  # dump foreign keys at the end to make sure all dependent tables exist.
110
- if @connection.supports_foreign_keys?
135
+ if @connection.use_foreign_keys?
111
136
  sorted_tables.each do |tbl|
112
137
  foreign_keys(tbl, stream) unless ignored?(tbl)
113
138
  end
@@ -154,6 +179,7 @@ HEADER
154
179
  columns.each do |column|
155
180
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
156
181
  next if column.name == pk
182
+
157
183
  type, colspec = column_spec(column)
158
184
  if type.is_a?(Symbol)
159
185
  tbl.print " t.#{type} #{column.name.inspect}"
@@ -166,12 +192,13 @@ HEADER
166
192
 
167
193
  indexes_in_create(table, tbl)
168
194
  check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
195
+ exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
196
+ unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
169
197
 
170
198
  tbl.puts " end"
171
199
  tbl.puts
172
200
 
173
- tbl.rewind
174
- stream.print tbl.read
201
+ stream.print tbl.string
175
202
  rescue => e
176
203
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
177
204
  stream.puts "# #{e.message}"
@@ -196,6 +223,18 @@ HEADER
196
223
 
197
224
  def indexes_in_create(table, stream)
198
225
  if (indexes = @connection.indexes(table)).any?
226
+ if @connection.supports_exclusion_constraints? && (exclusion_constraints = @connection.exclusion_constraints(table)).any?
227
+ exclusion_constraint_names = exclusion_constraints.collect(&:name)
228
+
229
+ indexes = indexes.reject { |index| exclusion_constraint_names.include?(index.name) }
230
+ end
231
+
232
+ if @connection.supports_unique_constraints? && (unique_constraints = @connection.unique_constraints(table)).any?
233
+ unique_constraint_names = unique_constraints.collect(&:name)
234
+
235
+ indexes = indexes.reject { |index| unique_constraint_names.include?(index.name) }
236
+ end
237
+
199
238
  index_statements = indexes.map do |index|
200
239
  " t.index #{index_parts(index).join(', ')}"
201
240
  end
@@ -214,6 +253,8 @@ HEADER
214
253
  index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present?
215
254
  index_parts << "where: #{index.where.inspect}" if index.where
216
255
  index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index)
256
+ index_parts << "include: #{index.include.inspect}" if index.include
257
+ index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
217
258
  index_parts << "type: #{index.type.inspect}" if index.type
218
259
  index_parts << "comment: #{index.comment.inspect}" if index.comment
219
260
  index_parts
@@ -230,6 +271,8 @@ HEADER
230
271
  parts << "name: #{check_constraint.name.inspect}"
231
272
  end
232
273
 
274
+ parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
275
+
233
276
  " #{parts.join(', ')}"
234
277
  end
235
278
 
@@ -245,7 +288,7 @@ HEADER
245
288
  remove_prefix_and_suffix(foreign_key.to_table).inspect,
246
289
  ]
247
290
 
248
- if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
291
+ if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
249
292
  parts << "column: #{foreign_key.column.inspect}"
250
293
  end
251
294
 
@@ -259,6 +302,8 @@ HEADER
259
302
 
260
303
  parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
261
304
  parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
305
+ parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
306
+ parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?
262
307
 
263
308
  " #{parts.join(', ')}"
264
309
  end
@@ -286,13 +331,17 @@ HEADER
286
331
  end
287
332
 
288
333
  def remove_prefix_and_suffix(table)
334
+ # This method appears at the top when profiling active_record test cases run.
335
+ # Avoid costly calculation when there are no prefix and suffix.
336
+ return table if @options[:table_name_prefix].blank? && @options[:table_name_suffix].blank?
337
+
289
338
  prefix = Regexp.escape(@options[:table_name_prefix].to_s)
290
339
  suffix = Regexp.escape(@options[:table_name_suffix].to_s)
291
340
  table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
292
341
  end
293
342
 
294
343
  def ignored?(table_name)
295
- [ActiveRecord::Base.schema_migrations_table_name, ActiveRecord::Base.internal_metadata_table_name, ignore_tables].flatten.any? do |ignored|
344
+ @ignore_tables.any? do |ignored|
296
345
  ignored === remove_prefix_and_suffix(table_name)
297
346
  end
298
347
  end