activerecord 6.1.4.6 → 7.0.2.3

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1188 -932
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +34 -27
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/join_dependency.rb +6 -2
  25. data/lib/active_record/associations/preloader/association.rb +187 -55
  26. data/lib/active_record/associations/preloader/batch.rb +48 -0
  27. data/lib/active_record/associations/preloader/branch.rb +147 -0
  28. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  29. data/lib/active_record/associations/preloader.rb +39 -113
  30. data/lib/active_record/associations/singular_association.rb +8 -2
  31. data/lib/active_record/associations/through_association.rb +3 -3
  32. data/lib/active_record/associations.rb +119 -90
  33. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  34. data/lib/active_record/attribute_assignment.rb +1 -1
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  37. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  38. data/lib/active_record/attribute_methods/query.rb +2 -2
  39. data/lib/active_record/attribute_methods/read.rb +7 -5
  40. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  42. data/lib/active_record/attribute_methods/write.rb +7 -10
  43. data/lib/active_record/attribute_methods.rb +13 -14
  44. data/lib/active_record/attributes.rb +24 -35
  45. data/lib/active_record/autosave_association.rb +8 -23
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +38 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +35 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +47 -53
  94. data/lib/active_record/core.rb +122 -132
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +16 -32
  100. data/lib/active_record/delegated_type.rb +52 -11
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +61 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +49 -42
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +17 -20
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +3 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +9 -3
  147. data/lib/active_record/log_subscriber.rb +14 -3
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +8 -3
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +4 -4
  152. data/lib/active_record/migration/compatibility.rb +107 -3
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +109 -79
  155. data/lib/active_record/model_schema.rb +45 -58
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +219 -52
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +15 -5
  163. data/lib/active_record/railtie.rb +127 -17
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +66 -129
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +67 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +3 -3
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +249 -61
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +184 -84
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +11 -7
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +61 -12
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +1 -1
  191. data/lib/active_record/signed_id.rb +1 -1
  192. data/lib/active_record/suppressor.rb +11 -15
  193. data/lib/active_record/tasks/database_tasks.rb +120 -58
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  196. data/lib/active_record/test_databases.rb +1 -1
  197. data/lib/active_record/test_fixtures.rb +4 -4
  198. data/lib/active_record/timestamp.rb +3 -4
  199. data/lib/active_record/transactions.rb +9 -14
  200. data/lib/active_record/translation.rb +2 -2
  201. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  202. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  203. data/lib/active_record/type/internal/timezone.rb +2 -2
  204. data/lib/active_record/type/serialized.rb +1 -1
  205. data/lib/active_record/type/type_map.rb +17 -20
  206. data/lib/active_record/type.rb +1 -2
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/uniqueness.rb +1 -1
  209. data/lib/active_record.rb +204 -28
  210. data/lib/arel/attributes/attribute.rb +0 -8
  211. data/lib/arel/crud.rb +28 -22
  212. data/lib/arel/delete_manager.rb +18 -4
  213. data/lib/arel/filter_predications.rb +9 -0
  214. data/lib/arel/insert_manager.rb +2 -3
  215. data/lib/arel/nodes/casted.rb +1 -1
  216. data/lib/arel/nodes/delete_statement.rb +12 -13
  217. data/lib/arel/nodes/filter.rb +10 -0
  218. data/lib/arel/nodes/function.rb +1 -0
  219. data/lib/arel/nodes/insert_statement.rb +2 -2
  220. data/lib/arel/nodes/select_core.rb +2 -2
  221. data/lib/arel/nodes/select_statement.rb +2 -2
  222. data/lib/arel/nodes/update_statement.rb +8 -3
  223. data/lib/arel/nodes.rb +1 -0
  224. data/lib/arel/predications.rb +11 -3
  225. data/lib/arel/select_manager.rb +10 -4
  226. data/lib/arel/table.rb +0 -1
  227. data/lib/arel/tree_manager.rb +0 -12
  228. data/lib/arel/update_manager.rb +18 -4
  229. data/lib/arel/visitors/dot.rb +80 -90
  230. data/lib/arel/visitors/mysql.rb +8 -2
  231. data/lib/arel/visitors/postgresql.rb +0 -10
  232. data/lib/arel/visitors/to_sql.rb +58 -2
  233. data/lib/arel.rb +2 -1
  234. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  235. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  236. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  237. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  238. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  239. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  240. metadata +56 -11
@@ -33,15 +33,27 @@ module ActiveRecord
33
33
  composites.each { |row| register_composite_type(row) }
34
34
  end
35
35
 
36
- def query_conditions_for_initial_load
36
+ def query_conditions_for_known_type_names
37
37
  known_type_names = @store.keys.map { |n| "'#{n}'" }
38
- known_type_types = %w('r' 'e' 'd')
39
- <<~SQL % [known_type_names.join(", "), known_type_types.join(", ")]
38
+ <<~SQL % known_type_names.join(", ")
40
39
  WHERE
41
40
  t.typname IN (%s)
42
- OR t.typtype IN (%s)
43
- OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure
44
- OR t.typelem != 0
41
+ SQL
42
+ end
43
+
44
+ def query_conditions_for_known_type_types
45
+ known_type_types = %w('r' 'e' 'd')
46
+ <<~SQL % known_type_types.join(", ")
47
+ WHERE
48
+ t.typtype IN (%s)
49
+ SQL
50
+ end
51
+
52
+ def query_conditions_for_array_types
53
+ known_type_oids = @store.keys.reject { |k| k.is_a?(String) }
54
+ <<~SQL % [known_type_oids.join(", ")]
55
+ WHERE
56
+ t.typelem IN (%s)
45
57
  SQL
46
58
  end
47
59
 
@@ -20,6 +20,8 @@ require "active_record/connection_adapters/postgresql/oid/point"
20
20
  require "active_record/connection_adapters/postgresql/oid/legacy_point"
21
21
  require "active_record/connection_adapters/postgresql/oid/range"
22
22
  require "active_record/connection_adapters/postgresql/oid/specialized_string"
23
+ require "active_record/connection_adapters/postgresql/oid/timestamp"
24
+ require "active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone"
23
25
  require "active_record/connection_adapters/postgresql/oid/uuid"
24
26
  require "active_record/connection_adapters/postgresql/oid/vector"
25
27
  require "active_record/connection_adapters/postgresql/oid/xml"
@@ -16,8 +16,33 @@ module ActiveRecord
16
16
  @connection.unescape_bytea(value) if value
17
17
  end
18
18
 
19
+ def quote(value) # :nodoc:
20
+ case value
21
+ when OID::Xml::Data
22
+ "xml '#{quote_string(value.to_s)}'"
23
+ when OID::Bit::Data
24
+ if value.binary?
25
+ "B'#{value}'"
26
+ elsif value.hex?
27
+ "X'#{value}'"
28
+ end
29
+ when Numeric
30
+ if value.finite?
31
+ super
32
+ else
33
+ "'#{value}'"
34
+ end
35
+ when OID::Array::Data
36
+ quote(encode_array(value))
37
+ when Range
38
+ quote(encode_range(value))
39
+ else
40
+ super
41
+ end
42
+ end
43
+
19
44
  # Quotes strings for use in SQL input.
20
- def quote_string(s) #:nodoc:
45
+ def quote_string(s) # :nodoc:
21
46
  PG::Connection.escape(s)
22
47
  end
23
48
 
@@ -48,7 +73,7 @@ module ActiveRecord
48
73
  end
49
74
 
50
75
  # Quote date/time values for use in SQL input.
51
- def quoted_date(value) #:nodoc:
76
+ def quoted_date(value) # :nodoc:
52
77
  if value.year <= 0
53
78
  bce_year = format("%04d", -value.year + 1)
54
79
  super.sub(/^-?\d+/, bce_year) + " BC"
@@ -74,6 +99,24 @@ module ActiveRecord
74
99
  end
75
100
  end
76
101
 
102
+ def type_cast(value) # :nodoc:
103
+ case value
104
+ when Type::Binary::Data
105
+ # Return a bind param hash with format as binary.
106
+ # See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
107
+ # for more information
108
+ { value: value.to_s, format: 1 }
109
+ when OID::Xml::Data, OID::Bit::Data
110
+ value.to_s
111
+ when OID::Array::Data
112
+ encode_array(value)
113
+ when Range
114
+ encode_range(value)
115
+ else
116
+ super
117
+ end
118
+ end
119
+
77
120
  def lookup_cast_type_from_column(column) # :nodoc:
78
121
  type_map.lookup(column.oid, column.fmod, column.sql_type)
79
122
  end
@@ -90,8 +133,8 @@ module ActiveRecord
90
133
  \A
91
134
  (
92
135
  (?:
93
- # "table_name"."column_name"::type_name | function(one or no argument)::type_name
94
- ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
136
+ # "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
137
+ ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
95
138
  )
96
139
  (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
97
140
  )
@@ -103,8 +146,8 @@ module ActiveRecord
103
146
  \A
104
147
  (
105
148
  (?:
106
- # "table_name"."column_name"::type_name | function(one or no argument)::type_name
107
- ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
149
+ # "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
150
+ ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
108
151
  )
109
152
  (?:\s+ASC|\s+DESC)?
110
153
  (?:\s+NULLS\s+(?:FIRST|LAST))?
@@ -120,49 +163,6 @@ module ActiveRecord
120
163
  super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
121
164
  end
122
165
 
123
- def _quote(value)
124
- case value
125
- when OID::Xml::Data
126
- "xml '#{quote_string(value.to_s)}'"
127
- when OID::Bit::Data
128
- if value.binary?
129
- "B'#{value}'"
130
- elsif value.hex?
131
- "X'#{value}'"
132
- end
133
- when Numeric
134
- if value.finite?
135
- super
136
- else
137
- "'#{value}'"
138
- end
139
- when OID::Array::Data
140
- _quote(encode_array(value))
141
- when Range
142
- _quote(encode_range(value))
143
- else
144
- super
145
- end
146
- end
147
-
148
- def _type_cast(value)
149
- case value
150
- when Type::Binary::Data
151
- # Return a bind param hash with format as binary.
152
- # See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
153
- # for more information
154
- { value: value.to_s, format: 1 }
155
- when OID::Xml::Data, OID::Bit::Data
156
- value.to_s
157
- when OID::Array::Data
158
- encode_array(value)
159
- when Range
160
- encode_range(value)
161
- else
162
- super
163
- end
164
- end
165
-
166
166
  def encode_array(array_data)
167
167
  encoder = array_data.encoder
168
168
  values = type_cast_array(array_data.values)
@@ -188,7 +188,7 @@ module ActiveRecord
188
188
  def type_cast_array(values)
189
189
  case values
190
190
  when ::Array then values.map { |item| type_cast_array(item) }
191
- else _type_cast(values)
191
+ else type_cast(values)
192
192
  end
193
193
  end
194
194
 
@@ -37,6 +37,38 @@ Rails needs superuser privileges to disable referential integrity.
37
37
  rescue ActiveRecord::ActiveRecordError
38
38
  end
39
39
  end
40
+
41
+ def all_foreign_keys_valid? # :nodoc:
42
+ sql = <<~SQL
43
+ do $$
44
+ declare r record;
45
+ BEGIN
46
+ FOR r IN (
47
+ SELECT FORMAT(
48
+ 'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I''; ALTER TABLE %I VALIDATE CONSTRAINT %I;',
49
+ constraint_name,
50
+ table_name,
51
+ constraint_name
52
+ ) AS constraint_check
53
+ FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY'
54
+ )
55
+ LOOP
56
+ EXECUTE (r.constraint_check);
57
+ END LOOP;
58
+ END;
59
+ $$;
60
+ SQL
61
+
62
+ begin
63
+ transaction(requires_new: true) do
64
+ execute(sql)
65
+ end
66
+
67
+ true
68
+ rescue ActiveRecord::StatementInvalid
69
+ false
70
+ end
71
+ end
40
72
  end
41
73
  end
42
74
  end
@@ -10,7 +10,14 @@ module ActiveRecord
10
10
  end
11
11
 
12
12
  def visit_AddForeignKey(o)
13
- super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
13
+ super.dup.tap do |sql|
14
+ if o.deferrable
15
+ sql << " DEFERRABLE"
16
+ sql << " INITIALLY #{o.deferrable.to_s.upcase}" unless o.deferrable == true
17
+ end
18
+
19
+ sql << " NOT VALID" unless o.validate?
20
+ end
14
21
  end
15
22
 
16
23
  def visit_CheckConstraintDefinition(o)
@@ -61,6 +68,19 @@ module ActiveRecord
61
68
  if options[:collation]
62
69
  sql << " COLLATE \"#{options[:collation]}\""
63
70
  end
71
+
72
+ if as = options[:as]
73
+ sql << " GENERATED ALWAYS AS (#{as})"
74
+
75
+ if options[:stored]
76
+ sql << " STORED"
77
+ else
78
+ raise ArgumentError, <<~MSG
79
+ PostgreSQL currently does not support VIRTUAL (not persisted) generated columns.
80
+ Specify 'stored: true' option for '#{options[:column].name}'
81
+ MSG
82
+ end
83
+ end
64
84
  super
65
85
  end
66
86
 
@@ -173,11 +173,19 @@ module ActiveRecord
173
173
  # :method: xml
174
174
  # :call-seq: xml(*names, **options)
175
175
 
176
+ ##
177
+ # :method: timestamptz
178
+ # :call-seq: timestamptz(*names, **options)
179
+
180
+ ##
181
+ # :method: enum
182
+ # :call-seq: enum(*names, **options)
183
+
176
184
  included do
177
185
  define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
178
186
  :hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
179
187
  :money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
180
- :serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml
188
+ :serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
181
189
  end
182
190
  end
183
191
 
@@ -191,7 +199,20 @@ module ActiveRecord
191
199
  @unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
192
200
  end
193
201
 
202
+ def new_column_definition(name, type, **options) # :nodoc:
203
+ case type
204
+ when :virtual
205
+ type = options[:type]
206
+ end
207
+
208
+ super
209
+ end
210
+
194
211
  private
212
+ def aliased_types(name, fallback)
213
+ fallback
214
+ end
215
+
195
216
  def integer_like_primary_key_type(type, options)
196
217
  if type == :bigint || options[:limit] == 8
197
218
  :bigserial
@@ -16,9 +16,30 @@ module ActiveRecord
16
16
  end
17
17
  end
18
18
 
19
+ def types(stream)
20
+ types = @connection.enum_types
21
+ if types.any?
22
+ stream.puts " # Custom types defined in this database."
23
+ stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
24
+ types.sort.each do |name, values|
25
+ stream.puts " create_enum #{name.inspect}, #{values.split(",").inspect}"
26
+ end
27
+ stream.puts
28
+ end
29
+ end
30
+
19
31
  def prepare_column_options(column)
20
32
  spec = super
21
33
  spec[:array] = "true" if column.array?
34
+
35
+ if @connection.supports_virtual_columns? && column.virtual?
36
+ spec[:as] = extract_expression_for_virtual_column(column)
37
+ spec[:stored] = true
38
+ spec = { type: schema_type(column).inspect }.merge!(spec)
39
+ end
40
+
41
+ spec[:enum_type] = "\"#{column.sql_type}\"" if column.enum?
42
+
22
43
  spec
23
44
  end
24
45
 
@@ -43,6 +64,10 @@ module ActiveRecord
43
64
  def schema_expression(column)
44
65
  super unless column.serial?
45
66
  end
67
+
68
+ def extract_expression_for_virtual_column(column)
69
+ column.default_function.inspect
70
+ end
46
71
  end
47
72
  end
48
73
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module SchemaStatements
7
7
  # Drops the database specified on the +name+ attribute
8
8
  # and creates it again using the provided +options+.
9
- def recreate_database(name, options = {}) #:nodoc:
9
+ def recreate_database(name, options = {}) # :nodoc:
10
10
  drop_database(name)
11
11
  create_database(name, options)
12
12
  end
@@ -50,7 +50,7 @@ module ActiveRecord
50
50
  #
51
51
  # Example:
52
52
  # drop_database 'matt_development'
53
- def drop_database(name) #:nodoc:
53
+ def drop_database(name) # :nodoc:
54
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
55
55
  end
56
56
 
@@ -244,7 +244,7 @@ module ActiveRecord
244
244
  end
245
245
 
246
246
  # Returns the sequence name for a table's primary key or some other specified key.
247
- def default_sequence_name(table_name, pk = "id") #:nodoc:
247
+ def default_sequence_name(table_name, pk = "id") # :nodoc:
248
248
  result = serial_sequence(table_name, pk)
249
249
  return nil unless result
250
250
  Utils.extract_schema_qualified_name(result).to_s
@@ -257,7 +257,7 @@ module ActiveRecord
257
257
  end
258
258
 
259
259
  # Sets the sequence of a table's primary key to the specified value.
260
- def set_pk_sequence!(table, value) #:nodoc:
260
+ def set_pk_sequence!(table, value) # :nodoc:
261
261
  pk, sequence = pk_and_sequence_for(table)
262
262
 
263
263
  if pk
@@ -272,7 +272,7 @@ module ActiveRecord
272
272
  end
273
273
 
274
274
  # Resets the sequence of a table's primary key to the maximum value.
275
- def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
275
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) # :nodoc:
276
276
  unless pk && sequence
277
277
  default_pk, default_sequence = pk_and_sequence_for(table)
278
278
 
@@ -300,7 +300,7 @@ module ActiveRecord
300
300
  end
301
301
 
302
302
  # Returns a table's primary key and belonging sequence.
303
- def pk_and_sequence_for(table) #:nodoc:
303
+ def pk_and_sequence_for(table) # :nodoc:
304
304
  # First try looking for a sequence with a dependency on the
305
305
  # given table's primary key.
306
306
  result = query(<<~SQL, "SCHEMA")[0]
@@ -393,13 +393,13 @@ module ActiveRecord
393
393
  rename_table_indexes(table_name, new_name)
394
394
  end
395
395
 
396
- def add_column(table_name, column_name, type, **options) #:nodoc:
396
+ def add_column(table_name, column_name, type, **options) # :nodoc:
397
397
  clear_cache!
398
398
  super
399
399
  change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
400
400
  end
401
401
 
402
- def change_column(table_name, column_name, type, **options) #:nodoc:
402
+ def change_column(table_name, column_name, type, **options) # :nodoc:
403
403
  clear_cache!
404
404
  sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
405
405
  execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
@@ -411,7 +411,7 @@ module ActiveRecord
411
411
  execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
412
412
  end
413
413
 
414
- def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
414
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
415
415
  clear_cache!
416
416
  unless null || default.nil?
417
417
  column = column_for(table_name, column_name)
@@ -435,13 +435,13 @@ module ActiveRecord
435
435
  end
436
436
 
437
437
  # Renames a column in a table.
438
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
438
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
439
439
  clear_cache!
440
440
  execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
441
441
  rename_column_indexes(table_name, column_name, new_column_name)
442
442
  end
443
443
 
444
- def add_index(table_name, column_name, **options) #:nodoc:
444
+ def add_index(table_name, column_name, **options) # :nodoc:
445
445
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
446
446
 
447
447
  create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
@@ -483,7 +483,7 @@ module ActiveRecord
483
483
  def foreign_keys(table_name)
484
484
  scope = quoted_scope(table_name)
485
485
  fk_info = exec_query(<<~SQL, "SCHEMA")
486
- SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
486
+ SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred
487
487
  FROM pg_constraint c
488
488
  JOIN pg_class t1 ON c.conrelid = t1.oid
489
489
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -505,6 +505,8 @@ module ActiveRecord
505
505
 
506
506
  options[:on_delete] = extract_foreign_key_action(row["on_delete"])
507
507
  options[:on_update] = extract_foreign_key_action(row["on_update"])
508
+ options[:deferrable] = extract_foreign_key_deferrable(row["deferrable"], row["deferred"])
509
+
508
510
  options[:validate] = row["valid"]
509
511
 
510
512
  ForeignKeyDefinition.new(table_name, row["to_table"], options)
@@ -523,7 +525,7 @@ module ActiveRecord
523
525
  scope = quoted_scope(table_name)
524
526
 
525
527
  check_info = exec_query(<<-SQL, "SCHEMA")
526
- SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.convalidated AS valid
528
+ SELECT conname, pg_get_constraintdef(c.oid, true) AS constraintdef, c.convalidated AS valid
527
529
  FROM pg_constraint c
528
530
  JOIN pg_class t ON c.conrelid = t.oid
529
531
  WHERE c.contype = 'c'
@@ -535,14 +537,14 @@ module ActiveRecord
535
537
  name: row["conname"],
536
538
  validate: row["valid"]
537
539
  }
538
- expression = row["constraintdef"][/CHECK \({2}(.+)\){2}/, 1]
540
+ expression = row["constraintdef"][/CHECK \((.+)\)/m, 1]
539
541
 
540
542
  CheckConstraintDefinition.new(table_name, expression, options)
541
543
  end
542
544
  end
543
545
 
544
546
  # Maps logical Rails types to PostgreSQL-specific data types.
545
- def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
547
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, enum_type: nil, **) # :nodoc:
546
548
  sql = \
547
549
  case type.to_s
548
550
  when "binary"
@@ -566,6 +568,10 @@ module ActiveRecord
566
568
  when 5..8; "bigint"
567
569
  else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
568
570
  end
571
+ when "enum"
572
+ raise ArgumentError, "enum_type is required for enums" if enum_type.nil?
573
+
574
+ enum_type
569
575
  else
570
576
  super
571
577
  end
@@ -576,7 +582,7 @@ module ActiveRecord
576
582
 
577
583
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
578
584
  # requires that the ORDER BY include the distinct column.
579
- def columns_for_distinct(columns, orders) #:nodoc:
585
+ def columns_for_distinct(columns, orders) # :nodoc:
580
586
  order_columns = orders.compact_blank.map { |s|
581
587
  # Convert Arel node to string
582
588
  s = visitor.compile(s) unless s.is_a?(String)
@@ -654,10 +660,15 @@ module ActiveRecord
654
660
  end
655
661
 
656
662
  def new_column_from_field(table_name, field)
657
- column_name, type, default, notnull, oid, fmod, collation, comment = field
663
+ column_name, type, default, notnull, oid, fmod, collation, comment, attgenerated = field
658
664
  type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
659
665
  default_value = extract_value_from_default(default)
660
- default_function = extract_default_function(default_value, default)
666
+
667
+ if attgenerated.present?
668
+ default_function = default
669
+ else
670
+ default_function = extract_default_function(default_value, default)
671
+ end
661
672
 
662
673
  if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
663
674
  serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
@@ -671,7 +682,8 @@ module ActiveRecord
671
682
  default_function,
672
683
  collation: collation,
673
684
  comment: comment.presence,
674
- serial: serial
685
+ serial: serial,
686
+ generated: attgenerated
675
687
  )
676
688
  end
677
689
 
@@ -711,6 +723,10 @@ module ActiveRecord
711
723
  end
712
724
  end
713
725
 
726
+ def extract_foreign_key_deferrable(deferrable, deferred)
727
+ deferrable && (deferred ? :deferred : true)
728
+ end
729
+
714
730
  def add_column_for_alter(table_name, column_name, type, **options)
715
731
  return super unless options.key?(:comment)
716
732
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]