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.
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