pg 1.3.3 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +15 -9
  4. data/.github/workflows/binary-gems.yml +43 -12
  5. data/.github/workflows/source-gem.yml +28 -20
  6. data/.gitignore +11 -2
  7. data/.travis.yml +2 -2
  8. data/{History.rdoc → History.md} +302 -115
  9. data/README.ja.md +276 -0
  10. data/README.md +286 -0
  11. data/Rakefile +15 -6
  12. data/Rakefile.cross +7 -11
  13. data/certs/larskanis-2023.pem +24 -0
  14. data/ext/errorcodes.def +4 -0
  15. data/ext/errorcodes.rb +0 -0
  16. data/ext/errorcodes.txt +2 -1
  17. data/ext/extconf.rb +0 -0
  18. data/ext/pg.c +14 -54
  19. data/ext/pg.h +12 -5
  20. data/ext/pg_binary_decoder.c +80 -1
  21. data/ext/pg_binary_encoder.c +225 -1
  22. data/ext/pg_coder.c +17 -8
  23. data/ext/pg_connection.c +385 -266
  24. data/ext/pg_copy_coder.c +307 -18
  25. data/ext/pg_errors.c +1 -1
  26. data/ext/pg_record_coder.c +12 -9
  27. data/ext/pg_result.c +117 -34
  28. data/ext/pg_text_decoder.c +28 -10
  29. data/ext/pg_text_encoder.c +23 -10
  30. data/ext/pg_tuple.c +36 -39
  31. data/ext/pg_type_map.c +4 -3
  32. data/ext/pg_type_map_all_strings.c +3 -3
  33. data/ext/pg_type_map_by_class.c +6 -4
  34. data/ext/pg_type_map_by_column.c +9 -5
  35. data/ext/pg_type_map_by_mri_type.c +1 -1
  36. data/ext/pg_type_map_by_oid.c +8 -5
  37. data/ext/pg_type_map_in_ruby.c +6 -3
  38. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  39. data/lib/pg/basic_type_map_for_queries.rb +13 -8
  40. data/lib/pg/basic_type_map_for_results.rb +26 -3
  41. data/lib/pg/basic_type_registry.rb +36 -33
  42. data/lib/pg/binary_decoder/date.rb +9 -0
  43. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  44. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  45. data/lib/pg/coder.rb +15 -13
  46. data/lib/pg/connection.rb +269 -139
  47. data/lib/pg/exceptions.rb +14 -1
  48. data/lib/pg/text_decoder/date.rb +18 -0
  49. data/lib/pg/text_decoder/inet.rb +9 -0
  50. data/lib/pg/text_decoder/json.rb +14 -0
  51. data/lib/pg/text_decoder/numeric.rb +9 -0
  52. data/lib/pg/text_decoder/timestamp.rb +30 -0
  53. data/lib/pg/text_encoder/date.rb +12 -0
  54. data/lib/pg/text_encoder/inet.rb +28 -0
  55. data/lib/pg/text_encoder/json.rb +14 -0
  56. data/lib/pg/text_encoder/numeric.rb +9 -0
  57. data/lib/pg/text_encoder/timestamp.rb +24 -0
  58. data/lib/pg/version.rb +1 -1
  59. data/lib/pg.rb +59 -19
  60. data/misc/openssl-pg-segfault.rb +0 -0
  61. data/pg.gemspec +4 -2
  62. data/rakelib/task_extension.rb +1 -1
  63. data/sample/array_insert.rb +0 -0
  64. data/sample/async_api.rb +3 -7
  65. data/sample/async_copyto.rb +0 -0
  66. data/sample/async_mixed.rb +0 -0
  67. data/sample/check_conn.rb +0 -0
  68. data/sample/copydata.rb +0 -0
  69. data/sample/copyfrom.rb +0 -0
  70. data/sample/copyto.rb +0 -0
  71. data/sample/cursor.rb +0 -0
  72. data/sample/disk_usage_report.rb +0 -0
  73. data/sample/issue-119.rb +0 -0
  74. data/sample/losample.rb +0 -0
  75. data/sample/minimal-testcase.rb +0 -0
  76. data/sample/notify_wait.rb +0 -0
  77. data/sample/pg_statistics.rb +0 -0
  78. data/sample/replication_monitor.rb +0 -0
  79. data/sample/test_binary_values.rb +0 -0
  80. data/sample/wal_shipper.rb +0 -0
  81. data/sample/warehouse_partitions.rb +0 -0
  82. data/translation/.po4a-version +7 -0
  83. data/translation/po/all.pot +910 -0
  84. data/translation/po/ja.po +1047 -0
  85. data/translation/po4a.cfg +12 -0
  86. data.tar.gz.sig +0 -0
  87. metadata +101 -32
  88. metadata.gz.sig +0 -0
  89. data/README.ja.rdoc +0 -13
  90. data/README.rdoc +0 -214
  91. data/lib/pg/binary_decoder.rb +0 -23
  92. data/lib/pg/constants.rb +0 -12
  93. data/lib/pg/text_decoder.rb +0 -46
  94. data/lib/pg/text_encoder.rb +0 -59
data/ext/pg_tuple.c CHANGED
@@ -128,7 +128,7 @@ static const rb_data_type_t pg_tuple_type = {
128
128
  pg_compact_callback(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
  /*
@@ -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 )
@@ -471,10 +474,7 @@ pg_tuple_dump(VALUE self)
471
474
  values = rb_ary_new4(this->num_fields, &this->values[0]);
472
475
  a = rb_ary_new3(2, field_names, values);
473
476
 
474
- if (FL_TEST(self, FL_EXIVAR)) {
475
- rb_copy_generic_ivar(a, self);
476
- FL_SET(a, FL_EXIVAR);
477
- }
477
+ rb_copy_generic_ivar(a, self);
478
478
 
479
479
  return a;
480
480
  }
@@ -523,35 +523,32 @@ pg_tuple_load(VALUE self, VALUE a)
523
523
  sizeof(*this->values) * num_fields +
524
524
  sizeof(*this->values) * (dup_names ? 1 : 0));
525
525
 
526
- this->result = Qnil;
527
- this->typemap = Qnil;
526
+ RB_OBJ_WRITE(self, &this->result, Qnil);
527
+ RB_OBJ_WRITE(self, &this->typemap, Qnil);
528
528
  this->row_num = -1;
529
529
  this->num_fields = num_fields;
530
- this->field_map = field_map;
530
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
531
531
 
532
532
  for( i = 0; i < num_fields; i++ ){
533
533
  VALUE v = RARRAY_AREF(values, i);
534
534
  if( v == Qundef )
535
535
  rb_raise(rb_eTypeError, "field %d is not materialized", i);
536
- this->values[i] = v;
536
+ RB_OBJ_WRITE(self, &this->values[i], v);
537
537
  }
538
538
 
539
539
  if( dup_names ){
540
- this->values[num_fields] = field_names;
540
+ RB_OBJ_WRITE(self, &this->values[num_fields], field_names);
541
541
  }
542
542
 
543
543
  RTYPEDDATA_DATA(self) = this;
544
544
 
545
- if (FL_TEST(a, FL_EXIVAR)) {
546
- rb_copy_generic_ivar(self, a);
547
- FL_SET(self, FL_EXIVAR);
548
- }
545
+ rb_copy_generic_ivar(self, a);
549
546
 
550
547
  return self;
551
548
  }
552
549
 
553
550
  void
554
- init_pg_tuple()
551
+ init_pg_tuple(void)
555
552
  {
556
553
  rb_cPG_Tuple = rb_define_class_under( rb_mPG, "Tuple", rb_cObject );
557
554
  rb_define_alloc_func( rb_cPG_Tuple, pg_tuple_s_allocate );
data/ext/pg_type_map.c CHANGED
@@ -37,7 +37,7 @@ const rb_data_type_t pg_typemap_type = {
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
  }
@@ -176,7 +177,7 @@ pg_typemap_with_default_type_map(VALUE self, VALUE typemap)
176
177
  }
177
178
 
178
179
  void
179
- init_pg_type_map()
180
+ init_pg_type_map(void)
180
181
  {
181
182
  s_id_fit_to_query = rb_intern("fit_to_query");
182
183
  s_id_fit_to_result = rb_intern("fit_to_result");
@@ -18,7 +18,7 @@ static const rb_data_type_t pg_tmas_type = {
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;
@@ -105,7 +105,7 @@ pg_tmas_s_allocate( VALUE klass )
105
105
 
106
106
 
107
107
  void
108
- init_pg_type_map_all_strings()
108
+ init_pg_type_map_all_strings(void)
109
109
  {
110
110
  /*
111
111
  * Document-class: PG::TypeMapAllStrings < PG::TypeMap
@@ -125,6 +125,6 @@ init_pg_type_map_all_strings()
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
  }
@@ -157,7 +157,7 @@ static const rb_data_type_t pg_tmbk_type = {
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{
@@ -247,7 +249,7 @@ pg_tmbk_coders( VALUE self )
247
249
  }
248
250
 
249
251
  void
250
- init_pg_type_map_by_class()
252
+ init_pg_type_map_by_class(void)
251
253
  {
252
254
  /*
253
255
  * Document-class: PG::TypeMapByClass < PG::TypeMap
@@ -232,7 +232,7 @@ static const rb_data_type_t pg_tmbc_type = {
232
232
  },
233
233
  &pg_typemap_type,
234
234
  0,
235
- RUBY_TYPED_FREE_IMMEDIATELY,
235
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
236
236
  };
237
237
 
238
238
  static VALUE
@@ -243,7 +243,7 @@ pg_tmbc_s_allocate( VALUE klass )
243
243
  }
244
244
 
245
245
  VALUE
246
- pg_tmbc_allocate()
246
+ pg_tmbc_allocate(void)
247
247
  {
248
248
  return pg_tmbc_s_allocate(rb_cTypeMapByColumn);
249
249
  }
@@ -266,13 +266,14 @@ pg_tmbc_init(VALUE self, VALUE conv_ary)
266
266
  t_tmbc *this;
267
267
  int conv_ary_len;
268
268
 
269
+ rb_check_frozen(self);
269
270
  Check_Type(conv_ary, T_ARRAY);
270
271
  conv_ary_len = RARRAY_LENINT(conv_ary);
271
272
  this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
272
273
  /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
273
274
  this->nfields = 0;
274
275
  this->typemap.funcs = pg_tmbc_funcs;
275
- this->typemap.default_typemap = pg_typemap_all_strings;
276
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
276
277
  RTYPEDDATA_DATA(self) = this;
277
278
 
278
279
  for(i=0; i<conv_ary_len; i++)
@@ -283,8 +284,11 @@ pg_tmbc_init(VALUE self, VALUE conv_ary)
283
284
  /* no type cast */
284
285
  this->convs[i].cconv = NULL;
285
286
  } else {
287
+ t_pg_coder *p_coder;
286
288
  /* Check argument type and store the coder pointer */
287
- TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, this->convs[i].cconv);
289
+ TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, p_coder);
290
+ RB_OBJ_WRITTEN(self, Qnil, p_coder->coder_obj);
291
+ this->convs[i].cconv = p_coder;
288
292
  }
289
293
  }
290
294
 
@@ -320,7 +324,7 @@ pg_tmbc_coders(VALUE self)
320
324
  }
321
325
 
322
326
  void
323
- init_pg_type_map_by_column()
327
+ init_pg_type_map_by_column(void)
324
328
  {
325
329
  s_id_decode = rb_intern("decode");
326
330
  s_id_encode = rb_intern("encode");
@@ -286,7 +286,7 @@ pg_tmbmt_coders( VALUE self )
286
286
  }
287
287
 
288
288
  void
289
- init_pg_type_map_by_mri_type()
289
+ init_pg_type_map_by_mri_type(void)
290
290
  {
291
291
  /*
292
292
  * Document-class: PG::TypeMapByMriType < PG::TypeMap
@@ -194,7 +194,7 @@ static const rb_data_type_t pg_tmbo_type = {
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
 
@@ -318,6 +320,7 @@ static VALUE
318
320
  pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
319
321
  {
320
322
  t_tmbo *this = RTYPEDDATA_DATA( self );
323
+ rb_check_frozen(self);
321
324
  this->max_rows_for_online_lookup = NUM2INT(value);
322
325
  return value;
323
326
  }
@@ -338,7 +341,7 @@ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
338
341
  * typemap.build_column_map( result )
339
342
  *
340
343
  * This builds a PG::TypeMapByColumn that fits to the given PG::Result object
341
- * based on it's type OIDs.
344
+ * based on it's type OIDs and binary/text format.
342
345
  *
343
346
  */
344
347
  static VALUE
@@ -356,7 +359,7 @@ pg_tmbo_build_column_map( VALUE self, VALUE result )
356
359
 
357
360
 
358
361
  void
359
- init_pg_type_map_by_oid()
362
+ init_pg_type_map_by_oid(void)
360
363
  {
361
364
  s_id_decode = rb_intern("decode");
362
365
 
@@ -44,7 +44,7 @@ static const rb_data_type_t pg_tmir_type = {
44
44
  },
45
45
  &pg_typemap_type,
46
46
  0,
47
- RUBY_TYPED_FREE_IMMEDIATELY,
47
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
48
48
  };
49
49
 
50
50
  /*
@@ -210,6 +210,9 @@ pg_tmir_typecast_query_param( VALUE self, VALUE param_value, VALUE field )
210
210
  * This method is called, when a type map is used for decoding copy data,
211
211
  * before the value is casted.
212
212
  *
213
+ * Should return the expected number of columns or 0 if the number of columns is unknown.
214
+ * This number is only used for memory pre-allocation.
215
+ *
213
216
  */
214
217
  static VALUE pg_tmir_fit_to_copy_get_dummy( VALUE self ){}
215
218
  #endif
@@ -291,7 +294,7 @@ pg_tmir_s_allocate( VALUE klass )
291
294
  this->typemap.funcs.typecast_result_value = pg_tmir_result_value;
292
295
  this->typemap.funcs.typecast_query_param = pg_tmir_query_param;
293
296
  this->typemap.funcs.typecast_copy_get = pg_tmir_copy_get;
294
- this->typemap.default_typemap = pg_typemap_all_strings;
297
+ RB_OBJ_WRITE(self, &this->typemap.default_typemap, pg_typemap_all_strings);
295
298
  this->self = self;
296
299
 
297
300
  return self;
@@ -299,7 +302,7 @@ pg_tmir_s_allocate( VALUE klass )
299
302
 
300
303
 
301
304
  void
302
- init_pg_type_map_in_ruby()
305
+ init_pg_type_map_in_ruby(void)
303
306
  {
304
307
  s_id_fit_to_result = rb_intern("fit_to_result");
305
308
  s_id_fit_to_query = rb_intern("fit_to_query");
@@ -32,7 +32,27 @@ require 'pg' unless defined?( PG )
32
32
  # conn.put_copy_data ['a', 123, [5,4,3]]
33
33
  # end
34
34
  # This inserts a single row into copytable with type casts from ruby to
35
- # database types.
35
+ # database types using text format.
36
+ #
37
+ # Very similar with binary format:
38
+ #
39
+ # conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, blob bytea, created_at timestamp)" )
40
+ # # Retrieve table OIDs per empty result set in binary format.
41
+ # res = conn.exec_params( "SELECT * FROM copytable LIMIT 0", [], 1 )
42
+ # # Build a type map for common ruby to database type encoders.
43
+ # btm = PG::BasicTypeMapBasedOnResult.new(conn)
44
+ # # Build a PG::TypeMapByColumn with encoders suitable for copytable.
45
+ # tm = btm.build_column_map( res )
46
+ # row_encoder = PG::BinaryEncoder::CopyRow.new type_map: tm
47
+ #
48
+ # conn.copy_data( "COPY copytable FROM STDIN WITH (FORMAT binary)", row_encoder ) do |res|
49
+ # conn.put_copy_data ['a', 123, "\xff\x00".b, Time.now]
50
+ # end
51
+ #
52
+ # This inserts a single row into copytable with type casts from ruby to
53
+ # database types using binary copy and value format.
54
+ # Binary COPY is faster than text format but less portable and less readable and pg offers fewer en-/decoders of database types.
55
+ #
36
56
  class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
37
57
  include PG::BasicTypeRegistry::Checker
38
58
 
@@ -47,16 +47,20 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
47
47
  # Options:
48
48
  # * +registry+: Custom type registry, nil for default global registry
49
49
  # * +if_undefined+: Optional +Proc+ object which is called, if no type for an parameter class is not defined in the registry.
50
+ # The +Proc+ object is called with the name and format of the missing type.
51
+ # Its return value is not used.
50
52
  def initialize(connection_or_coder_maps, registry: nil, if_undefined: nil)
51
53
  @coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
52
54
  @array_encoders_by_klass = array_encoders_by_klass
53
55
  @encode_array_as = :array
54
- @if_undefined = if_undefined || proc { |oid_name, format|
55
- raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
56
- }
56
+ @if_undefined = if_undefined || method(:raise_undefined_type).to_proc
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}"
62
+ end
63
+
60
64
  # Change the mechanism that is used to encode ruby array values
61
65
  #
62
66
  # Possible values:
@@ -162,7 +166,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
162
166
  @textarray_encoder
163
167
  end
164
168
 
165
- DEFAULT_TYPE_MAP = {
169
+ DEFAULT_TYPE_MAP = PG.make_shareable({
166
170
  TrueClass => [1, 'bool', 'bool'],
167
171
  FalseClass => [1, 'bool', 'bool'],
168
172
  # We use text format and no type OID for numbers, because setting the OID can lead
@@ -177,9 +181,10 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
177
181
  Hash => [0, 'json'],
178
182
  Array => :get_array_type,
179
183
  BinaryData => [1, 'bytea'],
180
- }
184
+ })
185
+ private_constant :DEFAULT_TYPE_MAP
181
186
 
182
- DEFAULT_ARRAY_TYPE_MAP = {
187
+ DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
183
188
  TrueClass => [0, '_bool'],
184
189
  FalseClass => [0, '_bool'],
185
190
  Integer => [0, '_int8'],
@@ -188,6 +193,6 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
188
193
  BigDecimal => [0, '_numeric'],
189
194
  Time => [0, '_timestamptz'],
190
195
  IPAddr => [0, '_inet'],
191
- }
192
-
196
+ })
197
+ private_constant :DEFAULT_ARRAY_TYPE_MAP
193
198
  end
@@ -46,22 +46,45 @@ require 'pg' unless defined?( PG )
46
46
  # This prints the rows with type casted columns:
47
47
  # ["a", 123, [5, 4, 3]]
48
48
  #
49
+ # Very similar with binary format:
50
+ #
51
+ # conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '2023-03-19 18:39:44'::TIMESTAMP)" )
52
+ #
53
+ # # Retrieve table OIDs per empty result set in binary format.
54
+ # res = conn.exec_params( "SELECT * FROM copytable LIMIT 0", [], 1 )
55
+ # # Build a type map for common database to ruby type decoders.
56
+ # btm = PG::BasicTypeMapForResults.new(conn)
57
+ # # Build a PG::TypeMapByColumn with decoders suitable for copytable.
58
+ # tm = btm.build_column_map( res )
59
+ # row_decoder = PG::BinaryDecoder::CopyRow.new type_map: tm
60
+ #
61
+ # conn.copy_data( "COPY copytable TO STDOUT WITH (FORMAT binary)", row_decoder ) do |res|
62
+ # while row=conn.get_copy_data
63
+ # p row
64
+ # end
65
+ # end
66
+ # This prints the rows with type casted columns:
67
+ # ["a", 123, 2023-03-19 18:39:44 UTC]
68
+ #
49
69
  # See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
50
70
  class PG::BasicTypeMapForResults < PG::TypeMapByOid
51
71
  include PG::BasicTypeRegistry::Checker
52
72
 
53
73
  class WarningTypeMap < PG::TypeMapInRuby
54
74
  def initialize(typenames)
55
- @already_warned = Hash.new{|h, k| h[k] = {} }
75
+ @already_warned = {}
56
76
  @typenames_by_oid = typenames
57
77
  end
58
78
 
59
79
  def typecast_result_value(result, _tuple, field)
60
80
  format = result.fformat(field)
61
81
  oid = result.ftype(field)
62
- unless @already_warned[format][oid]
82
+ unless @already_warned.dig(format, oid)
63
83
  warn "Warning: no type cast defined for type #{@typenames_by_oid[oid].inspect} format #{format} with oid #{oid}. Please cast this type explicitly to TEXT to be safe for future changes."
64
- @already_warned[format][oid] = true
84
+ unless frozen?
85
+ @already_warned[format] ||= {}
86
+ @already_warned[format][oid] = true
87
+ end
65
88
  end
66
89
  super
67
90
  end