pg 1.4.6 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +185 -3
  4. data/Gemfile +12 -3
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +75 -41
  7. data/README.md +86 -31
  8. data/Rakefile +95 -14
  9. data/certs/kanis@comcard.de.pem +20 -0
  10. data/certs/larskanis-2024.pem +24 -0
  11. data/ext/errorcodes.def +4 -5
  12. data/ext/errorcodes.txt +2 -5
  13. data/ext/extconf.rb +165 -14
  14. data/ext/gvl_wrappers.c +13 -2
  15. data/ext/gvl_wrappers.h +33 -0
  16. data/ext/pg.c +28 -35
  17. data/ext/pg.h +18 -14
  18. data/ext/pg_binary_decoder.c +231 -0
  19. data/ext/pg_binary_encoder.c +427 -0
  20. data/ext/pg_cancel_connection.c +360 -0
  21. data/ext/pg_coder.c +70 -12
  22. data/ext/pg_connection.c +473 -208
  23. data/ext/pg_copy_coder.c +316 -23
  24. data/ext/pg_record_coder.c +12 -11
  25. data/ext/pg_result.c +102 -30
  26. data/ext/pg_text_decoder.c +31 -10
  27. data/ext/pg_text_encoder.c +58 -26
  28. data/ext/pg_tuple.c +36 -33
  29. data/ext/pg_type_map.c +4 -3
  30. data/ext/pg_type_map_all_strings.c +3 -3
  31. data/ext/pg_type_map_by_class.c +6 -4
  32. data/ext/pg_type_map_by_column.c +9 -4
  33. data/ext/pg_type_map_by_mri_type.c +1 -1
  34. data/ext/pg_type_map_by_oid.c +10 -5
  35. data/ext/pg_type_map_in_ruby.c +6 -3
  36. data/lib/pg/basic_type_map_based_on_result.rb +21 -1
  37. data/lib/pg/basic_type_map_for_queries.rb +23 -10
  38. data/lib/pg/basic_type_map_for_results.rb +26 -3
  39. data/lib/pg/basic_type_registry.rb +46 -36
  40. data/lib/pg/binary_decoder/date.rb +9 -0
  41. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  42. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  43. data/lib/pg/cancel_connection.rb +53 -0
  44. data/lib/pg/coder.rb +18 -14
  45. data/lib/pg/connection.rb +387 -172
  46. data/lib/pg/exceptions.rb +6 -0
  47. data/lib/pg/text_decoder/date.rb +21 -0
  48. data/lib/pg/text_decoder/inet.rb +9 -0
  49. data/lib/pg/text_decoder/json.rb +17 -0
  50. data/lib/pg/text_decoder/numeric.rb +9 -0
  51. data/lib/pg/text_decoder/timestamp.rb +30 -0
  52. data/lib/pg/text_encoder/date.rb +13 -0
  53. data/lib/pg/text_encoder/inet.rb +31 -0
  54. data/lib/pg/text_encoder/json.rb +17 -0
  55. data/lib/pg/text_encoder/numeric.rb +9 -0
  56. data/lib/pg/text_encoder/timestamp.rb +24 -0
  57. data/lib/pg/version.rb +1 -1
  58. data/lib/pg.rb +78 -17
  59. data/misc/yugabyte/Dockerfile +9 -0
  60. data/misc/yugabyte/docker-compose.yml +28 -0
  61. data/misc/yugabyte/pg-test.rb +45 -0
  62. data/pg.gemspec +9 -5
  63. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  64. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  65. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  66. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  67. data/rakelib/pg_gem_helper.rb +64 -0
  68. data.tar.gz.sig +0 -0
  69. metadata +61 -49
  70. metadata.gz.sig +0 -0
  71. data/.appveyor.yml +0 -42
  72. data/.gems +0 -6
  73. data/.gemtest +0 -0
  74. data/.github/workflows/binary-gems.yml +0 -117
  75. data/.github/workflows/source-gem.yml +0 -137
  76. data/.gitignore +0 -19
  77. data/.hgsigs +0 -34
  78. data/.hgtags +0 -41
  79. data/.irbrc +0 -23
  80. data/.pryrc +0 -23
  81. data/.tm_properties +0 -21
  82. data/.travis.yml +0 -49
  83. data/Manifest.txt +0 -72
  84. data/Rakefile.cross +0 -298
  85. data/lib/pg/binary_decoder.rb +0 -23
  86. data/lib/pg/constants.rb +0 -12
  87. data/lib/pg/text_decoder.rb +0 -46
  88. data/lib/pg/text_encoder.rb +0 -59
  89. data/translation/.po4a-version +0 -7
  90. data/translation/po/all.pot +0 -875
  91. data/translation/po/ja.po +0 -868
  92. data/translation/po4a.cfg +0 -9
data/ext/pg_result.c CHANGED
@@ -147,9 +147,7 @@ pgresult_clear( void *_this )
147
147
  t_pg_result *this = (t_pg_result *)_this;
148
148
  if( this->pgresult && !this->autoclear ){
149
149
  PQclear(this->pgresult);
150
- #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
151
150
  rb_gc_adjust_memory_usage(-this->result_size);
152
- #endif
153
151
  }
154
152
  this->result_size = 0;
155
153
  this->nfields = -1;
@@ -180,10 +178,10 @@ static const rb_data_type_t pgresult_type = {
180
178
  pgresult_gc_mark,
181
179
  pgresult_gc_free,
182
180
  pgresult_memsize,
183
- pg_compact_callback(pgresult_gc_compact),
181
+ pgresult_gc_compact,
184
182
  },
185
183
  0, 0,
186
- RUBY_TYPED_FREE_IMMEDIATELY,
184
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
187
185
  };
188
186
 
189
187
  /* Needed by sequel_pg gem, do not delete */
@@ -208,6 +206,8 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
208
206
 
209
207
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
210
208
  this->pgresult = result;
209
+ /* Initialize connection and typemap prior to any object allocations,
210
+ * to make sure valid objects are marked. */
211
211
  this->connection = rb_pgconn;
212
212
  this->typemap = pg_typemap_all_strings;
213
213
  this->p_typemap = RTYPEDDATA_DATA( this->typemap );
@@ -224,7 +224,8 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
224
224
  t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
225
225
 
226
226
  this->enc_idx = p_conn->enc_idx;
227
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
227
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
228
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
228
229
  this->p_typemap = RTYPEDDATA_DATA( this->typemap );
229
230
  this->flags = p_conn->flags;
230
231
  } else {
@@ -250,9 +251,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
250
251
  */
251
252
  this->result_size = pgresult_approx_size(result);
252
253
 
253
- #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
254
254
  rb_gc_adjust_memory_usage(this->result_size);
255
- #endif
256
255
 
257
256
  return self;
258
257
  }
@@ -320,6 +319,9 @@ pg_result_check( VALUE self )
320
319
  case PGRES_COMMAND_OK:
321
320
  #ifdef HAVE_PQENTERPIPELINEMODE
322
321
  case PGRES_PIPELINE_SYNC:
322
+ #endif
323
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
324
+ case PGRES_TUPLES_CHUNK:
323
325
  #endif
324
326
  return self;
325
327
  case PGRES_BAD_RESPONSE:
@@ -374,17 +376,37 @@ VALUE
374
376
  pg_result_clear(VALUE self)
375
377
  {
376
378
  t_pg_result *this = pgresult_get_this(self);
379
+ rb_check_frozen(self);
377
380
  pgresult_clear( this );
378
381
  return Qnil;
379
382
  }
380
383
 
384
+ /*
385
+ * call-seq:
386
+ * res.freeze
387
+ *
388
+ * Freeze the PG::Result object and unlink the result from the related PG::Connection.
389
+ *
390
+ * A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
391
+ * It also denies setting a type_map or field_name_type.
392
+ *
393
+ */
394
+ static VALUE
395
+ pg_result_freeze(VALUE self)
396
+ {
397
+ t_pg_result *this = pgresult_get_this(self);
398
+
399
+ RB_OBJ_WRITE(self, &this->connection, Qnil);
400
+ return rb_call_super(0, NULL);
401
+ }
402
+
381
403
  /*
382
404
  * call-seq:
383
405
  * res.cleared? -> boolean
384
406
  *
385
407
  * Returns +true+ if the backend result memory has been freed.
386
408
  */
387
- VALUE
409
+ static VALUE
388
410
  pgresult_cleared_p( VALUE self )
389
411
  {
390
412
  t_pg_result *this = pgresult_get_this(self);
@@ -401,7 +423,7 @@ pgresult_cleared_p( VALUE self )
401
423
  * 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
424
  *
403
425
  */
404
- VALUE
426
+ static VALUE
405
427
  pgresult_autoclear_p( VALUE self )
406
428
  {
407
429
  t_pg_result *this = pgresult_get_this(self);
@@ -477,7 +499,8 @@ static void pgresult_init_fnames(VALUE self)
477
499
 
478
500
  for( i=0; i<nfields; i++ ){
479
501
  char *cfname = PQfname(this->pgresult, i);
480
- this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
502
+ VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
503
+ RB_OBJ_WRITE(self, &this->fnames[i], fname);
481
504
  this->nfields = i + 1;
482
505
  }
483
506
  this->nfields = nfields;
@@ -525,8 +548,11 @@ static void pgresult_init_fnames(VALUE self)
525
548
  * * +PGRES_FATAL_ERROR+
526
549
  * * +PGRES_COPY_BOTH+
527
550
  * * +PGRES_SINGLE_TUPLE+
551
+ * * +PGRES_TUPLES_CHUNK+
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
  }
@@ -565,14 +613,12 @@ pgresult_error_message(VALUE self)
565
613
  return ret;
566
614
  }
567
615
 
568
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
569
616
  /*
570
617
  * call-seq:
571
618
  * res.verbose_error_message( verbosity, show_context ) -> String
572
619
  *
573
620
  * Returns a reformatted version of the error message associated with a PGresult object.
574
621
  *
575
- * Available since PostgreSQL-9.6
576
622
  */
577
623
  static VALUE
578
624
  pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
@@ -591,7 +637,6 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
591
637
 
592
638
  return ret;
593
639
  }
594
- #endif
595
640
 
596
641
  /*
597
642
  * call-seq:
@@ -616,7 +661,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
616
661
  * An example:
617
662
  *
618
663
  * begin
619
- * conn.exec( "SELECT * FROM nonexistant_table" )
664
+ * conn.exec( "SELECT * FROM nonexistent_table" )
620
665
  * rescue PG::Error => err
621
666
  * p [
622
667
  * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
@@ -636,7 +681,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
636
681
  *
637
682
  * Outputs:
638
683
  *
639
- * ["ERROR", "42P01", "relation \"nonexistant_table\" does not exist", nil, nil,
684
+ * ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
640
685
  * "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
641
686
  */
642
687
  static VALUE
@@ -685,6 +730,21 @@ pgresult_nfields(VALUE self)
685
730
  return INT2NUM(PQnfields(pgresult_get(self)));
686
731
  }
687
732
 
733
+ /*
734
+ * call-seq:
735
+ * res.binary_tuples() -> Integer
736
+ *
737
+ * Returns 1 if the PGresult contains binary data and 0 if it contains text data.
738
+ *
739
+ * 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.
740
+ * Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
741
+ */
742
+ static VALUE
743
+ pgresult_binary_tuples(VALUE self)
744
+ {
745
+ return INT2NUM(PQbinaryTuples(pgresult_get(self)));
746
+ }
747
+
688
748
  /*
689
749
  * call-seq:
690
750
  * res.fname( index ) -> String or Symbol
@@ -1087,7 +1147,7 @@ pgresult_aref(VALUE self, VALUE index)
1087
1147
  }
1088
1148
  /* Store a copy of the filled hash for use at the next row. */
1089
1149
  if( num_tuples > 10 )
1090
- this->tuple_hash = rb_hash_dup(tuple);
1150
+ RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
1091
1151
 
1092
1152
  return tuple;
1093
1153
  }
@@ -1269,7 +1329,7 @@ static void ensure_init_for_tuple(VALUE self)
1269
1329
  rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1270
1330
  }
1271
1331
  rb_obj_freeze(field_map);
1272
- this->field_map = field_map;
1332
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
1273
1333
  }
1274
1334
  }
1275
1335
 
@@ -1357,11 +1417,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1357
1417
  t_pg_result *this = pgresult_get_this(self);
1358
1418
  t_typemap *p_typemap;
1359
1419
 
1420
+ rb_check_frozen(self);
1360
1421
  /* Check type of method param */
1361
1422
  TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
1362
1423
 
1363
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1364
- this->p_typemap = RTYPEDDATA_DATA( this->typemap );
1424
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
1425
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
1426
+ this->p_typemap = RTYPEDDATA_DATA( typemap );
1365
1427
 
1366
1428
  return typemap;
1367
1429
  }
@@ -1441,10 +1503,11 @@ VALUE
1441
1503
  pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
1442
1504
  {
1443
1505
  t_pg_result *this;
1444
- int nfields;
1506
+ int nfields, nfields2;
1445
1507
  PGconn *pgconn;
1446
1508
  PGresult *pgresult;
1447
1509
 
1510
+ rb_check_frozen(self);
1448
1511
  RETURN_ENUMERATOR(self, 0, NULL);
1449
1512
 
1450
1513
  this = pgresult_get_this_safe(self);
@@ -1462,11 +1525,20 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
1462
1525
  return self;
1463
1526
  rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1464
1527
  case PGRES_SINGLE_TUPLE:
1528
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
1529
+ case PGRES_TUPLES_CHUNK:
1530
+ #endif
1465
1531
  break;
1466
1532
  default:
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
 
@@ -1503,7 +1572,7 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
1503
1572
  * wrapping each row into a dedicated result object, it delivers data in nearly
1504
1573
  * the same speed as with ordinary results.
1505
1574
  *
1506
- * The base result must be in status PGRES_SINGLE_TUPLE.
1575
+ * The base result must be in status PGRES_SINGLE_TUPLE or PGRES_TUPLES_CHUNK.
1507
1576
  * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1508
1577
  * A PG::Error is raised for any errors from the server.
1509
1578
  *
@@ -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,22 +1703,23 @@ 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
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1639
1710
  rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1640
1711
  rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1641
- #endif
1642
1712
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1643
1713
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1644
1714
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
1715
+ rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
1645
1716
  rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
1646
1717
  rb_define_alias (rb_cPGresult, "check_result", "check");
1647
1718
  rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
1648
1719
  rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
1649
1720
  rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
1650
1721
  rb_define_alias(rb_cPGresult, "num_fields", "nfields");
1722
+ rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
1651
1723
  rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
1652
1724
  rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
1653
1725
  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 );