activerecord 7.0.8 → 7.1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1554 -1452
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +20 -4
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +31 -7
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +313 -217
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +52 -34
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  76. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  77. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  78. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
  80. data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
  81. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  82. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  83. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  84. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  85. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  86. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  87. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  88. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  89. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  90. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  91. data/lib/active_record/connection_adapters.rb +3 -1
  92. data/lib/active_record/connection_handling.rb +72 -95
  93. data/lib/active_record/core.rb +175 -153
  94. data/lib/active_record/counter_cache.rb +46 -25
  95. data/lib/active_record/database_configurations/database_config.rb +9 -3
  96. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  97. data/lib/active_record/database_configurations/url_config.rb +17 -11
  98. data/lib/active_record/database_configurations.rb +86 -33
  99. data/lib/active_record/delegated_type.rb +9 -4
  100. data/lib/active_record/deprecator.rb +7 -0
  101. data/lib/active_record/destroy_association_async_job.rb +2 -0
  102. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  103. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  104. data/lib/active_record/encryption/config.rb +25 -1
  105. data/lib/active_record/encryption/configurable.rb +12 -19
  106. data/lib/active_record/encryption/context.rb +10 -3
  107. data/lib/active_record/encryption/contexts.rb +5 -1
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  109. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  111. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  112. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  113. data/lib/active_record/encryption/key_generator.rb +12 -1
  114. data/lib/active_record/encryption/message_serializer.rb +2 -0
  115. data/lib/active_record/encryption/properties.rb +3 -3
  116. data/lib/active_record/encryption/scheme.rb +19 -22
  117. data/lib/active_record/encryption.rb +1 -0
  118. data/lib/active_record/enum.rb +112 -28
  119. data/lib/active_record/errors.rb +112 -18
  120. data/lib/active_record/explain.rb +23 -3
  121. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  122. data/lib/active_record/fixture_set/render_context.rb +2 -0
  123. data/lib/active_record/fixture_set/table_row.rb +29 -8
  124. data/lib/active_record/fixtures.rb +135 -71
  125. data/lib/active_record/future_result.rb +31 -5
  126. data/lib/active_record/gem_version.rb +4 -4
  127. data/lib/active_record/inheritance.rb +30 -16
  128. data/lib/active_record/insert_all.rb +57 -10
  129. data/lib/active_record/integration.rb +8 -8
  130. data/lib/active_record/internal_metadata.rb +120 -30
  131. data/lib/active_record/locking/pessimistic.rb +5 -2
  132. data/lib/active_record/log_subscriber.rb +29 -12
  133. data/lib/active_record/marshalling.rb +56 -0
  134. data/lib/active_record/message_pack.rb +124 -0
  135. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  136. data/lib/active_record/middleware/database_selector.rb +6 -8
  137. data/lib/active_record/middleware/shard_selector.rb +3 -1
  138. data/lib/active_record/migration/command_recorder.rb +104 -5
  139. data/lib/active_record/migration/compatibility.rb +139 -5
  140. data/lib/active_record/migration/default_strategy.rb +23 -0
  141. data/lib/active_record/migration/execution_strategy.rb +19 -0
  142. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  143. data/lib/active_record/migration.rb +219 -111
  144. data/lib/active_record/model_schema.rb +64 -44
  145. data/lib/active_record/nested_attributes.rb +24 -6
  146. data/lib/active_record/normalization.rb +167 -0
  147. data/lib/active_record/persistence.rb +188 -37
  148. data/lib/active_record/promise.rb +84 -0
  149. data/lib/active_record/query_cache.rb +3 -21
  150. data/lib/active_record/query_logs.rb +77 -52
  151. data/lib/active_record/query_logs_formatter.rb +41 -0
  152. data/lib/active_record/querying.rb +15 -2
  153. data/lib/active_record/railtie.rb +109 -47
  154. data/lib/active_record/railties/controller_runtime.rb +12 -6
  155. data/lib/active_record/railties/databases.rake +142 -148
  156. data/lib/active_record/railties/job_runtime.rb +23 -0
  157. data/lib/active_record/readonly_attributes.rb +32 -5
  158. data/lib/active_record/reflection.rb +174 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  160. data/lib/active_record/relation/batches.rb +190 -61
  161. data/lib/active_record/relation/calculations.rb +187 -63
  162. data/lib/active_record/relation/delegation.rb +23 -9
  163. data/lib/active_record/relation/finder_methods.rb +77 -16
  164. data/lib/active_record/relation/merger.rb +2 -0
  165. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  167. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  168. data/lib/active_record/relation/predicate_builder.rb +26 -14
  169. data/lib/active_record/relation/query_attribute.rb +2 -1
  170. data/lib/active_record/relation/query_methods.rb +352 -63
  171. data/lib/active_record/relation/spawn_methods.rb +18 -1
  172. data/lib/active_record/relation.rb +91 -35
  173. data/lib/active_record/result.rb +19 -5
  174. data/lib/active_record/runtime_registry.rb +24 -1
  175. data/lib/active_record/sanitization.rb +51 -11
  176. data/lib/active_record/schema.rb +2 -3
  177. data/lib/active_record/schema_dumper.rb +46 -7
  178. data/lib/active_record/schema_migration.rb +68 -33
  179. data/lib/active_record/scoping/default.rb +15 -5
  180. data/lib/active_record/scoping/named.rb +2 -2
  181. data/lib/active_record/scoping.rb +2 -1
  182. data/lib/active_record/secure_password.rb +60 -0
  183. data/lib/active_record/secure_token.rb +21 -3
  184. data/lib/active_record/signed_id.rb +7 -5
  185. data/lib/active_record/store.rb +8 -8
  186. data/lib/active_record/suppressor.rb +3 -1
  187. data/lib/active_record/table_metadata.rb +10 -1
  188. data/lib/active_record/tasks/database_tasks.rb +127 -105
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  192. data/lib/active_record/test_fixtures.rb +113 -96
  193. data/lib/active_record/timestamp.rb +27 -15
  194. data/lib/active_record/token_for.rb +113 -0
  195. data/lib/active_record/touch_later.rb +11 -6
  196. data/lib/active_record/transactions.rb +36 -10
  197. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  198. data/lib/active_record/type/internal/timezone.rb +7 -2
  199. data/lib/active_record/type/time.rb +4 -0
  200. data/lib/active_record/validations/absence.rb +1 -1
  201. data/lib/active_record/validations/numericality.rb +5 -4
  202. data/lib/active_record/validations/presence.rb +5 -28
  203. data/lib/active_record/validations/uniqueness.rb +47 -2
  204. data/lib/active_record/validations.rb +8 -4
  205. data/lib/active_record/version.rb +1 -1
  206. data/lib/active_record.rb +121 -16
  207. data/lib/arel/errors.rb +10 -0
  208. data/lib/arel/factory_methods.rb +4 -0
  209. data/lib/arel/nodes/binary.rb +6 -1
  210. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  211. data/lib/arel/nodes/cte.rb +36 -0
  212. data/lib/arel/nodes/fragments.rb +35 -0
  213. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  214. data/lib/arel/nodes/leading_join.rb +8 -0
  215. data/lib/arel/nodes/node.rb +111 -2
  216. data/lib/arel/nodes/sql_literal.rb +6 -0
  217. data/lib/arel/nodes/table_alias.rb +4 -0
  218. data/lib/arel/nodes.rb +4 -0
  219. data/lib/arel/predications.rb +2 -0
  220. data/lib/arel/table.rb +9 -5
  221. data/lib/arel/visitors/mysql.rb +8 -1
  222. data/lib/arel/visitors/to_sql.rb +81 -17
  223. data/lib/arel/visitors/visitor.rb +2 -2
  224. data/lib/arel.rb +16 -2
  225. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  226. data/lib/rails/generators/active_record/migration.rb +3 -1
  227. data/lib/rails/generators/active_record/model/USAGE +113 -0
  228. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  229. metadata +48 -12
  230. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  231. data/lib/active_record/null_relation.rb +0 -63
@@ -15,14 +15,14 @@ module ActiveRecord
15
15
 
16
16
  # Escapes binary strings for bytea input to the database.
17
17
  def escape_bytea(value)
18
- @connection.escape_bytea(value) if value
18
+ valid_raw_connection.escape_bytea(value) if value
19
19
  end
20
20
 
21
21
  # Unescapes bytea output from a database to the binary string it represents.
22
22
  # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
23
23
  # on escaped binary output from database drive.
24
24
  def unescape_bytea(value)
25
- @connection.unescape_bytea(value) if value
25
+ valid_raw_connection.unescape_bytea(value) if value
26
26
  end
27
27
 
28
28
  def check_int_in_range(value)
@@ -72,7 +72,9 @@ module ActiveRecord
72
72
 
73
73
  # Quotes strings for use in SQL input.
74
74
  def quote_string(s) # :nodoc:
75
- @connection.escape(s)
75
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
76
+ connection.escape(s)
77
+ end
76
78
  end
77
79
 
78
80
  # Checks the following cases:
@@ -118,7 +120,7 @@ module ActiveRecord
118
120
  def quote_default_expression(value, column) # :nodoc:
119
121
  if value.is_a?(Proc)
120
122
  value.call
121
- elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
123
+ elsif column.type == :uuid && value.is_a?(String) && value.include?("()")
122
124
  value # Does not quote function default values for UUID columns
123
125
  elsif column.respond_to?(:array?)
124
126
  type = lookup_cast_type_from_column(column)
@@ -147,6 +149,7 @@ module ActiveRecord
147
149
  end
148
150
 
149
151
  def lookup_cast_type_from_column(column) # :nodoc:
152
+ verify! if type_map.nil?
150
153
  type_map.lookup(column.oid, column.fmod, column.sql_type)
151
154
  end
152
155
 
@@ -163,7 +166,7 @@ module ActiveRecord
163
166
  (
164
167
  (?:
165
168
  # "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
166
- ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
169
+ ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
167
170
  )
168
171
  (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
169
172
  )
@@ -176,8 +179,9 @@ module ActiveRecord
176
179
  (
177
180
  (?:
178
181
  # "schema_name"."table_name"."column_name"::type_name | function(one or no argument)::type_name
179
- ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
182
+ ((?:\w+\.|"\w+"\.){,2}(?:\w+|"\w+")(?:::\w+)? | \w+\((?:|\g<2>)\)(?:::\w+)?)
180
183
  )
184
+ (?:\s+COLLATE\s+"\w+")?
181
185
  (?:\s+ASC|\s+DESC)?
182
186
  (?:\s+NULLS\s+(?:FIRST|LAST))?
183
187
  )
@@ -38,7 +38,7 @@ Rails needs superuser privileges to disable referential integrity.
38
38
  end
39
39
  end
40
40
 
41
- def all_foreign_keys_valid? # :nodoc:
41
+ def check_all_foreign_keys_valid! # :nodoc:
42
42
  sql = <<~SQL
43
43
  do $$
44
44
  declare r record;
@@ -61,14 +61,8 @@ Rails needs superuser privileges to disable referential integrity.
61
61
  $$;
62
62
  SQL
63
63
 
64
- begin
65
- transaction(requires_new: true) do
66
- execute(sql)
67
- end
68
-
69
- true
70
- rescue ActiveRecord::StatementInvalid
71
- false
64
+ transaction(requires_new: true) do
65
+ execute(sql)
72
66
  end
73
67
  end
74
68
  end
@@ -5,21 +5,30 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  class SchemaCreation < SchemaCreation # :nodoc:
7
7
  private
8
+ delegate :quoted_include_columns_for_index, to: :@conn
9
+
8
10
  def visit_AlterTable(o)
9
- super << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
11
+ sql = super
12
+ sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
13
+ sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
14
+ sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
15
+ sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
16
+ sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
10
17
  end
11
18
 
12
19
  def visit_AddForeignKey(o)
13
20
  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
-
21
+ sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
19
22
  sql << " NOT VALID" unless o.validate?
20
23
  end
21
24
  end
22
25
 
26
+ def visit_ForeignKeyDefinition(o)
27
+ super.dup.tap do |sql|
28
+ sql << " DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
29
+ end
30
+ end
31
+
23
32
  def visit_CheckConstraintDefinition(o)
24
33
  super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
25
34
  end
@@ -28,6 +37,54 @@ module ActiveRecord
28
37
  "VALIDATE CONSTRAINT #{quote_column_name(name)}"
29
38
  end
30
39
 
40
+ def visit_ExclusionConstraintDefinition(o)
41
+ sql = ["CONSTRAINT"]
42
+ sql << quote_column_name(o.name)
43
+ sql << "EXCLUDE"
44
+ sql << "USING #{o.using}" if o.using
45
+ sql << "(#{o.expression})"
46
+ sql << "WHERE (#{o.where})" if o.where
47
+ sql << "DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
48
+
49
+ sql.join(" ")
50
+ end
51
+
52
+ def visit_UniqueConstraintDefinition(o)
53
+ column_name = Array(o.column).map { |column| quote_column_name(column) }.join(", ")
54
+
55
+ sql = ["CONSTRAINT"]
56
+ sql << quote_column_name(o.name)
57
+ sql << "UNIQUE"
58
+
59
+ if o.using_index
60
+ sql << "USING INDEX #{quote_column_name(o.using_index)}"
61
+ else
62
+ sql << "(#{column_name})"
63
+ end
64
+
65
+ if o.deferrable
66
+ sql << "DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}"
67
+ end
68
+
69
+ sql.join(" ")
70
+ end
71
+
72
+ def visit_AddExclusionConstraint(o)
73
+ "ADD #{accept(o)}"
74
+ end
75
+
76
+ def visit_DropExclusionConstraint(name)
77
+ "DROP CONSTRAINT #{quote_column_name(name)}"
78
+ end
79
+
80
+ def visit_AddUniqueConstraint(o)
81
+ "ADD #{accept(o)}"
82
+ end
83
+
84
+ def visit_DropUniqueConstraint(name)
85
+ "DROP CONSTRAINT #{quote_column_name(name)}"
86
+ end
87
+
31
88
  def visit_ChangeColumnDefinition(o)
32
89
  column = o.column
33
90
  column.sql_type = type_to_sql(column.type, **column.options)
@@ -64,6 +121,15 @@ module ActiveRecord
64
121
  change_column_sql
65
122
  end
66
123
 
124
+ def visit_ChangeColumnDefaultDefinition(o)
125
+ sql = +"ALTER COLUMN #{quote_column_name(o.column.name)} "
126
+ if o.default.nil?
127
+ sql << "DROP DEFAULT"
128
+ else
129
+ sql << "SET DEFAULT #{quote_default_expression(o.default, o.column)}"
130
+ end
131
+ end
132
+
67
133
  def add_column_options!(sql, options)
68
134
  if options[:collation]
69
135
  sql << " COLLATE \"#{options[:collation]}\""
@@ -84,6 +150,10 @@ module ActiveRecord
84
150
  super
85
151
  end
86
152
 
153
+ def quoted_include_columns(o)
154
+ String === o ? o : quoted_include_columns_for_index(o)
155
+ end
156
+
87
157
  # Returns any SQL string to go between CREATE and TABLE. May be nil.
88
158
  def table_modifier_in_create(o)
89
159
  # A table cannot be both TEMPORARY and UNLOGGED, since all TEMPORARY
@@ -189,16 +189,83 @@ module ActiveRecord
189
189
  end
190
190
  end
191
191
 
192
+ ExclusionConstraintDefinition = Struct.new(:table_name, :expression, :options) do
193
+ def name
194
+ options[:name]
195
+ end
196
+
197
+ def using
198
+ options[:using]
199
+ end
200
+
201
+ def where
202
+ options[:where]
203
+ end
204
+
205
+ def deferrable
206
+ options[:deferrable]
207
+ end
208
+
209
+ def export_name_on_schema_dump?
210
+ !ActiveRecord::SchemaDumper.excl_ignore_pattern.match?(name) if name
211
+ end
212
+ end
213
+
214
+ UniqueConstraintDefinition = Struct.new(:table_name, :column, :options) do
215
+ def name
216
+ options[:name]
217
+ end
218
+
219
+ def deferrable
220
+ options[:deferrable]
221
+ end
222
+
223
+ def using_index
224
+ options[:using_index]
225
+ end
226
+
227
+ def export_name_on_schema_dump?
228
+ !ActiveRecord::SchemaDumper.unique_ignore_pattern.match?(name) if name
229
+ end
230
+
231
+ def defined_for?(name: nil, column: nil, **options)
232
+ (name.nil? || self.name == name.to_s) &&
233
+ (column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
234
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
235
+ end
236
+ end
237
+
238
+ # = Active Record PostgreSQL Adapter \Table Definition
192
239
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
193
240
  include ColumnMethods
194
241
 
195
- attr_reader :unlogged
242
+ attr_reader :exclusion_constraints, :unique_constraints, :unlogged
196
243
 
197
244
  def initialize(*, **)
198
245
  super
246
+ @exclusion_constraints = []
247
+ @unique_constraints = []
199
248
  @unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
200
249
  end
201
250
 
251
+ def exclusion_constraint(expression, **options)
252
+ exclusion_constraints << new_exclusion_constraint_definition(expression, options)
253
+ end
254
+
255
+ def unique_constraint(column_name, **options)
256
+ unique_constraints << new_unique_constraint_definition(column_name, options)
257
+ end
258
+
259
+ def new_exclusion_constraint_definition(expression, options) # :nodoc:
260
+ options = @conn.exclusion_constraint_options(name, expression, options)
261
+ ExclusionConstraintDefinition.new(name, expression, options)
262
+ end
263
+
264
+ def new_unique_constraint_definition(column_name, options) # :nodoc:
265
+ options = @conn.unique_constraint_options(name, column_name, options)
266
+ UniqueConstraintDefinition.new(name, column_name, options)
267
+ end
268
+
202
269
  def new_column_definition(name, type, **options) # :nodoc:
203
270
  case type
204
271
  when :virtual
@@ -209,6 +276,10 @@ module ActiveRecord
209
276
  end
210
277
 
211
278
  private
279
+ def valid_column_definition_options
280
+ super + [:array, :using, :cast_as, :as, :type, :enum_type, :stored]
281
+ end
282
+
212
283
  def aliased_types(name, fallback)
213
284
  fallback
214
285
  end
@@ -222,21 +293,79 @@ module ActiveRecord
222
293
  end
223
294
  end
224
295
 
296
+ # = Active Record PostgreSQL Adapter \Table
225
297
  class Table < ActiveRecord::ConnectionAdapters::Table
226
298
  include ColumnMethods
299
+
300
+ # Adds an exclusion constraint.
301
+ #
302
+ # t.exclusion_constraint("price WITH =, availability_range WITH &&", using: :gist, name: "price_check")
303
+ #
304
+ # See {connection.add_exclusion_constraint}[rdoc-ref:SchemaStatements#add_exclusion_constraint]
305
+ def exclusion_constraint(*args)
306
+ @base.add_exclusion_constraint(name, *args)
307
+ end
308
+
309
+ # Removes the given exclusion constraint from the table.
310
+ #
311
+ # t.remove_exclusion_constraint(name: "price_check")
312
+ #
313
+ # See {connection.remove_exclusion_constraint}[rdoc-ref:SchemaStatements#remove_exclusion_constraint]
314
+ def remove_exclusion_constraint(*args)
315
+ @base.remove_exclusion_constraint(name, *args)
316
+ end
317
+
318
+ # Adds a unique constraint.
319
+ #
320
+ # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
321
+ #
322
+ # See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
323
+ def unique_constraint(*args)
324
+ @base.add_unique_constraint(name, *args)
325
+ end
326
+
327
+ # Removes the given unique constraint from the table.
328
+ #
329
+ # t.remove_unique_constraint(name: "unique_position")
330
+ #
331
+ # See {connection.remove_unique_constraint}[rdoc-ref:SchemaStatements#remove_unique_constraint]
332
+ def remove_unique_constraint(*args)
333
+ @base.remove_unique_constraint(name, *args)
334
+ end
227
335
  end
228
336
 
337
+ # = Active Record PostgreSQL Adapter Alter \Table
229
338
  class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
230
- attr_reader :constraint_validations
339
+ attr_reader :constraint_validations, :exclusion_constraint_adds, :exclusion_constraint_drops, :unique_constraint_adds, :unique_constraint_drops
231
340
 
232
341
  def initialize(td)
233
342
  super
234
343
  @constraint_validations = []
344
+ @exclusion_constraint_adds = []
345
+ @exclusion_constraint_drops = []
346
+ @unique_constraint_adds = []
347
+ @unique_constraint_drops = []
235
348
  end
236
349
 
237
350
  def validate_constraint(name)
238
351
  @constraint_validations << name
239
352
  end
353
+
354
+ def add_exclusion_constraint(expression, options)
355
+ @exclusion_constraint_adds << @td.new_exclusion_constraint_definition(expression, options)
356
+ end
357
+
358
+ def drop_exclusion_constraint(constraint_name)
359
+ @exclusion_constraint_drops << constraint_name
360
+ end
361
+
362
+ def add_unique_constraint(column_name, options)
363
+ @unique_constraint_adds << @td.new_unique_constraint_definition(column_name, options)
364
+ end
365
+
366
+ def drop_unique_constraint(unique_constraint_name)
367
+ @unique_constraint_drops << unique_constraint_name
368
+ end
240
369
  end
241
370
  end
242
371
  end
@@ -28,6 +28,59 @@ module ActiveRecord
28
28
  end
29
29
  end
30
30
 
31
+ def schemas(stream)
32
+ schema_names = @connection.schema_names - ["public"]
33
+
34
+ if schema_names.any?
35
+ schema_names.sort.each do |name|
36
+ stream.puts " create_schema #{name.inspect}"
37
+ end
38
+ stream.puts
39
+ end
40
+ end
41
+
42
+ def exclusion_constraints_in_create(table, stream)
43
+ if (exclusion_constraints = @connection.exclusion_constraints(table)).any?
44
+ add_exclusion_constraint_statements = exclusion_constraints.map do |exclusion_constraint|
45
+ parts = [
46
+ "t.exclusion_constraint #{exclusion_constraint.expression.inspect}"
47
+ ]
48
+
49
+ parts << "where: #{exclusion_constraint.where.inspect}" if exclusion_constraint.where
50
+ parts << "using: #{exclusion_constraint.using.inspect}" if exclusion_constraint.using
51
+ parts << "deferrable: #{exclusion_constraint.deferrable.inspect}" if exclusion_constraint.deferrable
52
+
53
+ if exclusion_constraint.export_name_on_schema_dump?
54
+ parts << "name: #{exclusion_constraint.name.inspect}"
55
+ end
56
+
57
+ " #{parts.join(', ')}"
58
+ end
59
+
60
+ stream.puts add_exclusion_constraint_statements.sort.join("\n")
61
+ end
62
+ end
63
+
64
+ def unique_constraints_in_create(table, stream)
65
+ if (unique_constraints = @connection.unique_constraints(table)).any?
66
+ add_unique_constraint_statements = unique_constraints.map do |unique_constraint|
67
+ parts = [
68
+ "t.unique_constraint #{unique_constraint.column.inspect}"
69
+ ]
70
+
71
+ parts << "deferrable: #{unique_constraint.deferrable.inspect}" if unique_constraint.deferrable
72
+
73
+ if unique_constraint.export_name_on_schema_dump?
74
+ parts << "name: #{unique_constraint.name.inspect}"
75
+ end
76
+
77
+ " #{parts.join(', ')}"
78
+ end
79
+
80
+ stream.puts add_unique_constraint_statements.sort.join("\n")
81
+ end
82
+ end
83
+
31
84
  def prepare_column_options(column)
32
85
  spec = super
33
86
  spec[:array] = "true" if column.array?