activerecord-cipherstash-pg-adapter 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|