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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/activerecord-cipherstash-pg-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/column.rb +70 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/database_statements.rb +199 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/array.rb +91 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/cidr.rb +48 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/point.rb +64 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/type_map_initializer.rb +125 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/uuid.rb +35 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/oid.rb +38 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/quoting.rb +237 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_creation.rb +170 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_definitions.rb +372 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_dumper.rb +116 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/schema_statements.rb +1110 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/7.1/cipherstash_pg/utils.rb +79 -0
- data/lib/active_record/connection_adapters/7.1/postgres_cipherstash_adapter.rb +1266 -0
- data/lib/active_record/connection_adapters/postgres_cipherstash_adapter.rb +5 -1
- data/lib/version.rb +1 -1
- 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
|