pg 1.2.3 → 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 (135) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +986 -0
  4. data/Gemfile +23 -0
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +300 -0
  7. data/README.md +327 -0
  8. data/Rakefile +123 -144
  9. data/certs/ged.pem +24 -0
  10. data/certs/kanis@comcard.de.pem +20 -0
  11. data/certs/larskanis-2022.pem +26 -0
  12. data/certs/larskanis-2023.pem +24 -0
  13. data/certs/larskanis-2024.pem +24 -0
  14. data/ext/errorcodes.def +16 -5
  15. data/ext/errorcodes.rb +0 -0
  16. data/ext/errorcodes.txt +5 -5
  17. data/ext/extconf.rb +259 -33
  18. data/ext/gvl_wrappers.c +17 -2
  19. data/ext/gvl_wrappers.h +56 -0
  20. data/ext/pg.c +89 -63
  21. data/ext/pg.h +31 -8
  22. data/ext/pg_binary_decoder.c +232 -1
  23. data/ext/pg_binary_encoder.c +428 -1
  24. data/ext/pg_cancel_connection.c +360 -0
  25. data/ext/pg_coder.c +148 -36
  26. data/ext/pg_connection.c +1365 -817
  27. data/ext/pg_copy_coder.c +360 -38
  28. data/ext/pg_errors.c +1 -1
  29. data/ext/pg_record_coder.c +56 -25
  30. data/ext/pg_result.c +187 -76
  31. data/ext/pg_text_decoder.c +32 -11
  32. data/ext/pg_text_encoder.c +65 -33
  33. data/ext/pg_tuple.c +84 -61
  34. data/ext/pg_type_map.c +44 -10
  35. data/ext/pg_type_map_all_strings.c +17 -3
  36. data/ext/pg_type_map_by_class.c +54 -27
  37. data/ext/pg_type_map_by_column.c +74 -31
  38. data/ext/pg_type_map_by_mri_type.c +48 -19
  39. data/ext/pg_type_map_by_oid.c +61 -27
  40. data/ext/pg_type_map_in_ruby.c +55 -21
  41. data/ext/pg_util.c +2 -2
  42. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  43. data/lib/pg/basic_type_map_for_queries.rb +206 -0
  44. data/lib/pg/basic_type_map_for_results.rb +104 -0
  45. data/lib/pg/basic_type_registry.rb +311 -0
  46. data/lib/pg/binary_decoder/date.rb +9 -0
  47. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  48. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  49. data/lib/pg/cancel_connection.rb +53 -0
  50. data/lib/pg/coder.rb +18 -14
  51. data/lib/pg/connection.rb +894 -91
  52. data/lib/pg/exceptions.rb +20 -1
  53. data/lib/pg/text_decoder/date.rb +21 -0
  54. data/lib/pg/text_decoder/inet.rb +9 -0
  55. data/lib/pg/text_decoder/json.rb +17 -0
  56. data/lib/pg/text_decoder/numeric.rb +9 -0
  57. data/lib/pg/text_decoder/timestamp.rb +30 -0
  58. data/lib/pg/text_encoder/date.rb +13 -0
  59. data/lib/pg/text_encoder/inet.rb +31 -0
  60. data/lib/pg/text_encoder/json.rb +17 -0
  61. data/lib/pg/text_encoder/numeric.rb +9 -0
  62. data/lib/pg/text_encoder/timestamp.rb +24 -0
  63. data/lib/pg/version.rb +4 -0
  64. data/lib/pg.rb +109 -39
  65. data/misc/openssl-pg-segfault.rb +31 -0
  66. data/misc/postgres/History.txt +9 -0
  67. data/misc/postgres/Manifest.txt +5 -0
  68. data/misc/postgres/README.txt +21 -0
  69. data/misc/postgres/Rakefile +21 -0
  70. data/misc/postgres/lib/postgres.rb +16 -0
  71. data/misc/ruby-pg/History.txt +9 -0
  72. data/misc/ruby-pg/Manifest.txt +5 -0
  73. data/misc/ruby-pg/README.txt +21 -0
  74. data/misc/ruby-pg/Rakefile +21 -0
  75. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  76. data/misc/yugabyte/Dockerfile +9 -0
  77. data/misc/yugabyte/docker-compose.yml +28 -0
  78. data/misc/yugabyte/pg-test.rb +45 -0
  79. data/pg.gemspec +38 -0
  80. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  81. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  82. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  83. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  84. data/rakelib/pg_gem_helper.rb +64 -0
  85. data/rakelib/task_extension.rb +46 -0
  86. data/sample/array_insert.rb +20 -0
  87. data/sample/async_api.rb +102 -0
  88. data/sample/async_copyto.rb +39 -0
  89. data/sample/async_mixed.rb +56 -0
  90. data/sample/check_conn.rb +21 -0
  91. data/sample/copydata.rb +71 -0
  92. data/sample/copyfrom.rb +81 -0
  93. data/sample/copyto.rb +19 -0
  94. data/sample/cursor.rb +21 -0
  95. data/sample/disk_usage_report.rb +177 -0
  96. data/sample/issue-119.rb +94 -0
  97. data/sample/losample.rb +69 -0
  98. data/sample/minimal-testcase.rb +17 -0
  99. data/sample/notify_wait.rb +72 -0
  100. data/sample/pg_statistics.rb +285 -0
  101. data/sample/replication_monitor.rb +222 -0
  102. data/sample/test_binary_values.rb +33 -0
  103. data/sample/wal_shipper.rb +434 -0
  104. data/sample/warehouse_partitions.rb +311 -0
  105. data.tar.gz.sig +0 -0
  106. metadata +139 -213
  107. metadata.gz.sig +0 -0
  108. data/.gemtest +0 -0
  109. data/ChangeLog +0 -0
  110. data/History.rdoc +0 -578
  111. data/Manifest.txt +0 -73
  112. data/README.ja.rdoc +0 -13
  113. data/README.rdoc +0 -213
  114. data/Rakefile.cross +0 -299
  115. data/lib/pg/basic_type_mapping.rb +0 -522
  116. data/lib/pg/binary_decoder.rb +0 -23
  117. data/lib/pg/constants.rb +0 -12
  118. data/lib/pg/text_decoder.rb +0 -46
  119. data/lib/pg/text_encoder.rb +0 -59
  120. data/spec/data/expected_trace.out +0 -26
  121. data/spec/data/random_binary_data +0 -0
  122. data/spec/helpers.rb +0 -380
  123. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  124. data/spec/pg/connection_spec.rb +0 -1949
  125. data/spec/pg/connection_sync_spec.rb +0 -41
  126. data/spec/pg/result_spec.rb +0 -681
  127. data/spec/pg/tuple_spec.rb +0 -333
  128. data/spec/pg/type_map_by_class_spec.rb +0 -138
  129. data/spec/pg/type_map_by_column_spec.rb +0 -226
  130. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  131. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  132. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  133. data/spec/pg/type_map_spec.rb +0 -22
  134. data/spec/pg/type_spec.rb +0 -1123
  135. data/spec/pg_spec.rb +0 -50
@@ -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)
@@ -854,7 +869,7 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
854
869
 
855
870
  ip_int_native = read_nbo32(dst);
856
871
 
857
- /* Work around broken IPAddr behavior of convering portion
872
+ /* Work around broken IPAddr behavior of converting portion
858
873
  of address after netmask to 0 */
859
874
  switch (mask) {
860
875
  case 0:
@@ -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()
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()
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()
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()
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()
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;
@@ -191,7 +195,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
191
195
  if (neg)
192
196
  *out++ = '-';
193
197
 
194
- len = out - start;
198
+ len = (int)(out - start);
195
199
 
196
200
  /* Reverse string. */
197
201
  out--;
@@ -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) ){
@@ -252,7 +255,7 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
252
255
  }
253
256
 
254
257
  /*
255
- * The following computaion is roughly a conversion kind of
258
+ * The following computation is roughly a conversion kind of
256
259
  * sprintf( out, "%.16E", dvalue);
257
260
  */
258
261
 
@@ -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)
@@ -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
@@ -403,11 +428,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
403
428
  *optr++ = hextab[c >> 4];
404
429
  *optr++ = hextab[c & 0xf];
405
430
  }
406
- return optr - out;
431
+ return (int)(optr - out);
407
432
  }else{
408
433
  *intermediate = rb_obj_as_string(value);
409
434
  /* The output starts with "\x" and each character is converted to hex. */
410
- return 2 + RSTRING_LEN(*intermediate) * 2;
435
+ return 2 + RSTRING_LENINT(*intermediate) * 2;
411
436
  }
412
437
  }
413
438
 
@@ -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 );
@@ -512,7 +537,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
512
537
  }
513
538
 
514
539
  static char *
515
- 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)
516
541
  {
517
542
  int i;
518
543
 
@@ -520,6 +545,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
520
545
  current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
521
546
  *current_out++ = '{';
522
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
+
523
552
  for( i=0; i<RARRAY_LEN(value); i++){
524
553
  VALUE entry = rb_ary_entry(value, i);
525
554
 
@@ -529,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
529
558
  }
530
559
 
531
560
  switch(TYPE(entry)){
532
- case T_ARRAY:
533
- current_out = write_array(this, entry, current_out, string, quote, enc_idx);
534
- break;
535
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
+ }
536
565
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
537
566
  *current_out++ = 'N';
538
567
  *current_out++ = 'U';
539
568
  *current_out++ = 'L';
540
569
  *current_out++ = 'L';
541
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 */
542
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
+ }
543
581
  current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
544
582
  }
545
583
  }
@@ -571,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
571
609
  VALUE out_str = rb_str_new(NULL, 0);
572
610
  PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
573
611
 
574
- 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);
575
613
 
576
614
  rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
577
615
  *intermediate = out_str;
@@ -610,8 +648,8 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
610
648
  static char *
611
649
  pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
612
650
  {
613
- int i;
614
- int nr_elems;
651
+ long i;
652
+ long nr_elems;
615
653
 
616
654
  Check_Type(value, T_ARRAY);
617
655
  nr_elems = RARRAY_LEN(value);
@@ -673,22 +711,22 @@ static int
673
711
  quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
674
712
  char *ptr1;
675
713
  char *ptr2;
676
- int backslashs = 0;
714
+ int backslashes = 0;
677
715
 
678
716
  /* count required backlashs */
679
717
  for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
680
718
  if (*ptr1 == '\''){
681
- backslashs++;
719
+ backslashes++;
682
720
  }
683
721
  }
684
722
 
685
723
  ptr1 = p_in + strlen;
686
- ptr2 = p_out + strlen + backslashs + 2;
724
+ ptr2 = p_out + strlen + backslashes + 2;
687
725
  /* Write end quote */
688
726
  *--ptr2 = '\'';
689
727
 
690
728
  /* Then store the escaped string on the final position, walking
691
- * right to left, until all backslashs are placed. */
729
+ * right to left, until all backslashes are placed. */
692
730
  while( ptr1 != p_in ) {
693
731
  *--ptr2 = *--ptr1;
694
732
  if(*ptr2 == '\''){
@@ -697,7 +735,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
697
735
  }
698
736
  /* Write start quote */
699
737
  *p_out = '\'';
700
- return strlen + backslashs + 2;
738
+ return strlen + backslashes + 2;
701
739
  }
702
740
 
703
741
 
@@ -775,19 +813,15 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
775
813
 
776
814
 
777
815
  void
778
- init_pg_text_encoder()
816
+ init_pg_text_encoder(void)
779
817
  {
780
818
  s_id_encode = rb_intern("encode");
781
819
  s_id_to_i = rb_intern("to_i");
782
820
  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
821
 
789
822
  /* This module encapsulates all encoder classes with text output format */
790
823
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
824
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
791
825
 
792
826
  /* Make RDoc aware of the encoder classes... */
793
827
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
@@ -796,8 +830,6 @@ init_pg_text_encoder()
796
830
  pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
797
831
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
798
832
  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
833
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
802
834
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
803
835
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */