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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdfb2bb3cfba47a44161b2bf24d1a6dbeb6c61832cba81f4d076b05d560962cc
4
- data.tar.gz: 5683664b0a8102793f861a0a50b8ef6e05a0f3b103fcbcea6b4e9bed89a80b89
3
+ metadata.gz: f91280c762358755ec87fbfb247164cff70519f1b8eb1befcd2a7cb65e18f61d
4
+ data.tar.gz: fe9e7177ade9fbf67e02048c6b511de3d949d2d4333b45dfc96181ffacca296a
5
5
  SHA512:
6
- metadata.gz: 2d6ce8e4dfca6b40d7f2da7f2761c0889c0a4d37a7e6c85ee4c6c1bc85bd5804eefc76dd150fb4be8d82be560ae0ad466f11c53d817581c28d18eb1bfbaed467
7
- data.tar.gz: 4a2c9005b6258cb55b9a95b4331df99543ee8d0945d3994bd47655b7d66700f50f7bec05b0b68d88f5fcc7801e93e633d35f7ceebf004babe49c6de81b92afde
6
+ metadata.gz: 7620f50195e83042060930382b7af7021e261e48ad6b0b6ef3f13e23e50f9bd69210ab9c4ec621097baf9402fe628fae5d4438c75d11dc6e6df2d1d412e62ea3
7
+ data.tar.gz: 1b1f84b66a3fe9b60ad596341cd1fba0949eab05e473e540baffd23bd3f0ad62c711f4b59201899c80ae6e59f837919552b5efe859f4f3da30d374b2d8482ee3
data/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.8.2] - 2023-09-25
10
+
11
+ - Update supported Active Record version to <= 7.1.
12
+
9
13
  ## [0.8.1] - 2023-09-14
10
14
 
11
15
  - Update max supported Active Record dep to < version 7.1.
@@ -30,6 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ["lib"]
31
31
 
32
32
  # Runtime dependencies here; dev+test go in Gemfile.
33
- spec.add_dependency "activerecord", ">= 6.0.0", "< 7.1"
33
+ spec.add_dependency "activerecord", ">= 6.0.0", "<= 7.1"
34
34
  spec.add_dependency "cipherstash-pg", ">= 1.0.0.beta.17"
35
35
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ class Column < ConnectionAdapters::Column # :nodoc:
7
+ delegate :oid, :fmod, to: :sql_type_metadata
8
+
9
+ def initialize(*, serial: nil, generated: nil, **)
10
+ super
11
+ @serial = serial
12
+ @generated = generated
13
+ end
14
+
15
+ def serial?
16
+ @serial
17
+ end
18
+ alias_method :auto_incremented_by_db?, :serial?
19
+
20
+ def virtual?
21
+ # We assume every generated column is virtual, no matter the concrete type
22
+ @generated.present?
23
+ end
24
+
25
+ def has_default?
26
+ super && !virtual?
27
+ end
28
+
29
+ def array
30
+ sql_type_metadata.sql_type.end_with?("[]")
31
+ end
32
+ alias :array? :array
33
+
34
+ def enum?
35
+ type == :enum
36
+ end
37
+
38
+ def sql_type
39
+ super.delete_suffix("[]")
40
+ end
41
+
42
+ def init_with(coder)
43
+ @serial = coder["serial"]
44
+ @generated = coder["generated"]
45
+ super
46
+ end
47
+
48
+ def encode_with(coder)
49
+ coder["serial"] = @serial
50
+ coder["generated"] = @generated
51
+ super
52
+ end
53
+
54
+ def ==(other)
55
+ other.is_a?(Column) &&
56
+ super &&
57
+ serial? == other.serial?
58
+ end
59
+ alias :eql? :==
60
+
61
+ def hash
62
+ Column.hash ^
63
+ super.hash ^
64
+ serial?.hash
65
+ end
66
+ end
67
+ end
68
+ CipherStashPGColumn = CipherStashPG::Column # :nodoc:
69
+ end
70
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module DatabaseStatements
7
+ def explain(arel, binds = [], options = [])
8
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
9
+ result = internal_exec_query(sql, "EXPLAIN", binds)
10
+ CipherStashPG::ExplainPrettyPrinter.new.pp(result)
11
+ end
12
+
13
+ # Queries the database and returns the results in an Array-like object
14
+ def query(sql, name = nil) # :nodoc:
15
+ mark_transaction_written_if_write(sql)
16
+
17
+ log(sql, name) do
18
+ with_raw_connection do |conn|
19
+ conn.async_exec(sql).map_types!(@type_map_for_results).values
20
+ end
21
+ end
22
+ end
23
+
24
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
25
+ :close, :declare, :fetch, :move, :set, :show
26
+ ) # :nodoc:
27
+ private_constant :READ_QUERY
28
+
29
+ def write_query?(sql) # :nodoc:
30
+ !READ_QUERY.match?(sql)
31
+ rescue ArgumentError # Invalid encoding
32
+ !READ_QUERY.match?(sql.b)
33
+ end
34
+
35
+ # Executes an SQL statement, returning a ::CipherStashPG::Result object on success
36
+ # or raising a ::CipherStashPG::Error exception otherwise.
37
+ #
38
+ # Setting +allow_retry+ to true causes the db to reconnect and retry
39
+ # executing the SQL statement in case of a connection-related exception.
40
+ # This option should only be enabled for known idempotent queries.
41
+ #
42
+ # Note: the ::CipherStashPG::Result object is manually memory managed; if you don't
43
+ # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
44
+ def execute(...) # :nodoc:
45
+ super
46
+ ensure
47
+ @notice_receiver_sql_warnings = []
48
+ end
49
+
50
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
51
+ log(sql, name, async: async) do
52
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
53
+ result = conn.async_exec(sql)
54
+ handle_warnings(result)
55
+ result
56
+ end
57
+ end
58
+ end
59
+
60
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
61
+ execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
62
+ types = {}
63
+ fields = result.fields
64
+ fields.each_with_index do |fname, i|
65
+ ftype = result.ftype i
66
+ fmod = result.fmod i
67
+ types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
68
+ end
69
+ build_result(columns: fields, rows: result.values, column_types: types)
70
+ end
71
+ end
72
+
73
+ def exec_delete(sql, name = nil, binds = []) # :nodoc:
74
+ execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
75
+ end
76
+ alias :exec_update :exec_delete
77
+
78
+ def sql_for_insert(sql, pk, binds, returning) # :nodoc:
79
+ if pk.nil?
80
+ # Extract the table from the insert sql. Yuck.
81
+ table_ref = extract_table_ref_from_insert_sql(sql)
82
+ pk = primary_key(table_ref) if table_ref
83
+ end
84
+
85
+ returning_columns = returning || Array(pk)
86
+
87
+ returning_columns_statement = returning_columns.map { |c| quote_column_name(c) }.join(", ")
88
+ sql = "#{sql} RETURNING #{returning_columns_statement}" if returning_columns.any?
89
+
90
+ super
91
+ end
92
+ private :sql_for_insert
93
+
94
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
95
+ if use_insert_returning? || pk == false
96
+ super
97
+ else
98
+ result = internal_exec_query(sql, name, binds)
99
+ unless sequence_name
100
+ table_ref = extract_table_ref_from_insert_sql(sql)
101
+ if table_ref
102
+ pk = primary_key(table_ref) if pk.nil?
103
+ pk = suppress_composite_primary_key(pk)
104
+ sequence_name = default_sequence_name(table_ref, pk)
105
+ end
106
+ return result unless sequence_name
107
+ end
108
+ last_insert_id_result(sequence_name)
109
+ end
110
+ end
111
+
112
+ # Begins a transaction.
113
+ def begin_db_transaction # :nodoc:
114
+ internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
115
+ end
116
+
117
+ def begin_isolated_db_transaction(isolation) # :nodoc:
118
+ internal_execute("BEGIN ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
119
+ end
120
+
121
+ # Commits a transaction.
122
+ def commit_db_transaction # :nodoc:
123
+ internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
124
+ end
125
+
126
+ # Aborts a transaction.
127
+ def exec_rollback_db_transaction # :nodoc:
128
+ cancel_any_running_query
129
+ internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
130
+ end
131
+
132
+ def exec_restart_db_transaction # :nodoc:
133
+ cancel_any_running_query
134
+ internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
135
+ end
136
+
137
+ # From https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
138
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
139
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
140
+
141
+ def high_precision_current_timestamp
142
+ HIGH_PRECISION_CURRENT_TIMESTAMP
143
+ end
144
+
145
+ def build_explain_clause(options = [])
146
+ return "EXPLAIN" if options.empty?
147
+
148
+ "EXPLAIN (#{options.join(", ").upcase})"
149
+ end
150
+
151
+ private
152
+ IDLE_TRANSACTION_STATUSES = [::CipherStashPG::PQTRANS_IDLE, ::CipherStashPG::PQTRANS_INTRANS, ::CipherStashPG::PQTRANS_INERROR]
153
+ private_constant :IDLE_TRANSACTION_STATUSES
154
+
155
+ def cancel_any_running_query
156
+ return if @raw_connection.nil? || IDLE_TRANSACTION_STATUSES.include?(@raw_connection.transaction_status)
157
+
158
+ @raw_connection.cancel
159
+ @raw_connection.block
160
+ rescue ::CipherStashPG::Error
161
+ end
162
+
163
+ def execute_batch(statements, name = nil)
164
+ execute(combine_multi_statements(statements))
165
+ end
166
+
167
+ def build_truncate_statements(table_names)
168
+ ["TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"]
169
+ end
170
+
171
+ # Returns the current ID of a table's sequence.
172
+ def last_insert_id_result(sequence_name)
173
+ internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
174
+ end
175
+
176
+ def returning_column_values(result)
177
+ result.rows.first
178
+ end
179
+
180
+ def suppress_composite_primary_key(pk)
181
+ pk unless pk.is_a?(Array)
182
+ end
183
+
184
+ def handle_warnings(sql)
185
+ @notice_receiver_sql_warnings.each do |warning|
186
+ next if warning_ignored?(warning)
187
+
188
+ warning.sql = sql
189
+ ActiveRecord.db_warnings_action.call(warning)
190
+ end
191
+ end
192
+
193
+ def warning_ignored?(warning)
194
+ ["WARNING", "ERROR", "FATAL", "PANIC"].exclude?(warning.level) || super
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ class ExplainPrettyPrinter # :nodoc:
7
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
8
+ # PostgreSQL shell:
9
+ #
10
+ # QUERY PLAN
11
+ # ------------------------------------------------------------------------------
12
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
13
+ # Join Filter: (posts.user_id = users.id)
14
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
15
+ # Index Cond: (id = 1)
16
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
17
+ # Filter: (posts.user_id = 1)
18
+ # (6 rows)
19
+ #
20
+ def pp(result)
21
+ header = result.columns.first
22
+ lines = result.rows.map(&:first)
23
+
24
+ # We add 2 because there's one char of padding at both sides, note
25
+ # the extra hyphens in the example above.
26
+ width = [header, *lines].map(&:length).max + 2
27
+
28
+ pp = []
29
+
30
+ pp << header.center(width).rstrip
31
+ pp << "-" * width
32
+
33
+ pp += lines.map { |line| " #{line}" }
34
+
35
+ nrows = result.rows.length
36
+ rows_label = nrows == 1 ? "row" : "rows"
37
+ pp << "(#{nrows} #{rows_label})"
38
+
39
+ pp.join("\n") + "\n"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class Array < Type::Value # :nodoc:
8
+ include ActiveModel::Type::Helpers::Mutable
9
+
10
+ Data = Struct.new(:encoder, :values) # :nodoc:
11
+
12
+ attr_reader :subtype, :delimiter
13
+ delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype
14
+
15
+ def initialize(subtype, delimiter = ",")
16
+ @subtype = subtype
17
+ @delimiter = delimiter
18
+
19
+ @pg_encoder = ::CipherStashPG::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter
20
+ @pg_decoder = ::CipherStashPG::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter
21
+ end
22
+
23
+ def deserialize(value)
24
+ case value
25
+ when ::String
26
+ type_cast_array(@pg_decoder.decode(value), :deserialize)
27
+ when Data
28
+ type_cast_array(value.values, :deserialize)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def cast(value)
35
+ if value.is_a?(::String)
36
+ value = begin
37
+ @pg_decoder.decode(value)
38
+ rescue TypeError
39
+ # malformed array string is treated as [], will raise in PG 2.0 gem
40
+ # this keeps a consistent implementation
41
+ []
42
+ end
43
+ end
44
+ type_cast_array(value, :cast)
45
+ end
46
+
47
+ def serialize(value)
48
+ if value.is_a?(::Array)
49
+ casted_values = type_cast_array(value, :serialize)
50
+ Data.new(@pg_encoder, casted_values)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def ==(other)
57
+ other.is_a?(Array) &&
58
+ subtype == other.subtype &&
59
+ delimiter == other.delimiter
60
+ end
61
+
62
+ def type_cast_for_schema(value)
63
+ return super unless value.is_a?(::Array)
64
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
65
+ end
66
+
67
+ def map(value, &block)
68
+ value.map { |v| subtype.map(v, &block) }
69
+ end
70
+
71
+ def changed_in_place?(raw_old_value, new_value)
72
+ deserialize(raw_old_value) != new_value
73
+ end
74
+
75
+ def force_equality?(value)
76
+ value.is_a?(::Array)
77
+ end
78
+
79
+ private
80
+ def type_cast_array(value, method)
81
+ if value.is_a?(::Array)
82
+ value.map { |item| type_cast_array(item, method) }
83
+ else
84
+ @subtype.public_send(method, value)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class Bit < Type::Value # :nodoc:
8
+ def type
9
+ :bit
10
+ end
11
+
12
+ def cast_value(value)
13
+ if ::String === value
14
+ case value
15
+ when /^0x/i
16
+ value[2..-1].hex.to_s(2) # Hexadecimal notation
17
+ else
18
+ value # Bit-string notation
19
+ end
20
+ else
21
+ value.to_s
22
+ end
23
+ end
24
+
25
+ def serialize(value)
26
+ Data.new(super) if value
27
+ end
28
+
29
+ class Data
30
+ def initialize(value)
31
+ @value = value
32
+ end
33
+
34
+ def to_s
35
+ value
36
+ end
37
+
38
+ def binary?
39
+ /\A[01]*\Z/.match?(value)
40
+ end
41
+
42
+ def hex?
43
+ /\A[0-9A-F]*\Z/i.match?(value)
44
+ end
45
+
46
+ private
47
+ attr_reader :value
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class BitVarying < OID::Bit # :nodoc:
8
+ def type
9
+ :bit_varying
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class Bytea < Type::Binary # :nodoc:
8
+ def deserialize(value)
9
+ return if value.nil?
10
+ return value.to_s if value.is_a?(Type::Binary::Data)
11
+ PG::Connection.unescape_bytea(super)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ module CipherStashPG
8
+ module OID # :nodoc:
9
+ class Cidr < Type::Value # :nodoc:
10
+ def type
11
+ :cidr
12
+ end
13
+
14
+ def type_cast_for_schema(value)
15
+ # If the subnet mask is equal to /32, don't output it
16
+ if value.prefix == 32
17
+ "\"#{value}\""
18
+ else
19
+ "\"#{value}/#{value.prefix}\""
20
+ end
21
+ end
22
+
23
+ def serialize(value)
24
+ if IPAddr === value
25
+ "#{value}/#{value.prefix}"
26
+ else
27
+ value
28
+ end
29
+ end
30
+
31
+ def cast_value(value)
32
+ if value.nil?
33
+ nil
34
+ elsif String === value
35
+ begin
36
+ IPAddr.new(value)
37
+ rescue ArgumentError
38
+ nil
39
+ end
40
+ else
41
+ value
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class Date < Type::Date # :nodoc:
8
+ def cast_value(value)
9
+ case value
10
+ when "infinity" then ::Float::INFINITY
11
+ when "-infinity" then -::Float::INFINITY
12
+ when / BC$/
13
+ value = value.sub(/^\d+/) { |year| format("%04d", -year.to_i + 1) }
14
+ super(value.delete_suffix!(" BC"))
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def type_cast_for_schema(value)
21
+ case value
22
+ when ::Float::INFINITY then "::Float::INFINITY"
23
+ when -::Float::INFINITY then "-::Float::INFINITY"
24
+ else super
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class DateTime < Type::DateTime # :nodoc:
8
+ def cast_value(value)
9
+ case value
10
+ when "infinity" then ::Float::INFINITY
11
+ when "-infinity" then -::Float::INFINITY
12
+ when / BC$/
13
+ value = value.sub(/^\d+/) { |year| format("%04d", -year.to_i + 1) }
14
+ super(value.delete_suffix!(" BC"))
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def type_cast_for_schema(value)
21
+ case value
22
+ when ::Float::INFINITY then "::Float::INFINITY"
23
+ when -::Float::INFINITY then "-::Float::INFINITY"
24
+ else super
25
+ end
26
+ end
27
+
28
+ protected
29
+ def real_type_unless_aliased(real_type)
30
+ ActiveRecord::ConnectionAdapters::CipherStashPGAdapter.datetime_type == real_type ? :datetime : real_type
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class Decimal < Type::Decimal # :nodoc:
8
+ def infinity(options = {})
9
+ BigDecimal("Infinity") * (options[:negative] ? -1 : 1)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module CipherStashPG
6
+ module OID # :nodoc:
7
+ class Enum < Type::Value # :nodoc:
8
+ def type
9
+ :enum
10
+ end
11
+
12
+ private
13
+ def cast_value(value)
14
+ value.to_s
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end