pg 1.4.1 → 1.5.6

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 (72) 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 +45 -14
  5. data/.github/workflows/source-gem.yml +35 -23
  6. data/.gitignore +11 -2
  7. data/.travis.yml +2 -2
  8. data/Gemfile +3 -0
  9. data/{History.rdoc → History.md} +285 -140
  10. data/README.ja.md +300 -0
  11. data/README.md +286 -0
  12. data/Rakefile +18 -6
  13. data/Rakefile.cross +8 -11
  14. data/certs/kanis@comcard.de.pem +20 -0
  15. data/certs/larskanis-2023.pem +24 -0
  16. data/certs/larskanis-2024.pem +24 -0
  17. data/ext/errorcodes.def +4 -0
  18. data/ext/errorcodes.txt +2 -1
  19. data/ext/extconf.rb +4 -0
  20. data/ext/pg.c +15 -55
  21. data/ext/pg.h +11 -6
  22. data/ext/pg_binary_decoder.c +80 -1
  23. data/ext/pg_binary_encoder.c +225 -1
  24. data/ext/pg_coder.c +17 -8
  25. data/ext/pg_connection.c +201 -73
  26. data/ext/pg_copy_coder.c +307 -18
  27. data/ext/pg_errors.c +1 -1
  28. data/ext/pg_record_coder.c +6 -5
  29. data/ext/pg_result.c +102 -26
  30. data/ext/pg_text_decoder.c +28 -10
  31. data/ext/pg_text_encoder.c +23 -10
  32. data/ext/pg_tuple.c +35 -32
  33. data/ext/pg_type_map.c +4 -3
  34. data/ext/pg_type_map_all_strings.c +3 -3
  35. data/ext/pg_type_map_by_class.c +6 -4
  36. data/ext/pg_type_map_by_column.c +9 -5
  37. data/ext/pg_type_map_by_mri_type.c +1 -1
  38. data/ext/pg_type_map_by_oid.c +8 -5
  39. data/ext/pg_type_map_in_ruby.c +6 -3
  40. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  41. data/lib/pg/basic_type_map_for_queries.rb +19 -10
  42. data/lib/pg/basic_type_map_for_results.rb +26 -3
  43. data/lib/pg/basic_type_registry.rb +35 -33
  44. data/lib/pg/binary_decoder/date.rb +9 -0
  45. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  46. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  47. data/lib/pg/coder.rb +15 -13
  48. data/lib/pg/connection.rb +186 -104
  49. data/lib/pg/exceptions.rb +7 -0
  50. data/lib/pg/text_decoder/date.rb +18 -0
  51. data/lib/pg/text_decoder/inet.rb +9 -0
  52. data/lib/pg/text_decoder/json.rb +14 -0
  53. data/lib/pg/text_decoder/numeric.rb +9 -0
  54. data/lib/pg/text_decoder/timestamp.rb +30 -0
  55. data/lib/pg/text_encoder/date.rb +12 -0
  56. data/lib/pg/text_encoder/inet.rb +28 -0
  57. data/lib/pg/text_encoder/json.rb +14 -0
  58. data/lib/pg/text_encoder/numeric.rb +9 -0
  59. data/lib/pg/text_encoder/timestamp.rb +24 -0
  60. data/lib/pg/version.rb +1 -1
  61. data/lib/pg.rb +55 -15
  62. data/pg.gemspec +5 -3
  63. data/rakelib/task_extension.rb +1 -1
  64. data.tar.gz.sig +0 -0
  65. metadata +96 -32
  66. metadata.gz.sig +0 -0
  67. data/README.ja.rdoc +0 -13
  68. data/README.rdoc +0 -214
  69. data/lib/pg/binary_decoder.rb +0 -23
  70. data/lib/pg/constants.rb +0 -12
  71. data/lib/pg/text_decoder.rb +0 -46
  72. data/lib/pg/text_encoder.rb +0 -59
data/ext/pg_result.c CHANGED
@@ -183,7 +183,7 @@ static const rb_data_type_t pgresult_type = {
183
183
  pg_compact_callback(pgresult_gc_compact),
184
184
  },
185
185
  0, 0,
186
- RUBY_TYPED_FREE_IMMEDIATELY,
186
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
187
187
  };
188
188
 
189
189
  /* Needed by sequel_pg gem, do not delete */
@@ -208,6 +208,8 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
208
208
 
209
209
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
210
210
  this->pgresult = result;
211
+ /* Initialize connection and typemap prior to any object allocations,
212
+ * to make sure valid objects are marked. */
211
213
  this->connection = rb_pgconn;
212
214
  this->typemap = pg_typemap_all_strings;
213
215
  this->p_typemap = RTYPEDDATA_DATA( this->typemap );
@@ -224,7 +226,8 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
224
226
  t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
225
227
 
226
228
  this->enc_idx = p_conn->enc_idx;
227
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
229
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
230
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
228
231
  this->p_typemap = RTYPEDDATA_DATA( this->typemap );
229
232
  this->flags = p_conn->flags;
230
233
  } else {
@@ -374,17 +377,37 @@ VALUE
374
377
  pg_result_clear(VALUE self)
375
378
  {
376
379
  t_pg_result *this = pgresult_get_this(self);
380
+ rb_check_frozen(self);
377
381
  pgresult_clear( this );
378
382
  return Qnil;
379
383
  }
380
384
 
385
+ /*
386
+ * call-seq:
387
+ * res.freeze
388
+ *
389
+ * Freeze the PG::Result object and unlink the result from the related PG::Connection.
390
+ *
391
+ * A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
392
+ * It also denies setting a type_map or field_name_type.
393
+ *
394
+ */
395
+ static VALUE
396
+ pg_result_freeze(VALUE self)
397
+ {
398
+ t_pg_result *this = pgresult_get_this(self);
399
+
400
+ RB_OBJ_WRITE(self, &this->connection, Qnil);
401
+ return rb_call_super(0, NULL);
402
+ }
403
+
381
404
  /*
382
405
  * call-seq:
383
406
  * res.cleared? -> boolean
384
407
  *
385
408
  * Returns +true+ if the backend result memory has been freed.
386
409
  */
387
- VALUE
410
+ static VALUE
388
411
  pgresult_cleared_p( VALUE self )
389
412
  {
390
413
  t_pg_result *this = pgresult_get_this(self);
@@ -401,7 +424,7 @@ pgresult_cleared_p( VALUE self )
401
424
  * All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
402
425
  *
403
426
  */
404
- VALUE
427
+ static VALUE
405
428
  pgresult_autoclear_p( VALUE self )
406
429
  {
407
430
  t_pg_result *this = pgresult_get_this(self);
@@ -477,7 +500,8 @@ static void pgresult_init_fnames(VALUE self)
477
500
 
478
501
  for( i=0; i<nfields; i++ ){
479
502
  char *cfname = PQfname(this->pgresult, i);
480
- this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
503
+ VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
504
+ RB_OBJ_WRITE(self, &this->fnames[i], fname);
481
505
  this->nfields = i + 1;
482
506
  }
483
507
  this->nfields = nfields;
@@ -527,6 +551,8 @@ static void pgresult_init_fnames(VALUE self)
527
551
  * * +PGRES_SINGLE_TUPLE+
528
552
  * * +PGRES_PIPELINE_SYNC+
529
553
  * * +PGRES_PIPELINE_ABORTED+
554
+ *
555
+ * Use <tt>res.res_status</tt> to retrieve the string representation.
530
556
  */
531
557
  static VALUE
532
558
  pgresult_result_status(VALUE self)
@@ -536,16 +562,38 @@ pgresult_result_status(VALUE self)
536
562
 
537
563
  /*
538
564
  * call-seq:
539
- * res.res_status( status ) -> String
565
+ * PG::Result.res_status( status ) -> String
540
566
  *
541
567
  * Returns the string representation of +status+.
542
568
  *
543
569
  */
544
570
  static VALUE
545
- pgresult_res_status(VALUE self, VALUE status)
571
+ pgresult_s_res_status(VALUE self, VALUE status)
572
+ {
573
+ return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
574
+ }
575
+
576
+ /*
577
+ * call-seq:
578
+ * res.res_status -> String
579
+ * res.res_status( status ) -> String
580
+ *
581
+ * Returns the string representation of the status of the result or of the provided +status+.
582
+ *
583
+ */
584
+ static VALUE
585
+ pgresult_res_status(int argc, VALUE *argv, VALUE self)
546
586
  {
547
587
  t_pg_result *this = pgresult_get_this_safe(self);
548
- VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
588
+ VALUE ret;
589
+
590
+ if( argc == 0 ){
591
+ ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
592
+ }else if( argc == 1 ){
593
+ ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
594
+ }else{
595
+ rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
596
+ }
549
597
  PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
550
598
  return ret;
551
599
  }
@@ -685,6 +733,21 @@ pgresult_nfields(VALUE self)
685
733
  return INT2NUM(PQnfields(pgresult_get(self)));
686
734
  }
687
735
 
736
+ /*
737
+ * call-seq:
738
+ * res.binary_tuples() -> Integer
739
+ *
740
+ * Returns 1 if the PGresult contains binary data and 0 if it contains text data.
741
+ *
742
+ * This function is deprecated (except for its use in connection with COPY), because it is possible for a single PGresult to contain text data in some columns and binary data in others.
743
+ * Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
744
+ */
745
+ static VALUE
746
+ pgresult_binary_tuples(VALUE self)
747
+ {
748
+ return INT2NUM(PQbinaryTuples(pgresult_get(self)));
749
+ }
750
+
688
751
  /*
689
752
  * call-seq:
690
753
  * res.fname( index ) -> String or Symbol
@@ -1087,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
1087
1150
  }
1088
1151
  /* Store a copy of the filled hash for use at the next row. */
1089
1152
  if( num_tuples > 10 )
1090
- this->tuple_hash = rb_hash_dup(tuple);
1153
+ RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
1091
1154
 
1092
1155
  return tuple;
1093
1156
  }
@@ -1269,7 +1332,7 @@ static void ensure_init_for_tuple(VALUE self)
1269
1332
  rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1270
1333
  }
1271
1334
  rb_obj_freeze(field_map);
1272
- this->field_map = field_map;
1335
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
1273
1336
  }
1274
1337
  }
1275
1338
 
@@ -1357,11 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1357
1420
  t_pg_result *this = pgresult_get_this(self);
1358
1421
  t_typemap *p_typemap;
1359
1422
 
1423
+ rb_check_frozen(self);
1360
1424
  /* Check type of method param */
1361
1425
  TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
1362
1426
 
1363
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1364
- this->p_typemap = RTYPEDDATA_DATA( this->typemap );
1427
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
1428
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
1429
+ this->p_typemap = RTYPEDDATA_DATA( typemap );
1365
1430
 
1366
1431
  return typemap;
1367
1432
  }
@@ -1382,21 +1447,20 @@ pgresult_type_map_get(VALUE self)
1382
1447
  }
1383
1448
 
1384
1449
 
1385
- static void
1450
+ static int
1386
1451
  yield_hash(VALUE self, int ntuples, int nfields, void *data)
1387
1452
  {
1388
1453
  int tuple_num;
1389
- t_pg_result *this = pgresult_get_this(self);
1390
1454
  UNUSED(nfields);
1391
1455
 
1392
1456
  for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1393
1457
  rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1394
1458
  }
1395
1459
 
1396
- pgresult_clear( this );
1460
+ return 1; /* clear the result */
1397
1461
  }
1398
1462
 
1399
- static void
1463
+ static int
1400
1464
  yield_array(VALUE self, int ntuples, int nfields, void *data)
1401
1465
  {
1402
1466
  int row;
@@ -1413,10 +1477,10 @@ yield_array(VALUE self, int ntuples, int nfields, void *data)
1413
1477
  rb_yield( rb_ary_new4( nfields, row_values ));
1414
1478
  }
1415
1479
 
1416
- pgresult_clear( this );
1480
+ return 1; /* clear the result */
1417
1481
  }
1418
1482
 
1419
- static void
1483
+ static int
1420
1484
  yield_tuple(VALUE self, int ntuples, int nfields, void *data)
1421
1485
  {
1422
1486
  int tuple_num;
@@ -1434,17 +1498,19 @@ yield_tuple(VALUE self, int ntuples, int nfields, void *data)
1434
1498
  VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1435
1499
  rb_yield( tuple );
1436
1500
  }
1501
+ return 0; /* don't clear the result */
1437
1502
  }
1438
1503
 
1439
1504
  /* Non-static, and data pointer for use by sequel_pg */
1440
1505
  VALUE
1441
- pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int, void*), void* data)
1506
+ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
1442
1507
  {
1443
1508
  t_pg_result *this;
1444
- int nfields;
1509
+ int nfields, nfields2;
1445
1510
  PGconn *pgconn;
1446
1511
  PGresult *pgresult;
1447
1512
 
1513
+ rb_check_frozen(self);
1448
1514
  RETURN_ENUMERATOR(self, 0, NULL);
1449
1515
 
1450
1516
  this = pgresult_get_this_safe(self);
@@ -1467,7 +1533,15 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int, void*), void* d
1467
1533
  pg_result_check( self );
1468
1534
  }
1469
1535
 
1470
- yielder( self, ntuples, nfields, data );
1536
+ nfields2 = PQnfields(pgresult);
1537
+ if( nfields != nfields2 ){
1538
+ pgresult_clear( this );
1539
+ rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, nfields2);
1540
+ }
1541
+
1542
+ if( yielder( self, ntuples, nfields, data ) ){
1543
+ pgresult_clear( this );
1544
+ }
1471
1545
 
1472
1546
  if( gvl_PQisBusy(pgconn) ){
1473
1547
  /* wait for input (without blocking) before reading each result */
@@ -1478,9 +1552,6 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int, void*), void* d
1478
1552
  if( pgresult == NULL )
1479
1553
  rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
1480
1554
 
1481
- if( nfields != PQnfields(pgresult) )
1482
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, PQnfields(pgresult));
1483
-
1484
1555
  this->pgresult = pgresult;
1485
1556
  }
1486
1557
 
@@ -1584,6 +1655,8 @@ static VALUE
1584
1655
  pgresult_field_name_type_set(VALUE self, VALUE sym)
1585
1656
  {
1586
1657
  t_pg_result *this = pgresult_get_this(self);
1658
+
1659
+ rb_check_frozen(self);
1587
1660
  if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1588
1661
 
1589
1662
  this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
@@ -1617,7 +1690,7 @@ pgresult_field_name_type_get(VALUE self)
1617
1690
  }
1618
1691
 
1619
1692
  void
1620
- init_pg_result()
1693
+ init_pg_result(void)
1621
1694
  {
1622
1695
  sym_string = ID2SYM(rb_intern("string"));
1623
1696
  sym_symbol = ID2SYM(rb_intern("symbol"));
@@ -1630,7 +1703,8 @@ init_pg_result()
1630
1703
 
1631
1704
  /****** PG::Result INSTANCE METHODS: libpq ******/
1632
1705
  rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
1633
- rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1706
+ rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, -1);
1707
+ rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
1634
1708
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1635
1709
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1636
1710
  #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
@@ -1640,12 +1714,14 @@ init_pg_result()
1640
1714
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1641
1715
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1642
1716
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
1717
+ rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
1643
1718
  rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
1644
1719
  rb_define_alias (rb_cPGresult, "check_result", "check");
1645
1720
  rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
1646
1721
  rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
1647
1722
  rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
1648
1723
  rb_define_alias(rb_cPGresult, "num_fields", "nfields");
1724
+ rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
1649
1725
  rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
1650
1726
  rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
1651
1727
  rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
@@ -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()
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()
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()
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()
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()
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
@@ -775,19 +794,15 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
775
794
 
776
795
 
777
796
  void
778
- init_pg_text_encoder()
797
+ init_pg_text_encoder(void)
779
798
  {
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()
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,32 +523,32 @@ 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
  }
546
549
 
547
550
  void
548
- init_pg_tuple()
551
+ init_pg_tuple(void)
549
552
  {
550
553
  rb_cPG_Tuple = rb_define_class_under( rb_mPG, "Tuple", rb_cObject );
551
554
  rb_define_alloc_func( rb_cPG_Tuple, pg_tuple_s_allocate );