activerecord 6.0.0 → 6.1.4

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1178 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +30 -10
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -29
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +120 -13
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +63 -44
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +12 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  104. data/lib/active_record/connection_adapters/schema_cache.rb +106 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +219 -81
  114. data/lib/active_record/core.rb +268 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +124 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +2 -2
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +43 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +71 -20
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +344 -181
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -82
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +3 -3
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +79 -16
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +26 -73
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +15 -12
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +27 -24
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -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
 
@@ -74,7 +75,7 @@ module ActiveRecord
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]}
@@ -92,7 +93,7 @@ module ActiveRecord
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]}
@@ -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
 
@@ -376,6 +377,8 @@ module ActiveRecord
376
377
  # rename_table('octopuses', 'octopi')
377
378
  def rename_table(table_name, new_name)
378
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)
379
382
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
380
383
  pk, seq = pk_and_sequence_for(new_name)
381
384
  if pk
@@ -390,15 +393,15 @@ module ActiveRecord
390
393
  rename_table_indexes(table_name, new_name)
391
394
  end
392
395
 
393
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
396
+ def add_column(table_name, column_name, type, **options) #:nodoc:
394
397
  clear_cache!
395
398
  super
396
399
  change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
397
400
  end
398
401
 
399
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
402
+ def change_column(table_name, column_name, type, **options) #:nodoc:
400
403
  clear_cache!
401
- 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) }
402
405
  execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
403
406
  procs.each(&:call)
404
407
  end
@@ -434,21 +437,24 @@ module ActiveRecord
434
437
  # Renames a column in a table.
435
438
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
436
439
  clear_cache!
437
- 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)}")
438
441
  rename_column_indexes(table_name, column_name, new_column_name)
439
442
  end
440
443
 
441
- def add_index(table_name, column_name, options = {}) #:nodoc:
442
- index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
443
- 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
444
- execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
445
- 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
446
452
  end
447
453
 
448
- def remove_index(table_name, options = {}) #:nodoc:
454
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
449
455
  table = Utils.extract_schema_qualified_name(table_name.to_s)
450
456
 
451
- if options.is_a?(Hash) && options.key?(:name)
457
+ if options.key?(:name)
452
458
  provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
453
459
 
454
460
  options[:name] = provided_index.identifier
@@ -459,14 +465,11 @@ module ActiveRecord
459
465
  end
460
466
  end
461
467
 
462
- index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
463
- algorithm =
464
- if options.is_a?(Hash) && options.key?(:algorithm)
465
- index_algorithms.fetch(options[:algorithm]) do
466
- raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
467
- end
468
- end
469
- 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)}"
470
473
  end
471
474
 
472
475
  # Renames an index of a table. Raises error if length of new
@@ -516,6 +519,28 @@ module ActiveRecord
516
519
  query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
517
520
  end
518
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
+
519
544
  # Maps logical Rails types to PostgreSQL-specific data types.
520
545
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
521
546
  sql = \
@@ -552,13 +577,13 @@ module ActiveRecord
552
577
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
553
578
  # requires that the ORDER BY include the distinct column.
554
579
  def columns_for_distinct(columns, orders) #:nodoc:
555
- order_columns = orders.reject(&:blank?).map { |s|
556
- # Convert Arel node to string
557
- s = s.to_sql unless s.is_a?(String)
558
- # Remove any ASC/DESC modifiers
559
- s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
561
- }.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}" }
562
587
 
563
588
  (order_columns << super).join(", ")
564
589
  end
@@ -577,8 +602,6 @@ module ActiveRecord
577
602
  #
578
603
  # validate_constraint :accounts, :constraint_name
579
604
  def validate_constraint(table_name, constraint_name)
580
- return unless supports_validate_constraints?
581
-
582
605
  at = create_alter_table table_name
583
606
  at.validate_constraint constraint_name
584
607
 
@@ -601,20 +624,29 @@ module ActiveRecord
601
624
  #
602
625
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
603
626
  def validate_foreign_key(from_table, to_table = nil, **options)
604
- return unless supports_validate_constraints?
605
-
606
627
  fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
607
628
 
608
629
  validate_constraint from_table, fk_name_to_validate
609
630
  end
610
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
+
611
643
  private
612
644
  def schema_creation
613
645
  PostgreSQL::SchemaCreation.new(self)
614
646
  end
615
647
 
616
- def create_table_definition(*args)
617
- PostgreSQL::TableDefinition.new(self, *args)
648
+ def create_table_definition(name, **options)
649
+ PostgreSQL::TableDefinition.new(self, name, **options)
618
650
  end
619
651
 
620
652
  def create_alter_table(name)
@@ -679,14 +711,14 @@ module ActiveRecord
679
711
  end
680
712
  end
681
713
 
682
- def add_column_for_alter(table_name, column_name, type, options = {})
714
+ def add_column_for_alter(table_name, column_name, type, **options)
683
715
  return super unless options.key?(:comment)
684
716
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
685
717
  end
686
718
 
687
- def change_column_for_alter(table_name, column_name, type, options = {})
719
+ def change_column_for_alter(table_name, column_name, type, **options)
688
720
  td = create_table_definition(table_name)
689
- cd = td.new_column_definition(column_name, type, options)
721
+ cd = td.new_column_definition(column_name, type, **options)
690
722
  sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
691
723
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
692
724
  sqls
@@ -711,20 +743,6 @@ module ActiveRecord
711
743
  "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
712
744
  end
713
745
 
714
- def add_timestamps_for_alter(table_name, options = {})
715
- options[:null] = false if options[:null].nil?
716
-
717
- if !options.key?(:precision) && supports_datetime_with_precision?
718
- options[:precision] = 6
719
- end
720
-
721
- [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
722
- end
723
-
724
- def remove_timestamps_for_alter(table_name, options = {})
725
- [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
726
- end
727
-
728
746
  def add_index_opclass(quoted_columns, **options)
729
747
  opclasses = options_for_index_columns(options[:opclass])
730
748
  quoted_columns.each do |name, column|
@@ -733,7 +751,7 @@ module ActiveRecord
733
751
  end
734
752
 
735
753
  def add_options_for_index_columns(quoted_columns, **options)
736
- quoted_columns = add_index_opclass(quoted_columns, options)
754
+ quoted_columns = add_index_opclass(quoted_columns, **options)
737
755
  super
738
756
  end
739
757
 
@@ -7,6 +7,8 @@ module ActiveRecord
7
7
  class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
8
  undef to_yaml if method_defined?(:to_yaml)
9
9
 
10
+ include Deduplicable
11
+
10
12
  attr_reader :oid, :fmod
11
13
 
12
14
  def initialize(type_metadata, oid: nil, fmod: nil)
@@ -29,6 +31,12 @@ module ActiveRecord
29
31
  oid.hash ^
30
32
  fmod.hash
31
33
  end
34
+
35
+ private
36
+ def deduplicated
37
+ __setobj__(__getobj__.deduplicate)
38
+ super
39
+ end
32
40
  end
33
41
  end
34
42
  PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
@@ -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
@@ -1,17 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
4
- gem "pg", ">= 0.18", "< 2.0"
3
+ gem "pg", "~> 1.1"
5
4
  require "pg"
6
5
 
7
- # Use async_exec instead of exec_params on pg versions before 1.1
8
- class ::PG::Connection # :nodoc:
9
- unless self.public_method_defined?(:async_exec_params)
10
- remove_method :exec_params
11
- alias exec_params async_exec
12
- end
13
- end
14
-
6
+ require "active_support/core_ext/object/try"
15
7
  require "active_record/connection_adapters/abstract_adapter"
16
8
  require "active_record/connection_adapters/statement_pool"
17
9
  require "active_record/connection_adapters/postgresql/column"
@@ -31,9 +23,7 @@ module ActiveRecord
31
23
  module ConnectionHandling # :nodoc:
32
24
  # Establishes a connection to the database that's used by all Active Record objects
33
25
  def postgresql_connection(config)
34
- conn_params = config.symbolize_keys
35
-
36
- conn_params.delete_if { |_, v| v.nil? }
26
+ conn_params = config.symbolize_keys.compact
37
27
 
38
28
  # Map ActiveRecords param names to PGs.
39
29
  conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
@@ -43,19 +33,17 @@ module ActiveRecord
43
33
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
34
  conn_params.slice!(*valid_conn_param_keys)
45
35
 
46
- conn = PG.connect(conn_params)
47
- ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
- rescue ::PG::Error => error
49
- if error.message.include?(conn_params[:dbname])
50
- raise ActiveRecord::NoDatabaseError
51
- else
52
- raise
53
- end
36
+ ConnectionAdapters::PostgreSQLAdapter.new(
37
+ ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
38
+ logger,
39
+ conn_params,
40
+ config,
41
+ )
54
42
  end
55
43
  end
56
44
 
57
45
  module ConnectionAdapters
58
- # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
46
+ # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
59
47
  #
60
48
  # Options:
61
49
  #
@@ -85,6 +73,18 @@ module ActiveRecord
85
73
  class PostgreSQLAdapter < AbstractAdapter
86
74
  ADAPTER_NAME = "PostgreSQL"
87
75
 
76
+ class << self
77
+ def new_client(conn_params)
78
+ PG.connect(conn_params)
79
+ rescue ::PG::Error => error
80
+ if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
81
+ raise ActiveRecord::NoDatabaseError
82
+ else
83
+ raise ActiveRecord::ConnectionNotEstablished, error.message
84
+ end
85
+ end
86
+ end
87
+
88
88
  ##
89
89
  # :singleton-method:
90
90
  # PostgreSQL allows the creation of "unlogged" tables, which do not record
@@ -156,6 +156,10 @@ module ActiveRecord
156
156
  true
157
157
  end
158
158
 
159
+ def supports_partitioned_indexes?
160
+ database_version >= 110_000
161
+ end
162
+
159
163
  def supports_partial_index?
160
164
  true
161
165
  end
@@ -172,6 +176,10 @@ module ActiveRecord
172
176
  true
173
177
  end
174
178
 
179
+ def supports_check_constraints?
180
+ true
181
+ end
182
+
175
183
  def supports_validate_constraints?
176
184
  true
177
185
  end
@@ -219,11 +227,7 @@ module ActiveRecord
219
227
  end
220
228
 
221
229
  def next_key
222
- "a#{@counter + 1}"
223
- end
224
-
225
- def []=(sql, key)
226
- super.tap { @counter += 1 }
230
+ "a#{@counter += 1}"
227
231
  end
228
232
 
229
233
  private
@@ -337,11 +341,6 @@ module ActiveRecord
337
341
  true
338
342
  end
339
343
 
340
- def supports_ranges?
341
- true
342
- end
343
- deprecate :supports_ranges?
344
-
345
344
  def supports_materialized_views?
346
345
  true
347
346
  end
@@ -361,6 +360,10 @@ module ActiveRecord
361
360
  @has_pg_hint_plan
362
361
  end
363
362
 
363
+ def supports_common_table_expressions?
364
+ true
365
+ end
366
+
364
367
  def supports_lazy_transactions?
365
368
  true
366
369
  end
@@ -418,16 +421,6 @@ module ActiveRecord
418
421
  @use_insert_returning
419
422
  end
420
423
 
421
- def column_name_for_operation(operation, node) # :nodoc:
422
- OPERATION_ALIASES.fetch(operation) { operation.downcase }
423
- end
424
-
425
- OPERATION_ALIASES = { # :nodoc:
426
- "maximum" => "max",
427
- "minimum" => "min",
428
- "average" => "avg",
429
- }
430
-
431
424
  # Returns the version of the connected PostgreSQL server.
432
425
  def get_database_version # :nodoc:
433
426
  @connection.server_version
@@ -445,6 +438,7 @@ module ActiveRecord
445
438
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
446
439
  elsif insert.update_duplicates?
447
440
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
441
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
448
442
  sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
449
443
  end
450
444
 
@@ -459,7 +453,6 @@ module ActiveRecord
459
453
  end
460
454
 
461
455
  private
462
-
463
456
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
464
457
  VALUE_LIMIT_VIOLATION = "22001"
465
458
  NUMERIC_VALUE_OUT_OF_RANGE = "22003"
@@ -468,6 +461,7 @@ module ActiveRecord
468
461
  UNIQUE_VIOLATION = "23505"
469
462
  SERIALIZATION_FAILURE = "40001"
470
463
  DEADLOCK_DETECTED = "40P01"
464
+ DUPLICATE_DATABASE = "42P04"
471
465
  LOCK_NOT_AVAILABLE = "55P03"
472
466
  QUERY_CANCELED = "57014"
473
467
 
@@ -475,6 +469,12 @@ module ActiveRecord
475
469
  return exception unless exception.respond_to?(:result)
476
470
 
477
471
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
472
+ when nil
473
+ if exception.message.match?(/connection is closed/i)
474
+ ConnectionNotEstablished.new(exception)
475
+ else
476
+ super
477
+ end
478
478
  when UNIQUE_VIOLATION
479
479
  RecordNotUnique.new(message, sql: sql, binds: binds)
480
480
  when FOREIGN_KEY_VIOLATION
@@ -489,6 +489,8 @@ module ActiveRecord
489
489
  SerializationFailure.new(message, sql: sql, binds: binds)
490
490
  when DEADLOCK_DETECTED
491
491
  Deadlocked.new(message, sql: sql, binds: binds)
492
+ when DUPLICATE_DATABASE
493
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
492
494
  when LOCK_NOT_AVAILABLE
493
495
  LockWaitTimeout.new(message, sql: sql, binds: binds)
494
496
  when QUERY_CANCELED
@@ -540,7 +542,7 @@ module ActiveRecord
540
542
  m.register_type "uuid", OID::Uuid.new
541
543
  m.register_type "xml", OID::Xml.new
542
544
  m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
543
- m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
545
+ m.register_type "macaddr", OID::Macaddr.new
544
546
  m.register_type "citext", OID::SpecializedString.new(:citext)
545
547
  m.register_type "ltree", OID::SpecializedString.new(:ltree)
546
548
  m.register_type "line", OID::SpecializedString.new(:line)
@@ -550,11 +552,6 @@ module ActiveRecord
550
552
  m.register_type "polygon", OID::SpecializedString.new(:polygon)
551
553
  m.register_type "circle", OID::SpecializedString.new(:circle)
552
554
 
553
- m.register_type "interval" do |_, _, sql_type|
554
- precision = extract_precision(sql_type)
555
- OID::SpecializedString.new(:interval, precision: precision)
556
- end
557
-
558
555
  register_class_with_precision m, "time", Type::Time
559
556
  register_class_with_precision m, "timestamp", OID::DateTime
560
557
 
@@ -578,6 +575,11 @@ module ActiveRecord
578
575
  end
579
576
  end
580
577
 
578
+ m.register_type "interval" do |*args, sql_type|
579
+ precision = extract_precision(sql_type)
580
+ OID::Interval.new(precision: precision)
581
+ end
582
+
581
583
  load_additional_types
582
584
  end
583
585
 
@@ -626,7 +628,7 @@ module ActiveRecord
626
628
  SQL
627
629
 
628
630
  if oids
629
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
631
+ query += "WHERE t.oid IN (%s)" % oids.join(", ")
630
632
  else
631
633
  query += initializer.query_conditions_for_initial_load
632
634
  end
@@ -643,20 +645,22 @@ module ActiveRecord
643
645
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
644
646
  end
645
647
 
646
- if without_prepared_statement?(binds)
647
- result = exec_no_cache(sql, name, [])
648
- elsif !prepare
648
+ if !prepare || without_prepared_statement?(binds)
649
649
  result = exec_no_cache(sql, name, binds)
650
650
  else
651
651
  result = exec_cache(sql, name, binds)
652
652
  end
653
- ret = yield result
654
- result.clear
653
+ begin
654
+ ret = yield result
655
+ ensure
656
+ result.clear
657
+ end
655
658
  ret
656
659
  end
657
660
 
658
661
  def exec_no_cache(sql, name, binds)
659
662
  materialize_transactions
663
+ mark_transaction_written_if_write(sql)
660
664
 
661
665
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
662
666
  # made since we established the connection
@@ -672,6 +676,7 @@ module ActiveRecord
672
676
 
673
677
  def exec_cache(sql, name, binds)
674
678
  materialize_transactions
679
+ mark_transaction_written_if_write(sql)
675
680
  update_typemap_for_default_timezone
676
681
 
677
682
  stmt_key = prepare_statement(sql, binds)
@@ -707,11 +712,10 @@ module ActiveRecord
707
712
  #
708
713
  # Check here for more details:
709
714
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
710
- CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
711
715
  def is_cached_plan_failure?(e)
712
716
  pgerror = e.cause
713
- code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
714
- code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
717
+ pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
718
+ pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
715
719
  rescue
716
720
  false
717
721
  end
@@ -749,7 +753,7 @@ module ActiveRecord
749
753
  # Connects to a PostgreSQL server and sets up the adapter depending on the
750
754
  # connected server's characteristics.
751
755
  def connect
752
- @connection = PG.connect(@connection_parameters)
756
+ @connection = self.class.new_client(@connection_parameters)
753
757
  configure_connection
754
758
  add_pg_encoders
755
759
  add_pg_decoders
@@ -779,6 +783,9 @@ module ActiveRecord
779
783
  end
780
784
  end
781
785
 
786
+ # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
787
+ execute("SET intervalstyle = iso_8601", "SCHEMA")
788
+
782
789
  # SET statements from :variables config hash
783
790
  # https://www.postgresql.org/docs/current/static/sql-set.html
784
791
  variables.map do |k, v|
@@ -890,15 +897,12 @@ module ActiveRecord
890
897
  "oid" => PG::TextDecoder::Integer,
891
898
  "float4" => PG::TextDecoder::Float,
892
899
  "float8" => PG::TextDecoder::Float,
900
+ "numeric" => PG::TextDecoder::Numeric,
893
901
  "bool" => PG::TextDecoder::Boolean,
902
+ "timestamp" => PG::TextDecoder::TimestampUtc,
903
+ "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
894
904
  }
895
905
 
896
- if defined?(PG::TextDecoder::TimestampUtc)
897
- # Use native PG encoders available since pg-1.1
898
- coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
899
- coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
900
- end
901
-
902
906
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
903
907
  query = <<~SQL % known_coder_types.join(", ")
904
908
  SELECT t.oid, t.typname
@@ -915,6 +919,11 @@ module ActiveRecord
915
919
  coders.each { |coder| map.add_coder(coder) }
916
920
  @connection.type_map_for_results = map
917
921
 
922
+ @type_map_for_results = PG::TypeMapByOid.new
923
+ @type_map_for_results.default_type_map = map
924
+ @type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
925
+ @type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
926
+
918
927
  # extract timestamp decoder for use in update_typemap_for_default_timezone
919
928
  @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
920
929
  update_typemap_for_default_timezone
@@ -925,6 +934,14 @@ module ActiveRecord
925
934
  coder_class.new(oid: row["oid"].to_i, name: row["typname"])
926
935
  end
927
936
 
937
+ class MoneyDecoder < PG::SimpleDecoder # :nodoc:
938
+ TYPE = OID::Money.new
939
+
940
+ def decode(value, tuple = nil, field = nil)
941
+ TYPE.deserialize(value)
942
+ end
943
+ end
944
+
928
945
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
929
946
  ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
930
947
  ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
@@ -937,6 +954,7 @@ module ActiveRecord
937
954
  ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
938
955
  ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
939
956
  ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
957
+ ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
940
958
  ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
941
959
  ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
942
960
  ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)