pg 1.5.4 → 1.6.3

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 (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +132 -4
  4. data/Gemfile +15 -6
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +4 -4
  7. data/README.md +66 -23
  8. data/Rakefile +81 -14
  9. data/certs/kanis@comcard.de.pem +20 -0
  10. data/certs/larskanis-2024.pem +24 -0
  11. data/ext/errorcodes.def +11 -3
  12. data/ext/errorcodes.rb +1 -1
  13. data/ext/errorcodes.txt +9 -5
  14. data/ext/extconf.rb +192 -15
  15. data/ext/gvl_wrappers.c +13 -2
  16. data/ext/gvl_wrappers.h +33 -0
  17. data/ext/pg.c +17 -6
  18. data/ext/pg.h +15 -13
  19. data/ext/pg_binary_decoder.c +153 -1
  20. data/ext/pg_binary_encoder.c +213 -10
  21. data/ext/pg_cancel_connection.c +360 -0
  22. data/ext/pg_coder.c +54 -5
  23. data/ext/pg_connection.c +431 -169
  24. data/ext/pg_copy_coder.c +19 -15
  25. data/ext/pg_record_coder.c +7 -7
  26. data/ext/pg_result.c +106 -54
  27. data/ext/pg_text_decoder.c +5 -2
  28. data/ext/pg_text_encoder.c +39 -20
  29. data/ext/pg_tuple.c +8 -8
  30. data/ext/pg_type_map.c +4 -2
  31. data/ext/pg_type_map_all_strings.c +1 -1
  32. data/ext/pg_type_map_by_class.c +1 -1
  33. data/ext/pg_type_map_by_column.c +2 -1
  34. data/ext/pg_type_map_by_mri_type.c +1 -1
  35. data/ext/pg_type_map_by_oid.c +3 -1
  36. data/ext/pg_type_map_in_ruby.c +1 -1
  37. data/ext/pg_util.c +2 -2
  38. data/ext/pg_util.h +2 -2
  39. data/lib/pg/basic_type_map_for_queries.rb +15 -7
  40. data/lib/pg/basic_type_registry.rb +16 -4
  41. data/lib/pg/cancel_connection.rb +53 -0
  42. data/lib/pg/coder.rb +4 -2
  43. data/lib/pg/connection.rb +310 -167
  44. data/lib/pg/exceptions.rb +6 -0
  45. data/lib/pg/text_decoder/date.rb +3 -0
  46. data/lib/pg/text_decoder/json.rb +3 -0
  47. data/lib/pg/text_encoder/date.rb +1 -0
  48. data/lib/pg/text_encoder/inet.rb +3 -0
  49. data/lib/pg/text_encoder/json.rb +3 -0
  50. data/lib/pg/version.rb +2 -1
  51. data/lib/pg.rb +156 -120
  52. data/misc/glibc/Dockerfile +20 -0
  53. data/misc/glibc/docker-compose.yml +9 -0
  54. data/misc/glibc/glibc_spec.rb +5 -0
  55. data/misc/yugabyte/Dockerfile +9 -0
  56. data/misc/yugabyte/docker-compose.yml +28 -0
  57. data/misc/yugabyte/pg-test.rb +45 -0
  58. data/pg.gemspec +8 -4
  59. data/ports/patches/krb5/1.22.1/0001-Allow-static-linking-krb5-library.patch +30 -0
  60. data/ports/patches/krb5/1.22.1/0002-unknown-command-line-option-on-clang.patch +12 -0
  61. data/ports/patches/openssl/3.5.2/0001-aarch64-mingw.patch +21 -0
  62. data/ports/patches/postgresql/18.1/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  63. data/ports/patches/postgresql/18.1/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  64. data/rakelib/pg_gem_helper.rb +64 -0
  65. data.tar.gz.sig +0 -0
  66. metadata +49 -47
  67. metadata.gz.sig +0 -0
  68. data/.appveyor.yml +0 -42
  69. data/.gems +0 -6
  70. data/.gemtest +0 -0
  71. data/.github/workflows/binary-gems.yml +0 -117
  72. data/.github/workflows/source-gem.yml +0 -141
  73. data/.gitignore +0 -22
  74. data/.hgsigs +0 -34
  75. data/.hgtags +0 -41
  76. data/.irbrc +0 -23
  77. data/.pryrc +0 -23
  78. data/.tm_properties +0 -21
  79. data/.travis.yml +0 -49
  80. data/Manifest.txt +0 -72
  81. data/Rakefile.cross +0 -298
  82. data/translation/.po4a-version +0 -7
  83. data/translation/po/all.pot +0 -936
  84. data/translation/po/ja.po +0 -1036
  85. data/translation/po4a.cfg +0 -12
@@ -117,7 +117,7 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int
117
117
  /* create a buffer of the encoded length */
118
118
  VALUE out_value = rb_str_new(NULL, encoded_len);
119
119
 
120
- base64_encode( RSTRING_PTR(out_value), val, len );
120
+ rbpg_base64_encode( RSTRING_PTR(out_value), val, len );
121
121
 
122
122
  /* Is it a pure String conversion? Then we can directly send out_value to the user. */
123
123
  if( this->comp.format == 0 && dec_func == pg_text_dec_string ){
@@ -133,6 +133,154 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int
133
133
  return out_value;
134
134
  }
135
135
 
136
+ /*
137
+ * Maximum number of array subscripts (arbitrary limit)
138
+ */
139
+ #define MAXDIM 6
140
+
141
+ /*
142
+ * Document-class: PG::BinaryDecoder::Array < PG::CompositeDecoder
143
+ *
144
+ * This is a decoder class for conversion of binary array types.
145
+ *
146
+ * It returns an Array with possibly an arbitrary number of sub-Arrays.
147
+ * All values are decoded according to the #elements_type accessor.
148
+ * Sub-arrays are decoded recursively.
149
+ *
150
+ * This decoder simply ignores any dimension decorations preceding the array values.
151
+ * It returns all array values as regular ruby Array with a zero based index, regardless of the index given in the dimension decoration.
152
+ *
153
+ * An array decoder which respects dimension decorations is waiting to be implemented.
154
+ *
155
+ */
156
+ static VALUE
157
+ pg_bin_dec_array(t_pg_coder *conv, const char *input_line, int len, int tuple, int field, int enc_idx)
158
+ {
159
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
160
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
161
+
162
+ /* Current field */
163
+ VALUE field_str;
164
+
165
+ int32_t nitems32;
166
+ int i;
167
+ int ndim;
168
+ int nitems;
169
+ int flags;
170
+ int dim;
171
+ int dim_sizes[MAXDIM];
172
+ VALUE arrays[MAXDIM];
173
+ char *output_ptr;
174
+ const char *cur_ptr;
175
+ const char *line_end_ptr;
176
+ char *end_capa_ptr;
177
+
178
+ /* Allocate a new string with embedded capacity and realloc later with
179
+ * exponential growing size when needed. */
180
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
181
+
182
+ /* set pointer variables for loop */
183
+ cur_ptr = input_line;
184
+ line_end_ptr = input_line + len;
185
+
186
+ /* read number of dimensions */
187
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
188
+ ndim = read_nbo32(cur_ptr);
189
+ if (ndim < 0 || ndim > MAXDIM) {
190
+ rb_raise( rb_eArgError, "unsupported number of array dimensions: %d", ndim );
191
+ }
192
+ cur_ptr += 4;
193
+
194
+ /* read flags */
195
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
196
+ flags = read_nbo32(cur_ptr);
197
+ if (flags != 0 && flags != 1) {
198
+ rb_raise( rb_eArgError, "unsupported binary array flags: %d", flags );
199
+ }
200
+ cur_ptr += 4;
201
+
202
+ /* ignore element OID */
203
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
204
+ cur_ptr += 4;
205
+
206
+ nitems32 = ndim == 0 ? 0 : 1;
207
+ for (i = 0; i < ndim; i++) {
208
+ int64_t prod;
209
+
210
+ /* read size of dimensions and ignore lower bound */
211
+ if (line_end_ptr - cur_ptr < 8 ) goto length_error;
212
+ dim_sizes[i] = read_nbo32(cur_ptr);
213
+ prod = (int64_t) nitems32 * (int64_t) dim_sizes[i];
214
+ nitems32 = (int32_t) prod;
215
+ if (dim_sizes[i] < 0 || (int64_t) nitems32 != prod) {
216
+ rb_raise( rb_eArgError, "unsupported array size: %" PRId64, prod );
217
+ }
218
+ cur_ptr += 8;
219
+ }
220
+ nitems = (int)nitems32;
221
+
222
+ dim = 0;
223
+ arrays[dim] = rb_ary_new2(ndim == 0 ? 0 : dim_sizes[dim]);
224
+ for (i = 0; i < nitems; i++) {
225
+ int input_len;
226
+
227
+ /* traverse dimensions down */
228
+ while (dim < ndim - 1) {
229
+ dim++;
230
+ arrays[dim] = rb_ary_new2(dim_sizes[dim]);
231
+ rb_ary_push(arrays[dim - 1], arrays[dim]);
232
+ }
233
+
234
+ /* read element length */
235
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
236
+ input_len = read_nbo32(cur_ptr);
237
+ cur_ptr += 4;
238
+
239
+ /* convert and put element into array */
240
+ if (input_len < 0) {
241
+ if (input_len != -1) goto length_error;
242
+ /* NULL indicator */
243
+ rb_ary_push(arrays[dim], Qnil);
244
+ } else {
245
+ VALUE field_value;
246
+ if (line_end_ptr - cur_ptr < input_len ) goto length_error;
247
+
248
+ /* copy input data to field_str */
249
+ PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
250
+ memcpy(output_ptr, cur_ptr, input_len);
251
+ cur_ptr += input_len;
252
+ output_ptr += input_len;
253
+ /* convert field_str through the type map */
254
+ rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
255
+ field_value = dec_func(this->elem, RSTRING_PTR(field_str), input_len, tuple, field, enc_idx);
256
+
257
+ rb_ary_push(arrays[dim], field_value);
258
+
259
+ if( field_value == field_str ){
260
+ /* Our output string will be send to the user, so we can not reuse
261
+ * it for the next field. */
262
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
263
+ }
264
+ }
265
+
266
+ /* Reset the pointer to the start of the output/buffer string. */
267
+ output_ptr = RSTRING_PTR(field_str);
268
+
269
+ /* traverse dimensions up */
270
+ while (RARRAY_LEN(arrays[dim]) >= dim_sizes[dim] && dim > 0) {
271
+ dim--;
272
+ }
273
+ }
274
+
275
+ if (cur_ptr < line_end_ptr)
276
+ rb_raise( rb_eArgError, "trailing data after binary array data at position: %ld", (long)(cur_ptr - input_line) + 1 );
277
+
278
+ return arrays[0];
279
+
280
+ length_error:
281
+ rb_raise( rb_eArgError, "premature end of binary array data at position: %ld", (long)(cur_ptr - input_line) + 1 );
282
+ }
283
+
136
284
  #define PG_INT64_MIN (-0x7FFFFFFFFFFFFFFFL - 1)
137
285
  #define PG_INT64_MAX 0x7FFFFFFFFFFFFFFFL
138
286
 
@@ -233,6 +381,8 @@ j2date(int jd, int *year, int *month, int *day)
233
381
  *
234
382
  * This is a decoder class for conversion of PostgreSQL binary date
235
383
  * to Ruby Date objects.
384
+ *
385
+ * As soon as this class is used, it requires the ruby standard library 'date'.
236
386
  */
237
387
  static VALUE
238
388
  pg_bin_dec_date(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
@@ -303,6 +453,8 @@ init_pg_binary_decoder(void)
303
453
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
304
454
  pg_define_coder( "Timestamp", pg_bin_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
305
455
 
456
+ /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Array", rb_cPG_CompositeDecoder ); */
457
+ pg_define_coder( "Array", pg_bin_dec_array, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
306
458
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "ToBase64", rb_cPG_CompositeDecoder ); */
307
459
  pg_define_coder( "ToBase64", pg_bin_dec_to_base64, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
308
460
  }
@@ -185,7 +185,7 @@ pg_bin_enc_timestamp(t_pg_coder *this, VALUE value, char *out, VALUE *intermedia
185
185
  ts = rb_time_timespec(*intermediate);
186
186
  /* PostgreSQL's timestamp is based on year 2000 and Ruby's time is based on 1970.
187
187
  * Adjust the 30 years difference. */
188
- timestamp = (ts.tv_sec - 10957L * 24L * 3600L) * 1000000 + (ts.tv_nsec / 1000);
188
+ timestamp = ((int64_t)ts.tv_sec - 10957L * 24L * 3600L) * 1000000 + ((int64_t)ts.tv_nsec / 1000);
189
189
 
190
190
  if( this->flags & PG_CODER_TIMESTAMP_DB_LOCAL ) {
191
191
  /* send as local time */
@@ -271,14 +271,13 @@ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, i
271
271
  case T_FALSE:
272
272
  write_nbo32(PG_INT32_MIN, out);
273
273
  return 4;
274
+ } {
275
+ VALUE year = rb_funcall(value, s_id_year, 0);
276
+ VALUE month = rb_funcall(value, s_id_month, 0);
277
+ VALUE day = rb_funcall(value, s_id_day, 0);
278
+ int jday = date2j(NUM2INT(year), NUM2INT(month), NUM2INT(day)) - POSTGRES_EPOCH_JDATE;
279
+ write_nbo32(jday, out);
274
280
  }
275
-
276
- VALUE year = rb_funcall(value, s_id_year, 0);
277
- VALUE month = rb_funcall(value, s_id_month, 0);
278
- VALUE day = rb_funcall(value, s_id_day, 0);
279
- int jday = date2j(NUM2INT(year), NUM2INT(month), NUM2INT(day)) - POSTGRES_EPOCH_JDATE;
280
- write_nbo32(jday, out);
281
-
282
281
  }else{
283
282
  /* first call -> determine the required length */
284
283
  if(TYPE(value) == T_STRING){
@@ -305,6 +304,208 @@ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, i
305
304
  return 4;
306
305
  }
307
306
 
307
+ /*
308
+ * Maximum number of array subscripts (arbitrary limit)
309
+ */
310
+ #define MAXDIM 6
311
+
312
+ /*
313
+ * Document-class: PG::BinaryEncoder::Array < PG::CompositeEncoder
314
+ *
315
+ * This is the encoder class for PostgreSQL array types in binary format.
316
+ *
317
+ * All values are encoded according to the #elements_type
318
+ * accessor. Sub-arrays are encoded recursively.
319
+ *
320
+ * This encoder expects an Array of values or sub-arrays as input.
321
+ * Other values are passed through as byte string without interpretation.
322
+ *
323
+ * It is possible to enforce a number of dimensions to be encoded by #dimensions= .
324
+ * Deeper nested arrays are then passed to the elements encoder and less nested arrays raise an ArgumentError.
325
+ *
326
+ * The accessors needs_quotation and delimiter are ignored for binary encoding.
327
+ *
328
+ */
329
+ static int
330
+ pg_bin_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
331
+ {
332
+ if (TYPE(value) == T_ARRAY) {
333
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
334
+ t_pg_coder_enc_func enc_func = pg_coder_enc_func(this->elem);
335
+ int dim_sizes[MAXDIM];
336
+ int ndim = 1;
337
+ int nitems = 1;
338
+ VALUE el1 = value;
339
+
340
+ if (RARRAY_LEN(value) == 0) {
341
+ nitems = 0;
342
+ ndim = 0;
343
+ dim_sizes[0] = 0;
344
+ } else {
345
+ /* Determine number of dimensions, sizes of dimensions and number of items */
346
+ while(1) {
347
+ VALUE el2;
348
+
349
+ dim_sizes[ndim-1] = RARRAY_LENINT(el1);
350
+ nitems *= dim_sizes[ndim-1];
351
+ el2 = rb_ary_entry(el1, 0);
352
+ if ( (this->dimensions < 0 || ndim < this->dimensions) &&
353
+ TYPE(el2) == T_ARRAY) {
354
+ ndim++;
355
+ if (ndim > MAXDIM)
356
+ rb_raise( rb_eArgError, "unsupported number of array dimensions: >%d", ndim );
357
+ } else {
358
+ break;
359
+ }
360
+ el1 = el2;
361
+ }
362
+ }
363
+ if( this->dimensions >= 0 && (ndim==0 ? 1 : ndim) != this->dimensions ){
364
+ rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", ndim, this->dimensions);
365
+ }
366
+
367
+ if(out){
368
+ /* Second encoder pass -> write data to `out` */
369
+ int dimpos[MAXDIM];
370
+ VALUE arrays[MAXDIM];
371
+ int dim = 0;
372
+ int item_idx = 0;
373
+ int i;
374
+ char *orig_out = out;
375
+ Oid elem_oid = this->elem ? this->elem->oid : 0;
376
+
377
+ write_nbo32(ndim, out); out += 4;
378
+ write_nbo32(1 /* flags */, out); out += 4;
379
+ write_nbo32(elem_oid, out); out += 4;
380
+ for (i = 0; i < ndim; i++) {
381
+ dimpos[i] = 0;
382
+ write_nbo32(dim_sizes[i], out); out += 4;
383
+ write_nbo32(1 /* offset */, out); out += 4;
384
+ }
385
+ arrays[0] = value;
386
+
387
+ while(1) {
388
+ /* traverse tree down */
389
+ while (dim < ndim - 1) {
390
+ arrays[dim + 1] = rb_ary_entry(arrays[dim], dimpos[dim]);
391
+ dim++;
392
+ }
393
+
394
+ for (i = 0; i < dim_sizes[dim]; i++) {
395
+ VALUE item = rb_ary_entry(arrays[dim], i);
396
+
397
+ if (NIL_P(item)) {
398
+ write_nbo32(-1, out); out += 4;
399
+ } else {
400
+ /* Encoded string is returned in subint */
401
+ int strlen;
402
+ VALUE is_one_pass = rb_ary_entry(*intermediate, item_idx++);
403
+ VALUE subint = rb_ary_entry(*intermediate, item_idx++);
404
+
405
+ if (is_one_pass == Qtrue) {
406
+ strlen = RSTRING_LENINT(subint);
407
+ memcpy( out + 4, RSTRING_PTR(subint), strlen);
408
+ } else {
409
+ strlen = enc_func(this->elem, item, out + 4, &subint, enc_idx);
410
+ }
411
+ write_nbo32(strlen, out);
412
+ out += 4 /* length */ + strlen;
413
+ }
414
+ }
415
+
416
+ /* traverse tree up and go to next sibling array */
417
+ do {
418
+ if (dim > 0) {
419
+ dimpos[dim] = 0;
420
+ dim--;
421
+ dimpos[dim]++;
422
+ } else {
423
+ goto finished2;
424
+ }
425
+ } while (dimpos[dim] >= dim_sizes[dim]);
426
+ }
427
+ finished2:
428
+ return (int)(out - orig_out);
429
+
430
+ } else {
431
+ /* First encoder pass -> determine required buffer space for `out` */
432
+
433
+ int dimpos[MAXDIM];
434
+ VALUE arrays[MAXDIM];
435
+ int dim = 0;
436
+ int item_idx = 0;
437
+ int i;
438
+ int size_sum = 0;
439
+
440
+ *intermediate = rb_ary_new2(nitems);
441
+
442
+ for (i = 0; i < MAXDIM; i++) {
443
+ dimpos[i] = 0;
444
+ }
445
+ arrays[0] = value;
446
+
447
+ while(1) {
448
+
449
+ /* traverse tree down */
450
+ while (dim < ndim - 1) {
451
+ VALUE array = rb_ary_entry(arrays[dim], dimpos[dim]);
452
+ if (TYPE(array) != T_ARRAY) {
453
+ rb_raise( rb_eArgError, "expected Array instead of %+"PRIsVALUE" in dimension %d", array, dim + 1 );
454
+ }
455
+ if (dim_sizes[dim + 1] != RARRAY_LEN(array)) {
456
+ rb_raise( rb_eArgError, "varying number of array elements (%d and %d) in dimension %d", dim_sizes[dim + 1], RARRAY_LENINT(array), dim + 1 );
457
+ }
458
+ arrays[dim + 1] = array;
459
+ dim++;
460
+ }
461
+
462
+ for (i = 0; i < dim_sizes[dim]; i++) {
463
+ VALUE item = rb_ary_entry(arrays[dim], i);
464
+
465
+ if (NIL_P(item)) {
466
+ size_sum += 4 /* length bytes = -1 */;
467
+ } else {
468
+ VALUE subint;
469
+ int strlen = enc_func(this->elem, item, NULL, &subint, enc_idx);
470
+
471
+ /* Gather all intermediate values of elements into an array, which is returned as intermediate for the array encoder */
472
+ if( strlen == -1 ){
473
+ /* Encoded string is returned in subint */
474
+ rb_ary_store(*intermediate, item_idx++, Qtrue);
475
+ rb_ary_store(*intermediate, item_idx++, subint);
476
+
477
+ strlen = RSTRING_LENINT(subint);
478
+ } else {
479
+ /* Two passes necessary */
480
+ rb_ary_store(*intermediate, item_idx++, Qfalse);
481
+ rb_ary_store(*intermediate, item_idx++, subint);
482
+ }
483
+ size_sum += 4 /* length bytes */ + strlen;
484
+ }
485
+ }
486
+
487
+ /* traverse tree up and go to next sibling array */
488
+ do {
489
+ if (dim > 0) {
490
+ dimpos[dim] = 0;
491
+ dim--;
492
+ dimpos[dim]++;
493
+ } else {
494
+ goto finished1;
495
+ }
496
+ } while (dimpos[dim] >= dim_sizes[dim]);
497
+ }
498
+ finished1:;
499
+
500
+ return 4 /* ndim */ + 4 /* flags */ + 4 /* oid */ +
501
+ ndim * (4 /* dim size */ + 4 /* dim offset */) +
502
+ size_sum;
503
+ }
504
+ } else {
505
+ return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
506
+ }
507
+ }
508
+
308
509
  /*
309
510
  * Document-class: PG::BinaryEncoder::FromBase64 < PG::CompositeEncoder
310
511
  *
@@ -323,7 +524,7 @@ pg_bin_enc_from_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermed
323
524
  if(out){
324
525
  /* Second encoder pass, if required */
325
526
  strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
326
- strlen = base64_decode( out, out, strlen );
527
+ strlen = rbpg_base64_decode( out, out, strlen );
327
528
 
328
529
  return strlen;
329
530
  } else {
@@ -337,7 +538,7 @@ pg_bin_enc_from_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermed
337
538
  strlen = RSTRING_LENINT(subint);
338
539
  out_str = rb_str_new(NULL, BASE64_DECODED_SIZE(strlen));
339
540
 
340
- strlen = base64_decode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
541
+ strlen = rbpg_base64_decode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
341
542
  rb_str_set_len( out_str, strlen );
342
543
  *intermediate = out_str;
343
544
 
@@ -382,6 +583,8 @@ init_pg_binary_encoder(void)
382
583
  /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Date", rb_cPG_SimpleEncoder ); */
383
584
  pg_define_coder( "Date", pg_bin_enc_date, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
384
585
 
586
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Array", rb_cPG_CompositeEncoder ); */
587
+ pg_define_coder( "Array", pg_bin_enc_array, rb_cPG_CompositeEncoder, rb_mPG_BinaryEncoder );
385
588
  /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "FromBase64", rb_cPG_CompositeEncoder ); */
386
589
  pg_define_coder( "FromBase64", pg_bin_enc_from_base64, rb_cPG_CompositeEncoder, rb_mPG_BinaryEncoder );
387
590
  }