activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module ActiveRecord
4
- module ConnectionAdapters #:nodoc:
5
+ module ConnectionAdapters # :nodoc:
5
6
  # Abstract representation of an index definition on a table. Instances of
6
7
  # this type are typically created and returned by methods in database
7
8
  # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes
8
9
  class IndexDefinition # :nodoc:
9
- attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :comment
10
+ attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :include, :nulls_not_distinct, :comment, :valid
10
11
 
11
12
  def initialize(
12
13
  table, name,
@@ -18,7 +19,10 @@ module ActiveRecord
18
19
  where: nil,
19
20
  type: nil,
20
21
  using: nil,
21
- comment: nil
22
+ include: nil,
23
+ nulls_not_distinct: nil,
24
+ comment: nil,
25
+ valid: true
22
26
  )
23
27
  @table = table
24
28
  @name = name
@@ -30,7 +34,32 @@ module ActiveRecord
30
34
  @where = where
31
35
  @type = type
32
36
  @using = using
37
+ @include = include
38
+ @nulls_not_distinct = nulls_not_distinct
33
39
  @comment = comment
40
+ @valid = valid
41
+ end
42
+
43
+ def valid?
44
+ @valid
45
+ end
46
+
47
+ def column_options
48
+ {
49
+ length: lengths,
50
+ order: orders,
51
+ opclass: opclasses,
52
+ }
53
+ end
54
+
55
+ def defined_for?(columns = nil, name: nil, unique: nil, valid: nil, include: nil, nulls_not_distinct: nil, **options)
56
+ columns = options[:column] if columns.blank?
57
+ (columns.nil? || Array(self.columns) == Array(columns).map(&:to_s)) &&
58
+ (name.nil? || self.name == name.to_s) &&
59
+ (unique.nil? || self.unique == unique) &&
60
+ (valid.nil? || self.valid == valid) &&
61
+ (include.nil? || Array(self.include) == Array(include).map(&:to_s)) &&
62
+ (nulls_not_distinct.nil? || self.nulls_not_distinct == nulls_not_distinct)
34
63
  end
35
64
 
36
65
  private
@@ -48,11 +77,24 @@ module ActiveRecord
48
77
  # +columns+ attribute of said TableDefinition object, in order to be used
49
78
  # for generating a number of table creation or table changing SQL statements.
50
79
  ColumnDefinition = Struct.new(:name, :type, :options, :sql_type) do # :nodoc:
80
+ self::OPTION_NAMES = [
81
+ :limit,
82
+ :precision,
83
+ :scale,
84
+ :default,
85
+ :null,
86
+ :collation,
87
+ :comment,
88
+ :primary_key,
89
+ :if_exists,
90
+ :if_not_exists
91
+ ]
92
+
51
93
  def primary_key?
52
94
  options[:primary_key]
53
95
  end
54
96
 
55
- [:limit, :precision, :scale, :default, :null, :collation, :comment].each do |option_name|
97
+ (self::OPTION_NAMES - [:primary_key]).each do |option_name|
56
98
  module_eval <<-CODE, __FILE__, __LINE__ + 1
57
99
  def #{option_name}
58
100
  options[:#{option_name}]
@@ -63,15 +105,23 @@ module ActiveRecord
63
105
  end
64
106
  CODE
65
107
  end
108
+
109
+ def aliased_types(name, fallback)
110
+ "timestamp" == name ? :datetime : fallback
111
+ end
66
112
  end
67
113
 
68
114
  AddColumnDefinition = Struct.new(:column) # :nodoc:
69
115
 
70
- ChangeColumnDefinition = Struct.new(:column, :name) #:nodoc:
116
+ ChangeColumnDefinition = Struct.new(:column, :name) # :nodoc:
117
+
118
+ ChangeColumnDefaultDefinition = Struct.new(:column, :default) # :nodoc:
119
+
120
+ CreateIndexDefinition = Struct.new(:index, :algorithm, :if_not_exists) # :nodoc:
71
121
 
72
122
  PrimaryKeyDefinition = Struct.new(:name) # :nodoc:
73
123
 
74
- ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do #:nodoc:
124
+ ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do # :nodoc:
75
125
  def name
76
126
  options[:name]
77
127
  end
@@ -92,6 +142,10 @@ module ActiveRecord
92
142
  options[:on_update]
93
143
  end
94
144
 
145
+ def deferrable
146
+ options[:deferrable]
147
+ end
148
+
95
149
  def custom_primary_key?
96
150
  options[:primary_key] != default_primary_key
97
151
  end
@@ -105,9 +159,12 @@ module ActiveRecord
105
159
  !ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
106
160
  end
107
161
 
108
- def defined_for?(to_table: nil, **options)
162
+ def defined_for?(to_table: nil, validate: nil, **options)
163
+ options = options.slice(*self.options.keys)
164
+
109
165
  (to_table.nil? || to_table.to_s == self.to_table) &&
110
- options.all? { |k, v| self.options[k].to_s == v.to_s }
166
+ (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
167
+ options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
111
168
  end
112
169
 
113
170
  private
@@ -116,6 +173,29 @@ module ActiveRecord
116
173
  end
117
174
  end
118
175
 
176
+ CheckConstraintDefinition = Struct.new(:table_name, :expression, :options) do
177
+ def name
178
+ options[:name]
179
+ end
180
+
181
+ def validate?
182
+ options.fetch(:validate, true)
183
+ end
184
+ alias validated? validate?
185
+
186
+ def export_name_on_schema_dump?
187
+ !ActiveRecord::SchemaDumper.chk_ignore_pattern.match?(name) if name
188
+ end
189
+
190
+ def defined_for?(name:, expression: nil, validate: nil, **options)
191
+ options = options.slice(*self.options.keys)
192
+
193
+ self.name == name.to_s &&
194
+ (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
195
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
196
+ end
197
+ end
198
+
119
199
  class ReferenceDefinition # :nodoc:
120
200
  def initialize(
121
201
  name,
@@ -137,17 +217,31 @@ module ActiveRecord
137
217
  end
138
218
  end
139
219
 
220
+ def add(table_name, connection)
221
+ columns.each do |name, type, options|
222
+ connection.add_column(table_name, name, type, **options)
223
+ end
224
+
225
+ if index
226
+ connection.add_index(table_name, column_names, **index_options(table_name))
227
+ end
228
+
229
+ if foreign_key
230
+ connection.add_foreign_key(table_name, foreign_table_name, **foreign_key_options)
231
+ end
232
+ end
233
+
140
234
  def add_to(table)
141
- columns.each do |column_options|
142
- table.column(*column_options)
235
+ columns.each do |name, type, options|
236
+ table.column(name, type, **options)
143
237
  end
144
238
 
145
239
  if index
146
- table.index(column_names, index_options)
240
+ table.index(column_names, **index_options(table.name))
147
241
  end
148
242
 
149
243
  if foreign_key
150
- table.foreign_key(foreign_table_name, foreign_key_options)
244
+ table.foreign_key(foreign_table_name, **foreign_key_options)
151
245
  end
152
246
  end
153
247
 
@@ -158,16 +252,30 @@ module ActiveRecord
158
252
  value.is_a?(Hash) ? value : {}
159
253
  end
160
254
 
255
+ def conditional_options
256
+ options.slice(:if_exists, :if_not_exists)
257
+ end
258
+
161
259
  def polymorphic_options
162
- as_options(polymorphic).merge(options.slice(:null, :first, :after))
260
+ as_options(polymorphic).merge(conditional_options).merge(options.slice(:null, :first, :after))
163
261
  end
164
262
 
165
- def index_options
166
- as_options(index)
263
+ def polymorphic_index_name(table_name)
264
+ "index_#{table_name}_on_#{name}"
265
+ end
266
+
267
+ def index_options(table_name)
268
+ index_options = as_options(index).merge(conditional_options)
269
+
270
+ # legacy reference index names are used on versions 6.0 and earlier
271
+ return index_options if options[:_uses_legacy_reference_index_name]
272
+
273
+ index_options[:name] ||= polymorphic_index_name(table_name) if polymorphic
274
+ index_options
167
275
  end
168
276
 
169
277
  def foreign_key_options
170
- as_options(foreign_key).merge(column: column_name)
278
+ as_options(foreign_key).merge(column: column_name, **conditional_options)
171
279
  end
172
280
 
173
281
  def columns
@@ -199,7 +307,7 @@ module ActiveRecord
199
307
  # Appends a primary key definition to the table definition.
200
308
  # Can be called multiple times, but this is probably not a good idea.
201
309
  def primary_key(name, type = :primary_key, **options)
202
- column(name, type, options.merge(primary_key: true))
310
+ column(name, type, **options.merge(primary_key: true))
203
311
  end
204
312
 
205
313
  ##
@@ -217,30 +325,34 @@ module ActiveRecord
217
325
  define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
218
326
  :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
219
327
 
328
+ alias :blob :binary
220
329
  alias :numeric :decimal
221
330
  end
222
331
 
223
332
  class_methods do
224
- private def define_column_methods(*column_types) # :nodoc:
333
+ def define_column_methods(*column_types) # :nodoc:
225
334
  column_types.each do |column_type|
226
335
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
227
336
  def #{column_type}(*names, **options)
228
337
  raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
229
- names.each { |name| column(name, :#{column_type}, options) }
338
+ names.each { |name| column(name, :#{column_type}, **options) }
230
339
  end
231
340
  RUBY
232
341
  end
233
342
  end
343
+ private :define_column_methods
234
344
  end
235
345
  end
236
346
 
347
+ # = Active Record Connection Adapters \Table \Definition
348
+ #
237
349
  # Represents the schema of an SQL table in an abstract way. This class
238
350
  # provides methods for manipulating the schema representation.
239
351
  #
240
352
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
241
353
  # is actually of this type:
242
354
  #
243
- # class SomeMigration < ActiveRecord::Migration[5.0]
355
+ # class SomeMigration < ActiveRecord::Migration[7.2]
244
356
  # def up
245
357
  # create_table :foo do |t|
246
358
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -255,7 +367,7 @@ module ActiveRecord
255
367
  class TableDefinition
256
368
  include ColumnMethods
257
369
 
258
- attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
370
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys, :check_constraints
259
371
 
260
372
  def initialize(
261
373
  conn,
@@ -272,6 +384,7 @@ module ActiveRecord
272
384
  @indexes = []
273
385
  @foreign_keys = []
274
386
  @primary_keys = nil
387
+ @check_constraints = []
275
388
  @temporary = temporary
276
389
  @if_not_exists = if_not_exists
277
390
  @options = options
@@ -280,6 +393,23 @@ module ActiveRecord
280
393
  @comment = comment
281
394
  end
282
395
 
396
+ def set_primary_key(table_name, id, primary_key, **options)
397
+ if id && !as
398
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
399
+
400
+ if id.is_a?(Hash)
401
+ options.merge!(id.except(:type))
402
+ id = id.fetch(:type, :primary_key)
403
+ end
404
+
405
+ if pk.is_a?(Array)
406
+ primary_keys(pk)
407
+ else
408
+ primary_key(pk, id, **options)
409
+ end
410
+ end
411
+ end
412
+
283
413
  def primary_keys(name = nil) # :nodoc:
284
414
  @primary_keys = PrimaryKeyDefinition.new(name) if name
285
415
  @primary_keys
@@ -305,7 +435,7 @@ module ActiveRecord
305
435
  #
306
436
  # == Examples
307
437
  #
308
- # # Assuming +td+ is an instance of TableDefinition
438
+ # # Assuming `td` is an instance of TableDefinition
309
439
  # td.column(:granted, :boolean, index: true)
310
440
  #
311
441
  # == Short-hand examples
@@ -360,22 +490,18 @@ module ActiveRecord
360
490
  # t.references :tagger, polymorphic: true
361
491
  # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
362
492
  # end
363
- def column(name, type, **options)
493
+ def column(name, type, index: nil, **options)
364
494
  name = name.to_s
365
495
  type = type.to_sym if type
366
- options = options.dup
367
496
 
368
- if @columns_hash[name]
369
- if @columns_hash[name].primary_key?
370
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
371
- else
372
- raise ArgumentError, "you can't define an already defined column '#{name}'."
373
- end
497
+ raise_on_duplicate_column(name)
498
+ @columns_hash[name] = new_column_definition(name, type, **options)
499
+
500
+ if index
501
+ index_options = index.is_a?(Hash) ? index : {}
502
+ index(name, **index_options)
374
503
  end
375
504
 
376
- index_options = options.delete(:index)
377
- index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
378
- @columns_hash[name] = new_column_definition(name, type, options)
379
505
  self
380
506
  end
381
507
 
@@ -389,12 +515,16 @@ module ActiveRecord
389
515
  # This is primarily used to track indexes that need to be created after the table
390
516
  #
391
517
  # index(:account_id, name: 'index_projects_on_account_id')
392
- def index(column_name, options = {})
518
+ def index(column_name, **options)
393
519
  indexes << [column_name, options]
394
520
  end
395
521
 
396
- def foreign_key(table_name, options = {}) # :nodoc:
397
- foreign_keys << [table_name, options]
522
+ def foreign_key(to_table, **options)
523
+ foreign_keys << new_foreign_key_definition(to_table, options)
524
+ end
525
+
526
+ def check_constraint(expression, **options)
527
+ check_constraints << new_check_constraint_definition(expression, options)
398
528
  end
399
529
 
400
530
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -408,8 +538,8 @@ module ActiveRecord
408
538
  options[:precision] = 6
409
539
  end
410
540
 
411
- column(:created_at, :datetime, options)
412
- column(:updated_at, :datetime, options)
541
+ column(:created_at, :datetime, **options)
542
+ column(:updated_at, :datetime, **options)
413
543
  end
414
544
 
415
545
  # Adds a reference.
@@ -421,7 +551,7 @@ module ActiveRecord
421
551
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
422
552
  def references(*args, **options)
423
553
  args.each do |ref_name|
424
- ReferenceDefinition.new(ref_name, options).add_to(self)
554
+ ReferenceDefinition.new(ref_name, **options).add_to(self)
425
555
  end
426
556
  end
427
557
  alias :belongs_to :references
@@ -431,13 +561,41 @@ module ActiveRecord
431
561
  type = integer_like_primary_key_type(type, options)
432
562
  end
433
563
  type = aliased_types(type.to_s, type)
564
+
565
+ if @conn.supports_datetime_with_precision?
566
+ if type == :datetime && !options.key?(:precision)
567
+ options[:precision] = 6
568
+ end
569
+ end
570
+
434
571
  options[:primary_key] ||= type == :primary_key
435
572
  options[:null] = false if options[:primary_key]
436
573
  create_column_definition(name, type, options)
437
574
  end
438
575
 
576
+ def new_foreign_key_definition(to_table, options) # :nodoc:
577
+ prefix = ActiveRecord::Base.table_name_prefix
578
+ suffix = ActiveRecord::Base.table_name_suffix
579
+ to_table = "#{prefix}#{to_table}#{suffix}"
580
+ options = @conn.foreign_key_options(name, to_table, options)
581
+ ForeignKeyDefinition.new(name, to_table, options)
582
+ end
583
+
584
+ def new_check_constraint_definition(expression, options) # :nodoc:
585
+ options = @conn.check_constraint_options(name, expression, options)
586
+ CheckConstraintDefinition.new(name, expression, options)
587
+ end
588
+
439
589
  private
590
+ def valid_column_definition_options
591
+ @conn.valid_column_definition_options
592
+ end
593
+
440
594
  def create_column_definition(name, type, options)
595
+ unless options[:_skip_validate_options]
596
+ options.except(:_uses_legacy_reference_index_name, :_skip_validate_options).assert_valid_keys(valid_column_definition_options)
597
+ end
598
+
441
599
  ColumnDefinition.new(name, type, options)
442
600
  end
443
601
 
@@ -452,37 +610,59 @@ module ActiveRecord
452
610
  def integer_like_primary_key_type(type, options)
453
611
  type
454
612
  end
613
+
614
+ def raise_on_duplicate_column(name)
615
+ if @columns_hash[name]
616
+ if @columns_hash[name].primary_key?
617
+ raise ArgumentError, "you can't redefine the primary key column '#{name}' on '#{@name}'. To define a custom primary key, pass { id: false } to create_table."
618
+ else
619
+ raise ArgumentError, "you can't define an already defined column '#{name}' on '#{@name}'."
620
+ end
621
+ end
622
+ end
455
623
  end
456
624
 
457
625
  class AlterTable # :nodoc:
458
626
  attr_reader :adds
459
- attr_reader :foreign_key_adds
460
- attr_reader :foreign_key_drops
627
+ attr_reader :foreign_key_adds, :foreign_key_drops
628
+ attr_reader :check_constraint_adds, :check_constraint_drops
461
629
 
462
630
  def initialize(td)
463
631
  @td = td
464
632
  @adds = []
465
633
  @foreign_key_adds = []
466
634
  @foreign_key_drops = []
635
+ @check_constraint_adds = []
636
+ @check_constraint_drops = []
467
637
  end
468
638
 
469
639
  def name; @td.name; end
470
640
 
471
641
  def add_foreign_key(to_table, options)
472
- @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
642
+ @foreign_key_adds << @td.new_foreign_key_definition(to_table, options)
473
643
  end
474
644
 
475
645
  def drop_foreign_key(name)
476
646
  @foreign_key_drops << name
477
647
  end
478
648
 
479
- def add_column(name, type, options)
649
+ def add_check_constraint(expression, options)
650
+ @check_constraint_adds << @td.new_check_constraint_definition(expression, options)
651
+ end
652
+
653
+ def drop_check_constraint(constraint_name)
654
+ @check_constraint_drops << constraint_name
655
+ end
656
+
657
+ def add_column(name, type, **options)
480
658
  name = name.to_s
481
659
  type = type.to_sym
482
- @adds << AddColumnDefinition.new(@td.new_column_definition(name, type, options))
660
+ @adds << AddColumnDefinition.new(@td.new_column_definition(name, type, **options))
483
661
  end
484
662
  end
485
663
 
664
+ # = Active Record Connection Adapters \Table
665
+ #
486
666
  # Represents an SQL table in an abstract way for updating a table.
487
667
  # Also see TableDefinition and {connection.create_table}[rdoc-ref:SchemaStatements#create_table]
488
668
  #
@@ -496,9 +676,11 @@ module ActiveRecord
496
676
  # t.timestamps
497
677
  # t.change
498
678
  # t.change_default
679
+ # t.change_null
499
680
  # t.rename
500
681
  # t.references
501
682
  # t.belongs_to
683
+ # t.check_constraint
502
684
  # t.string
503
685
  # t.text
504
686
  # t.integer
@@ -511,6 +693,7 @@ module ActiveRecord
511
693
  # t.time
512
694
  # t.date
513
695
  # t.binary
696
+ # t.blob
514
697
  # t.boolean
515
698
  # t.foreign_key
516
699
  # t.json
@@ -520,6 +703,7 @@ module ActiveRecord
520
703
  # t.remove_references
521
704
  # t.remove_belongs_to
522
705
  # t.remove_index
706
+ # t.remove_check_constraint
523
707
  # t.remove_timestamps
524
708
  # end
525
709
  #
@@ -538,10 +722,13 @@ module ActiveRecord
538
722
  # t.column(:name, :string)
539
723
  #
540
724
  # See TableDefinition#column for details of the options you can use.
541
- def column(column_name, type, **options)
542
- index_options = options.delete(:index)
543
- @base.add_column(name, column_name, type, options)
544
- index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
725
+ def column(column_name, type, index: nil, **options)
726
+ raise_on_if_exist_options(options)
727
+ @base.add_column(name, column_name, type, **options)
728
+ if index
729
+ index_options = index.is_a?(Hash) ? index : {}
730
+ index(column_name, **index_options)
731
+ end
545
732
  end
546
733
 
547
734
  # Checks to see if a column exists.
@@ -549,8 +736,8 @@ module ActiveRecord
549
736
  # t.string(:name) unless t.column_exists?(:name, :string)
550
737
  #
551
738
  # See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
552
- def column_exists?(column_name, type = nil, options = {})
553
- @base.column_exists?(name, column_name, type, options)
739
+ def column_exists?(column_name, type = nil, **options)
740
+ @base.column_exists?(name, column_name, type, **options)
554
741
  end
555
742
 
556
743
  # Adds a new index to the table. +column_name+ can be a single Symbol, or
@@ -561,8 +748,9 @@ module ActiveRecord
561
748
  # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
562
749
  #
563
750
  # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
564
- def index(column_name, options = {})
565
- @base.add_index(name, column_name, options)
751
+ def index(column_name, **options)
752
+ raise_on_if_exist_options(options)
753
+ @base.add_index(name, column_name, **options)
566
754
  end
567
755
 
568
756
  # Checks to see if an index exists.
@@ -572,8 +760,8 @@ module ActiveRecord
572
760
  # end
573
761
  #
574
762
  # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
575
- def index_exists?(column_name, options = {})
576
- @base.index_exists?(name, column_name, options)
763
+ def index_exists?(column_name, **options)
764
+ @base.index_exists?(name, column_name, **options)
577
765
  end
578
766
 
579
767
  # Renames the given index on the table.
@@ -590,8 +778,9 @@ module ActiveRecord
590
778
  # t.timestamps(null: false)
591
779
  #
592
780
  # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
593
- def timestamps(options = {})
594
- @base.add_timestamps(name, options)
781
+ def timestamps(**options)
782
+ raise_on_if_exist_options(options)
783
+ @base.add_timestamps(name, **options)
595
784
  end
596
785
 
597
786
  # Changes the column's definition according to the new options.
@@ -600,8 +789,9 @@ module ActiveRecord
600
789
  # t.change(:description, :text)
601
790
  #
602
791
  # See TableDefinition#column for details of the options you can use.
603
- def change(column_name, type, options = {})
604
- @base.change_column(name, column_name, type, options)
792
+ def change(column_name, type, **options)
793
+ raise_on_if_exist_options(options)
794
+ @base.change_column(name, column_name, type, **options)
605
795
  end
606
796
 
607
797
  # Sets a new default value for a column.
@@ -615,14 +805,25 @@ module ActiveRecord
615
805
  @base.change_column_default(name, column_name, default_or_changes)
616
806
  end
617
807
 
808
+ # Sets or removes a NOT NULL constraint on a column.
809
+ #
810
+ # t.change_null(:qualification, true)
811
+ # t.change_null(:qualification, false, 0)
812
+ #
813
+ # See {connection.change_column_null}[rdoc-ref:SchemaStatements#change_column_null]
814
+ def change_null(column_name, null, default = nil)
815
+ @base.change_column_null(name, column_name, null, default)
816
+ end
817
+
618
818
  # Removes the column(s) from the table definition.
619
819
  #
620
820
  # t.remove(:qualification)
621
821
  # t.remove(:qualification, :experience)
622
822
  #
623
823
  # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
624
- def remove(*column_names)
625
- @base.remove_columns(name, *column_names)
824
+ def remove(*column_names, **options)
825
+ raise_on_if_exist_options(options)
826
+ @base.remove_columns(name, *column_names, **options)
626
827
  end
627
828
 
628
829
  # Removes the given index from the table.
@@ -630,10 +831,12 @@ module ActiveRecord
630
831
  # t.remove_index(:branch_id)
631
832
  # t.remove_index(column: [:branch_id, :party_id])
632
833
  # t.remove_index(name: :by_branch_party)
834
+ # t.remove_index(:branch_id, name: :by_branch_party)
633
835
  #
634
836
  # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
635
- def remove_index(options = {})
636
- @base.remove_index(name, options)
837
+ def remove_index(column_name = nil, **options)
838
+ raise_on_if_exist_options(options)
839
+ @base.remove_index(name, column_name, **options)
637
840
  end
638
841
 
639
842
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
@@ -641,8 +844,8 @@ module ActiveRecord
641
844
  # t.remove_timestamps
642
845
  #
643
846
  # See {connection.remove_timestamps}[rdoc-ref:SchemaStatements#remove_timestamps]
644
- def remove_timestamps(options = {})
645
- @base.remove_timestamps(name, options)
847
+ def remove_timestamps(**options)
848
+ @base.remove_timestamps(name, **options)
646
849
  end
647
850
 
648
851
  # Renames a column.
@@ -661,8 +864,9 @@ module ActiveRecord
661
864
  #
662
865
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
663
866
  def references(*args, **options)
867
+ raise_on_if_exist_options(options)
664
868
  args.each do |ref_name|
665
- @base.add_reference(name, ref_name, options)
869
+ @base.add_reference(name, ref_name, **options)
666
870
  end
667
871
  end
668
872
  alias :belongs_to :references
@@ -674,8 +878,9 @@ module ActiveRecord
674
878
  #
675
879
  # See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference]
676
880
  def remove_references(*args, **options)
881
+ raise_on_if_exist_options(options)
677
882
  args.each do |ref_name|
678
- @base.remove_reference(name, ref_name, options)
883
+ @base.remove_reference(name, ref_name, **options)
679
884
  end
680
885
  end
681
886
  alias :remove_belongs_to :remove_references
@@ -686,8 +891,9 @@ module ActiveRecord
686
891
  # t.foreign_key(:authors, column: :author_id, primary_key: "id")
687
892
  #
688
893
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
689
- def foreign_key(*args)
690
- @base.add_foreign_key(name, *args)
894
+ def foreign_key(*args, **options)
895
+ raise_on_if_exist_options(options)
896
+ @base.add_foreign_key(name, *args, **options)
691
897
  end
692
898
 
693
899
  # Removes the given foreign key from the table.
@@ -696,8 +902,9 @@ module ActiveRecord
696
902
  # t.remove_foreign_key(column: :author_id)
697
903
  #
698
904
  # See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
699
- def remove_foreign_key(*args)
700
- @base.remove_foreign_key(name, *args)
905
+ def remove_foreign_key(*args, **options)
906
+ raise_on_if_exist_options(options)
907
+ @base.remove_foreign_key(name, *args, **options)
701
908
  end
702
909
 
703
910
  # Checks to see if a foreign key exists.
@@ -705,9 +912,54 @@ module ActiveRecord
705
912
  # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
706
913
  #
707
914
  # See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?]
708
- def foreign_key_exists?(*args)
709
- @base.foreign_key_exists?(name, *args)
915
+ def foreign_key_exists?(*args, **options)
916
+ @base.foreign_key_exists?(name, *args, **options)
710
917
  end
918
+
919
+ # Adds a check constraint.
920
+ #
921
+ # t.check_constraint("price > 0", name: "price_check")
922
+ #
923
+ # See {connection.add_check_constraint}[rdoc-ref:SchemaStatements#add_check_constraint]
924
+ def check_constraint(*args, **options)
925
+ @base.add_check_constraint(name, *args, **options)
926
+ end
927
+
928
+ # Removes the given check constraint from the table.
929
+ #
930
+ # t.remove_check_constraint(name: "price_check")
931
+ #
932
+ # See {connection.remove_check_constraint}[rdoc-ref:SchemaStatements#remove_check_constraint]
933
+ def remove_check_constraint(*args, **options)
934
+ @base.remove_check_constraint(name, *args, **options)
935
+ end
936
+
937
+ # Checks if a check_constraint exists on a table.
938
+ #
939
+ # unless t.check_constraint_exists?(name: "price_check")
940
+ # t.check_constraint("price > 0", name: "price_check")
941
+ # end
942
+ #
943
+ # See {connection.check_constraint_exists?}[rdoc-ref:SchemaStatements#check_constraint_exists?]
944
+ def check_constraint_exists?(*args, **options)
945
+ @base.check_constraint_exists?(name, *args, **options)
946
+ end
947
+
948
+ private
949
+ def raise_on_if_exist_options(options)
950
+ unrecognized_option = options.keys.find do |key|
951
+ key == :if_exists || key == :if_not_exists
952
+ end
953
+ if unrecognized_option
954
+ conditional = unrecognized_option == :if_exists ? "if" : "unless"
955
+ message = <<~TXT
956
+ Option #{unrecognized_option} will be ignored. If you are calling an expression like
957
+ `t.column(.., #{unrecognized_option}: true)` from inside a change_table block, try a
958
+ conditional clause instead, as in `t.column(..) #{conditional} t.column_exists?(..)`
959
+ TXT
960
+ raise ArgumentError.new(message)
961
+ end
962
+ end
711
963
  end
712
964
  end
713
965
  end