activerecord-cipherstash-pg-adapter 0.8.1 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/activerecord-cipherstash-pg-adapter.gemspec +2 -2
  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 +45 -6
@@ -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