activerecord 7.0.8.7 → 7.1.0.beta1

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 (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1339 -1572
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -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 +18 -3
  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 +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  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 +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  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 +193 -97
  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 +40 -26
  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 +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  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 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -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 +17 -12
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -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 +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +128 -138
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +89 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +183 -33
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +107 -45
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +169 -45
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +85 -15
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +26 -14
  165. data/lib/active_record/relation/query_attribute.rb +2 -1
  166. data/lib/active_record/relation/query_methods.rb +351 -62
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +10 -1
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +36 -10
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/time.rb +4 -0
  196. data/lib/active_record/validations/absence.rb +1 -1
  197. data/lib/active_record/validations/numericality.rb +5 -4
  198. data/lib/active_record/validations/presence.rb +5 -28
  199. data/lib/active_record/validations/uniqueness.rb +47 -2
  200. data/lib/active_record/validations.rb +8 -4
  201. data/lib/active_record/version.rb +1 -1
  202. data/lib/active_record.rb +121 -16
  203. data/lib/arel/errors.rb +10 -0
  204. data/lib/arel/factory_methods.rb +4 -0
  205. data/lib/arel/nodes/binary.rb +6 -1
  206. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  207. data/lib/arel/nodes/cte.rb +36 -0
  208. data/lib/arel/nodes/fragments.rb +35 -0
  209. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  210. data/lib/arel/nodes/leading_join.rb +8 -0
  211. data/lib/arel/nodes/node.rb +111 -2
  212. data/lib/arel/nodes/sql_literal.rb +6 -0
  213. data/lib/arel/nodes/table_alias.rb +4 -0
  214. data/lib/arel/nodes.rb +4 -0
  215. data/lib/arel/predications.rb +2 -0
  216. data/lib/arel/table.rb +9 -5
  217. data/lib/arel/visitors/mysql.rb +8 -1
  218. data/lib/arel/visitors/to_sql.rb +81 -17
  219. data/lib/arel/visitors/visitor.rb +2 -2
  220. data/lib/arel.rb +16 -2
  221. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  222. data/lib/rails/generators/active_record/migration.rb +3 -1
  223. data/lib/rails/generators/active_record/model/USAGE +113 -0
  224. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  225. metadata +52 -17
  226. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  227. data/lib/active_record/null_relation.rb +0 -63
@@ -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_key_adds.map { |con| visit_AddUniqueKey con }.join(" ")
16
+ sql << o.unique_key_drops.map { |con| visit_DropUniqueKey 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_UniqueKeyDefinition(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_AddUniqueKey(o)
81
+ "ADD #{accept(o)}"
82
+ end
83
+
84
+ def visit_DropUniqueKey(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
+ UniqueKeyDefinition = 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_keys, :unlogged
196
243
 
197
244
  def initialize(*, **)
198
245
  super
246
+ @exclusion_constraints = []
247
+ @unique_keys = []
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_key(column_name, **options)
256
+ unique_keys << new_unique_key_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_key_definition(column_name, options) # :nodoc:
265
+ options = @conn.unique_key_options(name, column_name, options)
266
+ UniqueKeyDefinition.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 an unique constraint.
319
+ #
320
+ # t.unique_key(:position, name: 'unique_position', deferrable: :deferred)
321
+ #
322
+ # See {connection.add_unique_key}[rdoc-ref:SchemaStatements#add_unique_key]
323
+ def unique_key(*args)
324
+ @base.add_unique_key(name, *args)
325
+ end
326
+
327
+ # Removes the given unique constraint from the table.
328
+ #
329
+ # t.remove_unique_key(name: "unique_position")
330
+ #
331
+ # See {connection.remove_unique_key}[rdoc-ref:SchemaStatements#remove_unique_key]
332
+ def remove_unique_key(*args)
333
+ @base.remove_unique_key(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_key_adds, :unique_key_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_key_adds = []
347
+ @unique_key_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_key(column_name, options)
363
+ @unique_key_adds << @td.new_unique_key_definition(column_name, options)
364
+ end
365
+
366
+ def drop_unique_key(unique_key_name)
367
+ @unique_key_drops << unique_key_name
368
+ end
240
369
  end
241
370
  end
242
371
  end
@@ -28,6 +28,48 @@ module ActiveRecord
28
28
  end
29
29
  end
30
30
 
31
+ def exclusion_constraints_in_create(table, stream)
32
+ if (exclusion_constraints = @connection.exclusion_constraints(table)).any?
33
+ add_exclusion_constraint_statements = exclusion_constraints.map do |exclusion_constraint|
34
+ parts = [
35
+ "t.exclusion_constraint #{exclusion_constraint.expression.inspect}"
36
+ ]
37
+
38
+ parts << "where: #{exclusion_constraint.where.inspect}" if exclusion_constraint.where
39
+ parts << "using: #{exclusion_constraint.using.inspect}" if exclusion_constraint.using
40
+ parts << "deferrable: #{exclusion_constraint.deferrable.inspect}" if exclusion_constraint.deferrable
41
+
42
+ if exclusion_constraint.export_name_on_schema_dump?
43
+ parts << "name: #{exclusion_constraint.name.inspect}"
44
+ end
45
+
46
+ " #{parts.join(', ')}"
47
+ end
48
+
49
+ stream.puts add_exclusion_constraint_statements.sort.join("\n")
50
+ end
51
+ end
52
+
53
+ def unique_keys_in_create(table, stream)
54
+ if (unique_keys = @connection.unique_keys(table)).any?
55
+ add_unique_key_statements = unique_keys.map do |unique_key|
56
+ parts = [
57
+ "t.unique_key #{unique_key.column.inspect}"
58
+ ]
59
+
60
+ parts << "deferrable: #{unique_key.deferrable.inspect}" if unique_key.deferrable
61
+
62
+ if unique_key.export_name_on_schema_dump?
63
+ parts << "name: #{unique_key.name.inspect}"
64
+ end
65
+
66
+ " #{parts.join(', ')}"
67
+ end
68
+
69
+ stream.puts add_unique_key_statements.sort.join("\n")
70
+ end
71
+ end
72
+
31
73
  def prepare_column_options(column)
32
74
  spec = super
33
75
  spec[:array] = "true" if column.array?