pg 1.5.9 → 1.7.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +126 -0
  4. data/Gemfile +11 -7
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +83 -32
  7. data/README.md +79 -29
  8. data/Rakefile +84 -19
  9. data/certs/larskanis-2026.pem +24 -0
  10. data/ext/errorcodes.def +9 -0
  11. data/ext/errorcodes.rb +1 -1
  12. data/ext/errorcodes.txt +8 -1
  13. data/ext/extconf.rb +194 -30
  14. data/ext/gvl_wrappers.c +13 -2
  15. data/ext/gvl_wrappers.h +47 -134
  16. data/ext/pg.c +23 -16
  17. data/ext/pg.h +23 -24
  18. data/ext/pg_binary_decoder.c +157 -3
  19. data/ext/pg_binary_encoder.c +219 -9
  20. data/ext/pg_cancel_connection.c +360 -0
  21. data/ext/pg_coder.c +58 -9
  22. data/ext/pg_connection.c +558 -291
  23. data/ext/pg_copy_coder.c +36 -23
  24. data/ext/pg_record_coder.c +11 -7
  25. data/ext/pg_result.c +144 -76
  26. data/ext/pg_text_decoder.c +7 -7
  27. data/ext/pg_text_encoder.c +29 -11
  28. data/ext/pg_tuple.c +9 -9
  29. data/ext/pg_type_map.c +45 -3
  30. data/ext/pg_type_map_all_strings.c +3 -3
  31. data/ext/pg_type_map_by_class.c +2 -2
  32. data/ext/pg_type_map_by_column.c +4 -3
  33. data/ext/pg_type_map_by_mri_type.c +1 -1
  34. data/ext/pg_type_map_by_oid.c +4 -2
  35. data/ext/pg_type_map_in_ruby.c +2 -2
  36. data/ext/pg_util.c +2 -2
  37. data/ext/pg_util.h +2 -2
  38. data/lib/pg/basic_type_map_based_on_result.rb +5 -0
  39. data/lib/pg/basic_type_map_for_queries.rb +16 -5
  40. data/lib/pg/basic_type_map_for_results.rb +5 -0
  41. data/lib/pg/basic_type_registry.rb +2 -2
  42. data/lib/pg/cancel_connection.rb +53 -0
  43. data/lib/pg/coder.rb +4 -2
  44. data/lib/pg/connection.rb +393 -180
  45. data/lib/pg/text_decoder/date.rb +1 -1
  46. data/lib/pg/text_decoder/json.rb +1 -1
  47. data/lib/pg/text_encoder/date.rb +7 -1
  48. data/lib/pg/text_encoder/json.rb +1 -1
  49. data/lib/pg/version.rb +2 -1
  50. data/lib/pg.rb +156 -130
  51. data/misc/glibc/Dockerfile +20 -0
  52. data/misc/glibc/docker-compose.yml +9 -0
  53. data/misc/glibc/glibc_spec.rb +5 -0
  54. data/misc/yugabyte/Dockerfile +9 -0
  55. data/misc/yugabyte/docker-compose.yml +28 -0
  56. data/misc/yugabyte/pg-test.rb +45 -0
  57. data/pg.gemspec +5 -3
  58. data/ports/patches/krb5/1.22.2/0001-Allow-static-linking-krb5-library.patch +30 -0
  59. data/ports/patches/krb5/1.22.2/0002-unknown-command-line-option-on-clang.patch +12 -0
  60. data/ports/patches/postgresql/18.6/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  61. data/rakelib/pg_gem_helper.rb +64 -0
  62. data/sample/async_mixed.rb +1 -1
  63. data.tar.gz.sig +0 -0
  64. metadata +36 -22
  65. metadata.gz.sig +0 -0
  66. data/Manifest.txt +0 -72
  67. data/Rakefile.cross +0 -303
@@ -231,7 +231,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
231
231
  *
232
232
  */
233
233
  static int
234
- 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)
235
235
  {
236
236
  if(out){
237
237
  double dvalue = NUM2DBL(value);
@@ -239,7 +239,6 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
239
239
  int neg = 0;
240
240
  int exp2i, exp10i, i;
241
241
  unsigned long long ll, remainder, oldval;
242
- VALUE intermediate;
243
242
 
244
243
  /* Cast to the same strings as value.to_s . */
245
244
  if( isinf(dvalue) ){
@@ -283,6 +282,7 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
283
282
 
284
283
  if( exp10i <= -5 || exp10i >= 15 ) {
285
284
  /* Write the float in exponent format (1.23e45) */
285
+ VALUE intermediate;
286
286
 
287
287
  /* write fraction digits from right to left */
288
288
  for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
@@ -381,8 +381,8 @@ pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
381
381
  static VALUE
382
382
  init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
383
383
  {
384
+ rb_gc_register_address(&s_str_F);
384
385
  s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
385
- rb_global_variable(&s_str_F);
386
386
  rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
387
387
  s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
388
388
 
@@ -430,9 +430,14 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
430
430
  }
431
431
  return (int)(optr - out);
432
432
  }else{
433
+ long size;
433
434
  *intermediate = rb_obj_as_string(value);
435
+ size = RSTRING_LEN(*intermediate);
436
+
434
437
  /* The output starts with "\x" and each character is converted to hex. */
435
- return 2 + RSTRING_LENINT(*intermediate) * 2;
438
+ if( size > INT_MAX / 2 - 2 )
439
+ rb_raise( rb_eArgError, "too large input string for PG::TextEncoder::Bytea: %ld", size );
440
+ return (int)(2 + size * 2);
436
441
  }
437
442
  }
438
443
 
@@ -537,7 +542,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
537
542
  }
538
543
 
539
544
  static char *
540
- write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
545
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx, int dimension)
541
546
  {
542
547
  int i;
543
548
 
@@ -545,6 +550,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
545
550
  current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
546
551
  *current_out++ = '{';
547
552
 
553
+ if( RARRAY_LEN(value) == 0 && this->dimensions >= 0 && dimension != this->dimensions ){
554
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
555
+ }
556
+
548
557
  for( i=0; i<RARRAY_LEN(value); i++){
549
558
  VALUE entry = rb_ary_entry(value, i);
550
559
 
@@ -554,17 +563,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
554
563
  }
555
564
 
556
565
  switch(TYPE(entry)){
557
- case T_ARRAY:
558
- current_out = write_array(this, entry, current_out, string, quote, enc_idx);
559
- break;
560
566
  case T_NIL:
567
+ if( this->dimensions >= 0 && dimension != this->dimensions ){
568
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
569
+ }
561
570
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
562
571
  *current_out++ = 'N';
563
572
  *current_out++ = 'U';
564
573
  *current_out++ = 'L';
565
574
  *current_out++ = 'L';
566
575
  break;
576
+ case T_ARRAY:
577
+ if( this->dimensions < 0 || dimension < this->dimensions ){
578
+ current_out = write_array(this, entry, current_out, string, quote, enc_idx, dimension+1);
579
+ break;
580
+ }
581
+ /* Number of dimensions reached -> handle array as normal value */
567
582
  default:
583
+ if( this->dimensions >= 0 && dimension != this->dimensions ){
584
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
585
+ }
568
586
  current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
569
587
  }
570
588
  }
@@ -596,7 +614,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
596
614
  VALUE out_str = rb_str_new(NULL, 0);
597
615
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
598
616
 
599
- end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
617
+ end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx, 1);
600
618
 
601
619
  rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
602
620
  *intermediate = out_str;
@@ -771,7 +789,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
771
789
  if(out){
772
790
  /* Second encoder pass, if required */
773
791
  strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
774
- base64_encode( out, out, strlen );
792
+ rbpg_base64_encode( out, out, strlen );
775
793
 
776
794
  return BASE64_ENCODED_SIZE(strlen);
777
795
  } else {
@@ -786,7 +804,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
786
804
  out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
787
805
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
788
806
 
789
- base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
807
+ rbpg_base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
790
808
  *intermediate = out_str;
791
809
 
792
810
  return -1;
data/ext/pg_tuple.c CHANGED
@@ -125,17 +125,17 @@ 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
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
131
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
132
132
  };
133
133
 
134
134
  /*
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,11 +33,11 @@ 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,
40
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
40
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
41
41
  };
42
42
 
43
43
  VALUE rb_cTypeMap;
@@ -115,6 +115,45 @@ pg_typemap_s_allocate( VALUE klass )
115
115
  return self;
116
116
  }
117
117
 
118
+ /*
119
+ * call-seq:
120
+ * res.query_param_encoders(params)
121
+ *
122
+ * Retrieve the encoders that are used to encode the given values to be submitted to the database server.
123
+ * The selection of the encoders is defined in the derived type map class.
124
+ *
125
+ * +params+ must be an Array of values to be encoded.
126
+ * It's like +params+ given to exec_params .
127
+ *
128
+ * Returns an Array with the same length as +params+.
129
+ *
130
+ */
131
+ static VALUE
132
+ pg_typemap_query_param_encoders( VALUE self, VALUE params )
133
+ {
134
+ t_typemap *this = RTYPEDDATA_DATA( self );
135
+ int nParams;
136
+ int i=0;
137
+ VALUE res;
138
+
139
+ Check_Type(params, T_ARRAY);
140
+
141
+ this->funcs.fit_to_query( self, params );
142
+
143
+ nParams = RARRAY_LENINT(params);
144
+ res = rb_ary_new2(nParams);
145
+
146
+ for ( i = 0; i < nParams; i++ ) {
147
+ t_pg_coder *conv;
148
+ VALUE param_value = rb_ary_entry(params, i);
149
+
150
+ /* Let the given typemap select a coder for this param */
151
+ conv = this->funcs.typecast_query_param(this, param_value, i);
152
+ rb_ary_push(res, conv ? conv->coder_obj : Qnil);
153
+ }
154
+ return res;
155
+ }
156
+
118
157
  /*
119
158
  * call-seq:
120
159
  * res.default_type_map = typemap
@@ -187,11 +226,14 @@ init_pg_type_map(void)
187
226
  *
188
227
  * This is the base class for type maps.
189
228
  * See derived classes for implementations of different type cast strategies
190
- * ( PG::TypeMapByColumn, PG::TypeMapByOid ).
229
+ * ( PG::TypeMapByColumn, PG::TypeMapByOid, etc.).
230
+ *
231
+ * Find more type maps in the {README}[rdoc-ref:README.md@Type+Casts].
191
232
  *
192
233
  */
193
234
  rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
194
235
  rb_define_alloc_func( rb_cTypeMap, pg_typemap_s_allocate );
236
+ rb_define_method( rb_cTypeMap, "query_param_encoders", pg_typemap_query_param_encoders, 1 );
195
237
 
196
238
  rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable");
197
239
  rb_define_method( rb_mDefaultTypeMappable, "default_type_map=", pg_typemap_default_type_map_set, 1 );
@@ -14,11 +14,11 @@ 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,
21
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
21
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
22
22
  };
23
23
 
24
24
  VALUE rb_cTypeMapAllStrings;
@@ -125,6 +125,6 @@ init_pg_type_map_all_strings(void)
125
125
  rb_cTypeMapAllStrings = rb_define_class_under( rb_mPG, "TypeMapAllStrings", rb_cTypeMap );
126
126
  rb_define_alloc_func( rb_cTypeMapAllStrings, pg_tmas_s_allocate );
127
127
 
128
- pg_typemap_all_strings = rb_obj_freeze( rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 ));
129
128
  rb_gc_register_address( &pg_typemap_all_strings );
129
+ pg_typemap_all_strings = rb_obj_freeze( rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 ));
130
130
  }
@@ -153,11 +153,11 @@ 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,
160
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
160
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
161
161
  };
162
162
 
163
163
  static VALUE
@@ -54,7 +54,8 @@ 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
- nfields = (int)RARRAY_LEN( params );
57
+ Check_Type(params, T_ARRAY);
58
+ nfields = RARRAY_LENINT( 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)",
60
61
  nfields, this->nfields );
@@ -228,11 +229,11 @@ 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,
235
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
236
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
236
237
  };
237
238
 
238
239
  static VALUE
@@ -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,11 +190,11 @@ 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,
197
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
197
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
198
198
  };
199
199
 
200
200
  static VALUE
@@ -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,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
- pg_compact_callback(pg_tmir_compact),
43
+ pg_tmir_compact,
44
44
  },
45
45
  &pg_typemap_type,
46
46
  0,
47
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
47
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
48
48
  };
49
49
 
50
50
  /*
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
 
@@ -64,4 +64,9 @@ class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
64
64
  add_coder(coder)
65
65
  end
66
66
  end
67
+
68
+ # Returns the PG::BasicTypeRegistry::CoderMapsBundle used to translate result OIDs to encoders.
69
+ def coder_maps_bundle
70
+ @coder_maps
71
+ end
67
72
  end
@@ -53,14 +53,23 @@ 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
+ # Returns the PG::BasicTypeRegistry::CoderMapsBundle used to translate encoders to OIDs.
61
+ def coder_maps_bundle
62
+ @coder_maps
62
63
  end
63
64
 
65
+ class UndefinedDefault
66
+ def self.call(oid_name, format)
67
+ raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
68
+ end
69
+ end
70
+
71
+ private_constant :UndefinedDefault
72
+
64
73
  # Change the mechanism that is used to encode ruby array values
65
74
  #
66
75
  # Possible values:
@@ -173,13 +182,14 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
173
182
  end
174
183
 
175
184
  DEFAULT_TYPE_MAP = PG.make_shareable({
176
- TrueClass => [1, 'bool', 'bool'],
177
- FalseClass => [1, 'bool', 'bool'],
185
+ TrueClass => [0, 'bool', 'bool'],
186
+ FalseClass => [0, 'bool', 'bool'],
178
187
  # We use text format and no type OID for numbers, because setting the OID can lead
179
188
  # to unnecessary type conversions on server side.
180
189
  Integer => [0, 'int8'],
181
190
  Float => [0, 'float8'],
182
191
  Time => [0, 'timestamptz'],
192
+ Date => [0, 'date'],
183
193
  # We use text format and no type OID for IPAddr, because setting the OID can lead
184
194
  # to unnecessary inet/cidr conversions on the server side.
185
195
  IPAddr => [0, 'inet'],
@@ -196,6 +206,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
196
206
  String => [0, '_text'],
197
207
  Float => [0, '_float8'],
198
208
  Time => [0, '_timestamptz'],
209
+ Date => [0, '_date'],
199
210
  IPAddr => [0, '_inet'],
200
211
  }.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
201
212
  private_constant :DEFAULT_ARRAY_TYPE_MAP
@@ -101,4 +101,9 @@ class PG::BasicTypeMapForResults < PG::TypeMapByOid
101
101
  typenames = @coder_maps.typenames_by_oid
102
102
  self.default_type_map = WarningTypeMap.new(typenames)
103
103
  end
104
+
105
+ # Returns the PG::BasicTypeRegistry::CoderMapsBundle used to translate result OIDs to decoders.
106
+ def coder_maps_bundle
107
+ @coder_maps
108
+ end
104
109
  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] ||= {}
@@ -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