pg 1.4.6 → 1.6.1
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
- checksums.yaml.gz.sig +0 -0
- data/{History.md → CHANGELOG.md} +185 -3
- data/Gemfile +12 -3
- data/README-Windows.rdoc +1 -1
- data/README.ja.md +75 -41
- data/README.md +86 -31
- data/Rakefile +95 -14
- data/certs/kanis@comcard.de.pem +20 -0
- data/certs/larskanis-2024.pem +24 -0
- data/ext/errorcodes.def +4 -5
- data/ext/errorcodes.txt +2 -5
- data/ext/extconf.rb +165 -14
- data/ext/gvl_wrappers.c +13 -2
- data/ext/gvl_wrappers.h +33 -0
- data/ext/pg.c +28 -35
- data/ext/pg.h +18 -14
- data/ext/pg_binary_decoder.c +231 -0
- data/ext/pg_binary_encoder.c +427 -0
- data/ext/pg_cancel_connection.c +360 -0
- data/ext/pg_coder.c +70 -12
- data/ext/pg_connection.c +473 -208
- data/ext/pg_copy_coder.c +316 -23
- data/ext/pg_record_coder.c +12 -11
- data/ext/pg_result.c +102 -30
- data/ext/pg_text_decoder.c +31 -10
- data/ext/pg_text_encoder.c +58 -26
- data/ext/pg_tuple.c +36 -33
- data/ext/pg_type_map.c +4 -3
- data/ext/pg_type_map_all_strings.c +3 -3
- data/ext/pg_type_map_by_class.c +6 -4
- data/ext/pg_type_map_by_column.c +9 -4
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +10 -5
- data/ext/pg_type_map_in_ruby.c +6 -3
- data/lib/pg/basic_type_map_based_on_result.rb +21 -1
- data/lib/pg/basic_type_map_for_queries.rb +23 -10
- data/lib/pg/basic_type_map_for_results.rb +26 -3
- data/lib/pg/basic_type_registry.rb +46 -36
- data/lib/pg/binary_decoder/date.rb +9 -0
- data/lib/pg/binary_decoder/timestamp.rb +26 -0
- data/lib/pg/binary_encoder/timestamp.rb +20 -0
- data/lib/pg/cancel_connection.rb +53 -0
- data/lib/pg/coder.rb +18 -14
- data/lib/pg/connection.rb +387 -172
- data/lib/pg/exceptions.rb +6 -0
- data/lib/pg/text_decoder/date.rb +21 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +17 -0
- data/lib/pg/text_decoder/numeric.rb +9 -0
- data/lib/pg/text_decoder/timestamp.rb +30 -0
- data/lib/pg/text_encoder/date.rb +13 -0
- data/lib/pg/text_encoder/inet.rb +31 -0
- data/lib/pg/text_encoder/json.rb +17 -0
- data/lib/pg/text_encoder/numeric.rb +9 -0
- data/lib/pg/text_encoder/timestamp.rb +24 -0
- data/lib/pg/version.rb +1 -1
- data/lib/pg.rb +78 -17
- data/misc/yugabyte/Dockerfile +9 -0
- data/misc/yugabyte/docker-compose.yml +28 -0
- data/misc/yugabyte/pg-test.rb +45 -0
- data/pg.gemspec +9 -5
- data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
- data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
- data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
- data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
- data/rakelib/pg_gem_helper.rb +64 -0
- data.tar.gz.sig +0 -0
- metadata +61 -49
- metadata.gz.sig +0 -0
- data/.appveyor.yml +0 -42
- data/.gems +0 -6
- data/.gemtest +0 -0
- data/.github/workflows/binary-gems.yml +0 -117
- data/.github/workflows/source-gem.yml +0 -137
- data/.gitignore +0 -19
- data/.hgsigs +0 -34
- data/.hgtags +0 -41
- data/.irbrc +0 -23
- data/.pryrc +0 -23
- data/.tm_properties +0 -21
- data/.travis.yml +0 -49
- data/Manifest.txt +0 -72
- data/Rakefile.cross +0 -298
- data/lib/pg/binary_decoder.rb +0 -23
- data/lib/pg/constants.rb +0 -12
- data/lib/pg/text_decoder.rb +0 -46
- data/lib/pg/text_encoder.rb +0 -59
- data/translation/.po4a-version +0 -7
- data/translation/po/all.pot +0 -875
- data/translation/po/ja.po +0 -868
- data/translation/po4a.cfg +0 -9
data/ext/pg_type_map_in_ruby.c
CHANGED
@@ -40,11 +40,11 @@ static const rb_data_type_t pg_tmir_type = {
|
|
40
40
|
pg_typemap_mark,
|
41
41
|
RUBY_TYPED_DEFAULT_FREE,
|
42
42
|
pg_tmir_memsize,
|
43
|
-
|
43
|
+
pg_tmir_compact,
|
44
44
|
},
|
45
45
|
&pg_typemap_type,
|
46
46
|
0,
|
47
|
-
RUBY_TYPED_FREE_IMMEDIATELY,
|
47
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
48
48
|
};
|
49
49
|
|
50
50
|
/*
|
@@ -210,6 +210,9 @@ pg_tmir_typecast_query_param( VALUE self, VALUE param_value, VALUE field )
|
|
210
210
|
* This method is called, when a type map is used for decoding copy data,
|
211
211
|
* before the value is casted.
|
212
212
|
*
|
213
|
+
* Should return the expected number of columns or 0 if the number of columns is unknown.
|
214
|
+
* This number is only used for memory pre-allocation.
|
215
|
+
*
|
213
216
|
*/
|
214
217
|
static VALUE pg_tmir_fit_to_copy_get_dummy( VALUE self ){}
|
215
218
|
#endif
|
@@ -291,7 +294,7 @@ pg_tmir_s_allocate( VALUE klass )
|
|
291
294
|
this->typemap.funcs.typecast_result_value = pg_tmir_result_value;
|
292
295
|
this->typemap.funcs.typecast_query_param = pg_tmir_query_param;
|
293
296
|
this->typemap.funcs.typecast_copy_get = pg_tmir_copy_get;
|
294
|
-
this->typemap.default_typemap
|
297
|
+
RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
|
295
298
|
this->self = self;
|
296
299
|
|
297
300
|
return self;
|
@@ -32,7 +32,27 @@ require 'pg' unless defined?( PG )
|
|
32
32
|
# conn.put_copy_data ['a', 123, [5,4,3]]
|
33
33
|
# end
|
34
34
|
# This inserts a single row into copytable with type casts from ruby to
|
35
|
-
# database types.
|
35
|
+
# database types using text format.
|
36
|
+
#
|
37
|
+
# Very similar with binary format:
|
38
|
+
#
|
39
|
+
# conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, blob bytea, created_at timestamp)" )
|
40
|
+
# # Retrieve table OIDs per empty result set in binary format.
|
41
|
+
# res = conn.exec_params( "SELECT * FROM copytable LIMIT 0", [], 1 )
|
42
|
+
# # Build a type map for common ruby to database type encoders.
|
43
|
+
# btm = PG::BasicTypeMapBasedOnResult.new(conn)
|
44
|
+
# # Build a PG::TypeMapByColumn with encoders suitable for copytable.
|
45
|
+
# tm = btm.build_column_map( res )
|
46
|
+
# row_encoder = PG::BinaryEncoder::CopyRow.new type_map: tm
|
47
|
+
#
|
48
|
+
# conn.copy_data( "COPY copytable FROM STDIN WITH (FORMAT binary)", row_encoder ) do |res|
|
49
|
+
# conn.put_copy_data ['a', 123, "\xff\x00".b, Time.now]
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# This inserts a single row into copytable with type casts from ruby to
|
53
|
+
# database types using binary copy and value format.
|
54
|
+
# Binary COPY is faster than text format but less portable and less readable and pg offers fewer en-/decoders of database types.
|
55
|
+
#
|
36
56
|
class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
|
37
57
|
include PG::BasicTypeRegistry::Checker
|
38
58
|
|
@@ -47,16 +47,24 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
47
47
|
# Options:
|
48
48
|
# * +registry+: Custom type registry, nil for default global registry
|
49
49
|
# * +if_undefined+: Optional +Proc+ object which is called, if no type for an parameter class is not defined in the registry.
|
50
|
+
# The +Proc+ object is called with the name and format of the missing type.
|
51
|
+
# Its return value is not used.
|
50
52
|
def initialize(connection_or_coder_maps, registry: nil, if_undefined: nil)
|
51
53
|
@coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
|
52
54
|
@array_encoders_by_klass = array_encoders_by_klass
|
53
55
|
@encode_array_as = :array
|
54
|
-
@if_undefined = if_undefined ||
|
55
|
-
raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
|
56
|
-
}
|
56
|
+
@if_undefined = if_undefined || UndefinedDefault
|
57
57
|
init_encoders
|
58
58
|
end
|
59
59
|
|
60
|
+
class UndefinedDefault
|
61
|
+
def self.call(oid_name, format)
|
62
|
+
raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private_constant :UndefinedDefault
|
67
|
+
|
60
68
|
# Change the mechanism that is used to encode ruby array values
|
61
69
|
#
|
62
70
|
# Possible values:
|
@@ -162,14 +170,19 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
162
170
|
@textarray_encoder
|
163
171
|
end
|
164
172
|
|
165
|
-
|
173
|
+
begin
|
174
|
+
PG.require_bigdecimal_without_warning
|
175
|
+
has_bigdecimal = true
|
176
|
+
rescue LoadError
|
177
|
+
end
|
178
|
+
|
179
|
+
DEFAULT_TYPE_MAP = PG.make_shareable({
|
166
180
|
TrueClass => [1, 'bool', 'bool'],
|
167
181
|
FalseClass => [1, 'bool', 'bool'],
|
168
182
|
# We use text format and no type OID for numbers, because setting the OID can lead
|
169
183
|
# to unnecessary type conversions on server side.
|
170
184
|
Integer => [0, 'int8'],
|
171
185
|
Float => [0, 'float8'],
|
172
|
-
BigDecimal => [0, 'numeric'],
|
173
186
|
Time => [0, 'timestamptz'],
|
174
187
|
# We use text format and no type OID for IPAddr, because setting the OID can lead
|
175
188
|
# to unnecessary inet/cidr conversions on the server side.
|
@@ -177,17 +190,17 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
177
190
|
Hash => [0, 'json'],
|
178
191
|
Array => :get_array_type,
|
179
192
|
BinaryData => [1, 'bytea'],
|
180
|
-
}
|
193
|
+
}.merge(has_bigdecimal ? {BigDecimal => [0, 'numeric']} : {}))
|
194
|
+
private_constant :DEFAULT_TYPE_MAP
|
181
195
|
|
182
|
-
DEFAULT_ARRAY_TYPE_MAP = {
|
196
|
+
DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
|
183
197
|
TrueClass => [0, '_bool'],
|
184
198
|
FalseClass => [0, '_bool'],
|
185
199
|
Integer => [0, '_int8'],
|
186
200
|
String => [0, '_text'],
|
187
201
|
Float => [0, '_float8'],
|
188
|
-
BigDecimal => [0, '_numeric'],
|
189
202
|
Time => [0, '_timestamptz'],
|
190
203
|
IPAddr => [0, '_inet'],
|
191
|
-
}
|
192
|
-
|
204
|
+
}.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
|
205
|
+
private_constant :DEFAULT_ARRAY_TYPE_MAP
|
193
206
|
end
|
@@ -46,22 +46,45 @@ require 'pg' unless defined?( PG )
|
|
46
46
|
# This prints the rows with type casted columns:
|
47
47
|
# ["a", 123, [5, 4, 3]]
|
48
48
|
#
|
49
|
+
# Very similar with binary format:
|
50
|
+
#
|
51
|
+
# conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '2023-03-19 18:39:44'::TIMESTAMP)" )
|
52
|
+
#
|
53
|
+
# # Retrieve table OIDs per empty result set in binary format.
|
54
|
+
# res = conn.exec_params( "SELECT * FROM copytable LIMIT 0", [], 1 )
|
55
|
+
# # Build a type map for common database to ruby type decoders.
|
56
|
+
# btm = PG::BasicTypeMapForResults.new(conn)
|
57
|
+
# # Build a PG::TypeMapByColumn with decoders suitable for copytable.
|
58
|
+
# tm = btm.build_column_map( res )
|
59
|
+
# row_decoder = PG::BinaryDecoder::CopyRow.new type_map: tm
|
60
|
+
#
|
61
|
+
# conn.copy_data( "COPY copytable TO STDOUT WITH (FORMAT binary)", row_decoder ) do |res|
|
62
|
+
# while row=conn.get_copy_data
|
63
|
+
# p row
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
# This prints the rows with type casted columns:
|
67
|
+
# ["a", 123, 2023-03-19 18:39:44 UTC]
|
68
|
+
#
|
49
69
|
# See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
|
50
70
|
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
51
71
|
include PG::BasicTypeRegistry::Checker
|
52
72
|
|
53
73
|
class WarningTypeMap < PG::TypeMapInRuby
|
54
74
|
def initialize(typenames)
|
55
|
-
@already_warned =
|
75
|
+
@already_warned = {}
|
56
76
|
@typenames_by_oid = typenames
|
57
77
|
end
|
58
78
|
|
59
79
|
def typecast_result_value(result, _tuple, field)
|
60
80
|
format = result.fformat(field)
|
61
81
|
oid = result.ftype(field)
|
62
|
-
unless @already_warned
|
82
|
+
unless @already_warned.dig(format, oid)
|
63
83
|
warn "Warning: no type cast defined for type #{@typenames_by_oid[oid].inspect} format #{format} with oid #{oid}. Please cast this type explicitly to TEXT to be safe for future changes."
|
64
|
-
|
84
|
+
unless frozen?
|
85
|
+
@already_warned[format] ||= {}
|
86
|
+
@already_warned[format][oid] = true
|
87
|
+
end
|
65
88
|
end
|
66
89
|
super
|
67
90
|
end
|
@@ -39,7 +39,8 @@ class PG::BasicTypeRegistry
|
|
39
39
|
oid
|
40
40
|
bool
|
41
41
|
date timestamp timestamptz
|
42
|
-
].inject({}){|h,e| h[e] = true; h }
|
42
|
+
].inject({}){|h,e| h[e] = true; h }.freeze
|
43
|
+
private_constant :DONT_QUOTE_TYPES
|
43
44
|
|
44
45
|
def initialize(result, coders_by_name, format, arraycoder)
|
45
46
|
coder_map = {}
|
@@ -52,7 +53,7 @@ class PG::BasicTypeRegistry
|
|
52
53
|
coder.oid = row['oid'].to_i
|
53
54
|
coder.name = row['typname']
|
54
55
|
coder.format = format
|
55
|
-
coder_map[coder.oid] = coder
|
56
|
+
coder_map[coder.oid] = coder.freeze
|
56
57
|
end
|
57
58
|
|
58
59
|
if arraycoder
|
@@ -67,13 +68,14 @@ class PG::BasicTypeRegistry
|
|
67
68
|
coder.format = format
|
68
69
|
coder.elements_type = elements_coder
|
69
70
|
coder.needs_quotation = !DONT_QUOTE_TYPES[elements_coder.name]
|
70
|
-
coder_map[coder.oid] = coder
|
71
|
+
coder_map[coder.oid] = coder.freeze
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
74
|
-
@coders = coder_map.values
|
75
|
-
@coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }
|
76
|
-
@coders_by_oid = @coders.inject({}){|h, t| h[t.oid] = t; h }
|
75
|
+
@coders = coder_map.values.freeze
|
76
|
+
@coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }.freeze
|
77
|
+
@coders_by_oid = @coders.inject({}){|h, t| h[t.oid] = t; h }.freeze
|
78
|
+
freeze
|
77
79
|
end
|
78
80
|
|
79
81
|
attr_reader :coders
|
@@ -117,19 +119,24 @@ class PG::BasicTypeRegistry
|
|
117
119
|
JOIN pg_proc as ti ON ti.oid = t.typinput
|
118
120
|
SQL
|
119
121
|
|
122
|
+
init_maps(registry, result.freeze)
|
123
|
+
freeze
|
124
|
+
end
|
125
|
+
|
126
|
+
private def init_maps(registry, result)
|
120
127
|
@maps = [
|
121
128
|
[0, :encoder, PG::TextEncoder::Array],
|
122
129
|
[0, :decoder, PG::TextDecoder::Array],
|
123
|
-
[1, :encoder,
|
124
|
-
[1, :decoder,
|
130
|
+
[1, :encoder, PG::BinaryEncoder::Array],
|
131
|
+
[1, :decoder, PG::BinaryDecoder::Array],
|
125
132
|
].inject([]) do |h, (format, direction, arraycoder)|
|
126
133
|
coders = registry.coders_for(format, direction) || {}
|
127
134
|
h[format] ||= {}
|
128
135
|
h[format][direction] = CoderMap.new(result, coders, format, arraycoder)
|
129
136
|
h
|
130
|
-
end
|
137
|
+
end.each{|h| h.freeze }.freeze
|
131
138
|
|
132
|
-
@typenames_by_oid = result.inject({}){|h, t| h[t['oid'].to_i] = t['typname']; h }
|
139
|
+
@typenames_by_oid = result.inject({}){|h, t| h[t['oid'].to_i] = t['typname']; h }.freeze
|
133
140
|
end
|
134
141
|
|
135
142
|
def each_format(direction)
|
@@ -142,8 +149,9 @@ class PG::BasicTypeRegistry
|
|
142
149
|
end
|
143
150
|
|
144
151
|
module Checker
|
145
|
-
ValidFormats = { 0 => true, 1 => true }
|
146
|
-
ValidDirections = { :encoder => true, :decoder => true }
|
152
|
+
ValidFormats = { 0 => true, 1 => true }.freeze
|
153
|
+
ValidDirections = { :encoder => true, :decoder => true }.freeze
|
154
|
+
private_constant :ValidFormats, :ValidDirections
|
147
155
|
|
148
156
|
protected def check_format_and_direction(format, direction)
|
149
157
|
raise(ArgumentError, "Invalid format value %p" % format) unless ValidFormats[format]
|
@@ -155,7 +163,7 @@ class PG::BasicTypeRegistry
|
|
155
163
|
raise ArgumentError, "registry argument must be given to CoderMapsBundle" if registry
|
156
164
|
conn_or_maps
|
157
165
|
else
|
158
|
-
PG::BasicTypeRegistry::CoderMapsBundle.new(conn_or_maps, registry: registry)
|
166
|
+
PG::BasicTypeRegistry::CoderMapsBundle.new(conn_or_maps, registry: registry).freeze
|
159
167
|
end
|
160
168
|
end
|
161
169
|
end
|
@@ -163,7 +171,14 @@ class PG::BasicTypeRegistry
|
|
163
171
|
include Checker
|
164
172
|
|
165
173
|
def initialize
|
166
|
-
#
|
174
|
+
# @coders_by_name has a content of
|
175
|
+
# Array< Hash< Symbol: Hash< String: Coder > > >
|
176
|
+
#
|
177
|
+
# The layers are:
|
178
|
+
# * index of Array is 0 (text) and 1 (binary)
|
179
|
+
# * Symbol key in the middle Hash is :encoder and :decoder
|
180
|
+
# * String key in the inner Hash corresponds to the `typname` column in the table pg_type
|
181
|
+
# * Coder value in the inner Hash is the associated coder object
|
167
182
|
@coders_by_name = []
|
168
183
|
end
|
169
184
|
|
@@ -192,8 +207,8 @@ class PG::BasicTypeRegistry
|
|
192
207
|
# +name+ must correspond to the +typname+ column in the +pg_type+ table.
|
193
208
|
# +format+ can be 0 for text format and 1 for binary.
|
194
209
|
def register_type(format, name, encoder_class, decoder_class)
|
195
|
-
register_coder(encoder_class.new(name: name, format: format)) if encoder_class
|
196
|
-
register_coder(decoder_class.new(name: name, format: format)) if decoder_class
|
210
|
+
register_coder(encoder_class.new(name: name, format: format).freeze) if encoder_class
|
211
|
+
register_coder(decoder_class.new(name: name, format: format).freeze) if decoder_class
|
197
212
|
self
|
198
213
|
end
|
199
214
|
|
@@ -217,7 +232,11 @@ class PG::BasicTypeRegistry
|
|
217
232
|
alias_type 0, 'int8', 'int2'
|
218
233
|
alias_type 0, 'oid', 'int2'
|
219
234
|
|
220
|
-
|
235
|
+
begin
|
236
|
+
PG.require_bigdecimal_without_warning
|
237
|
+
register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
|
238
|
+
rescue LoadError
|
239
|
+
end
|
221
240
|
register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
|
222
241
|
alias_type 0, 'varchar', 'text'
|
223
242
|
alias_type 0, 'char', 'text'
|
@@ -232,9 +251,7 @@ class PG::BasicTypeRegistry
|
|
232
251
|
# alias_type 'uuid', 'text'
|
233
252
|
#
|
234
253
|
# register_type 'money', OID::Money.new
|
235
|
-
|
236
|
-
# in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
|
237
|
-
register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
|
254
|
+
register_type 0, 'bytea', PG::TextEncoder::Bytea, PG::TextDecoder::Bytea
|
238
255
|
register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
|
239
256
|
# register_type 'bit', OID::Bit.new
|
240
257
|
# register_type 'varbit', OID::Bit.new
|
@@ -242,6 +259,7 @@ class PG::BasicTypeRegistry
|
|
242
259
|
register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
|
243
260
|
alias_type 0, 'float8', 'float4'
|
244
261
|
|
262
|
+
# For compatibility reason the timestamp in text format is encoded as local time (TimestampWithoutTimeZone) instead of UTC
|
245
263
|
register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
|
246
264
|
register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
|
247
265
|
register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
|
@@ -260,6 +278,7 @@ class PG::BasicTypeRegistry
|
|
260
278
|
register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
|
261
279
|
alias_type 0, 'cidr', 'inet'
|
262
280
|
|
281
|
+
register_type 0, 'record', PG::TextEncoder::Record, PG::TextDecoder::Record
|
263
282
|
|
264
283
|
|
265
284
|
register_type 1, 'int2', PG::BinaryEncoder::Int2, PG::BinaryDecoder::Integer
|
@@ -276,26 +295,17 @@ class PG::BasicTypeRegistry
|
|
276
295
|
|
277
296
|
register_type 1, 'bytea', PG::BinaryEncoder::Bytea, PG::BinaryDecoder::Bytea
|
278
297
|
register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
|
279
|
-
register_type 1, 'float4',
|
280
|
-
register_type 1, 'float8',
|
281
|
-
register_type 1, 'timestamp',
|
282
|
-
register_type 1, 'timestamptz',
|
298
|
+
register_type 1, 'float4', PG::BinaryEncoder::Float4, PG::BinaryDecoder::Float
|
299
|
+
register_type 1, 'float8', PG::BinaryEncoder::Float8, PG::BinaryDecoder::Float
|
300
|
+
register_type 1, 'timestamp', PG::BinaryEncoder::TimestampUtc, PG::BinaryDecoder::TimestampUtc
|
301
|
+
register_type 1, 'timestamptz', PG::BinaryEncoder::TimestampUtc, PG::BinaryDecoder::TimestampUtcToLocal
|
302
|
+
register_type 1, 'date', PG::BinaryEncoder::Date, PG::BinaryDecoder::Date
|
283
303
|
|
284
304
|
self
|
285
305
|
end
|
286
306
|
|
287
307
|
alias define_default_types register_default_types
|
288
308
|
|
289
|
-
|
290
|
-
DEFAULT_TYPE_REGISTRY
|
291
|
-
|
292
|
-
# Delegate class method calls to DEFAULT_TYPE_REGISTRY
|
293
|
-
class << self
|
294
|
-
%i[ register_coder register_type alias_type ].each do |meth|
|
295
|
-
define_method(meth) do |*args|
|
296
|
-
warn "PG::BasicTypeRegistry.#{meth} is deprecated. Please use your own instance by PG::BasicTypeRegistry.new instead!"
|
297
|
-
DEFAULT_TYPE_REGISTRY.send(meth, *args)
|
298
|
-
end
|
299
|
-
end
|
300
|
-
end
|
309
|
+
DEFAULT_TYPE_REGISTRY = PG.make_shareable(PG::BasicTypeRegistry.new.register_default_types)
|
310
|
+
private_constant :DEFAULT_TYPE_REGISTRY
|
301
311
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module PG
|
5
|
+
module BinaryDecoder
|
6
|
+
# Convenience classes for timezone options
|
7
|
+
class TimestampUtc < Timestamp
|
8
|
+
def initialize(hash={}, **kwargs)
|
9
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) unless hash.empty?
|
10
|
+
super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
class TimestampUtcToLocal < Timestamp
|
14
|
+
def initialize(hash={}, **kwargs)
|
15
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) unless hash.empty?
|
16
|
+
super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
class TimestampLocal < Timestamp
|
20
|
+
def initialize(hash={}, **kwargs)
|
21
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) unless hash.empty?
|
22
|
+
super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end # module PG
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module PG
|
5
|
+
module BinaryEncoder
|
6
|
+
# Convenience classes for timezone options
|
7
|
+
class TimestampUtc < Timestamp
|
8
|
+
def initialize(hash={}, **kwargs)
|
9
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) unless hash.empty?
|
10
|
+
super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_UTC)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
class TimestampLocal < Timestamp
|
14
|
+
def initialize(hash={}, **kwargs)
|
15
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) unless hash.empty?
|
16
|
+
super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_LOCAL)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end # module PG
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pg' unless defined?( PG )
|
5
|
+
|
6
|
+
if defined?(PG::CancelConnection)
|
7
|
+
class PG::CancelConnection
|
8
|
+
include PG::Connection::Pollable
|
9
|
+
|
10
|
+
alias c_initialize initialize
|
11
|
+
|
12
|
+
def initialize(conn)
|
13
|
+
c_initialize(conn)
|
14
|
+
|
15
|
+
# A cancel connection is always to one destination server only.
|
16
|
+
# Prepare conninfo_hash with just enough information to allow a shared polling_loop.
|
17
|
+
@host = conn.host
|
18
|
+
@hostaddr = conn.hostaddr
|
19
|
+
@port = conn.port
|
20
|
+
|
21
|
+
@conninfo_hash = {
|
22
|
+
host: @host,
|
23
|
+
hostaddr: @hostaddr,
|
24
|
+
port: @port.to_s,
|
25
|
+
connect_timeout: conn.conninfo_hash[:connect_timeout],
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# call-seq:
|
30
|
+
# conn.cancel
|
31
|
+
#
|
32
|
+
# Requests that the server abandons processing of the current command in a blocking manner.
|
33
|
+
#
|
34
|
+
# If the cancel request wasn't successfully dispatched an error message is raised.
|
35
|
+
#
|
36
|
+
# Successful dispatch of the cancellation is no guarantee that the request will have any effect, however.
|
37
|
+
# If the cancellation is effective, the command being canceled will terminate early and raises an error.
|
38
|
+
# If the cancellation fails (say, because the server was already done processing the command), then there will be no visible result at all.
|
39
|
+
#
|
40
|
+
def cancel
|
41
|
+
start
|
42
|
+
polling_loop(:poll)
|
43
|
+
end
|
44
|
+
alias async_cancel cancel
|
45
|
+
|
46
|
+
# These private methods are there to allow a shared polling_loop.
|
47
|
+
private
|
48
|
+
attr_reader :host
|
49
|
+
attr_reader :hostaddr
|
50
|
+
attr_reader :port
|
51
|
+
attr_reader :conninfo_hash
|
52
|
+
end
|
53
|
+
end
|
data/lib/pg/coder.rb
CHANGED
@@ -6,22 +6,24 @@ module PG
|
|
6
6
|
class Coder
|
7
7
|
|
8
8
|
module BinaryFormatting
|
9
|
-
|
10
|
-
|
11
|
-
super(
|
9
|
+
def initialize(hash={}, **kwargs)
|
10
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) unless hash.empty?
|
11
|
+
super(format: 1, **hash, **kwargs)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
|
16
16
|
# Create a new coder object based on the attribute Hash.
|
17
|
-
def initialize(
|
18
|
-
|
17
|
+
def initialize(hash=nil, **kwargs)
|
18
|
+
warn("PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}", category: :deprecated) if hash
|
19
|
+
|
20
|
+
(hash || kwargs).each do |key, val|
|
19
21
|
send("#{key}=", val)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
def dup
|
24
|
-
self.class.new(to_h)
|
26
|
+
self.class.new(**to_h)
|
25
27
|
end
|
26
28
|
|
27
29
|
# Returns coder attributes as Hash.
|
@@ -43,7 +45,7 @@ module PG
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def marshal_load(str)
|
46
|
-
initialize
|
48
|
+
initialize(**Marshal.load(str))
|
47
49
|
end
|
48
50
|
|
49
51
|
def inspect
|
@@ -70,35 +72,37 @@ module PG
|
|
70
72
|
|
71
73
|
class CompositeCoder < Coder
|
72
74
|
def to_h
|
73
|
-
super
|
75
|
+
h = { **super,
|
74
76
|
elements_type: elements_type,
|
75
77
|
needs_quotation: needs_quotation?,
|
76
78
|
delimiter: delimiter,
|
77
|
-
}
|
79
|
+
}
|
80
|
+
h[:dimensions] = dimensions if dimensions # Write only when set, for Marshal compat with pg<1.6
|
81
|
+
h
|
78
82
|
end
|
79
83
|
|
80
84
|
def inspect
|
81
85
|
str = super
|
82
|
-
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation"
|
86
|
+
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation#{dimensions && " #{dimensions} dimensions"}"
|
83
87
|
str
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
87
91
|
class CopyCoder < Coder
|
88
92
|
def to_h
|
89
|
-
super
|
93
|
+
{ **super,
|
90
94
|
type_map: type_map,
|
91
95
|
delimiter: delimiter,
|
92
96
|
null_string: null_string,
|
93
|
-
}
|
97
|
+
}
|
94
98
|
end
|
95
99
|
end
|
96
100
|
|
97
101
|
class RecordCoder < Coder
|
98
102
|
def to_h
|
99
|
-
super
|
103
|
+
{ **super,
|
100
104
|
type_map: type_map,
|
101
|
-
}
|
105
|
+
}
|
102
106
|
end
|
103
107
|
end
|
104
108
|
end # module PG
|