pg 1.5.4 → 1.5.8

Sign up to get free protection for your applications and to get access to all the features.
data/ext/pg_copy_coder.c CHANGED
@@ -212,6 +212,7 @@ pg_copycoder_type_map_get(VALUE self)
212
212
  *
213
213
  * See also PG::TextDecoder::CopyRow for the decoding direction with
214
214
  * PG::Connection#get_copy_data .
215
+ * And see PG::BinaryEncoder::CopyRow for an encoder of the COPY binary format.
215
216
  */
216
217
  static int
217
218
  pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
@@ -235,7 +236,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
235
236
  char *ptr1;
236
237
  char *ptr2;
237
238
  int strlen;
238
- int backslashs;
239
+ int backslashes;
239
240
  VALUE subint;
240
241
  VALUE entry;
241
242
 
@@ -286,19 +287,19 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
286
287
  ptr2 = current_out + strlen;
287
288
 
288
289
  /* count required backlashs */
289
- for(backslashs = 0; ptr1 != ptr2; ptr1++) {
290
+ for(backslashes = 0; ptr1 != ptr2; ptr1++) {
290
291
  /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
291
292
  if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
292
- backslashs++;
293
+ backslashes++;
293
294
  }
294
295
  }
295
296
 
296
297
  ptr1 = current_out + strlen;
297
- ptr2 = current_out + strlen + backslashs;
298
+ ptr2 = current_out + strlen + backslashes;
298
299
  current_out = ptr2;
299
300
 
300
301
  /* Then store the escaped string on the final position, walking
301
- * right to left, until all backslashs are placed. */
302
+ * right to left, until all backslashes are placed. */
302
303
  while( ptr1 != ptr2 ) {
303
304
  *--ptr2 = *--ptr1;
304
305
  if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
@@ -358,6 +359,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
358
359
  *
359
360
  * See also PG::BinaryDecoder::CopyRow for the decoding direction with
360
361
  * PG::Connection#get_copy_data .
362
+ * And see PG::TextEncoder::CopyRow for an encoder of the COPY text format.
361
363
  */
362
364
  static int
363
365
  pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
@@ -391,7 +393,7 @@ pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediat
391
393
 
392
394
  switch(TYPE(entry)){
393
395
  case T_NIL:
394
- /* 4 bytes for -1 indicationg a NULL value */
396
+ /* 4 bytes for -1 indicating a NULL value */
395
397
  PG_RB_STR_ENSURE_CAPA( *intermediate, 4, current_out, end_capa_ptr );
396
398
  write_nbo32(-1, current_out);
397
399
  current_out += 4;
@@ -496,6 +498,7 @@ GetDecimalFromHex(char hex)
496
498
  *
497
499
  * See also PG::TextEncoder::CopyRow for the encoding direction with
498
500
  * PG::Connection#put_copy_data .
501
+ * And see PG::BinaryDecoder::CopyRow for a decoder of the COPY binary format.
499
502
  */
500
503
  /*
501
504
  * Parse the current line into separate attributes (fields),
@@ -763,6 +766,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
763
766
  *
764
767
  * See also PG::BinaryEncoder::CopyRow for the encoding direction with
765
768
  * PG::Connection#put_copy_data .
769
+ * And see PG::TextDecoder::CopyRow for a decoder of the COPY text format.
766
770
  */
767
771
  static VALUE
768
772
  pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tuple, int _field, int enc_idx)
@@ -795,26 +799,26 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
795
799
  cur_ptr = input_line;
796
800
  line_end_ptr = input_line + len;
797
801
 
798
- if (cur_ptr + 11 <= line_end_ptr && memcmp(cur_ptr, BinarySignature, 11) == 0){
802
+ if (line_end_ptr - cur_ptr >= 11 && memcmp(cur_ptr, BinarySignature, 11) == 0){
799
803
  /* binary COPY header signature detected -> just drop it */
800
804
  int ext_bytes;
801
805
  cur_ptr += 11;
802
806
 
803
807
  /* read flags */
804
- if (cur_ptr + 4 > line_end_ptr) goto length_error;
808
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
805
809
  cur_ptr += 4;
806
810
 
807
811
  /* read header extensions */
808
- if (cur_ptr + 4 > line_end_ptr) goto length_error;
812
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
809
813
  ext_bytes = read_nbo32(cur_ptr);
810
814
  if (ext_bytes < 0) goto length_error;
811
815
  cur_ptr += 4;
812
- if (cur_ptr + ext_bytes > line_end_ptr) goto length_error;
816
+ if (line_end_ptr - cur_ptr < ext_bytes ) goto length_error;
813
817
  cur_ptr += ext_bytes;
814
818
  }
815
819
 
816
820
  /* read row header */
817
- if (cur_ptr + 2 > line_end_ptr) goto length_error;
821
+ if (line_end_ptr - cur_ptr < 2 ) goto length_error;
818
822
  nfields = read_nbo16(cur_ptr);
819
823
  cur_ptr += 2;
820
824
 
@@ -830,7 +834,7 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
830
834
  VALUE field_value;
831
835
 
832
836
  /* read field size */
833
- if (cur_ptr + 4 > line_end_ptr) goto length_error;
837
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
834
838
  input_len = read_nbo32(cur_ptr);
835
839
  cur_ptr += 4;
836
840
 
@@ -839,7 +843,7 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
839
843
  /* NULL indicator */
840
844
  rb_ary_push(array, Qnil);
841
845
  } else {
842
- if (cur_ptr + input_len > line_end_ptr) goto length_error;
846
+ if (line_end_ptr - cur_ptr < input_len ) goto length_error;
843
847
 
844
848
  /* copy input data to field_str */
845
849
  PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
@@ -198,7 +198,7 @@ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate
198
198
  char *ptr1;
199
199
  char *ptr2;
200
200
  long strlen;
201
- int backslashs;
201
+ int backslashes;
202
202
  VALUE subint;
203
203
  VALUE entry;
204
204
 
@@ -249,19 +249,19 @@ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate
249
249
  ptr2 = current_out + strlen;
250
250
 
251
251
  /* count required backlashs */
252
- for(backslashs = 0; ptr1 != ptr2; ptr1++) {
252
+ for(backslashes = 0; ptr1 != ptr2; ptr1++) {
253
253
  /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
254
254
  if(*ptr1 == '"' || *ptr1 == '\\'){
255
- backslashs++;
255
+ backslashes++;
256
256
  }
257
257
  }
258
258
 
259
259
  ptr1 = current_out + strlen;
260
- ptr2 = current_out + strlen + backslashs;
260
+ ptr2 = current_out + strlen + backslashes;
261
261
  current_out = ptr2;
262
262
 
263
263
  /* Then store the escaped string on the final position, walking
264
- * right to left, until all backslashs are placed. */
264
+ * right to left, until all backslashes are placed. */
265
265
  while( ptr1 != ptr2 ) {
266
266
  *--ptr2 = *--ptr1;
267
267
  if(*ptr1 == '"' || *ptr1 == '\\'){
@@ -340,7 +340,7 @@ record_isspace(char ch)
340
340
  * conn.exec("SELECT * FROM my_table").map_types!(PG::TypeMapByColumn.new([deco]*2)).to_a
341
341
  * # => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]
342
342
  *
343
- * It's more very convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.
343
+ * It's more convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.
344
344
  * # Fetch a NULL record of our type to retrieve the OIDs of the two fields "r" and "i"
345
345
  * oids = conn.exec( "SELECT (NULL::complex).*" )
346
346
  * # Build a type map (PG::TypeMapByColumn) for decoding the "complex" type
data/ext/pg_result.c CHANGED
@@ -664,7 +664,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
664
664
  * An example:
665
665
  *
666
666
  * begin
667
- * conn.exec( "SELECT * FROM nonexistant_table" )
667
+ * conn.exec( "SELECT * FROM nonexistent_table" )
668
668
  * rescue PG::Error => err
669
669
  * p [
670
670
  * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
@@ -684,7 +684,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
684
684
  *
685
685
  * Outputs:
686
686
  *
687
- * ["ERROR", "42P01", "relation \"nonexistant_table\" does not exist", nil, nil,
687
+ * ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
688
688
  * "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
689
689
  */
690
690
  static VALUE
@@ -163,6 +163,8 @@ pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int f
163
163
  * This is a decoder class for conversion of PostgreSQL numeric types
164
164
  * to Ruby BigDecimal objects.
165
165
  *
166
+ * As soon as this class is used, it requires the 'bigdecimal' gem.
167
+ *
166
168
  */
167
169
  static VALUE
168
170
  pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -174,7 +176,7 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
174
176
  static VALUE
175
177
  init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
176
178
  {
177
- rb_require("bigdecimal");
179
+ rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
178
180
  s_id_BigDecimal = rb_intern("BigDecimal");
179
181
 
180
182
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
@@ -811,6 +813,7 @@ static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, i
811
813
  * This is a decoder class for conversion of PostgreSQL inet type
812
814
  * to Ruby IPAddr values.
813
815
  *
816
+ * As soon as this class is used, it requires the ruby standard library 'ipaddr'.
814
817
  */
815
818
  static VALUE
816
819
  pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -119,6 +119,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
119
119
  int
120
120
  pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
121
121
  {
122
+ /* Attention:
123
+ * In contrast to all other encoders, the "this" pointer of this encoder can be NULL.
124
+ * This is because it is used as a fall-back if no encoder is defined.
125
+ */
122
126
  VALUE str = rb_obj_as_string(value);
123
127
  if( ENCODING_GET(str) == enc_idx ){
124
128
  *intermediate = str;
@@ -345,6 +349,8 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
345
349
  *
346
350
  * It converts Integer, Float and BigDecimal objects.
347
351
  * All other objects are expected to respond to +to_s+.
352
+ *
353
+ * As soon as this class is used, it requires the 'bigdecimal' gem.
348
354
  */
349
355
  static int
350
356
  pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
@@ -377,7 +383,7 @@ init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
377
383
  {
378
384
  s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
379
385
  rb_global_variable(&s_str_F);
380
- rb_require("bigdecimal");
386
+ rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
381
387
  s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
382
388
 
383
389
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
@@ -437,7 +443,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
437
443
  t_pg_composite_coder *this = _this;
438
444
  char *ptr1;
439
445
  char *ptr2;
440
- int backslashs = 0;
446
+ int backslashes = 0;
441
447
  int needquote;
442
448
 
443
449
  /* count data plus backslashes; detect chars needing quotes */
@@ -454,7 +460,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
454
460
 
455
461
  if (ch == '"' || ch == '\\'){
456
462
  needquote = 1;
457
- backslashs++;
463
+ backslashes++;
458
464
  } else if (ch == '{' || ch == '}' || ch == this->delimiter ||
459
465
  ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
460
466
  needquote = 1;
@@ -463,12 +469,12 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
463
469
 
464
470
  if( needquote ){
465
471
  ptr1 = p_in + strlen;
466
- ptr2 = p_out + strlen + backslashs + 2;
472
+ ptr2 = p_out + strlen + backslashes + 2;
467
473
  /* Write end quote */
468
474
  *--ptr2 = '"';
469
475
 
470
476
  /* Then store the escaped string on the final position, walking
471
- * right to left, until all backslashs are placed. */
477
+ * right to left, until all backslashes are placed. */
472
478
  while( ptr1 != p_in ) {
473
479
  *--ptr2 = *--ptr1;
474
480
  if(*ptr2 == '"' || *ptr2 == '\\'){
@@ -477,7 +483,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
477
483
  }
478
484
  /* Write start quote */
479
485
  *p_out = '"';
480
- return strlen + backslashs + 2;
486
+ return strlen + backslashes + 2;
481
487
  } else {
482
488
  if( p_in != p_out )
483
489
  memcpy( p_out, p_in, strlen );
@@ -692,22 +698,22 @@ static int
692
698
  quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
693
699
  char *ptr1;
694
700
  char *ptr2;
695
- int backslashs = 0;
701
+ int backslashes = 0;
696
702
 
697
703
  /* count required backlashs */
698
704
  for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
699
705
  if (*ptr1 == '\''){
700
- backslashs++;
706
+ backslashes++;
701
707
  }
702
708
  }
703
709
 
704
710
  ptr1 = p_in + strlen;
705
- ptr2 = p_out + strlen + backslashs + 2;
711
+ ptr2 = p_out + strlen + backslashes + 2;
706
712
  /* Write end quote */
707
713
  *--ptr2 = '\'';
708
714
 
709
715
  /* Then store the escaped string on the final position, walking
710
- * right to left, until all backslashs are placed. */
716
+ * right to left, until all backslashes are placed. */
711
717
  while( ptr1 != p_in ) {
712
718
  *--ptr2 = *--ptr1;
713
719
  if(*ptr2 == '\''){
@@ -716,7 +722,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
716
722
  }
717
723
  /* Write start quote */
718
724
  *p_out = '\'';
719
- return strlen + backslashs + 2;
725
+ return strlen + backslashes + 2;
720
726
  }
721
727
 
722
728
 
@@ -166,6 +166,12 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
166
166
  @textarray_encoder
167
167
  end
168
168
 
169
+ begin
170
+ PG.require_bigdecimal_without_warning
171
+ has_bigdecimal = true
172
+ rescue LoadError
173
+ end
174
+
169
175
  DEFAULT_TYPE_MAP = PG.make_shareable({
170
176
  TrueClass => [1, 'bool', 'bool'],
171
177
  FalseClass => [1, 'bool', 'bool'],
@@ -173,7 +179,6 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
173
179
  # to unnecessary type conversions on server side.
174
180
  Integer => [0, 'int8'],
175
181
  Float => [0, 'float8'],
176
- BigDecimal => [0, 'numeric'],
177
182
  Time => [0, 'timestamptz'],
178
183
  # We use text format and no type OID for IPAddr, because setting the OID can lead
179
184
  # to unnecessary inet/cidr conversions on the server side.
@@ -181,7 +186,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
181
186
  Hash => [0, 'json'],
182
187
  Array => :get_array_type,
183
188
  BinaryData => [1, 'bytea'],
184
- })
189
+ }.merge(has_bigdecimal ? {BigDecimal => [0, 'numeric']} : {}))
185
190
  private_constant :DEFAULT_TYPE_MAP
186
191
 
187
192
  DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
@@ -190,9 +195,8 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
190
195
  Integer => [0, '_int8'],
191
196
  String => [0, '_text'],
192
197
  Float => [0, '_float8'],
193
- BigDecimal => [0, '_numeric'],
194
198
  Time => [0, '_timestamptz'],
195
199
  IPAddr => [0, '_inet'],
196
- })
200
+ }.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
197
201
  private_constant :DEFAULT_ARRAY_TYPE_MAP
198
202
  end
@@ -171,7 +171,14 @@ class PG::BasicTypeRegistry
171
171
  include Checker
172
172
 
173
173
  def initialize
174
- # The key of these hashs maps to the `typname` column from the table pg_type.
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
175
182
  @coders_by_name = []
176
183
  end
177
184
 
@@ -225,7 +232,11 @@ class PG::BasicTypeRegistry
225
232
  alias_type 0, 'int8', 'int2'
226
233
  alias_type 0, 'oid', 'int2'
227
234
 
228
- register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
235
+ begin
236
+ PG.require_bigdecimal_without_warning
237
+ register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
238
+ rescue LoadError
239
+ end
229
240
  register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
230
241
  alias_type 0, 'varchar', 'text'
231
242
  alias_type 0, 'char', 'text'
@@ -267,6 +278,7 @@ class PG::BasicTypeRegistry
267
278
  register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
268
279
  alias_type 0, 'cidr', 'inet'
269
280
 
281
+ register_type 0, 'record', PG::TextEncoder::Record, PG::TextDecoder::Record
270
282
 
271
283
 
272
284
  register_type 1, 'int2', PG::BinaryEncoder::Int2, PG::BinaryDecoder::Integer
data/lib/pg/connection.rb CHANGED
@@ -166,7 +166,10 @@ class PG::Connection
166
166
  # conn.put_copy_data ['more', 'data', 'to', 'copy']
167
167
  # end
168
168
  #
169
- # Also PG::BinaryEncoder::CopyRow can be used to send data in binary format to the server.
169
+ # All 4 CopyRow classes can take a type map to specify how the columns are mapped to and from the database format.
170
+ # For details see the particular CopyRow class description.
171
+ #
172
+ # PG::BinaryEncoder::CopyRow can be used to send data in binary format to the server.
170
173
  # In this case copy_data generates the header and trailer data automatically:
171
174
  # enco = PG::BinaryEncoder::CopyRow.new
172
175
  # conn.copy_data "COPY my_table FROM STDIN (FORMAT binary)", enco do
@@ -306,6 +309,11 @@ class PG::Connection
306
309
  rollback = false
307
310
  exec "BEGIN"
308
311
  yield(self)
312
+ rescue PG::RollbackTransaction
313
+ rollback = true
314
+ cancel if transaction_status == PG::PQTRANS_ACTIVE
315
+ block
316
+ exec "ROLLBACK"
309
317
  rescue Exception
310
318
  rollback = true
311
319
  cancel if transaction_status == PG::PQTRANS_ACTIVE
@@ -493,7 +501,7 @@ class PG::Connection
493
501
  # See also #copy_data.
494
502
  #
495
503
  def put_copy_data(buffer, encoder=nil)
496
- # sync_put_copy_data does a non-blocking attept to flush data.
504
+ # sync_put_copy_data does a non-blocking attempt to flush data.
497
505
  until res=sync_put_copy_data(buffer, encoder)
498
506
  # It didn't flush immediately and allocation of more buffering memory failed.
499
507
  # Wait for all data sent by doing a blocking flush.
@@ -565,7 +573,14 @@ class PG::Connection
565
573
  # Resets the backend connection. This method closes the
566
574
  # backend connection and tries to re-connect.
567
575
  def reset
568
- reset_start
576
+ # Use connection options from PG::Connection.new to reconnect with the same options but with renewed DNS resolution.
577
+ # Use conninfo_hash as a fallback when connect_start was used to create the connection object.
578
+ iopts = @iopts_for_reset || conninfo_hash.compact
579
+ if iopts[:host] && !iopts[:host].empty? && PG.library_version >= 100000
580
+ iopts = self.class.send(:resolve_hosts, iopts)
581
+ end
582
+ conninfo = self.class.parse_connect_args( iopts );
583
+ reset_start2(conninfo)
569
584
  async_connect_or_reset(:reset_poll)
570
585
  self
571
586
  end
@@ -773,46 +788,51 @@ class PG::Connection
773
788
  alias setdb new
774
789
  alias setdblogin new
775
790
 
791
+ # Resolve DNS in Ruby to avoid blocking state while connecting.
792
+ # Multiple comma-separated values are generated, if the hostname resolves to both IPv4 and IPv6 addresses.
793
+ # This requires PostgreSQL-10+, so no DNS resolving is done on earlier versions.
794
+ private def resolve_hosts(iopts)
795
+ ihosts = iopts[:host].split(",", -1)
796
+ iports = iopts[:port].split(",", -1)
797
+ iports = [nil] if iports.size == 0
798
+ iports = iports * ihosts.size if iports.size == 1
799
+ raise PG::ConnectionBad, "could not match #{iports.size} port numbers to #{ihosts.size} hosts" if iports.size != ihosts.size
800
+
801
+ dests = ihosts.each_with_index.flat_map do |mhost, idx|
802
+ unless host_is_named_pipe?(mhost)
803
+ if Fiber.respond_to?(:scheduler) &&
804
+ Fiber.scheduler &&
805
+ RUBY_VERSION < '3.1.'
806
+
807
+ # Use a second thread to avoid blocking of the scheduler.
808
+ # `TCPSocket.gethostbyname` isn't fiber aware before ruby-3.1.
809
+ hostaddrs = Thread.new{ Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue [''] }.value
810
+ else
811
+ hostaddrs = Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue ['']
812
+ end
813
+ else
814
+ # No hostname to resolve (UnixSocket)
815
+ hostaddrs = [nil]
816
+ end
817
+ hostaddrs.map { |hostaddr| [hostaddr, mhost, iports[idx]] }
818
+ end
819
+ iopts.merge(
820
+ hostaddr: dests.map{|d| d[0] }.join(","),
821
+ host: dests.map{|d| d[1] }.join(","),
822
+ port: dests.map{|d| d[2] }.join(","))
823
+ end
824
+
776
825
  private def connect_to_hosts(*args)
777
826
  option_string = parse_connect_args(*args)
778
827
  iopts = PG::Connection.conninfo_parse(option_string).each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }
779
828
  iopts = PG::Connection.conndefaults.each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }.merge(iopts)
780
829
 
830
+ iopts_for_reset = iopts
781
831
  if iopts[:hostaddr]
782
832
  # hostaddr is provided -> no need to resolve hostnames
783
833
 
784
834
  elsif iopts[:host] && !iopts[:host].empty? && PG.library_version >= 100000
785
- # Resolve DNS in Ruby to avoid blocking state while connecting.
786
- # Multiple comma-separated values are generated, if the hostname resolves to both IPv4 and IPv6 addresses.
787
- # This requires PostgreSQL-10+, so no DNS resolving is done on earlier versions.
788
- ihosts = iopts[:host].split(",", -1)
789
- iports = iopts[:port].split(",", -1)
790
- iports = [nil] if iports.size == 0
791
- iports = iports * ihosts.size if iports.size == 1
792
- raise PG::ConnectionBad, "could not match #{iports.size} port numbers to #{ihosts.size} hosts" if iports.size != ihosts.size
793
-
794
- dests = ihosts.each_with_index.flat_map do |mhost, idx|
795
- unless host_is_named_pipe?(mhost)
796
- if Fiber.respond_to?(:scheduler) &&
797
- Fiber.scheduler &&
798
- RUBY_VERSION < '3.1.'
799
-
800
- # Use a second thread to avoid blocking of the scheduler.
801
- # `TCPSocket.gethostbyname` isn't fiber aware before ruby-3.1.
802
- hostaddrs = Thread.new{ Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue [''] }.value
803
- else
804
- hostaddrs = Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue ['']
805
- end
806
- else
807
- # No hostname to resolve (UnixSocket)
808
- hostaddrs = [nil]
809
- end
810
- hostaddrs.map { |hostaddr| [hostaddr, mhost, iports[idx]] }
811
- end
812
- iopts.merge!(
813
- hostaddr: dests.map{|d| d[0] }.join(","),
814
- host: dests.map{|d| d[1] }.join(","),
815
- port: dests.map{|d| d[2] }.join(","))
835
+ iopts = resolve_hosts(iopts)
816
836
  else
817
837
  # No host given
818
838
  end
@@ -821,6 +841,8 @@ class PG::Connection
821
841
 
822
842
  raise PG::ConnectionBad, conn.error_message if conn.status == PG::CONNECTION_BAD
823
843
 
844
+ # save the connection options for conn.reset
845
+ conn.instance_variable_set(:@iopts_for_reset, iopts_for_reset)
824
846
  conn.send(:async_connect_or_reset, :connect_poll)
825
847
  conn
826
848
  end
data/lib/pg/exceptions.rb CHANGED
@@ -21,5 +21,11 @@ module PG
21
21
  class NotInBlockingMode < PG::Error
22
22
  end
23
23
 
24
+ # PG::Connection#transaction uses this exception to distinguish a deliberate rollback from other exceptional situations.
25
+ # Normally, raising an exception will cause the .transaction method to rollback the database transaction and pass on the exception.
26
+ # But if you raise an PG::RollbackTransaction exception, then the database transaction will be rolled back, without passing on the exception.
27
+ class RollbackTransaction < StandardError
28
+ end
29
+
24
30
  end # module PG
25
31
 
@@ -5,6 +5,9 @@ require 'date'
5
5
 
6
6
  module PG
7
7
  module TextDecoder
8
+ # This is a decoder class for conversion of PostgreSQL date type to Ruby Date values.
9
+ #
10
+ # As soon as this class is used, it requires the ruby standard library 'date'.
8
11
  class Date < SimpleDecoder
9
12
  def decode(string, tuple=nil, field=nil)
10
13
  if string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/
@@ -5,6 +5,9 @@ require 'json'
5
5
 
6
6
  module PG
7
7
  module TextDecoder
8
+ # This is a decoder class for conversion of PostgreSQL JSON/JSONB type to Ruby Hash, Array, String, Numeric, nil values.
9
+ #
10
+ # As soon as this class is used, it requires the ruby standard library 'json'.
8
11
  class JSON < SimpleDecoder
9
12
  def decode(string, tuple=nil, field=nil)
10
13
  ::JSON.parse(string, quirks_mode: true)
@@ -3,6 +3,7 @@
3
3
 
4
4
  module PG
5
5
  module TextEncoder
6
+ # This is a encoder class for conversion of Ruby Date values to PostgreSQL date type.
6
7
  class Date < SimpleEncoder
7
8
  def encode(value)
8
9
  value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : value
@@ -5,6 +5,9 @@ require 'ipaddr'
5
5
 
6
6
  module PG
7
7
  module TextEncoder
8
+ # This is a encoder class for conversion of Ruby IPAddr values to PostgreSQL inet type.
9
+ #
10
+ # As soon as this class is used, it requires the ruby standard library 'ipaddr'.
8
11
  class Inet < SimpleEncoder
9
12
  def encode(value)
10
13
  case value
@@ -5,6 +5,9 @@ require 'json'
5
5
 
6
6
  module PG
7
7
  module TextEncoder
8
+ # This is a encoder class for conversion of Ruby Hash, Array, String, Numeric, nil values to PostgreSQL JSON/JSONB type.
9
+ #
10
+ # As soon as this class is used, it requires the ruby standard library 'json'.
8
11
  class JSON < SimpleEncoder
9
12
  def encode(value)
10
13
  ::JSON.generate(value, quirks_mode: true)
data/lib/pg/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module PG
2
2
  # Library version
3
- VERSION = '1.5.4'
3
+ VERSION = '1.5.8'
4
4
  end
data/lib/pg.rb CHANGED
@@ -126,4 +126,14 @@ module PG
126
126
  Warning.extend(TruffleFixWarn)
127
127
  end
128
128
 
129
+ # Ruby-3.4+ prints a warning, if bigdecimal is required but not in the Gemfile.
130
+ # But it's a false positive, since we enable bigdecimal depending features only if it's available.
131
+ # And most people don't need these features.
132
+ def self.require_bigdecimal_without_warning
133
+ oldverb, $VERBOSE = $VERBOSE, nil
134
+ require "bigdecimal"
135
+ ensure
136
+ $VERBOSE = oldverb
137
+ end
138
+
129
139
  end # module PG
data/pg.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
25
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features|translation)/}) }
27
27
  end
28
28
  spec.extensions = ["ext/extconf.rb"]
29
29
  spec.require_paths = ["lib"]
data.tar.gz.sig CHANGED
Binary file