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
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module SchemaStatements
7
7
  # Drops the database specified on the +name+ attribute
8
8
  # and creates it again using the provided +options+.
9
- def recreate_database(name, options = {}) #:nodoc:
9
+ def recreate_database(name, options = {}) # :nodoc:
10
10
  drop_database(name)
11
11
  create_database(name, options)
12
12
  end
@@ -22,8 +22,8 @@ module ActiveRecord
22
22
  def create_database(name, options = {})
23
23
  options = { encoding: "utf8" }.merge!(options.symbolize_keys)
24
24
 
25
- option_string = options.inject("") do |memo, (key, value)|
26
- memo += case key
25
+ option_string = options.each_with_object(+"") do |(key, value), memo|
26
+ memo << case key
27
27
  when :owner
28
28
  " OWNER = \"#{value}\""
29
29
  when :template
@@ -50,11 +50,12 @@ module ActiveRecord
50
50
  #
51
51
  # Example:
52
52
  # drop_database 'matt_development'
53
- def drop_database(name) #:nodoc:
53
+ def drop_database(name) # :nodoc:
54
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
55
55
  end
56
56
 
57
- def drop_table(table_name, options = {}) # :nodoc:
57
+ def drop_table(table_name, **options) # :nodoc:
58
+ schema_cache.clear_data_source_cache!(table_name.to_s)
58
59
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
59
60
  end
60
61
 
@@ -68,13 +69,13 @@ module ActiveRecord
68
69
  table = quoted_scope(table_name)
69
70
  index = quoted_scope(index_name)
70
71
 
71
- query_value(<<-SQL, "SCHEMA").to_i > 0
72
+ query_value(<<~SQL, "SCHEMA").to_i > 0
72
73
  SELECT COUNT(*)
73
74
  FROM pg_class t
74
75
  INNER JOIN pg_index d ON t.oid = d.indrelid
75
76
  INNER JOIN pg_class i ON d.indexrelid = i.oid
76
77
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
77
- WHERE i.relkind = 'i'
78
+ WHERE i.relkind IN ('i', 'I')
78
79
  AND i.relname = #{index[:name]}
79
80
  AND t.relname = #{table[:name]}
80
81
  AND n.nspname = #{index[:schema]}
@@ -85,14 +86,14 @@ module ActiveRecord
85
86
  def indexes(table_name) # :nodoc:
86
87
  scope = quoted_scope(table_name)
87
88
 
88
- result = query(<<-SQL, "SCHEMA")
89
+ result = query(<<~SQL, "SCHEMA")
89
90
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
90
91
  pg_catalog.obj_description(i.oid, 'pg_class') AS comment
91
92
  FROM pg_class t
92
93
  INNER JOIN pg_index d ON t.oid = d.indrelid
93
94
  INNER JOIN pg_class i ON d.indexrelid = i.oid
94
95
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
95
- WHERE i.relkind = 'i'
96
+ WHERE i.relkind IN ('i', 'I')
96
97
  AND d.indisprimary = 'f'
97
98
  AND t.relname = #{scope[:name]}
98
99
  AND n.nspname = #{scope[:schema]}
@@ -115,7 +116,7 @@ module ActiveRecord
115
116
  if indkey.include?(0)
116
117
  columns = expressions
117
118
  else
118
- columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
119
+ columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
119
120
  SELECT a.attnum, a.attname
120
121
  FROM pg_attribute a
121
122
  WHERE a.attrelid = #{oid}
@@ -158,7 +159,7 @@ module ActiveRecord
158
159
  def table_comment(table_name) # :nodoc:
159
160
  scope = quoted_scope(table_name, type: "BASE TABLE")
160
161
  if scope[:name]
161
- query_value(<<-SQL.strip_heredoc, "SCHEMA")
162
+ query_value(<<~SQL, "SCHEMA")
162
163
  SELECT pg_catalog.obj_description(c.oid, 'pg_class')
163
164
  FROM pg_catalog.pg_class c
164
165
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -196,7 +197,7 @@ module ActiveRecord
196
197
 
197
198
  # Returns an array of schema names.
198
199
  def schema_names
199
- query_values(<<-SQL, "SCHEMA")
200
+ query_values(<<~SQL, "SCHEMA")
200
201
  SELECT nspname
201
202
  FROM pg_namespace
202
203
  WHERE nspname !~ '^pg_.*'
@@ -211,7 +212,7 @@ module ActiveRecord
211
212
  end
212
213
 
213
214
  # Drops the schema for the given schema name.
214
- def drop_schema(schema_name, options = {})
215
+ def drop_schema(schema_name, **options)
215
216
  execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
216
217
  end
217
218
 
@@ -243,7 +244,7 @@ module ActiveRecord
243
244
  end
244
245
 
245
246
  # Returns the sequence name for a table's primary key or some other specified key.
246
- def default_sequence_name(table_name, pk = "id") #:nodoc:
247
+ def default_sequence_name(table_name, pk = "id") # :nodoc:
247
248
  result = serial_sequence(table_name, pk)
248
249
  return nil unless result
249
250
  Utils.extract_schema_qualified_name(result).to_s
@@ -256,7 +257,7 @@ module ActiveRecord
256
257
  end
257
258
 
258
259
  # Sets the sequence of a table's primary key to the specified value.
259
- def set_pk_sequence!(table, value) #:nodoc:
260
+ def set_pk_sequence!(table, value) # :nodoc:
260
261
  pk, sequence = pk_and_sequence_for(table)
261
262
 
262
263
  if pk
@@ -271,7 +272,7 @@ module ActiveRecord
271
272
  end
272
273
 
273
274
  # Resets the sequence of a table's primary key to the maximum value.
274
- def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
275
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) # :nodoc:
275
276
  unless pk && sequence
276
277
  default_pk, default_sequence = pk_and_sequence_for(table)
277
278
 
@@ -287,7 +288,7 @@ module ActiveRecord
287
288
  quoted_sequence = quote_table_name(sequence)
288
289
  max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
289
290
  if max_pk.nil?
290
- if postgresql_version >= 100000
291
+ if database_version >= 100000
291
292
  minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
292
293
  else
293
294
  minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
@@ -299,10 +300,10 @@ module ActiveRecord
299
300
  end
300
301
 
301
302
  # Returns a table's primary key and belonging sequence.
302
- def pk_and_sequence_for(table) #:nodoc:
303
+ def pk_and_sequence_for(table) # :nodoc:
303
304
  # First try looking for a sequence with a dependency on the
304
305
  # given table's primary key.
305
- result = query(<<-end_sql, "SCHEMA")[0]
306
+ result = query(<<~SQL, "SCHEMA")[0]
306
307
  SELECT attr.attname, nsp.nspname, seq.relname
307
308
  FROM pg_class seq,
308
309
  pg_attribute attr,
@@ -319,10 +320,10 @@ module ActiveRecord
319
320
  AND cons.contype = 'p'
320
321
  AND dep.classid = 'pg_class'::regclass
321
322
  AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
322
- end_sql
323
+ SQL
323
324
 
324
325
  if result.nil? || result.empty?
325
- result = query(<<-end_sql, "SCHEMA")[0]
326
+ result = query(<<~SQL, "SCHEMA")[0]
326
327
  SELECT attr.attname, nsp.nspname,
327
328
  CASE
328
329
  WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
@@ -339,7 +340,7 @@ module ActiveRecord
339
340
  WHERE t.oid = #{quote(quote_table_name(table))}::regclass
340
341
  AND cons.contype = 'p'
341
342
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
342
- end_sql
343
+ SQL
343
344
  end
344
345
 
345
346
  pk = result.shift
@@ -353,7 +354,7 @@ module ActiveRecord
353
354
  end
354
355
 
355
356
  def primary_keys(table_name) # :nodoc:
356
- query_values(<<-SQL.strip_heredoc, "SCHEMA")
357
+ query_values(<<~SQL, "SCHEMA")
357
358
  SELECT a.attname
358
359
  FROM (
359
360
  SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
@@ -368,31 +369,6 @@ module ActiveRecord
368
369
  SQL
369
370
  end
370
371
 
371
- def bulk_change_table(table_name, operations)
372
- sql_fragments = []
373
- non_combinable_operations = []
374
-
375
- operations.each do |command, args|
376
- table, arguments = args.shift, args
377
- method = :"#{command}_for_alter"
378
-
379
- if respond_to?(method, true)
380
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
381
- sql_fragments << sqls
382
- non_combinable_operations.concat(procs)
383
- else
384
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
385
- non_combinable_operations.each(&:call)
386
- sql_fragments = []
387
- non_combinable_operations = []
388
- send(command, table, *arguments)
389
- end
390
- end
391
-
392
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
393
- non_combinable_operations.each(&:call)
394
- end
395
-
396
372
  # Renames a table.
397
373
  # Also renames a table's primary key sequence if the sequence name exists and
398
374
  # matches the Active Record default.
@@ -401,6 +377,8 @@ module ActiveRecord
401
377
  # rename_table('octopuses', 'octopi')
402
378
  def rename_table(table_name, new_name)
403
379
  clear_cache!
380
+ schema_cache.clear_data_source_cache!(table_name.to_s)
381
+ schema_cache.clear_data_source_cache!(new_name.to_s)
404
382
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
405
383
  pk, seq = pk_and_sequence_for(new_name)
406
384
  if pk
@@ -415,15 +393,15 @@ module ActiveRecord
415
393
  rename_table_indexes(table_name, new_name)
416
394
  end
417
395
 
418
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
396
+ def add_column(table_name, column_name, type, **options) # :nodoc:
419
397
  clear_cache!
420
398
  super
421
399
  change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
422
400
  end
423
401
 
424
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
402
+ def change_column(table_name, column_name, type, **options) # :nodoc:
425
403
  clear_cache!
426
- sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
404
+ sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
427
405
  execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
428
406
  procs.each(&:call)
429
407
  end
@@ -433,7 +411,7 @@ module ActiveRecord
433
411
  execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
434
412
  end
435
413
 
436
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
414
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
437
415
  clear_cache!
438
416
  unless null || default.nil?
439
417
  column = column_for(table_name, column_name)
@@ -443,35 +421,40 @@ module ActiveRecord
443
421
  end
444
422
 
445
423
  # Adds comment for given table column or drops it if +comment+ is a +nil+
446
- def change_column_comment(table_name, column_name, comment) # :nodoc:
424
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
447
425
  clear_cache!
426
+ comment = extract_new_comment_value(comment_or_changes)
448
427
  execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
449
428
  end
450
429
 
451
430
  # Adds comment for given table or drops it if +comment+ is a +nil+
452
- def change_table_comment(table_name, comment) # :nodoc:
431
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
453
432
  clear_cache!
433
+ comment = extract_new_comment_value(comment_or_changes)
454
434
  execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
455
435
  end
456
436
 
457
437
  # Renames a column in a table.
458
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
438
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
459
439
  clear_cache!
460
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
440
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
461
441
  rename_column_indexes(table_name, column_name, new_column_name)
462
442
  end
463
443
 
464
- def add_index(table_name, column_name, options = {}) #:nodoc:
465
- index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
466
- execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}").tap do
467
- execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
468
- end
444
+ def add_index(table_name, column_name, **options) # :nodoc:
445
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
446
+
447
+ create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
448
+ result = execute schema_creation.accept(create_index)
449
+
450
+ execute "COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}" if index.comment
451
+ result
469
452
  end
470
453
 
471
- def remove_index(table_name, options = {}) #:nodoc:
454
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
472
455
  table = Utils.extract_schema_qualified_name(table_name.to_s)
473
456
 
474
- if options.is_a?(Hash) && options.key?(:name)
457
+ if options.key?(:name)
475
458
  provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
476
459
 
477
460
  options[:name] = provided_index.identifier
@@ -482,14 +465,11 @@ module ActiveRecord
482
465
  end
483
466
  end
484
467
 
485
- index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
486
- algorithm =
487
- if options.is_a?(Hash) && options.key?(:algorithm)
488
- index_algorithms.fetch(options[:algorithm]) do
489
- raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
490
- end
491
- end
492
- execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
468
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
469
+
470
+ index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, column_name, options))
471
+
472
+ execute "DROP INDEX #{index_algorithm(options[:algorithm])} #{quote_table_name(index_to_remove)}"
493
473
  end
494
474
 
495
475
  # Renames an index of a table. Raises error if length of new
@@ -502,8 +482,8 @@ module ActiveRecord
502
482
 
503
483
  def foreign_keys(table_name)
504
484
  scope = quoted_scope(table_name)
505
- fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
506
- SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
485
+ fk_info = exec_query(<<~SQL, "SCHEMA")
486
+ SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred
507
487
  FROM pg_constraint c
508
488
  JOIN pg_class t1 ON c.conrelid = t1.oid
509
489
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -525,6 +505,8 @@ module ActiveRecord
525
505
 
526
506
  options[:on_delete] = extract_foreign_key_action(row["on_delete"])
527
507
  options[:on_update] = extract_foreign_key_action(row["on_update"])
508
+ options[:deferrable] = extract_foreign_key_deferrable(row["deferrable"], row["deferred"])
509
+
528
510
  options[:validate] = row["valid"]
529
511
 
530
512
  ForeignKeyDefinition.new(table_name, row["to_table"], options)
@@ -539,8 +521,30 @@ module ActiveRecord
539
521
  query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
540
522
  end
541
523
 
524
+ def check_constraints(table_name) # :nodoc:
525
+ scope = quoted_scope(table_name)
526
+
527
+ check_info = exec_query(<<-SQL, "SCHEMA")
528
+ SELECT conname, pg_get_constraintdef(c.oid, true) AS constraintdef, c.convalidated AS valid
529
+ FROM pg_constraint c
530
+ JOIN pg_class t ON c.conrelid = t.oid
531
+ WHERE c.contype = 'c'
532
+ AND t.relname = #{scope[:name]}
533
+ SQL
534
+
535
+ check_info.map do |row|
536
+ options = {
537
+ name: row["conname"],
538
+ validate: row["valid"]
539
+ }
540
+ expression = row["constraintdef"][/CHECK \((.+)\)/m, 1]
541
+
542
+ CheckConstraintDefinition.new(table_name, expression, options)
543
+ end
544
+ end
545
+
542
546
  # Maps logical Rails types to PostgreSQL-specific data types.
543
- def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
547
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, enum_type: nil, **) # :nodoc:
544
548
  sql = \
545
549
  case type.to_s
546
550
  when "binary"
@@ -548,22 +552,26 @@ module ActiveRecord
548
552
  # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
549
553
  case limit
550
554
  when nil, 0..0x3fffffff; super(type)
551
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
555
+ else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
552
556
  end
553
557
  when "text"
554
558
  # PostgreSQL doesn't support limits on text columns.
555
559
  # The hard limit is 1GB, according to section 8.3 in the manual.
556
560
  case limit
557
561
  when nil, 0..0x3fffffff; super(type)
558
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
562
+ else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
559
563
  end
560
564
  when "integer"
561
565
  case limit
562
566
  when 1, 2; "smallint"
563
567
  when nil, 3, 4; "integer"
564
568
  when 5..8; "bigint"
565
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
569
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
566
570
  end
571
+ when "enum"
572
+ raise ArgumentError, "enum_type is required for enums" if enum_type.nil?
573
+
574
+ enum_type
567
575
  else
568
576
  super
569
577
  end
@@ -574,14 +582,14 @@ module ActiveRecord
574
582
 
575
583
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
576
584
  # requires that the ORDER BY include the distinct column.
577
- def columns_for_distinct(columns, orders) #:nodoc:
578
- order_columns = orders.reject(&:blank?).map { |s|
579
- # Convert Arel node to string
580
- s = s.to_sql unless s.is_a?(String)
581
- # Remove any ASC/DESC modifiers
582
- s.gsub(/\s+(?:ASC|DESC)\b/i, "")
583
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
584
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
585
+ def columns_for_distinct(columns, orders) # :nodoc:
586
+ order_columns = orders.compact_blank.map { |s|
587
+ # Convert Arel node to string
588
+ s = visitor.compile(s) unless s.is_a?(String)
589
+ # Remove any ASC/DESC modifiers
590
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
591
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
592
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
585
593
 
586
594
  (order_columns << super).join(", ")
587
595
  end
@@ -600,8 +608,6 @@ module ActiveRecord
600
608
  #
601
609
  # validate_constraint :accounts, :constraint_name
602
610
  def validate_constraint(table_name, constraint_name)
603
- return unless supports_validate_constraints?
604
-
605
611
  at = create_alter_table table_name
606
612
  at.validate_constraint constraint_name
607
613
 
@@ -623,21 +629,30 @@ module ActiveRecord
623
629
  # validate_foreign_key :accounts, name: :special_fk_name
624
630
  #
625
631
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
626
- def validate_foreign_key(from_table, options_or_to_table = {})
627
- return unless supports_validate_constraints?
628
-
629
- fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name
632
+ def validate_foreign_key(from_table, to_table = nil, **options)
633
+ fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
630
634
 
631
635
  validate_constraint from_table, fk_name_to_validate
632
636
  end
633
637
 
638
+ # Validates the given check constraint.
639
+ #
640
+ # validate_check_constraint :products, name: "price_check"
641
+ #
642
+ # The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
643
+ def validate_check_constraint(table_name, **options)
644
+ chk_name_to_validate = check_constraint_for!(table_name, **options).name
645
+
646
+ validate_constraint table_name, chk_name_to_validate
647
+ end
648
+
634
649
  private
635
650
  def schema_creation
636
651
  PostgreSQL::SchemaCreation.new(self)
637
652
  end
638
653
 
639
- def create_table_definition(*args)
640
- PostgreSQL::TableDefinition.new(*args)
654
+ def create_table_definition(name, **options)
655
+ PostgreSQL::TableDefinition.new(self, name, **options)
641
656
  end
642
657
 
643
658
  def create_alter_table(name)
@@ -645,21 +660,30 @@ module ActiveRecord
645
660
  end
646
661
 
647
662
  def new_column_from_field(table_name, field)
648
- column_name, type, default, notnull, oid, fmod, collation, comment = field
663
+ column_name, type, default, notnull, oid, fmod, collation, comment, attgenerated = field
649
664
  type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
650
665
  default_value = extract_value_from_default(default)
651
- default_function = extract_default_function(default_value, default)
652
666
 
653
- PostgreSQLColumn.new(
667
+ if attgenerated.present?
668
+ default_function = default
669
+ else
670
+ default_function = extract_default_function(default_value, default)
671
+ end
672
+
673
+ if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
674
+ serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
675
+ end
676
+
677
+ PostgreSQL::Column.new(
654
678
  column_name,
655
679
  default_value,
656
680
  type_metadata,
657
681
  !notnull,
658
- table_name,
659
682
  default_function,
660
- collation,
683
+ collation: collation,
661
684
  comment: comment.presence,
662
- max_identifier_length: max_identifier_length
685
+ serial: serial,
686
+ generated: attgenerated
663
687
  )
664
688
  end
665
689
 
@@ -672,7 +696,23 @@ module ActiveRecord
672
696
  precision: cast_type.precision,
673
697
  scale: cast_type.scale,
674
698
  )
675
- PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
699
+ PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
700
+ end
701
+
702
+ def sequence_name_from_parts(table_name, column_name, suffix)
703
+ over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
704
+
705
+ if over_length > 0
706
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
707
+ over_length -= column_name.length - column_name_length
708
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
709
+ end
710
+
711
+ if over_length > 0
712
+ table_name = table_name[0, table_name.length - over_length]
713
+ end
714
+
715
+ "#{table_name}_#{column_name}_#{suffix}"
676
716
  end
677
717
 
678
718
  def extract_foreign_key_action(specifier)
@@ -683,14 +723,18 @@ module ActiveRecord
683
723
  end
684
724
  end
685
725
 
686
- def add_column_for_alter(table_name, column_name, type, options = {})
726
+ def extract_foreign_key_deferrable(deferrable, deferred)
727
+ deferrable && (deferred ? :deferred : true)
728
+ end
729
+
730
+ def add_column_for_alter(table_name, column_name, type, **options)
687
731
  return super unless options.key?(:comment)
688
732
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
689
733
  end
690
734
 
691
- def change_column_for_alter(table_name, column_name, type, options = {})
735
+ def change_column_for_alter(table_name, column_name, type, **options)
692
736
  td = create_table_definition(table_name)
693
- cd = td.new_column_definition(column_name, type, options)
737
+ cd = td.new_column_definition(column_name, type, **options)
694
738
  sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
695
739
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
696
740
  sqls
@@ -715,14 +759,6 @@ module ActiveRecord
715
759
  "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
716
760
  end
717
761
 
718
- def add_timestamps_for_alter(table_name, options = {})
719
- [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
720
- end
721
-
722
- def remove_timestamps_for_alter(table_name, options = {})
723
- [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
724
- end
725
-
726
762
  def add_index_opclass(quoted_columns, **options)
727
763
  opclasses = options_for_index_columns(options[:opclass])
728
764
  quoted_columns.each do |name, column|
@@ -731,7 +767,7 @@ module ActiveRecord
731
767
  end
732
768
 
733
769
  def add_options_for_index_columns(quoted_columns, **options)
734
- quoted_columns = add_index_opclass(quoted_columns, options)
770
+ quoted_columns = add_index_opclass(quoted_columns, **options)
735
771
  super
736
772
  end
737
773
 
@@ -739,7 +775,7 @@ module ActiveRecord
739
775
  scope = quoted_scope(name, type: type)
740
776
  scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
741
777
 
742
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
778
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
743
779
  sql << " WHERE n.nspname = #{scope[:schema]}"
744
780
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
745
781
  sql << " AND c.relkind IN (#{scope[:type]})"
@@ -1,39 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
+ # :stopdoc:
4
5
  module ConnectionAdapters
5
- class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
6
- undef to_yaml if method_defined?(:to_yaml)
6
+ module PostgreSQL
7
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
+ undef to_yaml if method_defined?(:to_yaml)
7
9
 
8
- attr_reader :oid, :fmod, :array
10
+ include Deduplicable
9
11
 
10
- def initialize(type_metadata, oid: nil, fmod: nil)
11
- super(type_metadata)
12
- @type_metadata = type_metadata
13
- @oid = oid
14
- @fmod = fmod
15
- @array = /\[\]$/.match?(type_metadata.sql_type)
16
- end
17
-
18
- def sql_type
19
- super.gsub(/\[\]$/, "".freeze)
20
- end
21
-
22
- def ==(other)
23
- other.is_a?(PostgreSQLTypeMetadata) &&
24
- attributes_for_hash == other.attributes_for_hash
25
- end
26
- alias eql? ==
12
+ attr_reader :oid, :fmod
27
13
 
28
- def hash
29
- attributes_for_hash.hash
30
- end
14
+ def initialize(type_metadata, oid: nil, fmod: nil)
15
+ super(type_metadata)
16
+ @oid = oid
17
+ @fmod = fmod
18
+ end
31
19
 
32
- protected
20
+ def ==(other)
21
+ other.is_a?(TypeMetadata) &&
22
+ __getobj__ == other.__getobj__ &&
23
+ oid == other.oid &&
24
+ fmod == other.fmod
25
+ end
26
+ alias eql? ==
33
27
 
34
- def attributes_for_hash
35
- [self.class, @type_metadata, oid, fmod]
28
+ def hash
29
+ TypeMetadata.hash ^
30
+ __getobj__.hash ^
31
+ oid.hash ^
32
+ fmod.hash
36
33
  end
34
+
35
+ private
36
+ def deduplicated
37
+ __setobj__(__getobj__.deduplicate)
38
+ super
39
+ end
40
+ end
37
41
  end
42
+ PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
38
43
  end
39
44
  end
@@ -37,7 +37,6 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  protected
40
-
41
40
  def parts
42
41
  @parts ||= [@schema, @identifier].compact
43
42
  end