pg 1.4.6 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +1 -1
  4. data/.gitignore +3 -0
  5. data/History.md +55 -0
  6. data/README.ja.md +29 -19
  7. data/README.md +29 -15
  8. data/Rakefile.cross +1 -1
  9. data/ext/pg.c +10 -28
  10. data/ext/pg.h +10 -5
  11. data/ext/pg_binary_decoder.c +79 -0
  12. data/ext/pg_binary_encoder.c +224 -0
  13. data/ext/pg_coder.c +16 -7
  14. data/ext/pg_connection.c +50 -34
  15. data/ext/pg_copy_coder.c +306 -17
  16. data/ext/pg_record_coder.c +5 -4
  17. data/ext/pg_result.c +88 -17
  18. data/ext/pg_text_decoder.c +28 -10
  19. data/ext/pg_text_encoder.c +22 -9
  20. data/ext/pg_tuple.c +34 -31
  21. data/ext/pg_type_map.c +3 -2
  22. data/ext/pg_type_map_all_strings.c +2 -2
  23. data/ext/pg_type_map_by_class.c +5 -3
  24. data/ext/pg_type_map_by_column.c +9 -3
  25. data/ext/pg_type_map_by_oid.c +7 -4
  26. data/ext/pg_type_map_in_ruby.c +5 -2
  27. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  28. data/lib/pg/basic_type_map_for_queries.rb +13 -8
  29. data/lib/pg/basic_type_map_for_results.rb +26 -3
  30. data/lib/pg/basic_type_registry.rb +30 -32
  31. data/lib/pg/binary_decoder/date.rb +9 -0
  32. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  33. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  34. data/lib/pg/coder.rb +15 -13
  35. data/lib/pg/connection.rb +63 -12
  36. data/lib/pg/text_decoder/date.rb +18 -0
  37. data/lib/pg/text_decoder/inet.rb +9 -0
  38. data/lib/pg/text_decoder/json.rb +14 -0
  39. data/lib/pg/text_decoder/numeric.rb +9 -0
  40. data/lib/pg/text_decoder/timestamp.rb +30 -0
  41. data/lib/pg/text_encoder/date.rb +12 -0
  42. data/lib/pg/text_encoder/inet.rb +28 -0
  43. data/lib/pg/text_encoder/json.rb +14 -0
  44. data/lib/pg/text_encoder/numeric.rb +9 -0
  45. data/lib/pg/text_encoder/timestamp.rb +24 -0
  46. data/lib/pg/version.rb +1 -1
  47. data/lib/pg.rb +44 -9
  48. data/pg.gemspec +1 -1
  49. data/translation/po/all.pot +170 -135
  50. data/translation/po/ja.po +365 -186
  51. data/translation/po4a.cfg +4 -1
  52. data.tar.gz.sig +0 -0
  53. metadata +28 -10
  54. metadata.gz.sig +0 -0
  55. data/lib/pg/binary_decoder.rb +0 -23
  56. data/lib/pg/constants.rb +0 -12
  57. data/lib/pg/text_decoder.rb +0 -46
  58. data/lib/pg/text_encoder.rb +0 -59
@@ -43,7 +43,6 @@
43
43
  #include <string.h>
44
44
 
45
45
  VALUE rb_mPG_TextDecoder;
46
- static ID s_id_decode;
47
46
  static ID s_id_Rational;
48
47
  static ID s_id_new;
49
48
  static ID s_id_utc;
@@ -171,6 +170,19 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
171
170
  return rb_funcall(rb_cObject, s_id_BigDecimal, 1, rb_str_new(val, len));
172
171
  }
173
172
 
173
+ /* called per autoload when TextDecoder::Numeric is used */
174
+ static VALUE
175
+ init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
176
+ {
177
+ rb_require("bigdecimal");
178
+ s_id_BigDecimal = rb_intern("BigDecimal");
179
+
180
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
181
+ pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
182
+
183
+ return Qnil;
184
+ }
185
+
174
186
  /*
175
187
  * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
176
188
  *
@@ -922,8 +934,9 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
922
934
  return ip;
923
935
  }
924
936
 
925
- void
926
- init_pg_text_decoder(void)
937
+ /* called per autoload when TextDecoder::Inet is used */
938
+ static VALUE
939
+ init_pg_text_decoder_inet(VALUE rb_mPG_TextDecoder)
927
940
  {
928
941
  rb_require("ipaddr");
929
942
  s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
@@ -942,14 +955,21 @@ init_pg_text_decoder(void)
942
955
  s_vmasks6 = rb_eval_string("a = [0]*129; a[0] = 0; a[128] = 0xffffffffffffffffffffffffffffffff; 127.downto(1){|i| a[i] = a[i+1] - (1 << (127 - i))}; a.freeze");
943
956
  rb_global_variable(&s_vmasks6);
944
957
 
945
- s_id_decode = rb_intern("decode");
958
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
959
+ pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
960
+
961
+ return Qnil;
962
+ }
963
+
964
+
965
+ void
966
+ init_pg_text_decoder(void)
967
+ {
946
968
  s_id_Rational = rb_intern("Rational");
947
969
  s_id_new = rb_intern("new");
948
970
  s_id_utc = rb_intern("utc");
949
971
  s_id_getlocal = rb_intern("getlocal");
950
972
 
951
- rb_require("bigdecimal");
952
- s_id_BigDecimal = rb_intern("BigDecimal");
953
973
  s_nan = rb_eval_string("0.0/0.0");
954
974
  rb_global_variable(&s_nan);
955
975
  s_pos_inf = rb_eval_string("1.0/0.0");
@@ -959,6 +979,8 @@ init_pg_text_decoder(void)
959
979
 
960
980
  /* This module encapsulates all decoder classes with text input format */
961
981
  rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
982
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_inet", init_pg_text_decoder_inet, 0);
983
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_numeric", init_pg_text_decoder_numeric, 0);
962
984
 
963
985
  /* Make RDoc aware of the decoder classes... */
964
986
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
@@ -967,8 +989,6 @@ init_pg_text_decoder(void)
967
989
  pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
968
990
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
969
991
  pg_define_coder( "Float", pg_text_dec_float, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
970
- /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
971
- pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
972
992
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
973
993
  pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
974
994
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
@@ -977,8 +997,6 @@ init_pg_text_decoder(void)
977
997
  pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
978
998
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
979
999
  pg_define_coder( "Timestamp", pg_text_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
980
- /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
981
- pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
982
1000
 
983
1001
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
984
1002
  pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
@@ -371,6 +371,21 @@ pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
371
371
  }
372
372
  }
373
373
 
374
+ /* called per autoload when TextEncoder::Numeric is used */
375
+ static VALUE
376
+ init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
377
+ {
378
+ s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
379
+ rb_global_variable(&s_str_F);
380
+ rb_require("bigdecimal");
381
+ s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
382
+
383
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
384
+ pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
385
+
386
+ return Qnil;
387
+ }
388
+
374
389
 
375
390
  static const char hextab[] = {
376
391
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
@@ -383,8 +398,12 @@ static const char hextab[] = {
383
398
  *
384
399
  * The binary String is converted to hexadecimal representation for transmission
385
400
  * 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.
401
+ * PG::BinaryEncoder::Bytea or the hash form <tt>{value: binary_string, format: 1}</tt> instead,
402
+ * in order to decrease network traffic and CPU usage.
403
+ * See PG::Connection#exec_params for using the hash form.
404
+ *
405
+ * This encoder is particular useful when PG::TextEncoder::CopyRow is used with the COPY command.
406
+ * 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
407
  *
389
408
  */
390
409
  static int
@@ -780,14 +799,10 @@ init_pg_text_encoder(void)
780
799
  s_id_encode = rb_intern("encode");
781
800
  s_id_to_i = rb_intern("to_i");
782
801
  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
802
 
789
803
  /* This module encapsulates all encoder classes with text output format */
790
804
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
805
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
791
806
 
792
807
  /* Make RDoc aware of the encoder classes... */
793
808
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
@@ -796,8 +811,6 @@ init_pg_text_encoder(void)
796
811
  pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
797
812
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
798
813
  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
814
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
802
815
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
803
816
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
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 )
@@ -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
@@ -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
  }
@@ -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;
@@ -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
  }
@@ -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{
@@ -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
@@ -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,13 @@ 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
+ if( p_coder ){
291
+ RB_OBJ_WRITTEN(self, Qnil, p_coder->coder_obj);
292
+ }
293
+ this->convs[i].cconv = p_coder;
288
294
  }
289
295
  }
290
296
 
@@ -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
@@ -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;
@@ -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