pg 1.4.6 → 1.5.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 (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +1 -1
  4. data/.gitignore +3 -0
  5. data/History.md +61 -0
  6. data/README.ja.md +29 -19
  7. data/README.md +29 -15
  8. data/Rakefile.cross +1 -1
  9. data/ext/pg.c +10 -28
  10. data/ext/pg.h +10 -5
  11. data/ext/pg_binary_decoder.c +79 -0
  12. data/ext/pg_binary_encoder.c +224 -0
  13. data/ext/pg_coder.c +16 -7
  14. data/ext/pg_connection.c +50 -34
  15. data/ext/pg_copy_coder.c +306 -17
  16. data/ext/pg_record_coder.c +5 -4
  17. data/ext/pg_result.c +88 -17
  18. data/ext/pg_text_decoder.c +28 -10
  19. data/ext/pg_text_encoder.c +22 -9
  20. data/ext/pg_tuple.c +34 -31
  21. data/ext/pg_type_map.c +3 -2
  22. data/ext/pg_type_map_all_strings.c +2 -2
  23. data/ext/pg_type_map_by_class.c +5 -3
  24. data/ext/pg_type_map_by_column.c +9 -3
  25. data/ext/pg_type_map_by_oid.c +7 -4
  26. data/ext/pg_type_map_in_ruby.c +5 -2
  27. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  28. data/lib/pg/basic_type_map_for_queries.rb +13 -8
  29. data/lib/pg/basic_type_map_for_results.rb +26 -3
  30. data/lib/pg/basic_type_registry.rb +30 -32
  31. data/lib/pg/binary_decoder/date.rb +9 -0
  32. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  33. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  34. data/lib/pg/coder.rb +15 -13
  35. data/lib/pg/connection.rb +84 -12
  36. data/lib/pg/text_decoder/date.rb +18 -0
  37. data/lib/pg/text_decoder/inet.rb +9 -0
  38. data/lib/pg/text_decoder/json.rb +14 -0
  39. data/lib/pg/text_decoder/numeric.rb +9 -0
  40. data/lib/pg/text_decoder/timestamp.rb +30 -0
  41. data/lib/pg/text_encoder/date.rb +12 -0
  42. data/lib/pg/text_encoder/inet.rb +28 -0
  43. data/lib/pg/text_encoder/json.rb +14 -0
  44. data/lib/pg/text_encoder/numeric.rb +9 -0
  45. data/lib/pg/text_encoder/timestamp.rb +24 -0
  46. data/lib/pg/version.rb +1 -1
  47. data/lib/pg.rb +44 -9
  48. data/pg.gemspec +1 -1
  49. data/translation/po/all.pot +170 -135
  50. data/translation/po/ja.po +365 -186
  51. data/translation/po4a.cfg +4 -1
  52. data.tar.gz.sig +0 -0
  53. metadata +28 -10
  54. metadata.gz.sig +0 -0
  55. data/lib/pg/binary_decoder.rb +0 -23
  56. data/lib/pg/constants.rb +0 -12
  57. data/lib/pg/text_decoder.rb +0 -46
  58. data/lib/pg/text_encoder.rb +0 -59
@@ -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,6 +119,11 @@ 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],
@@ -127,9 +134,9 @@ class PG::BasicTypeRegistry
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
@@ -192,8 +200,8 @@ class PG::BasicTypeRegistry
192
200
  # +name+ must correspond to the +typname+ column in the +pg_type+ table.
193
201
  # +format+ can be 0 for text format and 1 for binary.
194
202
  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
203
+ register_coder(encoder_class.new(name: name, format: format).freeze) if encoder_class
204
+ register_coder(decoder_class.new(name: name, format: format).freeze) if decoder_class
197
205
  self
198
206
  end
199
207
 
@@ -232,9 +240,7 @@ class PG::BasicTypeRegistry
232
240
  # alias_type 'uuid', 'text'
233
241
  #
234
242
  # register_type 'money', OID::Money.new
235
- # There is no PG::TextEncoder::Bytea, because it's simple and more efficient to send bytea-data
236
- # in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
237
- register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
243
+ register_type 0, 'bytea', PG::TextEncoder::Bytea, PG::TextDecoder::Bytea
238
244
  register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
239
245
  # register_type 'bit', OID::Bit.new
240
246
  # register_type 'varbit', OID::Bit.new
@@ -242,6 +248,7 @@ class PG::BasicTypeRegistry
242
248
  register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
243
249
  alias_type 0, 'float8', 'float4'
244
250
 
251
+ # For compatibility reason the timestamp in text format is encoded as local time (TimestampWithoutTimeZone) instead of UTC
245
252
  register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
246
253
  register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
247
254
  register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
@@ -276,26 +283,17 @@ class PG::BasicTypeRegistry
276
283
 
277
284
  register_type 1, 'bytea', PG::BinaryEncoder::Bytea, PG::BinaryDecoder::Bytea
278
285
  register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
279
- register_type 1, 'float4', nil, PG::BinaryDecoder::Float
280
- register_type 1, 'float8', nil, PG::BinaryDecoder::Float
281
- register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtc
282
- register_type 1, 'timestamptz', nil, PG::BinaryDecoder::TimestampUtcToLocal
286
+ register_type 1, 'float4', PG::BinaryEncoder::Float4, PG::BinaryDecoder::Float
287
+ register_type 1, 'float8', PG::BinaryEncoder::Float8, PG::BinaryDecoder::Float
288
+ register_type 1, 'timestamp', PG::BinaryEncoder::TimestampUtc, PG::BinaryDecoder::TimestampUtc
289
+ register_type 1, 'timestamptz', PG::BinaryEncoder::TimestampUtc, PG::BinaryDecoder::TimestampUtcToLocal
290
+ register_type 1, 'date', PG::BinaryEncoder::Date, PG::BinaryDecoder::Date
283
291
 
284
292
  self
285
293
  end
286
294
 
287
295
  alias define_default_types register_default_types
288
296
 
289
- # @private
290
- DEFAULT_TYPE_REGISTRY = PG::BasicTypeRegistry.new.register_default_types
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
297
+ DEFAULT_TYPE_REGISTRY = PG.make_shareable(PG::BasicTypeRegistry.new.register_default_types)
298
+ private_constant :DEFAULT_TYPE_REGISTRY
301
299
  end
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module BinaryDecoder
6
+ # Init C part of the decoder
7
+ init_date
8
+ end
9
+ end # module PG
@@ -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}" 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}" 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}" 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}" 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}" unless hash.empty?
16
+ super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_LOCAL)
17
+ end
18
+ end
19
+ end
20
+ end # module PG
data/lib/pg/coder.rb CHANGED
@@ -6,22 +6,24 @@ module PG
6
6
  class Coder
7
7
 
8
8
  module BinaryFormatting
9
- Params = { format: 1 }
10
- def initialize( params={} )
11
- super(Params.merge(params))
9
+ def initialize(hash={}, **kwargs)
10
+ warn "PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}" 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(params={})
18
- params.each do |key, val|
17
+ def initialize(hash=nil, **kwargs)
18
+ warn "PG::Coder.new(hash) is deprecated. Please use keyword arguments instead! Called from #{caller.first}" 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 Marshal.load(str)
48
+ initialize(**Marshal.load(str))
47
49
  end
48
50
 
49
51
  def inspect
@@ -70,11 +72,11 @@ module PG
70
72
 
71
73
  class CompositeCoder < Coder
72
74
  def to_h
73
- super.merge!({
75
+ { **super,
74
76
  elements_type: elements_type,
75
77
  needs_quotation: needs_quotation?,
76
78
  delimiter: delimiter,
77
- })
79
+ }
78
80
  end
79
81
 
80
82
  def inspect
@@ -86,19 +88,19 @@ module PG
86
88
 
87
89
  class CopyCoder < Coder
88
90
  def to_h
89
- super.merge!({
91
+ { **super,
90
92
  type_map: type_map,
91
93
  delimiter: delimiter,
92
94
  null_string: null_string,
93
- })
95
+ }
94
96
  end
95
97
  end
96
98
 
97
99
  class RecordCoder < Coder
98
100
  def to_h
99
- super.merge!({
101
+ { **super,
100
102
  type_map: type_map,
101
- })
103
+ }
102
104
  end
103
105
  end
104
106
  end # module PG
data/lib/pg/connection.rb CHANGED
@@ -2,8 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'pg' unless defined?( PG )
5
- require 'uri'
6
- require 'io/wait'
5
+ require 'io/wait' unless ::IO.public_instance_methods(false).include?(:wait_readable)
7
6
  require 'socket'
8
7
 
9
8
  # The PostgreSQL connection class. The interface for this class is based on
@@ -31,8 +30,8 @@ require 'socket'
31
30
  class PG::Connection
32
31
 
33
32
  # The order the options are passed to the ::connect method.
34
- CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
35
-
33
+ CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password].freeze
34
+ private_constant :CONNECT_ARGUMENT_ORDER
36
35
 
37
36
  ### Quote a single +value+ for use in a connection-parameter string.
38
37
  def self.quote_connstr( value )
@@ -46,6 +45,10 @@ class PG::Connection
46
45
  hash.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
47
46
  end
48
47
 
48
+ # Shareable program name for Ractor
49
+ PROGRAM_NAME = $PROGRAM_NAME.dup.freeze
50
+ private_constant :PROGRAM_NAME
51
+
49
52
  # Parse the connection +args+ into a connection-parameter string.
50
53
  # See PG::Connection.new for valid arguments.
51
54
  #
@@ -63,8 +66,8 @@ class PG::Connection
63
66
  iopts = {}
64
67
 
65
68
  if args.length == 1
66
- case args.first
67
- when URI, /=/, /:\/\//
69
+ case args.first.to_s
70
+ when /=/, /:\/\//
68
71
  # Option or URL string style
69
72
  conn_string = args.first.to_s
70
73
  iopts = PG::Connection.conninfo_parse(conn_string).each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }
@@ -87,7 +90,7 @@ class PG::Connection
87
90
  iopts.merge!( hash_arg )
88
91
 
89
92
  if !iopts[:fallback_application_name]
90
- iopts[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
93
+ iopts[:fallback_application_name] = PROGRAM_NAME.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
91
94
  end
92
95
 
93
96
  return connect_hash_to_string(iopts)
@@ -114,6 +117,9 @@ class PG::Connection
114
117
  return str
115
118
  end
116
119
 
120
+ BinarySignature = "PGCOPY\n\377\r\n\0".b
121
+ private_constant :BinarySignature
122
+
117
123
  # call-seq:
118
124
  # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
119
125
  #
@@ -160,6 +166,14 @@ class PG::Connection
160
166
  # conn.put_copy_data ['more', 'data', 'to', 'copy']
161
167
  # end
162
168
  #
169
+ # Also PG::BinaryEncoder::CopyRow can be used to send data in binary format to the server.
170
+ # In this case copy_data generates the header and trailer data automatically:
171
+ # enco = PG::BinaryEncoder::CopyRow.new
172
+ # conn.copy_data "COPY my_table FROM STDIN (FORMAT binary)", enco do
173
+ # conn.put_copy_data ['some', 'data', 'to', 'copy']
174
+ # conn.put_copy_data ['more', 'data', 'to', 'copy']
175
+ # end
176
+ #
163
177
  # Example with CSV output format:
164
178
  # conn.copy_data "COPY my_table TO STDOUT CSV" do
165
179
  # while row=conn.get_copy_data
@@ -181,6 +195,18 @@ class PG::Connection
181
195
  # This receives all rows of +my_table+ as ruby array:
182
196
  # ["some", "data", "to", "copy"]
183
197
  # ["more", "data", "to", "copy"]
198
+ #
199
+ # Also PG::BinaryDecoder::CopyRow can be used to retrieve data in binary format from the server.
200
+ # In this case the header and trailer data is processed by the decoder and the remaining +nil+ from get_copy_data is processed by copy_data, so that binary data can be processed equally to text data:
201
+ # deco = PG::BinaryDecoder::CopyRow.new
202
+ # conn.copy_data "COPY my_table TO STDOUT (FORMAT binary)", deco do
203
+ # while row=conn.get_copy_data
204
+ # p row
205
+ # end
206
+ # end
207
+ # This receives all rows of +my_table+ as ruby array:
208
+ # ["some", "data", "to", "copy"]
209
+ # ["more", "data", "to", "copy"]
184
210
 
185
211
  def copy_data( sql, coder=nil )
186
212
  raise PG::NotInBlockingMode.new("copy_data can not be used in nonblocking mode", connection: self) if nonblocking?
@@ -189,10 +215,16 @@ class PG::Connection
189
215
  case res.result_status
190
216
  when PGRES_COPY_IN
191
217
  begin
218
+ if coder && res.binary_tuples == 1
219
+ # Binary file header (11 byte signature, 32 bit flags and 32 bit extension length)
220
+ put_copy_data(BinarySignature + ("\x00" * 8))
221
+ end
222
+
192
223
  if coder
193
224
  old_coder = self.encoder_for_put_copy_data
194
225
  self.encoder_for_put_copy_data = coder
195
226
  end
227
+
196
228
  yield res
197
229
  rescue Exception => err
198
230
  errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
@@ -205,6 +237,12 @@ class PG::Connection
205
237
  raise err
206
238
  else
207
239
  begin
240
+ self.encoder_for_put_copy_data = old_coder if coder
241
+
242
+ if coder && res.binary_tuples == 1
243
+ put_copy_data("\xFF\xFF") # Binary file trailer 16 bit "-1"
244
+ end
245
+
208
246
  put_copy_end
209
247
  rescue PG::Error => err
210
248
  raise PG::LostCopyState.new("#{err} (probably by executing another SQL query while running a COPY command)", connection: self)
@@ -226,6 +264,14 @@ class PG::Connection
226
264
  discard_results
227
265
  raise
228
266
  else
267
+ if coder && res.binary_tuples == 1
268
+ # There are two end markers in binary mode: file trailer and the final nil.
269
+ # The file trailer is expected to be processed by BinaryDecoder::CopyRow and already returns nil, so that the remaining NULL from PQgetCopyData is retrieved here:
270
+ if get_copy_data
271
+ discard_results
272
+ raise PG::NotAllCopyDataRetrieved.new("Not all binary COPY data retrieved", connection: self)
273
+ end
274
+ end
229
275
  res = get_last_result
230
276
  if !res
231
277
  discard_results
@@ -320,6 +366,23 @@ class PG::Connection
320
366
  end
321
367
  end
322
368
 
369
+ # Read all pending socket input to internal memory and raise an exception in case of errors.
370
+ #
371
+ # This verifies that the connection socket is in a usable state and not aborted in any way.
372
+ # No communication is done with the server.
373
+ # Only pending data is read from the socket - the method doesn't wait for any outstanding server answers.
374
+ #
375
+ # Raises a kind of PG::Error if there was an error reading the data or if the socket is in a failure state.
376
+ #
377
+ # The method doesn't verify that the server is still responding.
378
+ # To verify that the communication to the server works, it is recommended to use something like <tt>conn.exec('')</tt> instead.
379
+ def check_socket
380
+ while socket_io.wait_readable(0)
381
+ consume_input
382
+ end
383
+ nil
384
+ end
385
+
323
386
  # call-seq:
324
387
  # conn.get_result() -> PG::Result
325
388
  # conn.get_result() {|pg_result| block }
@@ -774,7 +837,10 @@ class PG::Connection
774
837
  # PG::Connection.ping(connection_string) -> Integer
775
838
  # PG::Connection.ping(host, port, options, tty, dbname, login, password) -> Integer
776
839
  #
777
- # Check server status.
840
+ # PQpingParams reports the status of the server.
841
+ #
842
+ # It accepts connection parameters identical to those of PQ::Connection.new .
843
+ # It is not necessary to supply correct user name, password, or database name values to obtain the server status; however, if incorrect values are provided, the server will log a failed connection attempt.
778
844
  #
779
845
  # See PG::Connection.new for a description of the parameters.
780
846
  #
@@ -787,6 +853,8 @@ class PG::Connection
787
853
  # could not establish connection
788
854
  # [+PQPING_NO_ATTEMPT+]
789
855
  # connection not attempted (bad params)
856
+ #
857
+ # See also check_socket for a way to check the connection without doing any server communication.
790
858
  def ping(*args)
791
859
  if Fiber.respond_to?(:scheduler) && Fiber.scheduler
792
860
  # Run PQping in a second thread to avoid blocking of the scheduler.
@@ -798,23 +866,25 @@ class PG::Connection
798
866
  end
799
867
  alias async_ping ping
800
868
 
801
- REDIRECT_CLASS_METHODS = {
869
+ REDIRECT_CLASS_METHODS = PG.make_shareable({
802
870
  :new => [:async_connect, :sync_connect],
803
871
  :connect => [:async_connect, :sync_connect],
804
872
  :open => [:async_connect, :sync_connect],
805
873
  :setdb => [:async_connect, :sync_connect],
806
874
  :setdblogin => [:async_connect, :sync_connect],
807
875
  :ping => [:async_ping, :sync_ping],
808
- }
876
+ })
877
+ private_constant :REDIRECT_CLASS_METHODS
809
878
 
810
879
  # These methods are affected by PQsetnonblocking
811
- REDIRECT_SEND_METHODS = {
880
+ REDIRECT_SEND_METHODS = PG.make_shareable({
812
881
  :isnonblocking => [:async_isnonblocking, :sync_isnonblocking],
813
882
  :nonblocking? => [:async_isnonblocking, :sync_isnonblocking],
814
883
  :put_copy_data => [:async_put_copy_data, :sync_put_copy_data],
815
884
  :put_copy_end => [:async_put_copy_end, :sync_put_copy_end],
816
885
  :flush => [:async_flush, :sync_flush],
817
- }
886
+ })
887
+ private_constant :REDIRECT_SEND_METHODS
818
888
  REDIRECT_METHODS = {
819
889
  :exec => [:async_exec, :sync_exec],
820
890
  :query => [:async_exec, :sync_exec],
@@ -832,12 +902,14 @@ class PG::Connection
832
902
  :client_encoding= => [:async_set_client_encoding, :sync_set_client_encoding],
833
903
  :cancel => [:async_cancel, :sync_cancel],
834
904
  }
905
+ private_constant :REDIRECT_METHODS
835
906
 
836
907
  if PG::Connection.instance_methods.include? :async_encrypt_password
837
908
  REDIRECT_METHODS.merge!({
838
909
  :encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
839
910
  })
840
911
  end
912
+ PG.make_shareable(REDIRECT_METHODS)
841
913
 
842
914
  def async_send_api=(enable)
843
915
  REDIRECT_SEND_METHODS.each do |ali, (async, sync)|
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'date'
5
+
6
+ module PG
7
+ module TextDecoder
8
+ class Date < SimpleDecoder
9
+ def decode(string, tuple=nil, field=nil)
10
+ if string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/
11
+ ::Date.new $1.to_i, $2.to_i, $3.to_i
12
+ else
13
+ string
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end # module PG
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module TextDecoder
6
+ # Init C part of the decoder
7
+ init_inet
8
+ end
9
+ end # module PG
@@ -0,0 +1,14 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+
6
+ module PG
7
+ module TextDecoder
8
+ class JSON < SimpleDecoder
9
+ def decode(string, tuple=nil, field=nil)
10
+ ::JSON.parse(string, quirks_mode: true)
11
+ end
12
+ end
13
+ end
14
+ end # module PG
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module TextDecoder
6
+ # Init C part of the decoder
7
+ init_numeric
8
+ end
9
+ end # module PG
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module TextDecoder
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}" 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}" 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}" unless hash.empty?
22
+ super(**hash, **kwargs, flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL)
23
+ end
24
+ end
25
+
26
+ # For backward compatibility:
27
+ TimestampWithoutTimeZone = TimestampLocal
28
+ TimestampWithTimeZone = Timestamp
29
+ end
30
+ end # module PG
@@ -0,0 +1,12 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module TextEncoder
6
+ class Date < SimpleEncoder
7
+ def encode(value)
8
+ value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : value
9
+ end
10
+ end
11
+ end
12
+ end # module PG
@@ -0,0 +1,28 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'ipaddr'
5
+
6
+ module PG
7
+ module TextEncoder
8
+ class Inet < SimpleEncoder
9
+ def encode(value)
10
+ case value
11
+ when IPAddr
12
+ default_prefix = (value.family == Socket::AF_INET ? 32 : 128)
13
+ s = value.to_s
14
+ if value.respond_to?(:prefix)
15
+ prefix = value.prefix
16
+ else
17
+ range = value.to_range
18
+ prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
19
+ end
20
+ s << "/" << prefix.to_s if prefix != default_prefix
21
+ s
22
+ else
23
+ value
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end # module PG
@@ -0,0 +1,14 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+
6
+ module PG
7
+ module TextEncoder
8
+ class JSON < SimpleEncoder
9
+ def encode(value)
10
+ ::JSON.generate(value, quirks_mode: true)
11
+ end
12
+ end
13
+ end
14
+ end # module PG
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module TextEncoder
6
+ # Init C part of the decoder
7
+ init_numeric
8
+ end
9
+ end # module PG