activerecord 5.2.8.1 → 6.1.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  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 +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  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 +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -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
@@ -54,7 +54,8 @@ module ActiveRecord
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
 
@@ -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")
@@ -302,7 +303,7 @@ module ActiveRecord
302
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
@@ -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
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,7 +482,7 @@ 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")
485
+ fk_info = exec_query(<<~SQL, "SCHEMA")
506
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
507
487
  FROM pg_constraint c
508
488
  JOIN pg_class t1 ON c.conrelid = t1.oid
@@ -539,6 +519,28 @@ module ActiveRecord
539
519
  query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
540
520
  end
541
521
 
522
+ def check_constraints(table_name) # :nodoc:
523
+ scope = quoted_scope(table_name)
524
+
525
+ check_info = exec_query(<<-SQL, "SCHEMA")
526
+ SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.convalidated AS valid
527
+ FROM pg_constraint c
528
+ JOIN pg_class t ON c.conrelid = t.oid
529
+ WHERE c.contype = 'c'
530
+ AND t.relname = #{scope[:name]}
531
+ SQL
532
+
533
+ check_info.map do |row|
534
+ options = {
535
+ name: row["conname"],
536
+ validate: row["valid"]
537
+ }
538
+ expression = row["constraintdef"][/CHECK \({2}(.+)\){2}/, 1]
539
+
540
+ CheckConstraintDefinition.new(table_name, expression, options)
541
+ end
542
+ end
543
+
542
544
  # Maps logical Rails types to PostgreSQL-specific data types.
543
545
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
544
546
  sql = \
@@ -548,21 +550,21 @@ module ActiveRecord
548
550
  # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
549
551
  case limit
550
552
  when nil, 0..0x3fffffff; super(type)
551
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
553
+ else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
552
554
  end
553
555
  when "text"
554
556
  # PostgreSQL doesn't support limits on text columns.
555
557
  # The hard limit is 1GB, according to section 8.3 in the manual.
556
558
  case limit
557
559
  when nil, 0..0x3fffffff; super(type)
558
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
560
+ else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
559
561
  end
560
562
  when "integer"
561
563
  case limit
562
564
  when 1, 2; "smallint"
563
565
  when nil, 3, 4; "integer"
564
566
  when 5..8; "bigint"
565
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
567
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
566
568
  end
567
569
  else
568
570
  super
@@ -575,13 +577,13 @@ module ActiveRecord
575
577
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
576
578
  # requires that the ORDER BY include the distinct column.
577
579
  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}" }
580
+ order_columns = orders.compact_blank.map { |s|
581
+ # Convert Arel node to string
582
+ s = visitor.compile(s) unless s.is_a?(String)
583
+ # Remove any ASC/DESC modifiers
584
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
585
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
586
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
585
587
 
586
588
  (order_columns << super).join(", ")
587
589
  end
@@ -600,8 +602,6 @@ module ActiveRecord
600
602
  #
601
603
  # validate_constraint :accounts, :constraint_name
602
604
  def validate_constraint(table_name, constraint_name)
603
- return unless supports_validate_constraints?
604
-
605
605
  at = create_alter_table table_name
606
606
  at.validate_constraint constraint_name
607
607
 
@@ -623,21 +623,30 @@ module ActiveRecord
623
623
  # validate_foreign_key :accounts, name: :special_fk_name
624
624
  #
625
625
  # 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
626
+ def validate_foreign_key(from_table, to_table = nil, **options)
627
+ fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
630
628
 
631
629
  validate_constraint from_table, fk_name_to_validate
632
630
  end
633
631
 
632
+ # Validates the given check constraint.
633
+ #
634
+ # validate_check_constraint :products, name: "price_check"
635
+ #
636
+ # The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
637
+ def validate_check_constraint(table_name, **options)
638
+ chk_name_to_validate = check_constraint_for!(table_name, **options).name
639
+
640
+ validate_constraint table_name, chk_name_to_validate
641
+ end
642
+
634
643
  private
635
644
  def schema_creation
636
645
  PostgreSQL::SchemaCreation.new(self)
637
646
  end
638
647
 
639
- def create_table_definition(*args)
640
- PostgreSQL::TableDefinition.new(*args)
648
+ def create_table_definition(name, **options)
649
+ PostgreSQL::TableDefinition.new(self, name, **options)
641
650
  end
642
651
 
643
652
  def create_alter_table(name)
@@ -650,16 +659,19 @@ module ActiveRecord
650
659
  default_value = extract_value_from_default(default)
651
660
  default_function = extract_default_function(default_value, default)
652
661
 
653
- PostgreSQLColumn.new(
662
+ if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
663
+ serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
664
+ end
665
+
666
+ PostgreSQL::Column.new(
654
667
  column_name,
655
668
  default_value,
656
669
  type_metadata,
657
670
  !notnull,
658
- table_name,
659
671
  default_function,
660
- collation,
672
+ collation: collation,
661
673
  comment: comment.presence,
662
- max_identifier_length: max_identifier_length
674
+ serial: serial
663
675
  )
664
676
  end
665
677
 
@@ -672,7 +684,23 @@ module ActiveRecord
672
684
  precision: cast_type.precision,
673
685
  scale: cast_type.scale,
674
686
  )
675
- PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
687
+ PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
688
+ end
689
+
690
+ def sequence_name_from_parts(table_name, column_name, suffix)
691
+ over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
692
+
693
+ if over_length > 0
694
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
695
+ over_length -= column_name.length - column_name_length
696
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
697
+ end
698
+
699
+ if over_length > 0
700
+ table_name = table_name[0, table_name.length - over_length]
701
+ end
702
+
703
+ "#{table_name}_#{column_name}_#{suffix}"
676
704
  end
677
705
 
678
706
  def extract_foreign_key_action(specifier)
@@ -683,14 +711,14 @@ module ActiveRecord
683
711
  end
684
712
  end
685
713
 
686
- def add_column_for_alter(table_name, column_name, type, options = {})
714
+ def add_column_for_alter(table_name, column_name, type, **options)
687
715
  return super unless options.key?(:comment)
688
716
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
689
717
  end
690
718
 
691
- def change_column_for_alter(table_name, column_name, type, options = {})
719
+ def change_column_for_alter(table_name, column_name, type, **options)
692
720
  td = create_table_definition(table_name)
693
- cd = td.new_column_definition(column_name, type, options)
721
+ cd = td.new_column_definition(column_name, type, **options)
694
722
  sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
695
723
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
696
724
  sqls
@@ -715,14 +743,6 @@ module ActiveRecord
715
743
  "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
716
744
  end
717
745
 
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
746
  def add_index_opclass(quoted_columns, **options)
727
747
  opclasses = options_for_index_columns(options[:opclass])
728
748
  quoted_columns.each do |name, column|
@@ -731,7 +751,7 @@ module ActiveRecord
731
751
  end
732
752
 
733
753
  def add_options_for_index_columns(quoted_columns, **options)
734
- quoted_columns = add_index_opclass(quoted_columns, options)
754
+ quoted_columns = add_index_opclass(quoted_columns, **options)
735
755
  super
736
756
  end
737
757
 
@@ -739,7 +759,7 @@ module ActiveRecord
739
759
  scope = quoted_scope(name, type: type)
740
760
  scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
741
761
 
742
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
762
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
743
763
  sql << " WHERE n.nspname = #{scope[:schema]}"
744
764
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
745
765
  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