pg 1.4.4 → 1.5.9

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 (77) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/Gemfile +6 -0
  4. data/{History.rdoc → History.md} +303 -151
  5. data/README.ja.md +300 -0
  6. data/README.md +286 -0
  7. data/Rakefile +16 -4
  8. data/Rakefile.cross +15 -14
  9. data/certs/kanis@comcard.de.pem +20 -0
  10. data/certs/larskanis-2023.pem +24 -0
  11. data/certs/larskanis-2024.pem +24 -0
  12. data/ext/errorcodes.def +8 -5
  13. data/ext/errorcodes.txt +3 -5
  14. data/ext/extconf.rb +7 -0
  15. data/ext/pg.c +15 -30
  16. data/ext/pg.h +10 -6
  17. data/ext/pg_binary_decoder.c +81 -0
  18. data/ext/pg_binary_encoder.c +224 -0
  19. data/ext/pg_coder.c +16 -7
  20. data/ext/pg_connection.c +220 -82
  21. data/ext/pg_copy_coder.c +315 -22
  22. data/ext/pg_record_coder.c +11 -10
  23. data/ext/pg_result.c +93 -19
  24. data/ext/pg_text_decoder.c +31 -10
  25. data/ext/pg_text_encoder.c +38 -19
  26. data/ext/pg_tuple.c +34 -31
  27. data/ext/pg_type_map.c +3 -2
  28. data/ext/pg_type_map_all_strings.c +2 -2
  29. data/ext/pg_type_map_by_class.c +5 -3
  30. data/ext/pg_type_map_by_column.c +7 -3
  31. data/ext/pg_type_map_by_oid.c +7 -4
  32. data/ext/pg_type_map_in_ruby.c +5 -2
  33. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  34. data/lib/pg/basic_type_map_for_queries.rb +19 -10
  35. data/lib/pg/basic_type_map_for_results.rb +26 -3
  36. data/lib/pg/basic_type_registry.rb +44 -34
  37. data/lib/pg/binary_decoder/date.rb +9 -0
  38. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  39. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  40. data/lib/pg/coder.rb +15 -13
  41. data/lib/pg/connection.rb +158 -64
  42. data/lib/pg/exceptions.rb +13 -0
  43. data/lib/pg/text_decoder/date.rb +21 -0
  44. data/lib/pg/text_decoder/inet.rb +9 -0
  45. data/lib/pg/text_decoder/json.rb +17 -0
  46. data/lib/pg/text_decoder/numeric.rb +9 -0
  47. data/lib/pg/text_decoder/timestamp.rb +30 -0
  48. data/lib/pg/text_encoder/date.rb +13 -0
  49. data/lib/pg/text_encoder/inet.rb +31 -0
  50. data/lib/pg/text_encoder/json.rb +17 -0
  51. data/lib/pg/text_encoder/numeric.rb +9 -0
  52. data/lib/pg/text_encoder/timestamp.rb +24 -0
  53. data/lib/pg/version.rb +1 -1
  54. data/lib/pg.rb +65 -15
  55. data/pg.gemspec +7 -3
  56. data/rakelib/task_extension.rb +1 -1
  57. data.tar.gz.sig +2 -4
  58. metadata +104 -46
  59. metadata.gz.sig +0 -0
  60. data/.appveyor.yml +0 -36
  61. data/.gems +0 -6
  62. data/.gemtest +0 -0
  63. data/.github/workflows/binary-gems.yml +0 -86
  64. data/.github/workflows/source-gem.yml +0 -131
  65. data/.gitignore +0 -13
  66. data/.hgsigs +0 -34
  67. data/.hgtags +0 -41
  68. data/.irbrc +0 -23
  69. data/.pryrc +0 -23
  70. data/.tm_properties +0 -21
  71. data/.travis.yml +0 -49
  72. data/README.ja.rdoc +0 -13
  73. data/README.rdoc +0 -233
  74. data/lib/pg/binary_decoder.rb +0 -23
  75. data/lib/pg/constants.rb +0 -12
  76. data/lib/pg/text_decoder.rb +0 -46
  77. 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
  }
@@ -616,7 +664,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
616
664
  * An example:
617
665
  *
618
666
  * begin
619
- * conn.exec( "SELECT * FROM nonexistant_table" )
667
+ * conn.exec( "SELECT * FROM nonexistent_table" )
620
668
  * rescue PG::Error => err
621
669
  * p [
622
670
  * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
@@ -636,7 +684,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
636
684
  *
637
685
  * Outputs:
638
686
  *
639
- * ["ERROR", "42P01", "relation \"nonexistant_table\" does not exist", nil, nil,
687
+ * ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
640
688
  * "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
641
689
  */
642
690
  static VALUE
@@ -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
  }
@@ -1441,10 +1506,11 @@ VALUE
1441
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,6 +1533,12 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
1467
1533
  pg_result_check( self );
1468
1534
  }
1469
1535
 
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
+
1470
1542
  if( yielder( self, ntuples, nfields, data ) ){
1471
1543
  pgresult_clear( this );
1472
1544
  }
@@ -1480,9 +1552,6 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
1480
1552
  if( pgresult == NULL )
1481
1553
  rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
1482
1554
 
1483
- if( nfields != PQnfields(pgresult) )
1484
- 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));
1485
-
1486
1555
  this->pgresult = pgresult;
1487
1556
  }
1488
1557
 
@@ -1586,6 +1655,8 @@ static VALUE
1586
1655
  pgresult_field_name_type_set(VALUE self, VALUE sym)
1587
1656
  {
1588
1657
  t_pg_result *this = pgresult_get_this(self);
1658
+
1659
+ rb_check_frozen(self);
1589
1660
  if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1590
1661
 
1591
1662
  this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
@@ -1632,7 +1703,8 @@ init_pg_result(void)
1632
1703
 
1633
1704
  /****** PG::Result INSTANCE METHODS: libpq ******/
1634
1705
  rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
1635
- 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);
1636
1708
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1637
1709
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1638
1710
  #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
@@ -1642,12 +1714,14 @@ init_pg_result(void)
1642
1714
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1643
1715
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1644
1716
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
1717
+ rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
1645
1718
  rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
1646
1719
  rb_define_alias (rb_cPGresult, "check_result", "check");
1647
1720
  rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
1648
1721
  rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
1649
1722
  rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
1650
1723
  rb_define_alias(rb_cPGresult, "num_fields", "nfields");
1724
+ rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
1651
1725
  rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
1652
1726
  rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
1653
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;
@@ -164,6 +163,8 @@ pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int f
164
163
  * This is a decoder class for conversion of PostgreSQL numeric types
165
164
  * to Ruby BigDecimal objects.
166
165
  *
166
+ * As soon as this class is used, it requires the 'bigdecimal' gem.
167
+ *
167
168
  */
168
169
  static VALUE
169
170
  pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -171,6 +172,19 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
171
172
  return rb_funcall(rb_cObject, s_id_BigDecimal, 1, rb_str_new(val, len));
172
173
  }
173
174
 
175
+ /* called per autoload when TextDecoder::Numeric is used */
176
+ static VALUE
177
+ init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
178
+ {
179
+ rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
180
+ s_id_BigDecimal = rb_intern("BigDecimal");
181
+
182
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
183
+ pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
184
+
185
+ return Qnil;
186
+ }
187
+
174
188
  /*
175
189
  * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
176
190
  *
@@ -799,6 +813,7 @@ static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, i
799
813
  * This is a decoder class for conversion of PostgreSQL inet type
800
814
  * to Ruby IPAddr values.
801
815
  *
816
+ * As soon as this class is used, it requires the ruby standard library 'ipaddr'.
802
817
  */
803
818
  static VALUE
804
819
  pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -922,8 +937,9 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
922
937
  return ip;
923
938
  }
924
939
 
925
- void
926
- init_pg_text_decoder(void)
940
+ /* called per autoload when TextDecoder::Inet is used */
941
+ static VALUE
942
+ init_pg_text_decoder_inet(VALUE rb_mPG_TextDecoder)
927
943
  {
928
944
  rb_require("ipaddr");
929
945
  s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
@@ -942,14 +958,21 @@ init_pg_text_decoder(void)
942
958
  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
959
  rb_global_variable(&s_vmasks6);
944
960
 
945
- s_id_decode = rb_intern("decode");
961
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
962
+ pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
963
+
964
+ return Qnil;
965
+ }
966
+
967
+
968
+ void
969
+ init_pg_text_decoder(void)
970
+ {
946
971
  s_id_Rational = rb_intern("Rational");
947
972
  s_id_new = rb_intern("new");
948
973
  s_id_utc = rb_intern("utc");
949
974
  s_id_getlocal = rb_intern("getlocal");
950
975
 
951
- rb_require("bigdecimal");
952
- s_id_BigDecimal = rb_intern("BigDecimal");
953
976
  s_nan = rb_eval_string("0.0/0.0");
954
977
  rb_global_variable(&s_nan);
955
978
  s_pos_inf = rb_eval_string("1.0/0.0");
@@ -959,6 +982,8 @@ init_pg_text_decoder(void)
959
982
 
960
983
  /* This module encapsulates all decoder classes with text input format */
961
984
  rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
985
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_inet", init_pg_text_decoder_inet, 0);
986
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_numeric", init_pg_text_decoder_numeric, 0);
962
987
 
963
988
  /* Make RDoc aware of the decoder classes... */
964
989
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
@@ -967,8 +992,6 @@ init_pg_text_decoder(void)
967
992
  pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
968
993
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
969
994
  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
995
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
973
996
  pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
974
997
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
@@ -977,8 +1000,6 @@ init_pg_text_decoder(void)
977
1000
  pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
978
1001
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
979
1002
  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
1003
 
983
1004
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
984
1005
  pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
@@ -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;
@@ -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 );
@@ -673,22 +698,22 @@ static int
673
698
  quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
674
699
  char *ptr1;
675
700
  char *ptr2;
676
- int backslashs = 0;
701
+ int backslashes = 0;
677
702
 
678
703
  /* count required backlashs */
679
704
  for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
680
705
  if (*ptr1 == '\''){
681
- backslashs++;
706
+ backslashes++;
682
707
  }
683
708
  }
684
709
 
685
710
  ptr1 = p_in + strlen;
686
- ptr2 = p_out + strlen + backslashs + 2;
711
+ ptr2 = p_out + strlen + backslashes + 2;
687
712
  /* Write end quote */
688
713
  *--ptr2 = '\'';
689
714
 
690
715
  /* Then store the escaped string on the final position, walking
691
- * right to left, until all backslashs are placed. */
716
+ * right to left, until all backslashes are placed. */
692
717
  while( ptr1 != p_in ) {
693
718
  *--ptr2 = *--ptr1;
694
719
  if(*ptr2 == '\''){
@@ -697,7 +722,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
697
722
  }
698
723
  /* Write start quote */
699
724
  *p_out = '\'';
700
- return strlen + backslashs + 2;
725
+ return strlen + backslashes + 2;
701
726
  }
702
727
 
703
728
 
@@ -780,14 +805,10 @@ init_pg_text_encoder(void)
780
805
  s_id_encode = rb_intern("encode");
781
806
  s_id_to_i = rb_intern("to_i");
782
807
  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
808
 
789
809
  /* This module encapsulates all encoder classes with text output format */
790
810
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
811
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
791
812
 
792
813
  /* Make RDoc aware of the encoder classes... */
793
814
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
@@ -796,8 +817,6 @@ init_pg_text_encoder(void)
796
817
  pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
797
818
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
798
819
  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
820
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
802
821
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
803
822
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */