pg 1.5.4 → 1.6.3

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 (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +132 -4
  4. data/Gemfile +15 -6
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +4 -4
  7. data/README.md +66 -23
  8. data/Rakefile +81 -14
  9. data/certs/kanis@comcard.de.pem +20 -0
  10. data/certs/larskanis-2024.pem +24 -0
  11. data/ext/errorcodes.def +11 -3
  12. data/ext/errorcodes.rb +1 -1
  13. data/ext/errorcodes.txt +9 -5
  14. data/ext/extconf.rb +192 -15
  15. data/ext/gvl_wrappers.c +13 -2
  16. data/ext/gvl_wrappers.h +33 -0
  17. data/ext/pg.c +17 -6
  18. data/ext/pg.h +15 -13
  19. data/ext/pg_binary_decoder.c +153 -1
  20. data/ext/pg_binary_encoder.c +213 -10
  21. data/ext/pg_cancel_connection.c +360 -0
  22. data/ext/pg_coder.c +54 -5
  23. data/ext/pg_connection.c +431 -169
  24. data/ext/pg_copy_coder.c +19 -15
  25. data/ext/pg_record_coder.c +7 -7
  26. data/ext/pg_result.c +106 -54
  27. data/ext/pg_text_decoder.c +5 -2
  28. data/ext/pg_text_encoder.c +39 -20
  29. data/ext/pg_tuple.c +8 -8
  30. data/ext/pg_type_map.c +4 -2
  31. data/ext/pg_type_map_all_strings.c +1 -1
  32. data/ext/pg_type_map_by_class.c +1 -1
  33. data/ext/pg_type_map_by_column.c +2 -1
  34. data/ext/pg_type_map_by_mri_type.c +1 -1
  35. data/ext/pg_type_map_by_oid.c +3 -1
  36. data/ext/pg_type_map_in_ruby.c +1 -1
  37. data/ext/pg_util.c +2 -2
  38. data/ext/pg_util.h +2 -2
  39. data/lib/pg/basic_type_map_for_queries.rb +15 -7
  40. data/lib/pg/basic_type_registry.rb +16 -4
  41. data/lib/pg/cancel_connection.rb +53 -0
  42. data/lib/pg/coder.rb +4 -2
  43. data/lib/pg/connection.rb +310 -167
  44. data/lib/pg/exceptions.rb +6 -0
  45. data/lib/pg/text_decoder/date.rb +3 -0
  46. data/lib/pg/text_decoder/json.rb +3 -0
  47. data/lib/pg/text_encoder/date.rb +1 -0
  48. data/lib/pg/text_encoder/inet.rb +3 -0
  49. data/lib/pg/text_encoder/json.rb +3 -0
  50. data/lib/pg/version.rb +2 -1
  51. data/lib/pg.rb +156 -120
  52. data/misc/glibc/Dockerfile +20 -0
  53. data/misc/glibc/docker-compose.yml +9 -0
  54. data/misc/glibc/glibc_spec.rb +5 -0
  55. data/misc/yugabyte/Dockerfile +9 -0
  56. data/misc/yugabyte/docker-compose.yml +28 -0
  57. data/misc/yugabyte/pg-test.rb +45 -0
  58. data/pg.gemspec +8 -4
  59. data/ports/patches/krb5/1.22.1/0001-Allow-static-linking-krb5-library.patch +30 -0
  60. data/ports/patches/krb5/1.22.1/0002-unknown-command-line-option-on-clang.patch +12 -0
  61. data/ports/patches/openssl/3.5.2/0001-aarch64-mingw.patch +21 -0
  62. data/ports/patches/postgresql/18.1/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  63. data/ports/patches/postgresql/18.1/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  64. data/rakelib/pg_gem_helper.rb +64 -0
  65. data.tar.gz.sig +0 -0
  66. metadata +49 -47
  67. metadata.gz.sig +0 -0
  68. data/.appveyor.yml +0 -42
  69. data/.gems +0 -6
  70. data/.gemtest +0 -0
  71. data/.github/workflows/binary-gems.yml +0 -117
  72. data/.github/workflows/source-gem.yml +0 -141
  73. data/.gitignore +0 -22
  74. data/.hgsigs +0 -34
  75. data/.hgtags +0 -41
  76. data/.irbrc +0 -23
  77. data/.pryrc +0 -23
  78. data/.tm_properties +0 -21
  79. data/.travis.yml +0 -49
  80. data/Manifest.txt +0 -72
  81. data/Rakefile.cross +0 -298
  82. data/translation/.po4a-version +0 -7
  83. data/translation/po/all.pot +0 -936
  84. data/translation/po/ja.po +0 -1036
  85. data/translation/po4a.cfg +0 -12
@@ -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;
@@ -227,7 +231,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
227
231
  *
228
232
  */
229
233
  static int
230
- pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
234
+ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate1, int enc_idx)
231
235
  {
232
236
  if(out){
233
237
  double dvalue = NUM2DBL(value);
@@ -235,7 +239,6 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
235
239
  int neg = 0;
236
240
  int exp2i, exp10i, i;
237
241
  unsigned long long ll, remainder, oldval;
238
- VALUE intermediate;
239
242
 
240
243
  /* Cast to the same strings as value.to_s . */
241
244
  if( isinf(dvalue) ){
@@ -279,6 +282,7 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
279
282
 
280
283
  if( exp10i <= -5 || exp10i >= 15 ) {
281
284
  /* Write the float in exponent format (1.23e45) */
285
+ VALUE intermediate;
282
286
 
283
287
  /* write fraction digits from right to left */
284
288
  for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
@@ -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 );
@@ -531,7 +537,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
531
537
  }
532
538
 
533
539
  static char *
534
- write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
540
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx, int dimension)
535
541
  {
536
542
  int i;
537
543
 
@@ -539,6 +545,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
539
545
  current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
540
546
  *current_out++ = '{';
541
547
 
548
+ if( RARRAY_LEN(value) == 0 && this->dimensions >= 0 && dimension != this->dimensions ){
549
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
550
+ }
551
+
542
552
  for( i=0; i<RARRAY_LEN(value); i++){
543
553
  VALUE entry = rb_ary_entry(value, i);
544
554
 
@@ -548,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
548
558
  }
549
559
 
550
560
  switch(TYPE(entry)){
551
- case T_ARRAY:
552
- current_out = write_array(this, entry, current_out, string, quote, enc_idx);
553
- break;
554
561
  case T_NIL:
562
+ if( this->dimensions >= 0 && dimension != this->dimensions ){
563
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
564
+ }
555
565
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
556
566
  *current_out++ = 'N';
557
567
  *current_out++ = 'U';
558
568
  *current_out++ = 'L';
559
569
  *current_out++ = 'L';
560
570
  break;
571
+ case T_ARRAY:
572
+ if( this->dimensions < 0 || dimension < this->dimensions ){
573
+ current_out = write_array(this, entry, current_out, string, quote, enc_idx, dimension+1);
574
+ break;
575
+ }
576
+ /* Number of dimensions reached -> handle array as normal value */
561
577
  default:
578
+ if( this->dimensions >= 0 && dimension != this->dimensions ){
579
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
580
+ }
562
581
  current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
563
582
  }
564
583
  }
@@ -590,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
590
609
  VALUE out_str = rb_str_new(NULL, 0);
591
610
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
592
611
 
593
- end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
612
+ end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx, 1);
594
613
 
595
614
  rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
596
615
  *intermediate = out_str;
@@ -692,22 +711,22 @@ static int
692
711
  quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
693
712
  char *ptr1;
694
713
  char *ptr2;
695
- int backslashs = 0;
714
+ int backslashes = 0;
696
715
 
697
716
  /* count required backlashs */
698
717
  for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
699
718
  if (*ptr1 == '\''){
700
- backslashs++;
719
+ backslashes++;
701
720
  }
702
721
  }
703
722
 
704
723
  ptr1 = p_in + strlen;
705
- ptr2 = p_out + strlen + backslashs + 2;
724
+ ptr2 = p_out + strlen + backslashes + 2;
706
725
  /* Write end quote */
707
726
  *--ptr2 = '\'';
708
727
 
709
728
  /* Then store the escaped string on the final position, walking
710
- * right to left, until all backslashs are placed. */
729
+ * right to left, until all backslashes are placed. */
711
730
  while( ptr1 != p_in ) {
712
731
  *--ptr2 = *--ptr1;
713
732
  if(*ptr2 == '\''){
@@ -716,7 +735,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
716
735
  }
717
736
  /* Write start quote */
718
737
  *p_out = '\'';
719
- return strlen + backslashs + 2;
738
+ return strlen + backslashes + 2;
720
739
  }
721
740
 
722
741
 
@@ -765,7 +784,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
765
784
  if(out){
766
785
  /* Second encoder pass, if required */
767
786
  strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
768
- base64_encode( out, out, strlen );
787
+ rbpg_base64_encode( out, out, strlen );
769
788
 
770
789
  return BASE64_ENCODED_SIZE(strlen);
771
790
  } else {
@@ -780,7 +799,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
780
799
  out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
781
800
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
782
801
 
783
- base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
802
+ rbpg_base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
784
803
  *intermediate = out_str;
785
804
 
786
805
  return -1;
data/ext/pg_tuple.c CHANGED
@@ -125,7 +125,7 @@ static const rb_data_type_t pg_tuple_type = {
125
125
  pg_tuple_gc_mark,
126
126
  pg_tuple_gc_free,
127
127
  pg_tuple_memsize,
128
- pg_compact_callback(pg_tuple_gc_compact),
128
+ pg_tuple_gc_compact,
129
129
  },
130
130
  0, 0,
131
131
  RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
@@ -135,7 +135,7 @@ static const rb_data_type_t pg_tuple_type = {
135
135
  * Document-method: allocate
136
136
  *
137
137
  * call-seq:
138
- * PG::VeryTuple.allocate -> obj
138
+ * PG::Tuple.allocate -> obj
139
139
  */
140
140
  static VALUE
141
141
  pg_tuple_s_allocate( VALUE klass )
@@ -242,10 +242,10 @@ pg_tuple_materialize(VALUE self)
242
242
  * An integer +key+ is interpreted as column index.
243
243
  * Negative values of index count from the end of the array.
244
244
  *
245
- * Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
245
+ * Depending on PG::Result#field_name_type= a string or symbol +key+ is interpreted as column name.
246
246
  *
247
247
  * If the key can't be found, there are several options:
248
- * With no other arguments, it will raise a IndexError exception;
248
+ * With no other arguments, it will raise a +IndexError+ exception;
249
249
  * if default is given, then that will be returned;
250
250
  * if the optional code block is specified, then that will be run and its result returned.
251
251
  */
@@ -302,7 +302,7 @@ pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
302
302
  * An integer +key+ is interpreted as column index.
303
303
  * Negative values of index count from the end of the array.
304
304
  *
305
- * Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
305
+ * Depending on PG::Result#field_name_type= a string or symbol +key+ is interpreted as column name.
306
306
  *
307
307
  * If the key can't be found, it returns +nil+ .
308
308
  */
@@ -405,7 +405,7 @@ pg_tuple_each_value(VALUE self)
405
405
  * tup.values -> Array
406
406
  *
407
407
  * Returns the values of this tuple as Array.
408
- * +res.tuple(i).values+ is equal to +res.tuple_values(i)+ .
408
+ * <tt>res.tuple(i).values</tt> is equal to <tt>res.tuple_values(i)</tt> .
409
409
  */
410
410
  static VALUE
411
411
  pg_tuple_values(VALUE self)
@@ -474,7 +474,7 @@ pg_tuple_dump(VALUE self)
474
474
  values = rb_ary_new4(this->num_fields, &this->values[0]);
475
475
  a = rb_ary_new3(2, field_names, values);
476
476
 
477
- rb_copy_generic_ivar(a, self);
477
+ rb_copy_generic_ivar(a, self);
478
478
 
479
479
  return a;
480
480
  }
@@ -510,7 +510,7 @@ pg_tuple_load(VALUE self, VALUE a)
510
510
  if (RARRAY_LENINT(field_names) != num_fields)
511
511
  rb_raise(rb_eTypeError, "different number of fields and values");
512
512
 
513
- field_map = rb_hash_new();
513
+ field_map = rb_hash_new_capa(num_fields);
514
514
  for( i = 0; i < num_fields; i++ ){
515
515
  rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
516
516
  }
data/ext/pg_type_map.c CHANGED
@@ -33,7 +33,7 @@ const rb_data_type_t pg_typemap_type = {
33
33
  pg_typemap_mark,
34
34
  RUBY_TYPED_DEFAULT_FREE,
35
35
  pg_typemap_memsize,
36
- pg_compact_callback(pg_typemap_compact),
36
+ pg_typemap_compact,
37
37
  },
38
38
  0,
39
39
  0,
@@ -187,7 +187,9 @@ init_pg_type_map(void)
187
187
  *
188
188
  * This is the base class for type maps.
189
189
  * See derived classes for implementations of different type cast strategies
190
- * ( PG::TypeMapByColumn, PG::TypeMapByOid ).
190
+ * ( PG::TypeMapByColumn, PG::TypeMapByOid, etc.).
191
+ *
192
+ * Find more type maps in the {README}[rdoc-ref:README.md@Type+Casts].
191
193
  *
192
194
  */
193
195
  rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
@@ -14,7 +14,7 @@ static const rb_data_type_t pg_tmas_type = {
14
14
  pg_typemap_mark,
15
15
  RUBY_TYPED_DEFAULT_FREE,
16
16
  pg_typemap_memsize,
17
- pg_compact_callback(pg_typemap_compact),
17
+ pg_typemap_compact,
18
18
  },
19
19
  &pg_typemap_type,
20
20
  0,
@@ -153,7 +153,7 @@ static const rb_data_type_t pg_tmbk_type = {
153
153
  pg_tmbk_mark,
154
154
  RUBY_TYPED_DEFAULT_FREE,
155
155
  pg_tmbk_memsize,
156
- pg_compact_callback(pg_tmbk_compact),
156
+ pg_tmbk_compact,
157
157
  },
158
158
  &pg_typemap_type,
159
159
  0,
@@ -54,6 +54,7 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
54
54
  t_tmbc *this = RTYPEDDATA_DATA( self );
55
55
  t_typemap *default_tm;
56
56
 
57
+ Check_Type(params, T_ARRAY);
57
58
  nfields = (int)RARRAY_LEN( params );
58
59
  if ( this->nfields != nfields ) {
59
60
  rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
@@ -228,7 +229,7 @@ static const rb_data_type_t pg_tmbc_type = {
228
229
  pg_tmbc_mark,
229
230
  pg_tmbc_free,
230
231
  pg_tmbc_memsize,
231
- pg_compact_callback(pg_tmbc_compact),
232
+ pg_tmbc_compact,
232
233
  },
233
234
  &pg_typemap_type,
234
235
  0,
@@ -130,7 +130,7 @@ static const rb_data_type_t pg_tmbmt_type = {
130
130
  pg_tmbmt_mark,
131
131
  RUBY_TYPED_DEFAULT_FREE,
132
132
  pg_tmbmt_memsize,
133
- pg_compact_callback(pg_tmbmt_compact),
133
+ pg_tmbmt_compact,
134
134
  },
135
135
  &pg_typemap_type,
136
136
  0,
@@ -190,7 +190,7 @@ static const rb_data_type_t pg_tmbo_type = {
190
190
  pg_tmbo_mark,
191
191
  RUBY_TYPED_DEFAULT_FREE,
192
192
  pg_tmbo_memsize,
193
- pg_compact_callback(pg_tmbo_compact),
193
+ pg_tmbo_compact,
194
194
  },
195
195
  &pg_typemap_type,
196
196
  0,
@@ -315,6 +315,8 @@ pg_tmbo_coders( VALUE self )
315
315
  * The type map will do Hash lookups for each result value, if the number of rows
316
316
  * is below or equal +number+.
317
317
  *
318
+ * Default is 10.
319
+ *
318
320
  */
319
321
  static VALUE
320
322
  pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
@@ -40,7 +40,7 @@ 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
- pg_compact_callback(pg_tmir_compact),
43
+ pg_tmir_compact,
44
44
  },
45
45
  &pg_typemap_type,
46
46
  0,
data/ext/pg_util.c CHANGED
@@ -15,7 +15,7 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
15
15
  * in-place (with _out_ == _in_).
16
16
  */
17
17
  void
18
- base64_encode( char *out, const char *in, int len)
18
+ rbpg_base64_encode( char *out, const char *in, int len)
19
19
  {
20
20
  const unsigned char *in_ptr = (const unsigned char *)in + len;
21
21
  char *out_ptr = out + BASE64_ENCODED_SIZE(len);
@@ -72,7 +72,7 @@ static const unsigned char base64_decode_table[] =
72
72
  * It is possible to decode a string in-place (with _out_ == _in_).
73
73
  */
74
74
  int
75
- base64_decode( char *out, const char *in, unsigned int len)
75
+ rbpg_base64_decode( char *out, const char *in, unsigned int len)
76
76
  {
77
77
  unsigned char a, b, c, d;
78
78
  const unsigned char *in_ptr = (const unsigned char *)in;
data/ext/pg_util.h CHANGED
@@ -57,8 +57,8 @@
57
57
  #define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
58
58
  #define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
59
59
 
60
- void base64_encode( char *out, const char *in, int len);
61
- int base64_decode( char *out, const char *in, unsigned int len);
60
+ void rbpg_base64_encode( char *out, const char *in, int len);
61
+ int rbpg_base64_decode( char *out, const char *in, unsigned int len);
62
62
 
63
63
  int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
64
64
 
@@ -53,14 +53,18 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
53
53
  @coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
54
54
  @array_encoders_by_klass = array_encoders_by_klass
55
55
  @encode_array_as = :array
56
- @if_undefined = if_undefined || method(:raise_undefined_type).to_proc
56
+ @if_undefined = if_undefined || UndefinedDefault
57
57
  init_encoders
58
58
  end
59
59
 
60
- private def raise_undefined_type(oid_name, format)
61
- raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
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
62
64
  end
63
65
 
66
+ private_constant :UndefinedDefault
67
+
64
68
  # Change the mechanism that is used to encode ruby array values
65
69
  #
66
70
  # Possible values:
@@ -166,6 +170,12 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
166
170
  @textarray_encoder
167
171
  end
168
172
 
173
+ begin
174
+ PG.require_bigdecimal_without_warning
175
+ has_bigdecimal = true
176
+ rescue LoadError
177
+ end
178
+
169
179
  DEFAULT_TYPE_MAP = PG.make_shareable({
170
180
  TrueClass => [1, 'bool', 'bool'],
171
181
  FalseClass => [1, 'bool', 'bool'],
@@ -173,7 +183,6 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
173
183
  # to unnecessary type conversions on server side.
174
184
  Integer => [0, 'int8'],
175
185
  Float => [0, 'float8'],
176
- BigDecimal => [0, 'numeric'],
177
186
  Time => [0, 'timestamptz'],
178
187
  # We use text format and no type OID for IPAddr, because setting the OID can lead
179
188
  # to unnecessary inet/cidr conversions on the server side.
@@ -181,7 +190,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
181
190
  Hash => [0, 'json'],
182
191
  Array => :get_array_type,
183
192
  BinaryData => [1, 'bytea'],
184
- })
193
+ }.merge(has_bigdecimal ? {BigDecimal => [0, 'numeric']} : {}))
185
194
  private_constant :DEFAULT_TYPE_MAP
186
195
 
187
196
  DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
@@ -190,9 +199,8 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
190
199
  Integer => [0, '_int8'],
191
200
  String => [0, '_text'],
192
201
  Float => [0, '_float8'],
193
- BigDecimal => [0, '_numeric'],
194
202
  Time => [0, '_timestamptz'],
195
203
  IPAddr => [0, '_inet'],
196
- })
204
+ }.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
197
205
  private_constant :DEFAULT_ARRAY_TYPE_MAP
198
206
  end
@@ -127,8 +127,8 @@ class PG::BasicTypeRegistry
127
127
  @maps = [
128
128
  [0, :encoder, PG::TextEncoder::Array],
129
129
  [0, :decoder, PG::TextDecoder::Array],
130
- [1, :encoder, nil],
131
- [1, :decoder, nil],
130
+ [1, :encoder, PG::BinaryEncoder::Array],
131
+ [1, :decoder, PG::BinaryDecoder::Array],
132
132
  ].inject([]) do |h, (format, direction, arraycoder)|
133
133
  coders = registry.coders_for(format, direction) || {}
134
134
  h[format] ||= {}
@@ -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
@@ -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
@@ -72,16 +72,18 @@ module PG
72
72
 
73
73
  class CompositeCoder < Coder
74
74
  def to_h
75
- { **super,
75
+ h = { **super,
76
76
  elements_type: elements_type,
77
77
  needs_quotation: needs_quotation?,
78
78
  delimiter: delimiter,
79
79
  }
80
+ h[:dimensions] = dimensions if dimensions # Write only when set, for Marshal compat with pg<1.6
81
+ h
80
82
  end
81
83
 
82
84
  def inspect
83
85
  str = super
84
- 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"}"
85
87
  str
86
88
  end
87
89
  end