activerecord 5.2.8 → 7.0.2

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 (364) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1393 -587
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +10 -9
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +122 -47
  10. data/lib/active_record/associations/association_scope.rb +24 -24
  11. data/lib/active_record/associations/belongs_to_association.rb +67 -49
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
  13. data/lib/active_record/associations/builder/association.rb +52 -23
  14. data/lib/active_record/associations/builder/belongs_to.rb +44 -61
  15. data/lib/active_record/associations/builder/collection_association.rb +17 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +10 -3
  18. data/lib/active_record/associations/builder/has_one.rb +35 -3
  19. data/lib/active_record/associations/builder/singular_association.rb +5 -3
  20. data/lib/active_record/associations/collection_association.rb +59 -50
  21. data/lib/active_record/associations/collection_proxy.rb +32 -23
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +27 -14
  25. data/lib/active_record/associations/has_many_through_association.rb +26 -19
  26. data/lib/active_record/associations/has_one_association.rb +52 -37
  27. data/lib/active_record/associations/has_one_through_association.rb +6 -6
  28. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +97 -62
  31. data/lib/active_record/associations/preloader/association.rb +220 -60
  32. data/lib/active_record/associations/preloader/batch.rb +48 -0
  33. data/lib/active_record/associations/preloader/branch.rb +147 -0
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -40
  35. data/lib/active_record/associations/preloader.rb +44 -105
  36. data/lib/active_record/associations/singular_association.rb +9 -17
  37. data/lib/active_record/associations/through_association.rb +4 -4
  38. data/lib/active_record/associations.rb +207 -66
  39. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  40. data/lib/active_record/attribute_assignment.rb +17 -19
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
  42. data/lib/active_record/attribute_methods/dirty.rb +141 -47
  43. data/lib/active_record/attribute_methods/primary_key.rb +22 -27
  44. data/lib/active_record/attribute_methods/query.rb +6 -10
  45. data/lib/active_record/attribute_methods/read.rb +15 -55
  46. data/lib/active_record/attribute_methods/serialization.rb +77 -18
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
  48. data/lib/active_record/attribute_methods/write.rb +18 -37
  49. data/lib/active_record/attribute_methods.rb +90 -153
  50. data/lib/active_record/attributes.rb +38 -12
  51. data/lib/active_record/autosave_association.rb +50 -50
  52. data/lib/active_record/base.rb +23 -18
  53. data/lib/active_record/callbacks.rb +159 -44
  54. data/lib/active_record/coders/yaml_column.rb +12 -3
  55. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
  63. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
  71. data/lib/active_record/connection_adapters/column.rb +33 -11
  72. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  73. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  74. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  75. data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
  76. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  77. data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
  78. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  80. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
  81. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
  82. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  83. data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
  84. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  85. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
  95. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
  116. data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
  122. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
  123. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  124. data/lib/active_record/connection_adapters.rb +53 -0
  125. data/lib/active_record/connection_handling.rb +292 -38
  126. data/lib/active_record/core.rb +385 -158
  127. data/lib/active_record/counter_cache.rb +8 -30
  128. data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
  129. data/lib/active_record/database_configurations/database_config.rb +83 -0
  130. data/lib/active_record/database_configurations/hash_config.rb +154 -0
  131. data/lib/active_record/database_configurations/url_config.rb +53 -0
  132. data/lib/active_record/database_configurations.rb +256 -0
  133. data/lib/active_record/delegated_type.rb +250 -0
  134. data/lib/active_record/destroy_association_async_job.rb +36 -0
  135. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  136. data/lib/active_record/dynamic_matchers.rb +4 -5
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +44 -0
  140. data/lib/active_record/encryption/configurable.rb +61 -0
  141. data/lib/active_record/encryption/context.rb +35 -0
  142. data/lib/active_record/encryption/contexts.rb +72 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +155 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +42 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_serializer.rb +90 -0
  159. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  160. data/lib/active_record/encryption/properties.rb +76 -0
  161. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  162. data/lib/active_record/encryption/scheme.rb +99 -0
  163. data/lib/active_record/encryption.rb +55 -0
  164. data/lib/active_record/enum.rb +130 -51
  165. data/lib/active_record/errors.rb +129 -23
  166. data/lib/active_record/explain.rb +10 -6
  167. data/lib/active_record/explain_registry.rb +11 -6
  168. data/lib/active_record/explain_subscriber.rb +1 -1
  169. data/lib/active_record/fixture_set/file.rb +22 -15
  170. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  171. data/lib/active_record/fixture_set/render_context.rb +17 -0
  172. data/lib/active_record/fixture_set/table_row.rb +187 -0
  173. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  174. data/lib/active_record/fixtures.rb +206 -490
  175. data/lib/active_record/future_result.rb +139 -0
  176. data/lib/active_record/gem_version.rb +3 -3
  177. data/lib/active_record/inheritance.rb +104 -37
  178. data/lib/active_record/insert_all.rb +278 -0
  179. data/lib/active_record/integration.rb +69 -18
  180. data/lib/active_record/internal_metadata.rb +24 -9
  181. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  182. data/lib/active_record/locking/optimistic.rb +41 -26
  183. data/lib/active_record/locking/pessimistic.rb +18 -8
  184. data/lib/active_record/log_subscriber.rb +46 -35
  185. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  186. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  187. data/lib/active_record/middleware/database_selector.rb +82 -0
  188. data/lib/active_record/middleware/shard_selector.rb +60 -0
  189. data/lib/active_record/migration/command_recorder.rb +96 -44
  190. data/lib/active_record/migration/compatibility.rb +246 -64
  191. data/lib/active_record/migration/join_table.rb +1 -2
  192. data/lib/active_record/migration.rb +266 -187
  193. data/lib/active_record/model_schema.rb +165 -52
  194. data/lib/active_record/nested_attributes.rb +17 -19
  195. data/lib/active_record/no_touching.rb +11 -4
  196. data/lib/active_record/null_relation.rb +2 -7
  197. data/lib/active_record/persistence.rb +467 -92
  198. data/lib/active_record/query_cache.rb +21 -4
  199. data/lib/active_record/query_logs.rb +138 -0
  200. data/lib/active_record/querying.rb +51 -24
  201. data/lib/active_record/railtie.rb +224 -57
  202. data/lib/active_record/railties/console_sandbox.rb +2 -4
  203. data/lib/active_record/railties/controller_runtime.rb +31 -36
  204. data/lib/active_record/railties/databases.rake +369 -101
  205. data/lib/active_record/readonly_attributes.rb +15 -0
  206. data/lib/active_record/reflection.rb +170 -137
  207. data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
  208. data/lib/active_record/relation/batches.rb +46 -37
  209. data/lib/active_record/relation/calculations.rb +168 -96
  210. data/lib/active_record/relation/delegation.rb +37 -52
  211. data/lib/active_record/relation/finder_methods.rb +79 -58
  212. data/lib/active_record/relation/from_clause.rb +5 -1
  213. data/lib/active_record/relation/merger.rb +50 -51
  214. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  215. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  216. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  217. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  218. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  219. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  220. data/lib/active_record/relation/predicate_builder.rb +58 -46
  221. data/lib/active_record/relation/query_attribute.rb +9 -10
  222. data/lib/active_record/relation/query_methods.rb +685 -208
  223. data/lib/active_record/relation/record_fetch_warning.rb +9 -11
  224. data/lib/active_record/relation/spawn_methods.rb +10 -10
  225. data/lib/active_record/relation/where_clause.rb +108 -64
  226. data/lib/active_record/relation.rb +515 -151
  227. data/lib/active_record/result.rb +78 -42
  228. data/lib/active_record/runtime_registry.rb +9 -13
  229. data/lib/active_record/sanitization.rb +29 -44
  230. data/lib/active_record/schema.rb +37 -31
  231. data/lib/active_record/schema_dumper.rb +74 -23
  232. data/lib/active_record/schema_migration.rb +7 -9
  233. data/lib/active_record/scoping/default.rb +62 -17
  234. data/lib/active_record/scoping/named.rb +17 -32
  235. data/lib/active_record/scoping.rb +70 -41
  236. data/lib/active_record/secure_token.rb +16 -8
  237. data/lib/active_record/serialization.rb +6 -4
  238. data/lib/active_record/signed_id.rb +116 -0
  239. data/lib/active_record/statement_cache.rb +49 -6
  240. data/lib/active_record/store.rb +88 -9
  241. data/lib/active_record/suppressor.rb +13 -17
  242. data/lib/active_record/table_metadata.rb +42 -43
  243. data/lib/active_record/tasks/database_tasks.rb +352 -94
  244. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  245. data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
  246. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  247. data/lib/active_record/test_databases.rb +24 -0
  248. data/lib/active_record/test_fixtures.rb +287 -0
  249. data/lib/active_record/timestamp.rb +44 -34
  250. data/lib/active_record/touch_later.rb +23 -22
  251. data/lib/active_record/transactions.rb +67 -128
  252. data/lib/active_record/translation.rb +3 -3
  253. data/lib/active_record/type/adapter_specific_registry.rb +34 -19
  254. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  255. data/lib/active_record/type/internal/timezone.rb +2 -2
  256. data/lib/active_record/type/serialized.rb +7 -4
  257. data/lib/active_record/type/time.rb +10 -0
  258. data/lib/active_record/type/type_map.rb +17 -21
  259. data/lib/active_record/type/unsigned_integer.rb +0 -1
  260. data/lib/active_record/type.rb +9 -5
  261. data/lib/active_record/type_caster/connection.rb +15 -15
  262. data/lib/active_record/type_caster/map.rb +8 -8
  263. data/lib/active_record/validations/associated.rb +2 -3
  264. data/lib/active_record/validations/numericality.rb +35 -0
  265. data/lib/active_record/validations/uniqueness.rb +39 -31
  266. data/lib/active_record/validations.rb +4 -3
  267. data/lib/active_record.rb +209 -32
  268. data/lib/arel/alias_predication.rb +9 -0
  269. data/lib/arel/attributes/attribute.rb +33 -0
  270. data/lib/arel/collectors/bind.rb +29 -0
  271. data/lib/arel/collectors/composite.rb +39 -0
  272. data/lib/arel/collectors/plain_string.rb +20 -0
  273. data/lib/arel/collectors/sql_string.rb +27 -0
  274. data/lib/arel/collectors/substitute_binds.rb +35 -0
  275. data/lib/arel/crud.rb +48 -0
  276. data/lib/arel/delete_manager.rb +32 -0
  277. data/lib/arel/errors.rb +9 -0
  278. data/lib/arel/expressions.rb +29 -0
  279. data/lib/arel/factory_methods.rb +49 -0
  280. data/lib/arel/filter_predications.rb +9 -0
  281. data/lib/arel/insert_manager.rb +48 -0
  282. data/lib/arel/math.rb +45 -0
  283. data/lib/arel/nodes/and.rb +32 -0
  284. data/lib/arel/nodes/ascending.rb +23 -0
  285. data/lib/arel/nodes/binary.rb +126 -0
  286. data/lib/arel/nodes/bind_param.rb +44 -0
  287. data/lib/arel/nodes/case.rb +55 -0
  288. data/lib/arel/nodes/casted.rb +62 -0
  289. data/lib/arel/nodes/comment.rb +29 -0
  290. data/lib/arel/nodes/count.rb +12 -0
  291. data/lib/arel/nodes/delete_statement.rb +44 -0
  292. data/lib/arel/nodes/descending.rb +23 -0
  293. data/lib/arel/nodes/equality.rb +15 -0
  294. data/lib/arel/nodes/extract.rb +24 -0
  295. data/lib/arel/nodes/false.rb +16 -0
  296. data/lib/arel/nodes/filter.rb +10 -0
  297. data/lib/arel/nodes/full_outer_join.rb +8 -0
  298. data/lib/arel/nodes/function.rb +45 -0
  299. data/lib/arel/nodes/grouping.rb +11 -0
  300. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  301. data/lib/arel/nodes/in.rb +15 -0
  302. data/lib/arel/nodes/infix_operation.rb +92 -0
  303. data/lib/arel/nodes/inner_join.rb +8 -0
  304. data/lib/arel/nodes/insert_statement.rb +37 -0
  305. data/lib/arel/nodes/join_source.rb +20 -0
  306. data/lib/arel/nodes/matches.rb +18 -0
  307. data/lib/arel/nodes/named_function.rb +23 -0
  308. data/lib/arel/nodes/node.rb +51 -0
  309. data/lib/arel/nodes/node_expression.rb +13 -0
  310. data/lib/arel/nodes/ordering.rb +27 -0
  311. data/lib/arel/nodes/outer_join.rb +8 -0
  312. data/lib/arel/nodes/over.rb +15 -0
  313. data/lib/arel/nodes/regexp.rb +16 -0
  314. data/lib/arel/nodes/right_outer_join.rb +8 -0
  315. data/lib/arel/nodes/select_core.rb +67 -0
  316. data/lib/arel/nodes/select_statement.rb +41 -0
  317. data/lib/arel/nodes/sql_literal.rb +19 -0
  318. data/lib/arel/nodes/string_join.rb +11 -0
  319. data/lib/arel/nodes/table_alias.rb +31 -0
  320. data/lib/arel/nodes/terminal.rb +16 -0
  321. data/lib/arel/nodes/true.rb +16 -0
  322. data/lib/arel/nodes/unary.rb +44 -0
  323. data/lib/arel/nodes/unary_operation.rb +20 -0
  324. data/lib/arel/nodes/unqualified_column.rb +22 -0
  325. data/lib/arel/nodes/update_statement.rb +46 -0
  326. data/lib/arel/nodes/values_list.rb +9 -0
  327. data/lib/arel/nodes/window.rb +126 -0
  328. data/lib/arel/nodes/with.rb +11 -0
  329. data/lib/arel/nodes.rb +71 -0
  330. data/lib/arel/order_predications.rb +13 -0
  331. data/lib/arel/predications.rb +258 -0
  332. data/lib/arel/select_manager.rb +276 -0
  333. data/lib/arel/table.rb +117 -0
  334. data/lib/arel/tree_manager.rb +60 -0
  335. data/lib/arel/update_manager.rb +48 -0
  336. data/lib/arel/visitors/dot.rb +298 -0
  337. data/lib/arel/visitors/mysql.rb +99 -0
  338. data/lib/arel/visitors/postgresql.rb +110 -0
  339. data/lib/arel/visitors/sqlite.rb +38 -0
  340. data/lib/arel/visitors/to_sql.rb +955 -0
  341. data/lib/arel/visitors/visitor.rb +45 -0
  342. data/lib/arel/visitors.rb +13 -0
  343. data/lib/arel/window_predications.rb +9 -0
  344. data/lib/arel.rb +55 -0
  345. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  346. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  347. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  348. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  349. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  350. data/lib/rails/generators/active_record/migration.rb +19 -2
  351. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  352. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  353. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  354. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  355. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  356. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  357. metadata +162 -32
  358. data/lib/active_record/attribute_decorators.rb +0 -90
  359. data/lib/active_record/collection_cache_key.rb +0 -53
  360. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  361. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  362. data/lib/active_record/define_callbacks.rb +0 -22
  363. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  364. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  # ]
22
22
  #
23
23
  # # Get an array of hashes representing the result (column => value):
24
- # result.to_hash
24
+ # result.to_a
25
25
  # # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
26
26
  # {"id" => 2, "title" => "title_2", "body" => "body_2"},
27
27
  # ...
@@ -36,6 +36,10 @@ module ActiveRecord
36
36
 
37
37
  attr_reader :columns, :rows, :column_types
38
38
 
39
+ def self.empty # :nodoc:
40
+ EMPTY
41
+ end
42
+
39
43
  def initialize(columns, rows, column_types = {})
40
44
  @columns = columns
41
45
  @rows = rows
@@ -43,6 +47,14 @@ module ActiveRecord
43
47
  @column_types = column_types
44
48
  end
45
49
 
50
+ EMPTY = new([].freeze, [].freeze, {}.freeze)
51
+ private_constant :EMPTY
52
+
53
+ # Returns true if this result set includes the column named +name+
54
+ def includes_column?(name)
55
+ @columns.include? name
56
+ end
57
+
46
58
  # Returns the number of elements in the rows array.
47
59
  def length
48
60
  @rows.length
@@ -52,22 +64,14 @@ module ActiveRecord
52
64
  # row as parameter.
53
65
  #
54
66
  # Returns an +Enumerator+ if no block is given.
55
- def each
67
+ def each(&block)
56
68
  if block_given?
57
- hash_rows.each { |row| yield row }
69
+ hash_rows.each(&block)
58
70
  else
59
71
  hash_rows.to_enum { @rows.size }
60
72
  end
61
73
  end
62
74
 
63
- # Returns an array of hashes representing each row record.
64
- def to_hash
65
- hash_rows
66
- end
67
-
68
- alias :map! :map
69
- alias :collect! :map
70
-
71
75
  # Returns true if there are no records, otherwise false.
72
76
  def empty?
73
77
  rows.empty?
@@ -78,31 +82,49 @@ module ActiveRecord
78
82
  hash_rows
79
83
  end
80
84
 
85
+ alias :to_a :to_ary
86
+
81
87
  def [](idx)
82
88
  hash_rows[idx]
83
89
  end
84
90
 
85
- # Returns the first record from the rows collection.
86
- # If the rows collection is empty, returns +nil+.
87
- def first
88
- return nil if @rows.empty?
89
- Hash[@columns.zip(@rows.first)]
91
+ # Returns the last record from the rows collection.
92
+ def last(n = nil)
93
+ n ? hash_rows.last(n) : hash_rows.last
90
94
  end
91
95
 
92
- # Returns the last record from the rows collection.
93
- # If the rows collection is empty, returns +nil+.
94
- def last
95
- return nil if @rows.empty?
96
- Hash[@columns.zip(@rows.last)]
96
+ def result # :nodoc:
97
+ self
98
+ end
99
+
100
+ def cancel # :nodoc:
101
+ self
97
102
  end
98
103
 
99
104
  def cast_values(type_overrides = {}) # :nodoc:
100
- types = columns.map { |name| column_type(name, type_overrides) }
101
- result = rows.map do |values|
102
- types.zip(values).map { |type, value| type.deserialize(value) }
103
- end
105
+ if columns.one?
106
+ # Separated to avoid allocating an array per row
107
+
108
+ type = if type_overrides.is_a?(Array)
109
+ type_overrides.first
110
+ else
111
+ column_type(columns.first, type_overrides)
112
+ end
104
113
 
105
- columns.one? ? result.map!(&:first) : result
114
+ rows.map do |(value)|
115
+ type.deserialize(value)
116
+ end
117
+ else
118
+ types = if type_overrides.is_a?(Array)
119
+ type_overrides
120
+ else
121
+ columns.map { |name| column_type(name, type_overrides) }
122
+ end
123
+
124
+ rows.map do |values|
125
+ Array.new(values.size) { |i| types[i].deserialize(values[i]) }
126
+ end
127
+ end
106
128
  end
107
129
 
108
130
  def initialize_copy(other)
@@ -113,7 +135,6 @@ module ActiveRecord
113
135
  end
114
136
 
115
137
  private
116
-
117
138
  def column_type(name, type_overrides = {})
118
139
  type_overrides.fetch(name) do
119
140
  column_types.fetch(name, Type.default_value)
@@ -125,23 +146,38 @@ module ActiveRecord
125
146
  begin
126
147
  # We freeze the strings to prevent them getting duped when
127
148
  # used as keys in ActiveRecord::Base's @attributes hash
128
- columns = @columns.map { |c| c.dup.freeze }
149
+ columns = @columns.map(&:-@)
150
+ length = columns.length
151
+ template = nil
152
+
129
153
  @rows.map { |row|
130
- # In the past we used Hash[columns.zip(row)]
131
- # though elegant, the verbose way is much more efficient
132
- # both time and memory wise cause it avoids a big array allocation
133
- # this method is called a lot and needs to be micro optimised
134
- hash = {}
135
-
136
- index = 0
137
- length = columns.length
138
-
139
- while index < length
140
- hash[columns[index]] = row[index]
141
- index += 1
154
+ if template
155
+ # We use transform_values to build subsequent rows from the
156
+ # hash of the first row. This is faster because we avoid any
157
+ # reallocs and in Ruby 2.7+ avoid hashing entirely.
158
+ index = -1
159
+ template.transform_values do
160
+ row[index += 1]
161
+ end
162
+ else
163
+ # In the past we used Hash[columns.zip(row)]
164
+ # though elegant, the verbose way is much more efficient
165
+ # both time and memory wise cause it avoids a big array allocation
166
+ # this method is called a lot and needs to be micro optimised
167
+ hash = {}
168
+
169
+ index = 0
170
+ while index < length
171
+ hash[columns[index]] = row[index]
172
+ index += 1
173
+ end
174
+
175
+ # It's possible to select the same column twice, in which case
176
+ # we can't use a template
177
+ template = hash if hash.length == length
178
+
179
+ hash
142
180
  end
143
-
144
- hash
145
181
  }
146
182
  end
147
183
  end
@@ -1,24 +1,20 @@
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
16
11
 
17
- attr_accessor :connection_handler, :sql_runtime
12
+ def sql_runtime
13
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime]
14
+ end
18
15
 
19
- [:connection_handler, :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__
16
+ def sql_runtime=(runtime)
17
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
22
18
  end
23
19
  end
24
20
  end
@@ -54,19 +54,20 @@ module ActiveRecord
54
54
  # Accepts an array, or string of SQL conditions and sanitizes
55
55
  # them into a valid SQL fragment for an ORDER clause.
56
56
  #
57
- # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
57
+ # sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
58
58
  # # => "field(id, 1,3,2)"
59
59
  #
60
60
  # sanitize_sql_for_order("id ASC")
61
61
  # # => "id ASC"
62
62
  def sanitize_sql_for_order(condition)
63
63
  if condition.is_a?(Array) && condition.first.to_s.include?("?")
64
- enforce_raw_sql_whitelist([condition.first],
65
- whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST
64
+ disallow_raw_sql!(
65
+ [condition.first],
66
+ permit: connection.column_name_with_order_matcher
66
67
  )
67
68
 
68
69
  # Ensure we aren't dealing with a subclass of String that might
69
- # override methods we use (eg. Arel::Nodes::SqlLiteral).
70
+ # override methods we use (e.g. Arel::Nodes::SqlLiteral).
70
71
  if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
71
72
  condition = [String.new(condition.first), *condition[1..-1]]
72
73
  end
@@ -133,44 +134,26 @@ module ActiveRecord
133
134
  end
134
135
  end
135
136
 
136
- private
137
- # Accepts a hash of SQL conditions and replaces those attributes
138
- # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
139
- # relationship with their expanded aggregate attribute values.
140
- #
141
- # Given:
142
- #
143
- # class Person < ActiveRecord::Base
144
- # composed_of :address, class_name: "Address",
145
- # mapping: [%w(address_street street), %w(address_city city)]
146
- # end
147
- #
148
- # Then:
149
- #
150
- # { address: Address.new("813 abc st.", "chicago") }
151
- # # => { address_street: "813 abc st.", address_city: "chicago" }
152
- def expand_hash_conditions_for_aggregates(attrs) # :doc:
153
- expanded_attrs = {}
154
- attrs.each do |attr, value|
155
- if aggregation = reflect_on_aggregation(attr.to_sym)
156
- mapping = aggregation.mapping
157
- mapping.each do |field_attr, aggregate_attr|
158
- expanded_attrs[field_attr] = if value.is_a?(Array)
159
- value.map { |it| it.send(aggregate_attr) }
160
- elsif mapping.size == 1 && !value.respond_to?(aggregate_attr)
161
- value
162
- else
163
- value.send(aggregate_attr)
164
- end
165
- end
166
- else
167
- expanded_attrs[attr] = value
168
- end
169
- end
170
- expanded_attrs
137
+ def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
138
+ unexpected = nil
139
+ args.each do |arg|
140
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
141
+ (unexpected ||= []) << arg
171
142
  end
172
- deprecate :expand_hash_conditions_for_aggregates
173
143
 
144
+ if unexpected
145
+ raise(ActiveRecord::UnknownAttributeReference,
146
+ "Dangerous query method (method whose arguments are used as raw " \
147
+ "SQL) called with non-attribute argument(s): " \
148
+ "#{unexpected.map(&:inspect).join(", ")}." \
149
+ "This method should not be called with user-provided values, such as request " \
150
+ "parameters or model attributes. Known-safe values can be passed " \
151
+ "by wrapping them in Arel.sql()."
152
+ )
153
+ end
154
+ end
155
+
156
+ private
174
157
  def replace_bind_variables(statement, values)
175
158
  raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
176
159
  bound = values.dup
@@ -202,13 +185,15 @@ module ActiveRecord
202
185
 
203
186
  def quote_bound_value(value, c = connection)
204
187
  if value.respond_to?(:map) && !value.acts_like?(:string)
205
- if value.respond_to?(:empty?) && value.empty?
206
- c.quote(nil)
188
+ values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
189
+ if values.empty?
190
+ c.quote_bound_value(nil)
207
191
  else
208
- value.map { |v| c.quote(v) }.join(",")
192
+ values.map! { |v| c.quote_bound_value(v) }.join(",")
209
193
  end
210
194
  else
211
- c.quote(value)
195
+ value = value.id_for_database if value.respond_to?(:id_for_database)
196
+ c.quote_bound_value(value)
212
197
  end
213
198
  end
214
199
 
@@ -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,41 +30,47 @@ 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
48
-
49
- def define(info, &block) # :nodoc:
50
- instance_eval(&block)
33
+ module Definition
34
+ extend ActiveSupport::Concern
51
35
 
52
- if info[:version].present?
53
- ActiveRecord::SchemaMigration.create_table
54
- connection.assume_migrated_upto_version(info[:version], migrations_paths)
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
55
52
  end
56
53
 
57
- ActiveRecord::InternalMetadata.create_table
58
- ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
54
+ def define(info, &block) # :nodoc:
55
+ instance_eval(&block)
56
+
57
+ if info[:version].present?
58
+ connection.schema_migration.create_table
59
+ connection.assume_migrated_upto_version(info[:version])
60
+ end
61
+
62
+ ActiveRecord::InternalMetadata.create_table
63
+ ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
64
+ end
59
65
  end
60
66
 
61
- private
62
- # Returns the migrations paths.
63
- #
64
- # ActiveRecord::Schema.new.migrations_paths
65
- # # => ["db/migrate"] # Rails migration path by default.
66
- def migrations_paths
67
- ActiveRecord::Migrator.migrations_paths
67
+ include Definition
68
+
69
+ def self.[](version)
70
+ @class_for_version ||= {}
71
+ @class_for_version[version] ||= Class.new(Migration::Compatibility.find(version)) do
72
+ include Definition
68
73
  end
74
+ end
69
75
  end
70
76
  end
@@ -7,16 +7,28 @@ 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 as well as regexp if ActiveRecord.schema_format == :ruby.
17
+ # Only strings are accepted if ActiveRecord.schema_format == :sql.
18
18
  cattr_accessor :ignore_tables, default: []
19
19
 
20
+ ##
21
+ # :singleton-method:
22
+ # Specify a custom regular expression matching foreign keys which name
23
+ # should not be dumped to db/schema.rb.
24
+ cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/
25
+
26
+ ##
27
+ # :singleton-method:
28
+ # Specify a custom regular expression matching check constraints which name
29
+ # should not be dumped to db/schema.rb.
30
+ cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/
31
+
20
32
  class << self
21
33
  def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
22
34
  connection.create_schema_dumper(generate_options(config)).dump(stream)
@@ -35,12 +47,14 @@ module ActiveRecord
35
47
  def dump(stream)
36
48
  header(stream)
37
49
  extensions(stream)
50
+ types(stream)
38
51
  tables(stream)
39
52
  trailer(stream)
40
53
  stream
41
54
  end
42
55
 
43
56
  private
57
+ attr_accessor :table_name
44
58
 
45
59
  def initialize(connection, options = {})
46
60
  @connection = connection
@@ -60,22 +74,21 @@ module ActiveRecord
60
74
  end
61
75
 
62
76
  def header(stream)
63
- stream.puts <<HEADER
64
- # This file is auto-generated from the current state of the database. Instead
65
- # of editing this file, please use the migrations feature of Active Record to
66
- # incrementally modify your database, and then regenerate this schema definition.
67
- #
68
- # Note that this schema.rb definition is the authoritative source for your
69
- # database schema. If you need to create the application database on another
70
- # system, you should be using db:schema:load, not running all the migrations
71
- # from scratch. The latter is a flawed and unsustainable approach (the more migrations
72
- # you'll amass, the slower it'll run and the greater likelihood for issues).
73
- #
74
- # It's strongly recommended that you check this file into your version control system.
75
-
76
- ActiveRecord::Schema.define(#{define_params}) do
77
-
78
- HEADER
77
+ stream.puts <<~HEADER
78
+ # This file is auto-generated from the current state of the database. Instead
79
+ # of editing this file, please use the migrations feature of Active Record to
80
+ # incrementally modify your database, and then regenerate this schema definition.
81
+ #
82
+ # This file is the source Rails uses to define your schema when running `bin/rails
83
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
84
+ # be faster and is potentially less error prone than running all of your
85
+ # migrations from scratch. Old migrations may fail to apply correctly if those
86
+ # migrations use external dependencies or application code.
87
+ #
88
+ # It's strongly recommended that you check this file into your version control system.
89
+
90
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
91
+ HEADER
79
92
  end
80
93
 
81
94
  def trailer(stream)
@@ -86,6 +99,10 @@ HEADER
86
99
  def extensions(stream)
87
100
  end
88
101
 
102
+ # (enum) types are only supported by PostgreSQL
103
+ def types(stream)
104
+ end
105
+
89
106
  def tables(stream)
90
107
  sorted_tables = @connection.tables.sort
91
108
 
@@ -104,6 +121,8 @@ HEADER
104
121
  def table(table, stream)
105
122
  columns = @connection.columns(table)
106
123
  begin
124
+ self.table_name = table
125
+
107
126
  tbl = StringIO.new
108
127
 
109
128
  # first dump primary key column
@@ -116,7 +135,10 @@ HEADER
116
135
  tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
117
136
  pkcol = columns.detect { |c| c.name == pk }
118
137
  pkcolspec = column_spec_for_primary_key(pkcol)
119
- if pkcolspec.present?
138
+ unless pkcolspec.empty?
139
+ if pkcolspec != pkcolspec.slice(:id, :default)
140
+ pkcolspec = { id: { type: pkcolspec.delete(:id), **pkcolspec }.compact }
141
+ end
120
142
  tbl.print ", #{format_colspec(pkcolspec)}"
121
143
  end
122
144
  when Array
@@ -136,13 +158,19 @@ HEADER
136
158
  columns.each do |column|
137
159
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
138
160
  next if column.name == pk
161
+
139
162
  type, colspec = column_spec(column)
140
- tbl.print " t.#{type} #{column.name.inspect}"
163
+ if type.is_a?(Symbol)
164
+ tbl.print " t.#{type} #{column.name.inspect}"
165
+ else
166
+ tbl.print " t.column #{column.name.inspect}, #{type.inspect}"
167
+ end
141
168
  tbl.print ", #{format_colspec(colspec)}" if colspec.present?
142
169
  tbl.puts
143
170
  end
144
171
 
145
172
  indexes_in_create(table, tbl)
173
+ check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
146
174
 
147
175
  tbl.puts " end"
148
176
  tbl.puts
@@ -153,6 +181,8 @@ HEADER
153
181
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
154
182
  stream.puts "# #{e.message}"
155
183
  stream.puts
184
+ ensure
185
+ self.table_name = nil
156
186
  end
157
187
  end
158
188
 
@@ -194,6 +224,24 @@ HEADER
194
224
  index_parts
195
225
  end
196
226
 
227
+ def check_constraints_in_create(table, stream)
228
+ if (check_constraints = @connection.check_constraints(table)).any?
229
+ add_check_constraint_statements = check_constraints.map do |check_constraint|
230
+ parts = [
231
+ "t.check_constraint #{check_constraint.expression.inspect}"
232
+ ]
233
+
234
+ if check_constraint.export_name_on_schema_dump?
235
+ parts << "name: #{check_constraint.name.inspect}"
236
+ end
237
+
238
+ " #{parts.join(', ')}"
239
+ end
240
+
241
+ stream.puts add_check_constraint_statements.sort.join("\n")
242
+ end
243
+ end
244
+
197
245
  def foreign_keys(table, stream)
198
246
  if (foreign_keys = @connection.foreign_keys(table)).any?
199
247
  add_foreign_key_statements = foreign_keys.map do |foreign_key|
@@ -210,12 +258,13 @@ HEADER
210
258
  parts << "primary_key: #{foreign_key.primary_key.inspect}"
211
259
  end
212
260
 
213
- if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
261
+ if foreign_key.export_name_on_schema_dump?
214
262
  parts << "name: #{foreign_key.name.inspect}"
215
263
  end
216
264
 
217
265
  parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
218
266
  parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
267
+ parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
219
268
 
220
269
  " #{parts.join(', ')}"
221
270
  end
@@ -225,7 +274,9 @@ HEADER
225
274
  end
226
275
 
227
276
  def format_colspec(colspec)
228
- colspec.map { |key, value| "#{key}: #{value}" }.join(", ")
277
+ colspec.map do |key, value|
278
+ "#{key}: #{ value.is_a?(Hash) ? "{ #{format_colspec(value)} }" : value }"
279
+ end.join(", ")
229
280
  end
230
281
 
231
282
  def format_options(options)
@@ -15,19 +15,13 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  def table_name
18
- "#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
19
- end
20
-
21
- def table_exists?
22
- connection.table_exists?(table_name)
18
+ "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
23
19
  end
24
20
 
25
21
  def create_table
26
- unless table_exists?
27
- version_options = connection.internal_string_options_for_primary_key
28
-
22
+ unless connection.table_exists?(table_name)
29
23
  connection.create_table(table_name, id: false) do |t|
30
- t.string :version, version_options
24
+ t.string :version, **connection.internal_string_options_for_primary_key
31
25
  end
32
26
  end
33
27
  end
@@ -47,6 +41,10 @@ module ActiveRecord
47
41
  def all_versions
48
42
  order(:version).pluck(:version)
49
43
  end
44
+
45
+ def table_exists?
46
+ connection.data_source_exists?(table_name)
47
+ end
50
48
  end
51
49
 
52
50
  def version