pg 1.4.6 → 1.6.0

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} +177 -3
  4. data/Gemfile +12 -3
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +75 -41
  7. data/README.md +34 -19
  8. data/Rakefile +84 -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 +68 -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 +17 -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.c CHANGED
@@ -33,7 +33,6 @@
33
33
  *
34
34
  * - PQfreemem -- unnecessary: copied to ruby object, then freed. Ruby object's
35
35
  * memory is freed when it is garbage collected.
36
- * - PQbinaryTuples -- better to use PQfformat
37
36
  * - PQprint -- not very useful
38
37
  * - PQsetdb -- not very useful
39
38
  * - PQoidStatus -- deprecated, use PQoidValue
@@ -74,6 +73,7 @@ VALUE rb_mPGconstants;
74
73
  * The mapping from canonical encoding names in PostgreSQL to ones in Ruby.
75
74
  */
76
75
  const char * const (pg_enc_pg2ruby_mapping[][2]) = {
76
+ {"UTF8", "UTF-8" },
77
77
  {"BIG5", "Big5" },
78
78
  {"EUC_CN", "GB2312" },
79
79
  {"EUC_JP", "EUC-JP" },
@@ -105,7 +105,6 @@ const char * const (pg_enc_pg2ruby_mapping[][2]) = {
105
105
  {"SHIFT_JIS_2004","Windows-31J" },
106
106
  /* {"SQL_ASCII", NULL }, special case*/
107
107
  {"UHC", "CP949" },
108
- {"UTF8", "UTF-8" },
109
108
  {"WIN866", "IBM866" },
110
109
  {"WIN874", "Windows-874" },
111
110
  {"WIN1250", "Windows-1250"},
@@ -120,36 +119,17 @@ const char * const (pg_enc_pg2ruby_mapping[][2]) = {
120
119
  };
121
120
 
122
121
 
123
- /*
124
- * A cache of mapping from PostgreSQL's encoding indices to Ruby's rb_encoding*s.
125
- */
126
- static struct st_table *enc_pg2ruby;
127
-
128
-
129
122
  /*
130
123
  * Return the given PostgreSQL encoding ID as an rb_encoding.
131
124
  *
132
125
  * - returns NULL if the client encoding is 'SQL_ASCII'.
133
126
  * - returns ASCII-8BIT if the client encoding is unknown.
134
127
  */
135
- rb_encoding *
128
+ static rb_encoding *
136
129
  pg_get_pg_encoding_as_rb_encoding( int enc_id )
137
130
  {
138
- rb_encoding *enc;
139
-
140
- /* Use the cached value if it exists */
141
- if ( st_lookup(enc_pg2ruby, (st_data_t)enc_id, (st_data_t*)&enc) ) {
142
- return enc;
143
- }
144
- else {
145
- const char *name = pg_encoding_to_char( enc_id );
146
-
147
- enc = pg_get_pg_encname_as_rb_encoding( name );
148
- st_insert( enc_pg2ruby, (st_data_t)enc_id, (st_data_t)enc );
149
-
150
- return enc;
151
- }
152
-
131
+ const char *name = pg_encoding_to_char( enc_id );
132
+ return pg_get_pg_encname_as_rb_encoding( name );
153
133
  }
154
134
 
155
135
  /*
@@ -354,6 +334,11 @@ pg_s_init_ssl(VALUE self, VALUE do_ssl)
354
334
  void
355
335
  Init_pg_ext(void)
356
336
  {
337
+
338
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
339
+ rb_ext_ractor_safe(PQisthreadsafe());
340
+ #endif
341
+
357
342
  if( RTEST(rb_eval_string("ENV['PG_SKIP_DEPRECATION_WARNING']")) ){
358
343
  /* Set all bits to disable all deprecation warnings. */
359
344
  pg_skip_deprecation_warning = 0xFFFF;
@@ -372,8 +357,8 @@ Init_pg_ext(void)
372
357
  SINGLETON_ALIAS( rb_mPG, "is_threadsafe?", "isthreadsafe" );
373
358
  SINGLETON_ALIAS( rb_mPG, "threadsafe?", "isthreadsafe" );
374
359
 
375
- rb_define_singleton_method( rb_mPG, "init_openssl", pg_s_init_openssl, 2 );
376
- rb_define_singleton_method( rb_mPG, "init_ssl", pg_s_init_ssl, 1 );
360
+ rb_define_singleton_method( rb_mPG, "init_openssl", pg_s_init_openssl, 2 );
361
+ rb_define_singleton_method( rb_mPG, "init_ssl", pg_s_init_ssl, 1 );
377
362
 
378
363
 
379
364
  /****** PG::Connection CLASS CONSTANTS: Connection Status ******/
@@ -419,6 +404,10 @@ Init_pg_ext(void)
419
404
  /* Checking if server is in standby mode. Available since PostgreSQL-14. */
420
405
  rb_define_const(rb_mPGconstants, "CONNECTION_CHECK_STANDBY", INT2FIX(CONNECTION_CHECK_STANDBY));
421
406
  #endif
407
+ #if PG_MAJORVERSION_NUM >= 17
408
+ /* Waiting for connection attempt to be started. Available since PostgreSQL-17. */
409
+ rb_define_const(rb_mPGconstants, "CONNECTION_ALLOCATED", INT2FIX(CONNECTION_ALLOCATED));
410
+ #endif
422
411
 
423
412
  /****** PG::Connection CLASS CONSTANTS: Nonblocking connection polling status ******/
424
413
 
@@ -466,14 +455,12 @@ Init_pg_ext(void)
466
455
  rb_define_const(rb_mPGconstants, "PQERRORS_SQLSTATE", INT2FIX(PQERRORS_SQLSTATE));
467
456
  #endif
468
457
 
469
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
470
458
  /* See Connection#set_error_context_visibility */
471
459
  rb_define_const(rb_mPGconstants, "PQSHOW_CONTEXT_NEVER", INT2FIX(PQSHOW_CONTEXT_NEVER));
472
460
  /* See Connection#set_error_context_visibility */
473
461
  rb_define_const(rb_mPGconstants, "PQSHOW_CONTEXT_ERRORS", INT2FIX(PQSHOW_CONTEXT_ERRORS));
474
462
  /* See Connection#set_error_context_visibility */
475
463
  rb_define_const(rb_mPGconstants, "PQSHOW_CONTEXT_ALWAYS", INT2FIX(PQSHOW_CONTEXT_ALWAYS));
476
- #endif
477
464
 
478
465
  /****** PG::Connection CLASS CONSTANTS: Check Server Status ******/
479
466
 
@@ -521,6 +508,10 @@ Init_pg_ext(void)
521
508
  rb_define_const(rb_mPGconstants, "PGRES_COPY_BOTH", INT2FIX(PGRES_COPY_BOTH));
522
509
  /* Result#result_status constant - Single tuple from larger resultset. */
523
510
  rb_define_const(rb_mPGconstants, "PGRES_SINGLE_TUPLE", INT2FIX(PGRES_SINGLE_TUPLE));
511
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
512
+ /* Result#result_status constant - tuple chunk from larger resultset. */
513
+ rb_define_const(rb_mPGconstants, "PGRES_TUPLES_CHUNK", INT2FIX(PGRES_TUPLES_CHUNK));
514
+ #endif
524
515
 
525
516
  #ifdef HAVE_PQENTERPIPELINEMODE
526
517
  /* Result#result_status constant - The PG::Result represents a synchronization point in pipeline mode, requested by Connection#pipeline_sync.
@@ -545,20 +536,18 @@ Init_pg_ext(void)
545
536
  */
546
537
  rb_define_const(rb_mPGconstants, "PG_DIAG_SEVERITY", INT2FIX(PG_DIAG_SEVERITY));
547
538
 
548
- #ifdef PG_DIAG_SEVERITY_NONLOCALIZED
549
539
  /* Result#result_error_field argument constant
550
540
  *
551
541
  * The severity; the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message).
552
542
  * This is identical to the PG_DIAG_SEVERITY field except that the contents are never localized.
553
543
  *
554
- * Available since PostgreSQL-9.6
555
544
  */
556
545
  rb_define_const(rb_mPGconstants, "PG_DIAG_SEVERITY_NONLOCALIZED", INT2FIX(PG_DIAG_SEVERITY_NONLOCALIZED));
557
- #endif
546
+
558
547
  /* Result#result_error_field argument constant
559
548
  *
560
549
  * The SQLSTATE code for the error.
561
- * The SQLSTATE code identies the type of error that has occurred; it can be used by front-end applications to perform specific operations (such as error handling) in response to a particular database error.
550
+ * The SQLSTATE code identifies the type of error that has occurred; it can be used by front-end applications to perform specific operations (such as error handling) in response to a particular database error.
562
551
  * For a list of the possible SQLSTATE codes, see Appendix A.
563
552
  * This field is not localizable, and is always present.
564
553
  */
@@ -682,11 +671,15 @@ Init_pg_ext(void)
682
671
  /* PostgreSQL compiled in default port */
683
672
  rb_define_const(rb_mPGconstants, "DEF_PGPORT", INT2FIX(DEF_PGPORT));
684
673
 
674
+ #ifdef PG_IS_BINARY_GEM
675
+ rb_define_const(rb_mPG, "IS_BINARY_GEM", Qtrue);
676
+ #else
677
+ rb_define_const(rb_mPG, "IS_BINARY_GEM", Qfalse);
678
+ #endif
679
+
685
680
  /* Add the constants to the toplevel namespace */
686
681
  rb_include_module( rb_mPG, rb_mPGconstants );
687
682
 
688
- enc_pg2ruby = st_init_numtable();
689
-
690
683
  /* Initialize the main extension classes */
691
684
  init_pg_connection();
692
685
  init_pg_result();
@@ -706,5 +699,5 @@ Init_pg_ext(void)
706
699
  init_pg_copycoder();
707
700
  init_pg_recordcoder();
708
701
  init_pg_tuple();
702
+ init_pg_cancon();
709
703
  }
710
-
data/ext/pg.h CHANGED
@@ -76,19 +76,14 @@ typedef long suseconds_t;
76
76
  #define PG_MAX_COLUMNS 4000
77
77
  #endif
78
78
 
79
- #ifndef RARRAY_AREF
80
- #define RARRAY_AREF(a, i) (RARRAY_PTR(a)[i])
81
- #endif
82
-
83
- #ifdef HAVE_RB_GC_MARK_MOVABLE
84
- #define pg_compact_callback(x) (x)
85
79
  #define pg_gc_location(x) x = rb_gc_location(x)
80
+
81
+ /* For compatibility with ruby < 3.0 */
82
+ #ifndef RUBY_TYPED_FROZEN_SHAREABLE
83
+ #define PG_RUBY_TYPED_FROZEN_SHAREABLE 0
86
84
  #else
87
- #define rb_gc_mark_movable(x) rb_gc_mark(x)
88
- #define pg_compact_callback(x) {(x)}
89
- #define pg_gc_location(x) UNUSED(x)
85
+ #define PG_RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
90
86
  #endif
91
-
92
87
  #define PG_ENC_IDX_BITS 28
93
88
 
94
89
  /* The data behind each PG::Connection object */
@@ -97,6 +92,9 @@ typedef struct {
97
92
 
98
93
  /* Cached IO object for the socket descriptor */
99
94
  VALUE socket_io;
95
+ /* function pointers of the original libpq notice receivers */
96
+ PQnoticeReceiver default_notice_receiver;
97
+ PQnoticeProcessor default_notice_processor;
100
98
  /* Proc object that receives notices as PG::Result objects */
101
99
  VALUE notice_receiver;
102
100
  /* Proc object that receives notices as String objects */
@@ -118,10 +116,8 @@ typedef struct {
118
116
  /* enable automatic flushing of send data at the end of send_query calls */
119
117
  unsigned int flush_data : 1;
120
118
 
121
- #if defined(_WIN32)
122
119
  /* File descriptor to be used for rb_w32_unwrap_io_handle() */
123
120
  int ruby_sd;
124
- #endif
125
121
  } t_pg_connection;
126
122
 
127
123
  typedef struct pg_coder t_pg_coder;
@@ -210,6 +206,7 @@ typedef struct {
210
206
  t_pg_coder comp;
211
207
  t_pg_coder *elem;
212
208
  int needs_quotation;
209
+ int dimensions;
213
210
  char delimiter;
214
211
  } t_pg_composite_coder;
215
212
 
@@ -307,6 +304,7 @@ void init_pg_text_decoder _(( void ));
307
304
  void init_pg_binary_encoder _(( void ));
308
305
  void init_pg_binary_decoder _(( void ));
309
306
  void init_pg_tuple _(( void ));
307
+ void init_pg_cancon _(( void ));
310
308
  VALUE lookup_error_class _(( const char * ));
311
309
  VALUE pg_bin_dec_bytea _(( t_pg_coder*, const char *, int, int, int, int ));
312
310
  VALUE pg_text_dec_string _(( t_pg_coder*, const char *, int, int, int, int ));
@@ -314,7 +312,7 @@ int pg_coder_enc_to_s _(( t_pg_coder*, VALUE, c
314
312
  int pg_text_enc_identifier _(( t_pg_coder*, VALUE, char *, VALUE *, int));
315
313
  t_pg_coder_enc_func pg_coder_enc_func _(( t_pg_coder* ));
316
314
  t_pg_coder_dec_func pg_coder_dec_func _(( t_pg_coder*, int ));
317
- void pg_define_coder _(( const char *, void *, VALUE, VALUE ));
315
+ VALUE pg_define_coder _(( const char *, void *, VALUE, VALUE ));
318
316
  VALUE pg_obj_to_i _(( VALUE ));
319
317
  VALUE pg_tmbc_allocate _(( void ));
320
318
  void pg_coder_init_encoder _(( VALUE ));
@@ -346,6 +344,13 @@ void pg_typemap_compact _(( void * ));
346
344
  PGconn *pg_get_pgconn _(( VALUE ));
347
345
  t_pg_connection *pg_get_connection _(( VALUE ));
348
346
  VALUE pgconn_block _(( int, VALUE *, VALUE ));
347
+ #ifdef __GNUC__
348
+ __attribute__((format(printf, 3, 4)))
349
+ #endif
350
+ NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
351
+ VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
352
+ void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
353
+
349
354
 
350
355
  VALUE pg_new_result _(( PGresult *, VALUE ));
351
356
  VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
@@ -364,7 +369,6 @@ pgresult_get_this( VALUE self )
364
369
  }
365
370
 
366
371
 
367
- rb_encoding * pg_get_pg_encoding_as_rb_encoding _(( int ));
368
372
  rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
369
373
  const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
370
374
  rb_encoding *pg_conn_enc_get _(( PGconn * ));
@@ -12,6 +12,8 @@
12
12
  #endif
13
13
 
14
14
  VALUE rb_mPG_BinaryDecoder;
15
+ static VALUE s_Date;
16
+ static ID s_id_new;
15
17
 
16
18
 
17
19
  /*
@@ -131,6 +133,154 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int
131
133
  return out_value;
132
134
  }
133
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
+
134
284
  #define PG_INT64_MIN (-0x7FFFFFFFFFFFFFFFL - 1)
135
285
  #define PG_INT64_MAX 0x7FFFFFFFFFFFFFFFL
136
286
 
@@ -195,6 +345,84 @@ pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int
195
345
  }
196
346
  }
197
347
 
348
+ #define PG_INT32_MIN (-0x7FFFFFFF-1)
349
+ #define PG_INT32_MAX (0x7FFFFFFF)
350
+ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
351
+ #define MONTHS_PER_YEAR 12
352
+
353
+ /* taken from PostgreSQL sources at src/backend/utils/adt/datetime.c */
354
+ void
355
+ j2date(int jd, int *year, int *month, int *day)
356
+ {
357
+ unsigned int julian;
358
+ unsigned int quad;
359
+ unsigned int extra;
360
+ int y;
361
+
362
+ julian = jd;
363
+ julian += 32044;
364
+ quad = julian / 146097;
365
+ extra = (julian - quad * 146097) * 4 + 3;
366
+ julian += 60 + quad * 3 + extra / 146097;
367
+ quad = julian / 1461;
368
+ julian -= quad * 1461;
369
+ y = julian * 4 / 1461;
370
+ julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
371
+ + 123;
372
+ y += quad * 4;
373
+ *year = y - 4800;
374
+ quad = julian * 2141 / 65536;
375
+ *day = julian - 7834 * quad / 256;
376
+ *month = (quad + 10) % MONTHS_PER_YEAR + 1;
377
+ } /* j2date() */
378
+
379
+ /*
380
+ * Document-class: PG::BinaryDecoder::Date < PG::SimpleDecoder
381
+ *
382
+ * This is a decoder class for conversion of PostgreSQL binary date
383
+ * to Ruby Date objects.
384
+ *
385
+ * As soon as this class is used, it requires the ruby standard library 'date'.
386
+ */
387
+ static VALUE
388
+ pg_bin_dec_date(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
389
+ {
390
+ int year, month, day;
391
+ int date;
392
+
393
+ if (len != 4) {
394
+ rb_raise(rb_eTypeError, "unexpected date format != 4 bytes");
395
+ }
396
+
397
+ date = read_nbo32(val);
398
+ switch(date){
399
+ case PG_INT32_MAX:
400
+ return rb_str_new2("infinity");
401
+ case PG_INT32_MIN:
402
+ return rb_str_new2("-infinity");
403
+ default:
404
+ j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
405
+
406
+ return rb_funcall(s_Date, s_id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
407
+ }
408
+ }
409
+
410
+ /* called per autoload when BinaryDecoder::Date is used */
411
+ static VALUE
412
+ init_pg_bin_decoder_date(VALUE rb_mPG_BinaryDecoder)
413
+ {
414
+ rb_require("date");
415
+ s_Date = rb_const_get(rb_cObject, rb_intern("Date"));
416
+ rb_gc_register_mark_object(s_Date);
417
+ s_id_new = rb_intern("new");
418
+
419
+ /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Date", rb_cPG_SimpleDecoder ); */
420
+ pg_define_coder( "Date", pg_bin_dec_date, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
421
+
422
+ return Qnil;
423
+ }
424
+
425
+
198
426
  /*
199
427
  * Document-class: PG::BinaryDecoder::String < PG::SimpleDecoder
200
428
  *
@@ -209,6 +437,7 @@ init_pg_binary_decoder(void)
209
437
  {
210
438
  /* This module encapsulates all decoder classes with binary input format */
211
439
  rb_mPG_BinaryDecoder = rb_define_module_under( rb_mPG, "BinaryDecoder" );
440
+ rb_define_private_method(rb_singleton_class(rb_mPG_BinaryDecoder), "init_date", init_pg_bin_decoder_date, 0);
212
441
 
213
442
  /* Make RDoc aware of the decoder classes... */
214
443
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
@@ -224,6 +453,8 @@ init_pg_binary_decoder(void)
224
453
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
225
454
  pg_define_coder( "Timestamp", pg_bin_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
226
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 );
227
458
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "ToBase64", rb_cPG_CompositeDecoder ); */
228
459
  pg_define_coder( "ToBase64", pg_bin_dec_to_base64, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
229
460
  }