activerecord-yugabytedb-adapter 7.0.4.1 → 7.1.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,21 +5,30 @@ module ActiveRecord
5
5
  module YugabyteDB
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::YugabyteDBAdapter.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?