activerecord-cipherstash-pg-adapter 0.8.1 → 0.8.2

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/activerecord-cipherstash-pg-adapter.gemspec +1 -1
  4. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/column.rb +70 -0
  5. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/database_statements.rb +199 -0
  6. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/explain_pretty_printer.rb +44 -0
  7. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/array.rb +91 -0
  8. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/bit.rb +53 -0
  9. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/bit_varying.rb +15 -0
  10. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/bytea.rb +17 -0
  11. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/cidr.rb +48 -0
  12. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/date.rb +31 -0
  13. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/date_time.rb +36 -0
  14. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/decimal.rb +15 -0
  15. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/enum.rb +20 -0
  16. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/hstore.rb +109 -0
  17. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/inet.rb +15 -0
  18. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/interval.rb +49 -0
  19. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/jsonb.rb +15 -0
  20. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/legacy_point.rb +44 -0
  21. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/macaddr.rb +25 -0
  22. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/money.rb +41 -0
  23. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/oid.rb +15 -0
  24. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/point.rb +64 -0
  25. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/range.rb +124 -0
  26. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/specialized_string.rb +18 -0
  27. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/timestamp.rb +15 -0
  28. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/timestamp_with_time_zone.rb +30 -0
  29. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/type_map_initializer.rb +125 -0
  30. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/uuid.rb +35 -0
  31. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/vector.rb +28 -0
  32. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/xml.rb +30 -0
  33. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid.rb +38 -0
  34. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/quoting.rb +237 -0
  35. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/referential_integrity.rb +71 -0
  36. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_creation.rb +170 -0
  37. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_definitions.rb +372 -0
  38. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_dumper.rb +116 -0
  39. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_statements.rb +1110 -0
  40. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/type_metadata.rb +44 -0
  41. data/lib/active_record/connection_adapters/7.1/cipherstash_pg/utils.rb +79 -0
  42. data/lib/active_record/connection_adapters/7.1/postgres_cipherstash_adapter.rb +1266 -0
  43. data/lib/active_record/connection_adapters/postgres_cipherstash_adapter.rb +5 -1
  44. data/lib/version.rb +1 -1
  45. metadata +44 -5
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ class SchemaCreation < SchemaCreation # :nodoc:
7
+ private
8
+ delegate :quoted_include_columns_for_index, to: :@conn
9
+
10
+ def visit_AlterTable(o)
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(" ")
17
+ end
18
+
19
+ def visit_AddForeignKey(o)
20
+ super.dup.tap do |sql|
21
+ sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
22
+ sql << " NOT VALID" unless o.validate?
23
+ end
24
+ end
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
+
32
+ def visit_CheckConstraintDefinition(o)
33
+ super.dup.tap { |sql| sql << " NOT VALID" unless o.validate? }
34
+ end
35
+
36
+ def visit_ValidateConstraint(name)
37
+ "VALIDATE CONSTRAINT #{quote_column_name(name)}"
38
+ end
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
+
88
+ def visit_ChangeColumnDefinition(o)
89
+ column = o.column
90
+ column.sql_type = type_to_sql(column.type, **column.options)
91
+ quoted_column_name = quote_column_name(o.name)
92
+
93
+ change_column_sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}"
94
+
95
+ options = column_options(column)
96
+
97
+ if options[:collation]
98
+ change_column_sql << " COLLATE \"#{options[:collation]}\""
99
+ end
100
+
101
+ if options[:using]
102
+ change_column_sql << " USING #{options[:using]}"
103
+ elsif options[:cast_as]
104
+ cast_as_type = type_to_sql(options[:cast_as], **options)
105
+ change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
106
+ end
107
+
108
+ if options.key?(:default)
109
+ if options[:default].nil?
110
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
111
+ else
112
+ quoted_default = quote_default_expression(options[:default], column)
113
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
114
+ end
115
+ end
116
+
117
+ if options.key?(:null)
118
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
119
+ end
120
+
121
+ change_column_sql
122
+ end
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
+
133
+ def add_column_options!(sql, options)
134
+ if options[:collation]
135
+ sql << " COLLATE \"#{options[:collation]}\""
136
+ end
137
+
138
+ if as = options[:as]
139
+ sql << " GENERATED ALWAYS AS (#{as})"
140
+
141
+ if options[:stored]
142
+ sql << " STORED"
143
+ else
144
+ raise ArgumentError, <<~MSG
145
+ PostgreSQL currently does not support VIRTUAL (not persisted) generated columns.
146
+ Specify 'stored: true' option for '#{options[:column].name}'
147
+ MSG
148
+ end
149
+ end
150
+ super
151
+ end
152
+
153
+ def quoted_include_columns(o)
154
+ String === o ? o : quoted_include_columns_for_index(o)
155
+ end
156
+
157
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
158
+ def table_modifier_in_create(o)
159
+ # A table cannot be both TEMPORARY and UNLOGGED, since all TEMPORARY
160
+ # tables are already UNLOGGED.
161
+ if o.temporary
162
+ " TEMPORARY"
163
+ elsif o.unlogged
164
+ " UNLOGGED"
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,372 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module ColumnMethods
7
+ extend ActiveSupport::Concern
8
+
9
+ # Defines the primary key field.
10
+ # Use of the native PostgreSQL UUID type is supported, and can be used
11
+ # by defining your tables as such:
12
+ #
13
+ # create_table :stuffs, id: :uuid do |t|
14
+ # t.string :content
15
+ # t.timestamps
16
+ # end
17
+ #
18
+ # By default, this will use the <tt>gen_random_uuid()</tt> function from the
19
+ # +pgcrypto+ extension. As that extension is only available in
20
+ # PostgreSQL 9.4+, for earlier versions an explicit default can be set
21
+ # to use <tt>uuid_generate_v4()</tt> from the +uuid-ossp+ extension instead:
22
+ #
23
+ # create_table :stuffs, id: false do |t|
24
+ # t.primary_key :id, :uuid, default: "uuid_generate_v4()"
25
+ # t.uuid :foo_id
26
+ # t.timestamps
27
+ # end
28
+ #
29
+ # To enable the appropriate extension, which is a requirement, use
30
+ # the +enable_extension+ method in your migrations.
31
+ #
32
+ # To use a UUID primary key without any of the extensions, set the
33
+ # +:default+ option to +nil+:
34
+ #
35
+ # create_table :stuffs, id: false do |t|
36
+ # t.primary_key :id, :uuid, default: nil
37
+ # t.uuid :foo_id
38
+ # t.timestamps
39
+ # end
40
+ #
41
+ # You may also pass a custom stored procedure that returns a UUID or use a
42
+ # different UUID generation function from another library.
43
+ #
44
+ # Note that setting the UUID primary key default value to +nil+ will
45
+ # require you to assure that you always provide a UUID value before saving
46
+ # a record (as primary keys cannot be +nil+). This might be done via the
47
+ # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
48
+ def primary_key(name, type = :primary_key, **options)
49
+ if type == :uuid
50
+ options[:default] = options.fetch(:default, "gen_random_uuid()")
51
+ end
52
+
53
+ super
54
+ end
55
+
56
+ ##
57
+ # :method: bigserial
58
+ # :call-seq: bigserial(*names, **options)
59
+
60
+ ##
61
+ # :method: bit
62
+ # :call-seq: bit(*names, **options)
63
+
64
+ ##
65
+ # :method: bit_varying
66
+ # :call-seq: bit_varying(*names, **options)
67
+
68
+ ##
69
+ # :method: cidr
70
+ # :call-seq: cidr(*names, **options)
71
+
72
+ ##
73
+ # :method: citext
74
+ # :call-seq: citext(*names, **options)
75
+
76
+ ##
77
+ # :method: daterange
78
+ # :call-seq: daterange(*names, **options)
79
+
80
+ ##
81
+ # :method: hstore
82
+ # :call-seq: hstore(*names, **options)
83
+
84
+ ##
85
+ # :method: inet
86
+ # :call-seq: inet(*names, **options)
87
+
88
+ ##
89
+ # :method: interval
90
+ # :call-seq: interval(*names, **options)
91
+
92
+ ##
93
+ # :method: int4range
94
+ # :call-seq: int4range(*names, **options)
95
+
96
+ ##
97
+ # :method: int8range
98
+ # :call-seq: int8range(*names, **options)
99
+
100
+ ##
101
+ # :method: jsonb
102
+ # :call-seq: jsonb(*names, **options)
103
+
104
+ ##
105
+ # :method: ltree
106
+ # :call-seq: ltree(*names, **options)
107
+
108
+ ##
109
+ # :method: macaddr
110
+ # :call-seq: macaddr(*names, **options)
111
+
112
+ ##
113
+ # :method: money
114
+ # :call-seq: money(*names, **options)
115
+
116
+ ##
117
+ # :method: numrange
118
+ # :call-seq: numrange(*names, **options)
119
+
120
+ ##
121
+ # :method: oid
122
+ # :call-seq: oid(*names, **options)
123
+
124
+ ##
125
+ # :method: point
126
+ # :call-seq: point(*names, **options)
127
+
128
+ ##
129
+ # :method: line
130
+ # :call-seq: line(*names, **options)
131
+
132
+ ##
133
+ # :method: lseg
134
+ # :call-seq: lseg(*names, **options)
135
+
136
+ ##
137
+ # :method: box
138
+ # :call-seq: box(*names, **options)
139
+
140
+ ##
141
+ # :method: path
142
+ # :call-seq: path(*names, **options)
143
+
144
+ ##
145
+ # :method: polygon
146
+ # :call-seq: polygon(*names, **options)
147
+
148
+ ##
149
+ # :method: circle
150
+ # :call-seq: circle(*names, **options)
151
+
152
+ ##
153
+ # :method: serial
154
+ # :call-seq: serial(*names, **options)
155
+
156
+ ##
157
+ # :method: tsrange
158
+ # :call-seq: tsrange(*names, **options)
159
+
160
+ ##
161
+ # :method: tstzrange
162
+ # :call-seq: tstzrange(*names, **options)
163
+
164
+ ##
165
+ # :method: tsvector
166
+ # :call-seq: tsvector(*names, **options)
167
+
168
+ ##
169
+ # :method: uuid
170
+ # :call-seq: uuid(*names, **options)
171
+
172
+ ##
173
+ # :method: xml
174
+ # :call-seq: xml(*names, **options)
175
+
176
+ ##
177
+ # :method: timestamptz
178
+ # :call-seq: timestamptz(*names, **options)
179
+
180
+ ##
181
+ # :method: enum
182
+ # :call-seq: enum(*names, **options)
183
+
184
+ included do
185
+ define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
186
+ :hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
187
+ :money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
188
+ :serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
189
+ end
190
+ end
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
239
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
240
+ include ColumnMethods
241
+
242
+ attr_reader :exclusion_constraints, :unique_keys, :unlogged
243
+
244
+ def initialize(*, **)
245
+ super
246
+ @exclusion_constraints = []
247
+ @unique_keys = []
248
+ @unlogged = ActiveRecord::ConnectionAdapters::CipherStashPGAdapter.create_unlogged_tables
249
+ end
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
+
269
+ def new_column_definition(name, type, **options) # :nodoc:
270
+ case type
271
+ when :virtual
272
+ type = options[:type]
273
+ end
274
+
275
+ super
276
+ end
277
+
278
+ private
279
+ def valid_column_definition_options
280
+ super + [:array, :using, :cast_as, :as, :type, :enum_type, :stored]
281
+ end
282
+
283
+ def aliased_types(name, fallback)
284
+ fallback
285
+ end
286
+
287
+ def integer_like_primary_key_type(type, options)
288
+ if type == :bigint || options[:limit] == 8
289
+ :bigserial
290
+ else
291
+ :serial
292
+ end
293
+ end
294
+ end
295
+
296
+ # = Active Record PostgreSQL Adapter \Table
297
+ class Table < ActiveRecord::ConnectionAdapters::Table
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
335
+ end
336
+
337
+ # = Active Record PostgreSQL Adapter Alter \Table
338
+ class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
339
+ attr_reader :constraint_validations, :exclusion_constraint_adds, :exclusion_constraint_drops, :unique_key_adds, :unique_key_drops
340
+
341
+ def initialize(td)
342
+ super
343
+ @constraint_validations = []
344
+ @exclusion_constraint_adds = []
345
+ @exclusion_constraint_drops = []
346
+ @unique_key_adds = []
347
+ @unique_key_drops = []
348
+ end
349
+
350
+ def validate_constraint(name)
351
+ @constraint_validations << name
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
369
+ end
370
+ end
371
+ end
372
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
+ private
8
+ def extensions(stream)
9
+ extensions = @connection.extensions
10
+ if extensions.any?
11
+ stream.puts " # These are extensions that must be enabled in order to support this database"
12
+ extensions.sort.each do |extension|
13
+ stream.puts " enable_extension #{extension.inspect}"
14
+ end
15
+ stream.puts
16
+ end
17
+ end
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
+
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
+
73
+ def prepare_column_options(column)
74
+ spec = super
75
+ spec[:array] = "true" if column.array?
76
+
77
+ if @connection.supports_virtual_columns? && column.virtual?
78
+ spec[:as] = extract_expression_for_virtual_column(column)
79
+ spec[:stored] = true
80
+ spec = { type: schema_type(column).inspect }.merge!(spec)
81
+ end
82
+
83
+ spec[:enum_type] = "\"#{column.sql_type}\"" if column.enum?
84
+
85
+ spec
86
+ end
87
+
88
+ def default_primary_key?(column)
89
+ schema_type(column) == :bigserial
90
+ end
91
+
92
+ def explicit_primary_key_default?(column)
93
+ column.type == :uuid || (column.type == :integer && !column.serial?)
94
+ end
95
+
96
+ def schema_type(column)
97
+ return super unless column.serial?
98
+
99
+ if column.bigint?
100
+ :bigserial
101
+ else
102
+ :serial
103
+ end
104
+ end
105
+
106
+ def schema_expression(column)
107
+ super unless column.serial?
108
+ end
109
+
110
+ def extract_expression_for_virtual_column(column)
111
+ column.default_function.inspect
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end