activerecord 6.0.0 → 6.1.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1413 -614
  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 +38 -13
  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 +49 -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 +24 -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 +44 -35
  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 +145 -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 +32 -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 +20 -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 +23 -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 +30 -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 +84 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -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 +40 -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 +283 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +125 -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 +3 -3
  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 +18 -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 +75 -21
  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 +346 -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 +9 -4
  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 +87 -20
  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 +31 -27
  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
@@ -243,7 +247,7 @@ module ActiveRecord
243
247
  def initialize(connection, logger, connection_parameters, config)
244
248
  super(connection, logger, config)
245
249
 
246
- @connection_parameters = connection_parameters
250
+ @connection_parameters = connection_parameters || {}
247
251
 
248
252
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
249
253
  @local_tz = nil
@@ -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)