activerecord 6.0.6 → 6.1.0

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +783 -910
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +43 -26
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  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 -1
  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 +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  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 +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +32 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +223 -66
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +27 -10
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +2 -2
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +34 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +16 -7
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -13
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +266 -95
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +318 -195
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +39 -51
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +28 -30
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -7,12 +7,22 @@ module ActiveRecord
7
7
  class Uuid < Type::Value # :nodoc:
8
8
  ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
9
9
 
10
- alias_method :serialize, :deserialize
10
+ alias :serialize :deserialize
11
11
 
12
12
  def type
13
13
  :uuid
14
14
  end
15
15
 
16
+ def changed?(old_value, new_value, _new_value_before_type_cast)
17
+ old_value.class != new_value.class ||
18
+ new_value && old_value.casecmp(new_value) != 0
19
+ end
20
+
21
+ def changed_in_place?(raw_old_value, new_value)
22
+ raw_old_value.class != new_value.class ||
23
+ new_value && raw_old_value.casecmp(new_value) != 0
24
+ end
25
+
16
26
  private
17
27
  def cast_value(value)
18
28
  casted = value.to_s
@@ -11,7 +11,9 @@ require "active_record/connection_adapters/postgresql/oid/decimal"
11
11
  require "active_record/connection_adapters/postgresql/oid/enum"
12
12
  require "active_record/connection_adapters/postgresql/oid/hstore"
13
13
  require "active_record/connection_adapters/postgresql/oid/inet"
14
+ require "active_record/connection_adapters/postgresql/oid/interval"
14
15
  require "active_record/connection_adapters/postgresql/oid/jsonb"
16
+ require "active_record/connection_adapters/postgresql/oid/macaddr"
15
17
  require "active_record/connection_adapters/postgresql/oid/money"
16
18
  require "active_record/connection_adapters/postgresql/oid/oid"
17
19
  require "active_record/connection_adapters/postgresql/oid/point"
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
 
19
19
  # Quotes strings for use in SQL input.
20
20
  def quote_string(s) #:nodoc:
21
- @connection.escape(s)
21
+ PG::Connection.escape(s)
22
22
  end
23
23
 
24
24
  # Checks the following cases:
@@ -67,8 +67,8 @@ module ActiveRecord
67
67
  elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
68
68
  value # Does not quote function default values for UUID columns
69
69
  elsif column.respond_to?(:array?)
70
- value = type_cast_from_column(column, value)
71
- quote(value)
70
+ type = lookup_cast_type_from_column(column)
71
+ quote(type.serialize(value))
72
72
  else
73
73
  super
74
74
  end
@@ -93,7 +93,7 @@ module ActiveRecord
93
93
  # "table_name"."column_name"::type_name | function(one or no argument)::type_name
94
94
  ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
95
95
  )
96
- (?:\s+AS\s+(?:\w+|"\w+"))?
96
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
97
97
  )
98
98
  (?:\s*,\s*\g<1>)*
99
99
  \z
@@ -24,7 +24,7 @@ WARNING: Rails was not able to disable referential integrity.
24
24
  This is most likely caused due to missing permissions.
25
25
  Rails needs superuser privileges to disable referential integrity.
26
26
 
27
- cause: #{original_exception.try(:message)}
27
+ cause: #{original_exception&.message}
28
28
 
29
29
  WARNING
30
30
  raise e
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
- class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
6
+ class SchemaCreation < SchemaCreation # :nodoc:
7
7
  private
8
8
  def visit_AlterTable(o)
9
9
  super << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
@@ -13,6 +13,10 @@ module ActiveRecord
13
13
  super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
14
14
  end
15
15
 
16
+ def visit_CheckConstraintDefinition(o)
17
+ super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
18
+ end
19
+
16
20
  def visit_ValidateConstraint(name)
17
21
  "VALIDATE CONSTRAINT #{quote_column_name(name)}"
18
22
  end
@@ -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
 
@@ -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
@@ -396,9 +399,9 @@ module ActiveRecord
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|
580
+ order_columns = orders.compact_blank.map { |s|
556
581
  # Convert Arel node to string
557
582
  s = visitor.compile(s) unless s.is_a?(String)
558
583
  # Remove any ASC/DESC modifiers
559
584
  s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
585
  .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
561
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{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, **options)
617
- PostgreSQL::TableDefinition.new(self, *args, **options)
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)
@@ -684,7 +716,7 @@ module ActiveRecord
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
721
  cd = td.new_column_definition(column_name, type, **options)
690
722
  sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
@@ -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
@@ -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
@@ -176,6 +176,10 @@ module ActiveRecord
176
176
  true
177
177
  end
178
178
 
179
+ def supports_check_constraints?
180
+ true
181
+ end
182
+
179
183
  def supports_validate_constraints?
180
184
  true
181
185
  end
@@ -341,11 +345,6 @@ module ActiveRecord
341
345
  true
342
346
  end
343
347
 
344
- def supports_ranges?
345
- true
346
- end
347
- deprecate :supports_ranges?
348
-
349
348
  def supports_materialized_views?
350
349
  true
351
350
  end
@@ -426,16 +425,6 @@ module ActiveRecord
426
425
  @use_insert_returning
427
426
  end
428
427
 
429
- def column_name_for_operation(operation, node) # :nodoc:
430
- OPERATION_ALIASES.fetch(operation) { operation.downcase }
431
- end
432
-
433
- OPERATION_ALIASES = { # :nodoc:
434
- "maximum" => "max",
435
- "minimum" => "min",
436
- "average" => "avg",
437
- }
438
-
439
428
  # Returns the version of the connected PostgreSQL server.
440
429
  def get_database_version # :nodoc:
441
430
  @connection.server_version
@@ -453,6 +442,7 @@ module ActiveRecord
453
442
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
454
443
  elsif insert.update_duplicates?
455
444
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
445
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
456
446
  sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
457
447
  end
458
448
 
@@ -475,6 +465,7 @@ module ActiveRecord
475
465
  UNIQUE_VIOLATION = "23505"
476
466
  SERIALIZATION_FAILURE = "40001"
477
467
  DEADLOCK_DETECTED = "40P01"
468
+ DUPLICATE_DATABASE = "42P04"
478
469
  LOCK_NOT_AVAILABLE = "55P03"
479
470
  QUERY_CANCELED = "57014"
480
471
 
@@ -482,6 +473,12 @@ module ActiveRecord
482
473
  return exception unless exception.respond_to?(:result)
483
474
 
484
475
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
476
+ when nil
477
+ if exception.message.match?(/connection is closed/i)
478
+ ConnectionNotEstablished.new(exception)
479
+ else
480
+ super
481
+ end
485
482
  when UNIQUE_VIOLATION
486
483
  RecordNotUnique.new(message, sql: sql, binds: binds)
487
484
  when FOREIGN_KEY_VIOLATION
@@ -496,6 +493,8 @@ module ActiveRecord
496
493
  SerializationFailure.new(message, sql: sql, binds: binds)
497
494
  when DEADLOCK_DETECTED
498
495
  Deadlocked.new(message, sql: sql, binds: binds)
496
+ when DUPLICATE_DATABASE
497
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
499
498
  when LOCK_NOT_AVAILABLE
500
499
  LockWaitTimeout.new(message, sql: sql, binds: binds)
501
500
  when QUERY_CANCELED
@@ -547,7 +546,7 @@ module ActiveRecord
547
546
  m.register_type "uuid", OID::Uuid.new
548
547
  m.register_type "xml", OID::Xml.new
549
548
  m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
550
- m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
549
+ m.register_type "macaddr", OID::Macaddr.new
551
550
  m.register_type "citext", OID::SpecializedString.new(:citext)
552
551
  m.register_type "ltree", OID::SpecializedString.new(:ltree)
553
552
  m.register_type "line", OID::SpecializedString.new(:line)
@@ -557,11 +556,6 @@ module ActiveRecord
557
556
  m.register_type "polygon", OID::SpecializedString.new(:polygon)
558
557
  m.register_type "circle", OID::SpecializedString.new(:circle)
559
558
 
560
- m.register_type "interval" do |_, _, sql_type|
561
- precision = extract_precision(sql_type)
562
- OID::SpecializedString.new(:interval, precision: precision)
563
- end
564
-
565
559
  register_class_with_precision m, "time", Type::Time
566
560
  register_class_with_precision m, "timestamp", OID::DateTime
567
561
 
@@ -585,6 +579,11 @@ module ActiveRecord
585
579
  end
586
580
  end
587
581
 
582
+ m.register_type "interval" do |*args, sql_type|
583
+ precision = extract_precision(sql_type)
584
+ OID::Interval.new(precision: precision)
585
+ end
586
+
588
587
  load_additional_types
589
588
  end
590
589
 
@@ -657,13 +656,17 @@ module ActiveRecord
657
656
  else
658
657
  result = exec_cache(sql, name, binds)
659
658
  end
660
- ret = yield result
661
- result.clear
659
+ begin
660
+ ret = yield result
661
+ ensure
662
+ result.clear
663
+ end
662
664
  ret
663
665
  end
664
666
 
665
667
  def exec_no_cache(sql, name, binds)
666
668
  materialize_transactions
669
+ mark_transaction_written_if_write(sql)
667
670
 
668
671
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
669
672
  # made since we established the connection
@@ -679,6 +682,7 @@ module ActiveRecord
679
682
 
680
683
  def exec_cache(sql, name, binds)
681
684
  materialize_transactions
685
+ mark_transaction_written_if_write(sql)
682
686
  update_typemap_for_default_timezone
683
687
 
684
688
  stmt_key = prepare_statement(sql, binds)
@@ -714,11 +718,10 @@ module ActiveRecord
714
718
  #
715
719
  # Check here for more details:
716
720
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
717
- CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
718
721
  def is_cached_plan_failure?(e)
719
722
  pgerror = e.cause
720
- code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
721
- code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
723
+ pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
724
+ pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
722
725
  rescue
723
726
  false
724
727
  end
@@ -756,7 +759,7 @@ module ActiveRecord
756
759
  # Connects to a PostgreSQL server and sets up the adapter depending on the
757
760
  # connected server's characteristics.
758
761
  def connect
759
- @connection = PG.connect(@connection_parameters)
762
+ @connection = self.class.new_client(@connection_parameters)
760
763
  configure_connection
761
764
  add_pg_encoders
762
765
  add_pg_decoders
@@ -786,6 +789,9 @@ module ActiveRecord
786
789
  end
787
790
  end
788
791
 
792
+ # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
793
+ execute("SET intervalstyle = iso_8601", "SCHEMA")
794
+
789
795
  # SET statements from :variables config hash
790
796
  # https://www.postgresql.org/docs/current/static/sql-set.html
791
797
  variables.map do |k, v|
@@ -897,15 +903,12 @@ module ActiveRecord
897
903
  "oid" => PG::TextDecoder::Integer,
898
904
  "float4" => PG::TextDecoder::Float,
899
905
  "float8" => PG::TextDecoder::Float,
906
+ "numeric" => PG::TextDecoder::Numeric,
900
907
  "bool" => PG::TextDecoder::Boolean,
908
+ "timestamp" => PG::TextDecoder::TimestampUtc,
909
+ "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
901
910
  }
902
911
 
903
- if defined?(PG::TextDecoder::TimestampUtc)
904
- # Use native PG encoders available since pg-1.1
905
- coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
906
- coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
907
- end
908
-
909
912
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
910
913
  query = <<~SQL % known_coder_types.join(", ")
911
914
  SELECT t.oid, t.typname
@@ -922,6 +925,11 @@ module ActiveRecord
922
925
  coders.each { |coder| map.add_coder(coder) }
923
926
  @connection.type_map_for_results = map
924
927
 
928
+ @type_map_for_results = PG::TypeMapByOid.new
929
+ @type_map_for_results.default_type_map = map
930
+ @type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
931
+ @type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
932
+
925
933
  # extract timestamp decoder for use in update_typemap_for_default_timezone
926
934
  @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
927
935
  update_typemap_for_default_timezone
@@ -932,6 +940,14 @@ module ActiveRecord
932
940
  coder_class.new(oid: row["oid"].to_i, name: row["typname"])
933
941
  end
934
942
 
943
+ class MoneyDecoder < PG::SimpleDecoder # :nodoc:
944
+ TYPE = OID::Money.new
945
+
946
+ def decode(value, tuple = nil, field = nil)
947
+ TYPE.deserialize(value)
948
+ end
949
+ end
950
+
935
951
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
936
952
  ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
937
953
  ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
@@ -944,6 +960,7 @@ module ActiveRecord
944
960
  ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
945
961
  ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
946
962
  ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
963
+ ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
947
964
  ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
948
965
  ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
949
966
  ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)