pg 1.5.4 → 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 (78) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +106 -4
  4. data/Gemfile +12 -3
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +4 -4
  7. data/README.md +58 -17
  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 +161 -14
  14. data/ext/gvl_wrappers.c +13 -2
  15. data/ext/gvl_wrappers.h +33 -0
  16. data/ext/pg.c +17 -6
  17. data/ext/pg.h +9 -9
  18. data/ext/pg_binary_decoder.c +152 -0
  19. data/ext/pg_binary_encoder.c +211 -8
  20. data/ext/pg_cancel_connection.c +360 -0
  21. data/ext/pg_coder.c +54 -5
  22. data/ext/pg_connection.c +409 -167
  23. data/ext/pg_copy_coder.c +19 -15
  24. data/ext/pg_record_coder.c +7 -7
  25. data/ext/pg_result.c +11 -13
  26. data/ext/pg_text_decoder.c +4 -1
  27. data/ext/pg_text_encoder.c +37 -18
  28. data/ext/pg_tuple.c +2 -2
  29. data/ext/pg_type_map.c +1 -1
  30. data/ext/pg_type_map_all_strings.c +1 -1
  31. data/ext/pg_type_map_by_class.c +1 -1
  32. data/ext/pg_type_map_by_column.c +2 -1
  33. data/ext/pg_type_map_by_mri_type.c +1 -1
  34. data/ext/pg_type_map_by_oid.c +3 -1
  35. data/ext/pg_type_map_in_ruby.c +1 -1
  36. data/lib/pg/basic_type_map_for_queries.rb +15 -7
  37. data/lib/pg/basic_type_registry.rb +16 -4
  38. data/lib/pg/cancel_connection.rb +53 -0
  39. data/lib/pg/coder.rb +4 -2
  40. data/lib/pg/connection.rb +310 -167
  41. data/lib/pg/exceptions.rb +6 -0
  42. data/lib/pg/text_decoder/date.rb +3 -0
  43. data/lib/pg/text_decoder/json.rb +3 -0
  44. data/lib/pg/text_encoder/date.rb +1 -0
  45. data/lib/pg/text_encoder/inet.rb +3 -0
  46. data/lib/pg/text_encoder/json.rb +3 -0
  47. data/lib/pg/version.rb +1 -1
  48. data/lib/pg.rb +23 -8
  49. data/misc/yugabyte/Dockerfile +9 -0
  50. data/misc/yugabyte/docker-compose.yml +28 -0
  51. data/misc/yugabyte/pg-test.rb +45 -0
  52. data/pg.gemspec +8 -4
  53. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  54. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  55. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  56. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  57. data/rakelib/pg_gem_helper.rb +64 -0
  58. data.tar.gz.sig +0 -0
  59. metadata +45 -47
  60. metadata.gz.sig +0 -0
  61. data/.appveyor.yml +0 -42
  62. data/.gems +0 -6
  63. data/.gemtest +0 -0
  64. data/.github/workflows/binary-gems.yml +0 -117
  65. data/.github/workflows/source-gem.yml +0 -141
  66. data/.gitignore +0 -22
  67. data/.hgsigs +0 -34
  68. data/.hgtags +0 -41
  69. data/.irbrc +0 -23
  70. data/.pryrc +0 -23
  71. data/.tm_properties +0 -21
  72. data/.travis.yml +0 -49
  73. data/Manifest.txt +0 -72
  74. data/Rakefile.cross +0 -298
  75. data/translation/.po4a-version +0 -7
  76. data/translation/po/all.pot +0 -936
  77. data/translation/po/ja.po +0 -1036
  78. data/translation/po4a.cfg +0 -12
data/ext/pg_copy_coder.c CHANGED
@@ -51,7 +51,7 @@ static const rb_data_type_t pg_copycoder_type = {
51
51
  pg_copycoder_mark,
52
52
  RUBY_TYPED_DEFAULT_FREE,
53
53
  pg_copycoder_memsize,
54
- pg_compact_callback(pg_copycoder_compact),
54
+ pg_copycoder_compact,
55
55
  },
56
56
  &pg_coder_type,
57
57
  0,
@@ -212,6 +212,7 @@ pg_copycoder_type_map_get(VALUE self)
212
212
  *
213
213
  * See also PG::TextDecoder::CopyRow for the decoding direction with
214
214
  * PG::Connection#get_copy_data .
215
+ * And see PG::BinaryEncoder::CopyRow for an encoder of the COPY binary format.
215
216
  */
216
217
  static int
217
218
  pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
@@ -235,7 +236,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
235
236
  char *ptr1;
236
237
  char *ptr2;
237
238
  int strlen;
238
- int backslashs;
239
+ int backslashes;
239
240
  VALUE subint;
240
241
  VALUE entry;
241
242
 
@@ -286,19 +287,19 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
286
287
  ptr2 = current_out + strlen;
287
288
 
288
289
  /* count required backlashs */
289
- for(backslashs = 0; ptr1 != ptr2; ptr1++) {
290
+ for(backslashes = 0; ptr1 != ptr2; ptr1++) {
290
291
  /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
291
292
  if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
292
- backslashs++;
293
+ backslashes++;
293
294
  }
294
295
  }
295
296
 
296
297
  ptr1 = current_out + strlen;
297
- ptr2 = current_out + strlen + backslashs;
298
+ ptr2 = current_out + strlen + backslashes;
298
299
  current_out = ptr2;
299
300
 
300
301
  /* Then store the escaped string on the final position, walking
301
- * right to left, until all backslashs are placed. */
302
+ * right to left, until all backslashes are placed. */
302
303
  while( ptr1 != ptr2 ) {
303
304
  *--ptr2 = *--ptr1;
304
305
  if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
@@ -358,6 +359,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
358
359
  *
359
360
  * See also PG::BinaryDecoder::CopyRow for the decoding direction with
360
361
  * PG::Connection#get_copy_data .
362
+ * And see PG::TextEncoder::CopyRow for an encoder of the COPY text format.
361
363
  */
362
364
  static int
363
365
  pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
@@ -391,7 +393,7 @@ pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediat
391
393
 
392
394
  switch(TYPE(entry)){
393
395
  case T_NIL:
394
- /* 4 bytes for -1 indicationg a NULL value */
396
+ /* 4 bytes for -1 indicating a NULL value */
395
397
  PG_RB_STR_ENSURE_CAPA( *intermediate, 4, current_out, end_capa_ptr );
396
398
  write_nbo32(-1, current_out);
397
399
  current_out += 4;
@@ -496,6 +498,7 @@ GetDecimalFromHex(char hex)
496
498
  *
497
499
  * See also PG::TextEncoder::CopyRow for the encoding direction with
498
500
  * PG::Connection#put_copy_data .
501
+ * And see PG::BinaryDecoder::CopyRow for a decoder of the COPY binary format.
499
502
  */
500
503
  /*
501
504
  * Parse the current line into separate attributes (fields),
@@ -763,6 +766,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
763
766
  *
764
767
  * See also PG::BinaryEncoder::CopyRow for the encoding direction with
765
768
  * PG::Connection#put_copy_data .
769
+ * And see PG::TextDecoder::CopyRow for a decoder of the COPY text format.
766
770
  */
767
771
  static VALUE
768
772
  pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tuple, int _field, int enc_idx)
@@ -795,26 +799,26 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
795
799
  cur_ptr = input_line;
796
800
  line_end_ptr = input_line + len;
797
801
 
798
- if (cur_ptr + 11 <= line_end_ptr && memcmp(cur_ptr, BinarySignature, 11) == 0){
802
+ if (line_end_ptr - cur_ptr >= 11 && memcmp(cur_ptr, BinarySignature, 11) == 0){
799
803
  /* binary COPY header signature detected -> just drop it */
800
804
  int ext_bytes;
801
805
  cur_ptr += 11;
802
806
 
803
807
  /* read flags */
804
- if (cur_ptr + 4 > line_end_ptr) goto length_error;
808
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
805
809
  cur_ptr += 4;
806
810
 
807
811
  /* read header extensions */
808
- if (cur_ptr + 4 > line_end_ptr) goto length_error;
812
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
809
813
  ext_bytes = read_nbo32(cur_ptr);
810
814
  if (ext_bytes < 0) goto length_error;
811
815
  cur_ptr += 4;
812
- if (cur_ptr + ext_bytes > line_end_ptr) goto length_error;
816
+ if (line_end_ptr - cur_ptr < ext_bytes ) goto length_error;
813
817
  cur_ptr += ext_bytes;
814
818
  }
815
819
 
816
820
  /* read row header */
817
- if (cur_ptr + 2 > line_end_ptr) goto length_error;
821
+ if (line_end_ptr - cur_ptr < 2 ) goto length_error;
818
822
  nfields = read_nbo16(cur_ptr);
819
823
  cur_ptr += 2;
820
824
 
@@ -827,10 +831,9 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
827
831
 
828
832
  for( fieldno = 0; fieldno < nfields; fieldno++){
829
833
  long input_len;
830
- VALUE field_value;
831
834
 
832
835
  /* read field size */
833
- if (cur_ptr + 4 > line_end_ptr) goto length_error;
836
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
834
837
  input_len = read_nbo32(cur_ptr);
835
838
  cur_ptr += 4;
836
839
 
@@ -839,7 +842,8 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
839
842
  /* NULL indicator */
840
843
  rb_ary_push(array, Qnil);
841
844
  } else {
842
- if (cur_ptr + input_len > line_end_ptr) goto length_error;
845
+ VALUE field_value;
846
+ if (line_end_ptr - cur_ptr < input_len ) goto length_error;
843
847
 
844
848
  /* copy input data to field_str */
845
849
  PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
@@ -43,7 +43,7 @@ static const rb_data_type_t pg_recordcoder_type = {
43
43
  pg_recordcoder_mark,
44
44
  RUBY_TYPED_DEFAULT_FREE,
45
45
  pg_recordcoder_memsize,
46
- pg_compact_callback(pg_recordcoder_compact),
46
+ pg_recordcoder_compact,
47
47
  },
48
48
  &pg_coder_type,
49
49
  0,
@@ -198,7 +198,7 @@ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate
198
198
  char *ptr1;
199
199
  char *ptr2;
200
200
  long strlen;
201
- int backslashs;
201
+ int backslashes;
202
202
  VALUE subint;
203
203
  VALUE entry;
204
204
 
@@ -249,19 +249,19 @@ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate
249
249
  ptr2 = current_out + strlen;
250
250
 
251
251
  /* count required backlashs */
252
- for(backslashs = 0; ptr1 != ptr2; ptr1++) {
252
+ for(backslashes = 0; ptr1 != ptr2; ptr1++) {
253
253
  /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
254
254
  if(*ptr1 == '"' || *ptr1 == '\\'){
255
- backslashs++;
255
+ backslashes++;
256
256
  }
257
257
  }
258
258
 
259
259
  ptr1 = current_out + strlen;
260
- ptr2 = current_out + strlen + backslashs;
260
+ ptr2 = current_out + strlen + backslashes;
261
261
  current_out = ptr2;
262
262
 
263
263
  /* Then store the escaped string on the final position, walking
264
- * right to left, until all backslashs are placed. */
264
+ * right to left, until all backslashes are placed. */
265
265
  while( ptr1 != ptr2 ) {
266
266
  *--ptr2 = *--ptr1;
267
267
  if(*ptr1 == '"' || *ptr1 == '\\'){
@@ -340,7 +340,7 @@ record_isspace(char ch)
340
340
  * conn.exec("SELECT * FROM my_table").map_types!(PG::TypeMapByColumn.new([deco]*2)).to_a
341
341
  * # => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]
342
342
  *
343
- * It's more very convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.
343
+ * It's more convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.
344
344
  * # Fetch a NULL record of our type to retrieve the OIDs of the two fields "r" and "i"
345
345
  * oids = conn.exec( "SELECT (NULL::complex).*" )
346
346
  * # Build a type map (PG::TypeMapByColumn) for decoding the "complex" type
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,7 +178,7 @@ 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
184
  RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
@@ -253,9 +251,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
253
251
  */
254
252
  this->result_size = pgresult_approx_size(result);
255
253
 
256
- #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
257
254
  rb_gc_adjust_memory_usage(this->result_size);
258
- #endif
259
255
 
260
256
  return self;
261
257
  }
@@ -323,6 +319,9 @@ pg_result_check( VALUE self )
323
319
  case PGRES_COMMAND_OK:
324
320
  #ifdef HAVE_PQENTERPIPELINEMODE
325
321
  case PGRES_PIPELINE_SYNC:
322
+ #endif
323
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
324
+ case PGRES_TUPLES_CHUNK:
326
325
  #endif
327
326
  return self;
328
327
  case PGRES_BAD_RESPONSE:
@@ -549,6 +548,7 @@ static void pgresult_init_fnames(VALUE self)
549
548
  * * +PGRES_FATAL_ERROR+
550
549
  * * +PGRES_COPY_BOTH+
551
550
  * * +PGRES_SINGLE_TUPLE+
551
+ * * +PGRES_TUPLES_CHUNK+
552
552
  * * +PGRES_PIPELINE_SYNC+
553
553
  * * +PGRES_PIPELINE_ABORTED+
554
554
  *
@@ -613,14 +613,12 @@ pgresult_error_message(VALUE self)
613
613
  return ret;
614
614
  }
615
615
 
616
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
617
616
  /*
618
617
  * call-seq:
619
618
  * res.verbose_error_message( verbosity, show_context ) -> String
620
619
  *
621
620
  * Returns a reformatted version of the error message associated with a PGresult object.
622
621
  *
623
- * Available since PostgreSQL-9.6
624
622
  */
625
623
  static VALUE
626
624
  pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
@@ -639,7 +637,6 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
639
637
 
640
638
  return ret;
641
639
  }
642
- #endif
643
640
 
644
641
  /*
645
642
  * call-seq:
@@ -664,7 +661,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
664
661
  * An example:
665
662
  *
666
663
  * begin
667
- * conn.exec( "SELECT * FROM nonexistant_table" )
664
+ * conn.exec( "SELECT * FROM nonexistent_table" )
668
665
  * rescue PG::Error => err
669
666
  * p [
670
667
  * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
@@ -684,7 +681,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
684
681
  *
685
682
  * Outputs:
686
683
  *
687
- * ["ERROR", "42P01", "relation \"nonexistant_table\" does not exist", nil, nil,
684
+ * ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
688
685
  * "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
689
686
  */
690
687
  static VALUE
@@ -1528,6 +1525,9 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
1528
1525
  return self;
1529
1526
  rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1530
1527
  case PGRES_SINGLE_TUPLE:
1528
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
1529
+ case PGRES_TUPLES_CHUNK:
1530
+ #endif
1531
1531
  break;
1532
1532
  default:
1533
1533
  pg_result_check( self );
@@ -1572,7 +1572,7 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
1572
1572
  * wrapping each row into a dedicated result object, it delivers data in nearly
1573
1573
  * the same speed as with ordinary results.
1574
1574
  *
1575
- * 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.
1576
1576
  * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1577
1577
  * A PG::Error is raised for any errors from the server.
1578
1578
  *
@@ -1707,10 +1707,8 @@ init_pg_result(void)
1707
1707
  rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
1708
1708
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1709
1709
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1710
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1711
1710
  rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1712
1711
  rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1713
- #endif
1714
1712
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1715
1713
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1716
1714
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
@@ -163,6 +163,8 @@ pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int f
163
163
  * This is a decoder class for conversion of PostgreSQL numeric types
164
164
  * to Ruby BigDecimal objects.
165
165
  *
166
+ * As soon as this class is used, it requires the 'bigdecimal' gem.
167
+ *
166
168
  */
167
169
  static VALUE
168
170
  pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -174,7 +176,7 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
174
176
  static VALUE
175
177
  init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
176
178
  {
177
- rb_require("bigdecimal");
179
+ rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
178
180
  s_id_BigDecimal = rb_intern("BigDecimal");
179
181
 
180
182
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
@@ -811,6 +813,7 @@ static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, i
811
813
  * This is a decoder class for conversion of PostgreSQL inet type
812
814
  * to Ruby IPAddr values.
813
815
  *
816
+ * As soon as this class is used, it requires the ruby standard library 'ipaddr'.
814
817
  */
815
818
  static VALUE
816
819
  pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -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;
@@ -227,7 +231,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
227
231
  *
228
232
  */
229
233
  static int
230
- pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
234
+ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate1, int enc_idx)
231
235
  {
232
236
  if(out){
233
237
  double dvalue = NUM2DBL(value);
@@ -235,7 +239,6 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
235
239
  int neg = 0;
236
240
  int exp2i, exp10i, i;
237
241
  unsigned long long ll, remainder, oldval;
238
- VALUE intermediate;
239
242
 
240
243
  /* Cast to the same strings as value.to_s . */
241
244
  if( isinf(dvalue) ){
@@ -279,6 +282,7 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
279
282
 
280
283
  if( exp10i <= -5 || exp10i >= 15 ) {
281
284
  /* Write the float in exponent format (1.23e45) */
285
+ VALUE intermediate;
282
286
 
283
287
  /* write fraction digits from right to left */
284
288
  for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
@@ -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)
@@ -377,7 +383,7 @@ init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
377
383
  {
378
384
  s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
379
385
  rb_global_variable(&s_str_F);
380
- rb_require("bigdecimal");
386
+ rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
381
387
  s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
382
388
 
383
389
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
@@ -437,7 +443,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
437
443
  t_pg_composite_coder *this = _this;
438
444
  char *ptr1;
439
445
  char *ptr2;
440
- int backslashs = 0;
446
+ int backslashes = 0;
441
447
  int needquote;
442
448
 
443
449
  /* count data plus backslashes; detect chars needing quotes */
@@ -454,7 +460,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
454
460
 
455
461
  if (ch == '"' || ch == '\\'){
456
462
  needquote = 1;
457
- backslashs++;
463
+ backslashes++;
458
464
  } else if (ch == '{' || ch == '}' || ch == this->delimiter ||
459
465
  ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
460
466
  needquote = 1;
@@ -463,12 +469,12 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
463
469
 
464
470
  if( needquote ){
465
471
  ptr1 = p_in + strlen;
466
- ptr2 = p_out + strlen + backslashs + 2;
472
+ ptr2 = p_out + strlen + backslashes + 2;
467
473
  /* Write end quote */
468
474
  *--ptr2 = '"';
469
475
 
470
476
  /* Then store the escaped string on the final position, walking
471
- * right to left, until all backslashs are placed. */
477
+ * right to left, until all backslashes are placed. */
472
478
  while( ptr1 != p_in ) {
473
479
  *--ptr2 = *--ptr1;
474
480
  if(*ptr2 == '"' || *ptr2 == '\\'){
@@ -477,7 +483,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
477
483
  }
478
484
  /* Write start quote */
479
485
  *p_out = '"';
480
- return strlen + backslashs + 2;
486
+ return strlen + backslashes + 2;
481
487
  } else {
482
488
  if( p_in != p_out )
483
489
  memcpy( p_out, p_in, strlen );
@@ -531,7 +537,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
531
537
  }
532
538
 
533
539
  static char *
534
- write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
540
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx, int dimension)
535
541
  {
536
542
  int i;
537
543
 
@@ -539,6 +545,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
539
545
  current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
540
546
  *current_out++ = '{';
541
547
 
548
+ if( RARRAY_LEN(value) == 0 && this->dimensions >= 0 && dimension != this->dimensions ){
549
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
550
+ }
551
+
542
552
  for( i=0; i<RARRAY_LEN(value); i++){
543
553
  VALUE entry = rb_ary_entry(value, i);
544
554
 
@@ -548,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
548
558
  }
549
559
 
550
560
  switch(TYPE(entry)){
551
- case T_ARRAY:
552
- current_out = write_array(this, entry, current_out, string, quote, enc_idx);
553
- break;
554
561
  case T_NIL:
562
+ if( this->dimensions >= 0 && dimension != this->dimensions ){
563
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
564
+ }
555
565
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
556
566
  *current_out++ = 'N';
557
567
  *current_out++ = 'U';
558
568
  *current_out++ = 'L';
559
569
  *current_out++ = 'L';
560
570
  break;
571
+ case T_ARRAY:
572
+ if( this->dimensions < 0 || dimension < this->dimensions ){
573
+ current_out = write_array(this, entry, current_out, string, quote, enc_idx, dimension+1);
574
+ break;
575
+ }
576
+ /* Number of dimensions reached -> handle array as normal value */
561
577
  default:
578
+ if( this->dimensions >= 0 && dimension != this->dimensions ){
579
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
580
+ }
562
581
  current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
563
582
  }
564
583
  }
@@ -590,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
590
609
  VALUE out_str = rb_str_new(NULL, 0);
591
610
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
592
611
 
593
- end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
612
+ end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx, 1);
594
613
 
595
614
  rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
596
615
  *intermediate = out_str;
@@ -692,22 +711,22 @@ static int
692
711
  quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
693
712
  char *ptr1;
694
713
  char *ptr2;
695
- int backslashs = 0;
714
+ int backslashes = 0;
696
715
 
697
716
  /* count required backlashs */
698
717
  for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
699
718
  if (*ptr1 == '\''){
700
- backslashs++;
719
+ backslashes++;
701
720
  }
702
721
  }
703
722
 
704
723
  ptr1 = p_in + strlen;
705
- ptr2 = p_out + strlen + backslashs + 2;
724
+ ptr2 = p_out + strlen + backslashes + 2;
706
725
  /* Write end quote */
707
726
  *--ptr2 = '\'';
708
727
 
709
728
  /* Then store the escaped string on the final position, walking
710
- * right to left, until all backslashs are placed. */
729
+ * right to left, until all backslashes are placed. */
711
730
  while( ptr1 != p_in ) {
712
731
  *--ptr2 = *--ptr1;
713
732
  if(*ptr2 == '\''){
@@ -716,7 +735,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
716
735
  }
717
736
  /* Write start quote */
718
737
  *p_out = '\'';
719
- return strlen + backslashs + 2;
738
+ return strlen + backslashes + 2;
720
739
  }
721
740
 
722
741
 
data/ext/pg_tuple.c CHANGED
@@ -125,7 +125,7 @@ static const rb_data_type_t pg_tuple_type = {
125
125
  pg_tuple_gc_mark,
126
126
  pg_tuple_gc_free,
127
127
  pg_tuple_memsize,
128
- pg_compact_callback(pg_tuple_gc_compact),
128
+ pg_tuple_gc_compact,
129
129
  },
130
130
  0, 0,
131
131
  RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
@@ -135,7 +135,7 @@ static const rb_data_type_t pg_tuple_type = {
135
135
  * Document-method: allocate
136
136
  *
137
137
  * call-seq:
138
- * PG::VeryTuple.allocate -> obj
138
+ * PG::Tuple.allocate -> obj
139
139
  */
140
140
  static VALUE
141
141
  pg_tuple_s_allocate( VALUE klass )
data/ext/pg_type_map.c CHANGED
@@ -33,7 +33,7 @@ const rb_data_type_t pg_typemap_type = {
33
33
  pg_typemap_mark,
34
34
  RUBY_TYPED_DEFAULT_FREE,
35
35
  pg_typemap_memsize,
36
- pg_compact_callback(pg_typemap_compact),
36
+ pg_typemap_compact,
37
37
  },
38
38
  0,
39
39
  0,
@@ -14,7 +14,7 @@ static const rb_data_type_t pg_tmas_type = {
14
14
  pg_typemap_mark,
15
15
  RUBY_TYPED_DEFAULT_FREE,
16
16
  pg_typemap_memsize,
17
- pg_compact_callback(pg_typemap_compact),
17
+ pg_typemap_compact,
18
18
  },
19
19
  &pg_typemap_type,
20
20
  0,
@@ -153,7 +153,7 @@ static const rb_data_type_t pg_tmbk_type = {
153
153
  pg_tmbk_mark,
154
154
  RUBY_TYPED_DEFAULT_FREE,
155
155
  pg_tmbk_memsize,
156
- pg_compact_callback(pg_tmbk_compact),
156
+ pg_tmbk_compact,
157
157
  },
158
158
  &pg_typemap_type,
159
159
  0,
@@ -54,6 +54,7 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
54
54
  t_tmbc *this = RTYPEDDATA_DATA( self );
55
55
  t_typemap *default_tm;
56
56
 
57
+ Check_Type(params, T_ARRAY);
57
58
  nfields = (int)RARRAY_LEN( params );
58
59
  if ( this->nfields != nfields ) {
59
60
  rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
@@ -228,7 +229,7 @@ static const rb_data_type_t pg_tmbc_type = {
228
229
  pg_tmbc_mark,
229
230
  pg_tmbc_free,
230
231
  pg_tmbc_memsize,
231
- pg_compact_callback(pg_tmbc_compact),
232
+ pg_tmbc_compact,
232
233
  },
233
234
  &pg_typemap_type,
234
235
  0,
@@ -130,7 +130,7 @@ static const rb_data_type_t pg_tmbmt_type = {
130
130
  pg_tmbmt_mark,
131
131
  RUBY_TYPED_DEFAULT_FREE,
132
132
  pg_tmbmt_memsize,
133
- pg_compact_callback(pg_tmbmt_compact),
133
+ pg_tmbmt_compact,
134
134
  },
135
135
  &pg_typemap_type,
136
136
  0,
@@ -190,7 +190,7 @@ static const rb_data_type_t pg_tmbo_type = {
190
190
  pg_tmbo_mark,
191
191
  RUBY_TYPED_DEFAULT_FREE,
192
192
  pg_tmbo_memsize,
193
- pg_compact_callback(pg_tmbo_compact),
193
+ pg_tmbo_compact,
194
194
  },
195
195
  &pg_typemap_type,
196
196
  0,
@@ -315,6 +315,8 @@ pg_tmbo_coders( VALUE self )
315
315
  * The type map will do Hash lookups for each result value, if the number of rows
316
316
  * is below or equal +number+.
317
317
  *
318
+ * Default is 10.
319
+ *
318
320
  */
319
321
  static VALUE
320
322
  pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
@@ -40,7 +40,7 @@ static const rb_data_type_t pg_tmir_type = {
40
40
  pg_typemap_mark,
41
41
  RUBY_TYPED_DEFAULT_FREE,
42
42
  pg_tmir_memsize,
43
- pg_compact_callback(pg_tmir_compact),
43
+ pg_tmir_compact,
44
44
  },
45
45
  &pg_typemap_type,
46
46
  0,
@@ -53,14 +53,18 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
53
53
  @coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
54
54
  @array_encoders_by_klass = array_encoders_by_klass
55
55
  @encode_array_as = :array
56
- @if_undefined = if_undefined || method(:raise_undefined_type).to_proc
56
+ @if_undefined = if_undefined || UndefinedDefault
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}"
60
+ class UndefinedDefault
61
+ def self.call(oid_name, format)
62
+ raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
63
+ end
62
64
  end
63
65
 
66
+ private_constant :UndefinedDefault
67
+
64
68
  # Change the mechanism that is used to encode ruby array values
65
69
  #
66
70
  # Possible values:
@@ -166,6 +170,12 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
166
170
  @textarray_encoder
167
171
  end
168
172
 
173
+ begin
174
+ PG.require_bigdecimal_without_warning
175
+ has_bigdecimal = true
176
+ rescue LoadError
177
+ end
178
+
169
179
  DEFAULT_TYPE_MAP = PG.make_shareable({
170
180
  TrueClass => [1, 'bool', 'bool'],
171
181
  FalseClass => [1, 'bool', 'bool'],
@@ -173,7 +183,6 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
173
183
  # to unnecessary type conversions on server side.
174
184
  Integer => [0, 'int8'],
175
185
  Float => [0, 'float8'],
176
- BigDecimal => [0, 'numeric'],
177
186
  Time => [0, 'timestamptz'],
178
187
  # We use text format and no type OID for IPAddr, because setting the OID can lead
179
188
  # to unnecessary inet/cidr conversions on the server side.
@@ -181,7 +190,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
181
190
  Hash => [0, 'json'],
182
191
  Array => :get_array_type,
183
192
  BinaryData => [1, 'bytea'],
184
- })
193
+ }.merge(has_bigdecimal ? {BigDecimal => [0, 'numeric']} : {}))
185
194
  private_constant :DEFAULT_TYPE_MAP
186
195
 
187
196
  DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
@@ -190,9 +199,8 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
190
199
  Integer => [0, '_int8'],
191
200
  String => [0, '_text'],
192
201
  Float => [0, '_float8'],
193
- BigDecimal => [0, '_numeric'],
194
202
  Time => [0, '_timestamptz'],
195
203
  IPAddr => [0, '_inet'],
196
- })
204
+ }.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
197
205
  private_constant :DEFAULT_ARRAY_TYPE_MAP
198
206
  end