pg 1.4.6 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +185 -3
  4. data/Gemfile +12 -3
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +75 -41
  7. data/README.md +86 -31
  8. data/Rakefile +95 -14
  9. data/certs/kanis@comcard.de.pem +20 -0
  10. data/certs/larskanis-2024.pem +24 -0
  11. data/ext/errorcodes.def +4 -5
  12. data/ext/errorcodes.txt +2 -5
  13. data/ext/extconf.rb +165 -14
  14. data/ext/gvl_wrappers.c +13 -2
  15. data/ext/gvl_wrappers.h +33 -0
  16. data/ext/pg.c +28 -35
  17. data/ext/pg.h +18 -14
  18. data/ext/pg_binary_decoder.c +231 -0
  19. data/ext/pg_binary_encoder.c +427 -0
  20. data/ext/pg_cancel_connection.c +360 -0
  21. data/ext/pg_coder.c +70 -12
  22. data/ext/pg_connection.c +473 -208
  23. data/ext/pg_copy_coder.c +316 -23
  24. data/ext/pg_record_coder.c +12 -11
  25. data/ext/pg_result.c +102 -30
  26. data/ext/pg_text_decoder.c +31 -10
  27. data/ext/pg_text_encoder.c +58 -26
  28. data/ext/pg_tuple.c +36 -33
  29. data/ext/pg_type_map.c +4 -3
  30. data/ext/pg_type_map_all_strings.c +3 -3
  31. data/ext/pg_type_map_by_class.c +6 -4
  32. data/ext/pg_type_map_by_column.c +9 -4
  33. data/ext/pg_type_map_by_mri_type.c +1 -1
  34. data/ext/pg_type_map_by_oid.c +10 -5
  35. data/ext/pg_type_map_in_ruby.c +6 -3
  36. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  37. data/lib/pg/basic_type_map_for_queries.rb +23 -10
  38. data/lib/pg/basic_type_map_for_results.rb +26 -3
  39. data/lib/pg/basic_type_registry.rb +46 -36
  40. data/lib/pg/binary_decoder/date.rb +9 -0
  41. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  42. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  43. data/lib/pg/cancel_connection.rb +53 -0
  44. data/lib/pg/coder.rb +18 -14
  45. data/lib/pg/connection.rb +387 -172
  46. data/lib/pg/exceptions.rb +6 -0
  47. data/lib/pg/text_decoder/date.rb +21 -0
  48. data/lib/pg/text_decoder/inet.rb +9 -0
  49. data/lib/pg/text_decoder/json.rb +17 -0
  50. data/lib/pg/text_decoder/numeric.rb +9 -0
  51. data/lib/pg/text_decoder/timestamp.rb +30 -0
  52. data/lib/pg/text_encoder/date.rb +13 -0
  53. data/lib/pg/text_encoder/inet.rb +31 -0
  54. data/lib/pg/text_encoder/json.rb +17 -0
  55. data/lib/pg/text_encoder/numeric.rb +9 -0
  56. data/lib/pg/text_encoder/timestamp.rb +24 -0
  57. data/lib/pg/version.rb +1 -1
  58. data/lib/pg.rb +78 -17
  59. data/misc/yugabyte/Dockerfile +9 -0
  60. data/misc/yugabyte/docker-compose.yml +28 -0
  61. data/misc/yugabyte/pg-test.rb +45 -0
  62. data/pg.gemspec +9 -5
  63. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  64. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  65. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  66. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  67. data/rakelib/pg_gem_helper.rb +64 -0
  68. data.tar.gz.sig +0 -0
  69. metadata +61 -49
  70. metadata.gz.sig +0 -0
  71. data/.appveyor.yml +0 -42
  72. data/.gems +0 -6
  73. data/.gemtest +0 -0
  74. data/.github/workflows/binary-gems.yml +0 -117
  75. data/.github/workflows/source-gem.yml +0 -137
  76. data/.gitignore +0 -19
  77. data/.hgsigs +0 -34
  78. data/.hgtags +0 -41
  79. data/.irbrc +0 -23
  80. data/.pryrc +0 -23
  81. data/.tm_properties +0 -21
  82. data/.travis.yml +0 -49
  83. data/Manifest.txt +0 -72
  84. data/Rakefile.cross +0 -298
  85. data/lib/pg/binary_decoder.rb +0 -23
  86. data/lib/pg/constants.rb +0 -12
  87. data/lib/pg/text_decoder.rb +0 -46
  88. data/lib/pg/text_encoder.rb +0 -59
  89. data/translation/.po4a-version +0 -7
  90. data/translation/po/all.pot +0 -875
  91. data/translation/po/ja.po +0 -868
  92. data/translation/po4a.cfg +0 -9
@@ -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)
@@ -371,6 +377,21 @@ pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
371
377
  }
372
378
  }
373
379
 
380
+ /* called per autoload when TextEncoder::Numeric is used */
381
+ static VALUE
382
+ init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
383
+ {
384
+ s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
385
+ rb_global_variable(&s_str_F);
386
+ rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
387
+ s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
388
+
389
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
390
+ pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
391
+
392
+ return Qnil;
393
+ }
394
+
374
395
 
375
396
  static const char hextab[] = {
376
397
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
@@ -383,8 +404,12 @@ static const char hextab[] = {
383
404
  *
384
405
  * The binary String is converted to hexadecimal representation for transmission
385
406
  * in text format. For query bind parameters it is recommended to use
386
- * PG::BinaryEncoder::Bytea instead, in order to decrease network traffic and
387
- * CPU usage.
407
+ * PG::BinaryEncoder::Bytea or the hash form <tt>{value: binary_string, format: 1}</tt> instead,
408
+ * in order to decrease network traffic and CPU usage.
409
+ * See PG::Connection#exec_params for using the hash form.
410
+ *
411
+ * This encoder is particular useful when PG::TextEncoder::CopyRow is used with the COPY command.
412
+ * In this case there's no way to change the format of a single column to binary, so that the data have to be converted to bytea hex representation.
388
413
  *
389
414
  */
390
415
  static int
@@ -418,7 +443,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
418
443
  t_pg_composite_coder *this = _this;
419
444
  char *ptr1;
420
445
  char *ptr2;
421
- int backslashs = 0;
446
+ int backslashes = 0;
422
447
  int needquote;
423
448
 
424
449
  /* count data plus backslashes; detect chars needing quotes */
@@ -435,7 +460,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
435
460
 
436
461
  if (ch == '"' || ch == '\\'){
437
462
  needquote = 1;
438
- backslashs++;
463
+ backslashes++;
439
464
  } else if (ch == '{' || ch == '}' || ch == this->delimiter ||
440
465
  ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
441
466
  needquote = 1;
@@ -444,12 +469,12 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
444
469
 
445
470
  if( needquote ){
446
471
  ptr1 = p_in + strlen;
447
- ptr2 = p_out + strlen + backslashs + 2;
472
+ ptr2 = p_out + strlen + backslashes + 2;
448
473
  /* Write end quote */
449
474
  *--ptr2 = '"';
450
475
 
451
476
  /* Then store the escaped string on the final position, walking
452
- * right to left, until all backslashs are placed. */
477
+ * right to left, until all backslashes are placed. */
453
478
  while( ptr1 != p_in ) {
454
479
  *--ptr2 = *--ptr1;
455
480
  if(*ptr2 == '"' || *ptr2 == '\\'){
@@ -458,7 +483,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
458
483
  }
459
484
  /* Write start quote */
460
485
  *p_out = '"';
461
- return strlen + backslashs + 2;
486
+ return strlen + backslashes + 2;
462
487
  } else {
463
488
  if( p_in != p_out )
464
489
  memcpy( p_out, p_in, strlen );
@@ -512,7 +537,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
512
537
  }
513
538
 
514
539
  static char *
515
- 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)
516
541
  {
517
542
  int i;
518
543
 
@@ -520,6 +545,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
520
545
  current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
521
546
  *current_out++ = '{';
522
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
+
523
552
  for( i=0; i<RARRAY_LEN(value); i++){
524
553
  VALUE entry = rb_ary_entry(value, i);
525
554
 
@@ -529,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
529
558
  }
530
559
 
531
560
  switch(TYPE(entry)){
532
- case T_ARRAY:
533
- current_out = write_array(this, entry, current_out, string, quote, enc_idx);
534
- break;
535
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
+ }
536
565
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
537
566
  *current_out++ = 'N';
538
567
  *current_out++ = 'U';
539
568
  *current_out++ = 'L';
540
569
  *current_out++ = 'L';
541
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 */
542
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
+ }
543
581
  current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
544
582
  }
545
583
  }
@@ -571,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
571
609
  VALUE out_str = rb_str_new(NULL, 0);
572
610
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
573
611
 
574
- 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);
575
613
 
576
614
  rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
577
615
  *intermediate = out_str;
@@ -673,22 +711,22 @@ static int
673
711
  quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
674
712
  char *ptr1;
675
713
  char *ptr2;
676
- int backslashs = 0;
714
+ int backslashes = 0;
677
715
 
678
716
  /* count required backlashs */
679
717
  for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
680
718
  if (*ptr1 == '\''){
681
- backslashs++;
719
+ backslashes++;
682
720
  }
683
721
  }
684
722
 
685
723
  ptr1 = p_in + strlen;
686
- ptr2 = p_out + strlen + backslashs + 2;
724
+ ptr2 = p_out + strlen + backslashes + 2;
687
725
  /* Write end quote */
688
726
  *--ptr2 = '\'';
689
727
 
690
728
  /* Then store the escaped string on the final position, walking
691
- * right to left, until all backslashs are placed. */
729
+ * right to left, until all backslashes are placed. */
692
730
  while( ptr1 != p_in ) {
693
731
  *--ptr2 = *--ptr1;
694
732
  if(*ptr2 == '\''){
@@ -697,7 +735,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
697
735
  }
698
736
  /* Write start quote */
699
737
  *p_out = '\'';
700
- return strlen + backslashs + 2;
738
+ return strlen + backslashes + 2;
701
739
  }
702
740
 
703
741
 
@@ -780,14 +818,10 @@ init_pg_text_encoder(void)
780
818
  s_id_encode = rb_intern("encode");
781
819
  s_id_to_i = rb_intern("to_i");
782
820
  s_id_to_s = rb_intern("to_s");
783
- s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
784
- rb_global_variable(&s_str_F);
785
- rb_require("bigdecimal");
786
- s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
787
-
788
821
 
789
822
  /* This module encapsulates all encoder classes with text output format */
790
823
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
824
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
791
825
 
792
826
  /* Make RDoc aware of the encoder classes... */
793
827
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
@@ -796,8 +830,6 @@ init_pg_text_encoder(void)
796
830
  pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
797
831
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
798
832
  pg_define_coder( "Float", pg_text_enc_float, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
799
- /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
800
- pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
801
833
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
802
834
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
803
835
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
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,
131
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_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 )
@@ -159,9 +159,9 @@ pg_tuple_new(VALUE result, int row_num)
159
159
  sizeof(*this->values) * num_fields +
160
160
  sizeof(*this->values) * (dup_names ? 1 : 0));
161
161
 
162
- this->result = result;
163
- this->typemap = p_result->typemap;
164
- this->field_map = field_map;
162
+ RB_OBJ_WRITE(self, &this->result, result);
163
+ RB_OBJ_WRITE(self, &this->typemap, p_result->typemap);
164
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
165
165
  this->row_num = row_num;
166
166
  this->num_fields = num_fields;
167
167
 
@@ -173,7 +173,8 @@ pg_tuple_new(VALUE result, int row_num)
173
173
  /* Some of the column names are duplicated -> we need the keys as Array in addition.
174
174
  * Store it behind the values to save the space in the common case of no dups.
175
175
  */
176
- this->values[num_fields] = rb_obj_freeze(rb_ary_new4(num_fields, p_result->fnames));
176
+ VALUE keys_array = rb_obj_freeze(rb_ary_new4(num_fields, p_result->fnames));
177
+ RB_OBJ_WRITE(self, &this->values[num_fields], keys_array);
177
178
  }
178
179
 
179
180
  RTYPEDDATA_DATA(self) = this;
@@ -193,8 +194,9 @@ pg_tuple_get_this( VALUE self )
193
194
  }
194
195
 
195
196
  static VALUE
196
- pg_tuple_materialize_field(t_pg_tuple *this, int col)
197
+ pg_tuple_materialize_field(VALUE self, int col)
197
198
  {
199
+ t_pg_tuple *this = RTYPEDDATA_DATA( self );
198
200
  VALUE value = this->values[col];
199
201
 
200
202
  if( value == Qundef ){
@@ -202,29 +204,31 @@ pg_tuple_materialize_field(t_pg_tuple *this, int col)
202
204
 
203
205
  pgresult_get(this->result); /* make sure we have a valid PGresult object */
204
206
  value = p_typemap->funcs.typecast_result_value(p_typemap, this->result, this->row_num, col);
205
- this->values[col] = value;
207
+ RB_OBJ_WRITE(self, &this->values[col], value);
206
208
  }
207
209
 
208
210
  return value;
209
211
  }
210
212
 
211
213
  static void
212
- pg_tuple_detach(t_pg_tuple *this)
214
+ pg_tuple_detach(VALUE self)
213
215
  {
214
- this->result = Qnil;
215
- this->typemap = Qnil;
216
+ t_pg_tuple *this = RTYPEDDATA_DATA( self );
217
+ RB_OBJ_WRITE(self, &this->result, Qnil);
218
+ RB_OBJ_WRITE(self, &this->typemap, Qnil);
216
219
  this->row_num = -1;
217
220
  }
218
221
 
219
222
  static void
220
- pg_tuple_materialize(t_pg_tuple *this)
223
+ pg_tuple_materialize(VALUE self)
221
224
  {
225
+ t_pg_tuple *this = RTYPEDDATA_DATA( self );
222
226
  int field_num;
223
227
  for(field_num = 0; field_num < this->num_fields; field_num++) {
224
- pg_tuple_materialize_field(this, field_num);
228
+ pg_tuple_materialize_field(self, field_num);
225
229
  }
226
230
 
227
- pg_tuple_detach(this);
231
+ pg_tuple_detach(self);
228
232
  }
229
233
 
230
234
  /*
@@ -286,7 +290,7 @@ pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
286
290
  field_num = NUM2INT(index);
287
291
  }
288
292
 
289
- return pg_tuple_materialize_field(this, field_num);
293
+ return pg_tuple_materialize_field(self, field_num);
290
294
  }
291
295
 
292
296
  /*
@@ -324,7 +328,7 @@ pg_tuple_aref(VALUE self, VALUE key)
324
328
  field_num = NUM2INT(index);
325
329
  }
326
330
 
327
- return pg_tuple_materialize_field(this, field_num);
331
+ return pg_tuple_materialize_field(self, field_num);
328
332
  }
329
333
 
330
334
  static VALUE
@@ -335,10 +339,9 @@ pg_tuple_num_fields_for_enum(VALUE self, VALUE args, VALUE eobj)
335
339
  }
336
340
 
337
341
  static int
338
- pg_tuple_yield_key_value(VALUE key, VALUE index, VALUE _this)
342
+ pg_tuple_yield_key_value(VALUE key, VALUE index, VALUE self)
339
343
  {
340
- t_pg_tuple *this = (t_pg_tuple *)_this;
341
- VALUE value = pg_tuple_materialize_field(this, NUM2INT(index));
344
+ VALUE value = pg_tuple_materialize_field(self, NUM2INT(index));
342
345
  rb_yield_values(2, key, value);
343
346
  return ST_CONTINUE;
344
347
  }
@@ -360,16 +363,16 @@ pg_tuple_each(VALUE self)
360
363
  field_names = pg_tuple_get_field_names(this);
361
364
 
362
365
  if( field_names == Qfalse ){
363
- rb_hash_foreach(this->field_map, pg_tuple_yield_key_value, (VALUE)this);
366
+ rb_hash_foreach(this->field_map, pg_tuple_yield_key_value, self);
364
367
  } else {
365
368
  int i;
366
369
  for( i = 0; i < this->num_fields; i++ ){
367
- VALUE value = pg_tuple_materialize_field(this, i);
370
+ VALUE value = pg_tuple_materialize_field(self, i);
368
371
  rb_yield_values(2, RARRAY_AREF(field_names, i), value);
369
372
  }
370
373
  }
371
374
 
372
- pg_tuple_detach(this);
375
+ pg_tuple_detach(self);
373
376
  return self;
374
377
  }
375
378
 
@@ -388,11 +391,11 @@ pg_tuple_each_value(VALUE self)
388
391
  RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
389
392
 
390
393
  for(field_num = 0; field_num < this->num_fields; field_num++) {
391
- VALUE value = pg_tuple_materialize_field(this, field_num);
394
+ VALUE value = pg_tuple_materialize_field(self, field_num);
392
395
  rb_yield(value);
393
396
  }
394
397
 
395
- pg_tuple_detach(this);
398
+ pg_tuple_detach(self);
396
399
  return self;
397
400
  }
398
401
 
@@ -409,7 +412,7 @@ pg_tuple_values(VALUE self)
409
412
  {
410
413
  t_pg_tuple *this = pg_tuple_get_this(self);
411
414
 
412
- pg_tuple_materialize(this);
415
+ pg_tuple_materialize(self);
413
416
  return rb_ary_new4(this->num_fields, &this->values[0]);
414
417
  }
415
418
 
@@ -462,7 +465,7 @@ pg_tuple_dump(VALUE self)
462
465
  VALUE a;
463
466
  t_pg_tuple *this = pg_tuple_get_this(self);
464
467
 
465
- pg_tuple_materialize(this);
468
+ pg_tuple_materialize(self);
466
469
 
467
470
  field_names = pg_tuple_get_field_names(this);
468
471
  if( field_names == Qfalse )
@@ -520,26 +523,26 @@ pg_tuple_load(VALUE self, VALUE a)
520
523
  sizeof(*this->values) * num_fields +
521
524
  sizeof(*this->values) * (dup_names ? 1 : 0));
522
525
 
523
- this->result = Qnil;
524
- this->typemap = Qnil;
526
+ RB_OBJ_WRITE(self, &this->result, Qnil);
527
+ RB_OBJ_WRITE(self, &this->typemap, Qnil);
525
528
  this->row_num = -1;
526
529
  this->num_fields = num_fields;
527
- this->field_map = field_map;
530
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
528
531
 
529
532
  for( i = 0; i < num_fields; i++ ){
530
533
  VALUE v = RARRAY_AREF(values, i);
531
534
  if( v == Qundef )
532
535
  rb_raise(rb_eTypeError, "field %d is not materialized", i);
533
- this->values[i] = v;
536
+ RB_OBJ_WRITE(self, &this->values[i], v);
534
537
  }
535
538
 
536
539
  if( dup_names ){
537
- this->values[num_fields] = field_names;
540
+ RB_OBJ_WRITE(self, &this->values[num_fields], field_names);
538
541
  }
539
542
 
540
543
  RTYPEDDATA_DATA(self) = this;
541
544
 
542
- rb_copy_generic_ivar(self, a);
545
+ rb_copy_generic_ivar(self, a);
543
546
 
544
547
  return self;
545
548
  }
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,
40
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
41
41
  };
42
42
 
43
43
  VALUE rb_cTypeMap;
@@ -132,9 +132,10 @@ pg_typemap_default_type_map_set(VALUE self, VALUE typemap)
132
132
  t_typemap *tm;
133
133
  UNUSED(tm);
134
134
 
135
+ rb_check_frozen(self);
135
136
  /* Check type of method param */
136
137
  TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, tm);
137
- this->default_typemap = typemap;
138
+ RB_OBJ_WRITE(self, &this->default_typemap, typemap);
138
139
 
139
140
  return typemap;
140
141
  }
@@ -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,
21
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_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_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 );
128
+ pg_typemap_all_strings = rb_obj_freeze( rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 ));
129
129
  rb_gc_register_address( &pg_typemap_all_strings );
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,
160
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
161
161
  };
162
162
 
163
163
  static VALUE
@@ -173,12 +173,12 @@ pg_tmbk_s_allocate( VALUE klass )
173
173
  this->typemap.funcs.typecast_result_value = pg_typemap_result_value;
174
174
  this->typemap.funcs.typecast_query_param = pg_tmbk_typecast_query_param;
175
175
  this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
176
- this->typemap.default_typemap = pg_typemap_all_strings;
176
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
177
177
 
178
178
  /* We need to store self in the this-struct, because pg_tmbk_typecast_query_param(),
179
179
  * is called with the this-pointer only. */
180
180
  this->self = self;
181
- this->klass_to_coder = rb_hash_new();
181
+ RB_OBJ_WRITE(self, &this->klass_to_coder, rb_hash_new());
182
182
 
183
183
  /* The cache is properly initialized by TypedData_Make_Struct(). */
184
184
 
@@ -205,6 +205,8 @@ pg_tmbk_aset( VALUE self, VALUE klass, VALUE coder )
205
205
  {
206
206
  t_tmbk *this = RTYPEDDATA_DATA( self );
207
207
 
208
+ rb_check_frozen(self);
209
+
208
210
  if(NIL_P(coder)){
209
211
  rb_hash_delete( this->klass_to_coder, klass );
210
212
  }else{
@@ -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,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,
236
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
236
237
  };
237
238
 
238
239
  static VALUE
@@ -266,13 +267,14 @@ pg_tmbc_init(VALUE self, VALUE conv_ary)
266
267
  t_tmbc *this;
267
268
  int conv_ary_len;
268
269
 
270
+ rb_check_frozen(self);
269
271
  Check_Type(conv_ary, T_ARRAY);
270
272
  conv_ary_len = RARRAY_LENINT(conv_ary);
271
273
  this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
272
274
  /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
273
275
  this->nfields = 0;
274
276
  this->typemap.funcs = pg_tmbc_funcs;
275
- this->typemap.default_typemap = pg_typemap_all_strings;
277
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
276
278
  RTYPEDDATA_DATA(self) = this;
277
279
 
278
280
  for(i=0; i<conv_ary_len; i++)
@@ -283,8 +285,11 @@ pg_tmbc_init(VALUE self, VALUE conv_ary)
283
285
  /* no type cast */
284
286
  this->convs[i].cconv = NULL;
285
287
  } else {
288
+ t_pg_coder *p_coder;
286
289
  /* Check argument type and store the coder pointer */
287
- TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, this->convs[i].cconv);
290
+ TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, p_coder);
291
+ RB_OBJ_WRITTEN(self, Qnil, p_coder->coder_obj);
292
+ this->convs[i].cconv = p_coder;
288
293
  }
289
294
  }
290
295
 
@@ -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,
197
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
198
198
  };
199
199
 
200
200
  static VALUE
@@ -212,11 +212,11 @@ pg_tmbo_s_allocate( VALUE klass )
212
212
  this->typemap.funcs.typecast_result_value = pg_tmbo_result_value;
213
213
  this->typemap.funcs.typecast_query_param = pg_typemap_typecast_query_param;
214
214
  this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
215
- this->typemap.default_typemap = pg_typemap_all_strings;
215
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
216
216
  this->max_rows_for_online_lookup = 10;
217
217
 
218
218
  for( i=0; i<2; i++){
219
- this->format[i].oid_to_coder = rb_hash_new();
219
+ RB_OBJ_WRITE(self, &this->format[i].oid_to_coder, rb_hash_new());
220
220
  }
221
221
 
222
222
  return self;
@@ -242,6 +242,7 @@ pg_tmbo_add_coder( VALUE self, VALUE coder )
242
242
  t_pg_coder *p_coder;
243
243
  struct pg_tmbo_oid_cache_entry *p_ce;
244
244
 
245
+ rb_check_frozen(self);
245
246
  TypedData_Get_Struct(coder, t_pg_coder, &pg_coder_type, p_coder);
246
247
 
247
248
  if( p_coder->format < 0 || p_coder->format > 1 )
@@ -276,6 +277,7 @@ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
276
277
  int i_format = NUM2INT(format);
277
278
  struct pg_tmbo_oid_cache_entry *p_ce;
278
279
 
280
+ rb_check_frozen(self);
279
281
  if( i_format < 0 || i_format > 1 )
280
282
  rb_raise(rb_eArgError, "invalid format code %d", i_format);
281
283
 
@@ -313,11 +315,14 @@ pg_tmbo_coders( VALUE self )
313
315
  * The type map will do Hash lookups for each result value, if the number of rows
314
316
  * is below or equal +number+.
315
317
  *
318
+ * Default is 10.
319
+ *
316
320
  */
317
321
  static VALUE
318
322
  pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
319
323
  {
320
324
  t_tmbo *this = RTYPEDDATA_DATA( self );
325
+ rb_check_frozen(self);
321
326
  this->max_rows_for_online_lookup = NUM2INT(value);
322
327
  return value;
323
328
  }
@@ -338,7 +343,7 @@ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
338
343
  * typemap.build_column_map( result )
339
344
  *
340
345
  * This builds a PG::TypeMapByColumn that fits to the given PG::Result object
341
- * based on it's type OIDs.
346
+ * based on it's type OIDs and binary/text format.
342
347
  *
343
348
  */
344
349
  static VALUE