pg 1.1.3 → 1.5.4

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 (140) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +141 -0
  7. data/.gitignore +22 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.md +884 -0
  16. data/Manifest.txt +3 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.md +300 -0
  19. data/README.md +286 -0
  20. data/Rakefile +37 -137
  21. data/Rakefile.cross +62 -62
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/certs/larskanis-2023.pem +24 -0
  25. data/ext/errorcodes.def +80 -0
  26. data/ext/errorcodes.rb +0 -0
  27. data/ext/errorcodes.txt +22 -2
  28. data/ext/extconf.rb +105 -26
  29. data/ext/gvl_wrappers.c +4 -0
  30. data/ext/gvl_wrappers.h +23 -0
  31. data/ext/pg.c +204 -152
  32. data/ext/pg.h +52 -21
  33. data/ext/pg_binary_decoder.c +100 -17
  34. data/ext/pg_binary_encoder.c +238 -13
  35. data/ext/pg_coder.c +109 -34
  36. data/ext/pg_connection.c +1383 -983
  37. data/ext/pg_copy_coder.c +356 -35
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +522 -0
  40. data/ext/pg_result.c +439 -172
  41. data/ext/pg_text_decoder.c +42 -18
  42. data/ext/pg_text_encoder.c +201 -56
  43. data/ext/pg_tuple.c +97 -66
  44. data/ext/pg_type_map.c +45 -11
  45. data/ext/pg_type_map_all_strings.c +21 -7
  46. data/ext/pg_type_map_by_class.c +59 -27
  47. data/ext/pg_type_map_by_column.c +80 -37
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +62 -29
  50. data/ext/pg_type_map_in_ruby.c +56 -22
  51. data/ext/{util.c → pg_util.c} +7 -7
  52. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  53. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  54. data/lib/pg/basic_type_map_for_results.rb +104 -0
  55. data/lib/pg/basic_type_registry.rb +299 -0
  56. data/lib/pg/binary_decoder/date.rb +9 -0
  57. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  58. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  59. data/lib/pg/coder.rb +35 -12
  60. data/lib/pg/connection.rb +744 -84
  61. data/lib/pg/exceptions.rb +15 -1
  62. data/lib/pg/result.rb +13 -1
  63. data/lib/pg/text_decoder/date.rb +18 -0
  64. data/lib/pg/text_decoder/inet.rb +9 -0
  65. data/lib/pg/text_decoder/json.rb +14 -0
  66. data/lib/pg/text_decoder/numeric.rb +9 -0
  67. data/lib/pg/text_decoder/timestamp.rb +30 -0
  68. data/lib/pg/text_encoder/date.rb +12 -0
  69. data/lib/pg/text_encoder/inet.rb +28 -0
  70. data/lib/pg/text_encoder/json.rb +14 -0
  71. data/lib/pg/text_encoder/numeric.rb +9 -0
  72. data/lib/pg/text_encoder/timestamp.rb +24 -0
  73. data/lib/pg/type_map_by_column.rb +2 -1
  74. data/lib/pg/version.rb +4 -0
  75. data/lib/pg.rb +94 -39
  76. data/misc/openssl-pg-segfault.rb +31 -0
  77. data/misc/postgres/History.txt +9 -0
  78. data/misc/postgres/Manifest.txt +5 -0
  79. data/misc/postgres/README.txt +21 -0
  80. data/misc/postgres/Rakefile +21 -0
  81. data/misc/postgres/lib/postgres.rb +16 -0
  82. data/misc/ruby-pg/History.txt +9 -0
  83. data/misc/ruby-pg/Manifest.txt +5 -0
  84. data/misc/ruby-pg/README.txt +21 -0
  85. data/misc/ruby-pg/Rakefile +21 -0
  86. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  87. data/pg.gemspec +34 -0
  88. data/rakelib/task_extension.rb +46 -0
  89. data/sample/array_insert.rb +20 -0
  90. data/sample/async_api.rb +102 -0
  91. data/sample/async_copyto.rb +39 -0
  92. data/sample/async_mixed.rb +56 -0
  93. data/sample/check_conn.rb +21 -0
  94. data/sample/copydata.rb +71 -0
  95. data/sample/copyfrom.rb +81 -0
  96. data/sample/copyto.rb +19 -0
  97. data/sample/cursor.rb +21 -0
  98. data/sample/disk_usage_report.rb +177 -0
  99. data/sample/issue-119.rb +94 -0
  100. data/sample/losample.rb +69 -0
  101. data/sample/minimal-testcase.rb +17 -0
  102. data/sample/notify_wait.rb +72 -0
  103. data/sample/pg_statistics.rb +285 -0
  104. data/sample/replication_monitor.rb +222 -0
  105. data/sample/test_binary_values.rb +33 -0
  106. data/sample/wal_shipper.rb +434 -0
  107. data/sample/warehouse_partitions.rb +311 -0
  108. data/translation/.po4a-version +7 -0
  109. data/translation/po/all.pot +936 -0
  110. data/translation/po/ja.po +1036 -0
  111. data/translation/po4a.cfg +12 -0
  112. data.tar.gz.sig +0 -0
  113. metadata +144 -222
  114. metadata.gz.sig +0 -0
  115. data/ChangeLog +0 -6595
  116. data/History.rdoc +0 -485
  117. data/README.ja.rdoc +0 -14
  118. data/README.rdoc +0 -178
  119. data/lib/pg/basic_type_mapping.rb +0 -459
  120. data/lib/pg/binary_decoder.rb +0 -22
  121. data/lib/pg/constants.rb +0 -11
  122. data/lib/pg/text_decoder.rb +0 -47
  123. data/lib/pg/text_encoder.rb +0 -69
  124. data/spec/data/expected_trace.out +0 -26
  125. data/spec/data/random_binary_data +0 -0
  126. data/spec/helpers.rb +0 -381
  127. data/spec/pg/basic_type_mapping_spec.rb +0 -508
  128. data/spec/pg/connection_spec.rb +0 -1849
  129. data/spec/pg/connection_sync_spec.rb +0 -41
  130. data/spec/pg/result_spec.rb +0 -491
  131. data/spec/pg/tuple_spec.rb +0 -280
  132. data/spec/pg/type_map_by_class_spec.rb +0 -138
  133. data/spec/pg/type_map_by_column_spec.rb +0 -222
  134. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  135. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  136. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  137. data/spec/pg/type_map_spec.rb +0 -22
  138. data/spec/pg/type_spec.rb +0 -949
  139. data/spec/pg_spec.rb +0 -50
  140. /data/ext/{util.h → pg_util.h} +0 -0
data/ext/pg.h CHANGED
@@ -11,6 +11,7 @@
11
11
  #include <sys/types.h>
12
12
  #if !defined(_WIN32)
13
13
  # include <sys/time.h>
14
+ # include <sys/socket.h>
14
15
  #endif
15
16
  #if defined(HAVE_UNISTD_H) && !defined(_WIN32)
16
17
  # include <unistd.h>
@@ -56,6 +57,7 @@
56
57
  #endif
57
58
 
58
59
  /* PostgreSQL headers */
60
+ #include "pg_config.h"
59
61
  #include "libpq-fe.h"
60
62
  #include "libpq/libpq-fs.h" /* large-object interface */
61
63
  #include "pg_config_manual.h"
@@ -74,9 +76,22 @@ typedef long suseconds_t;
74
76
  #define PG_MAX_COLUMNS 4000
75
77
  #endif
76
78
 
77
- #ifndef RARRAY_AREF
78
- #define RARRAY_AREF(a, i) (RARRAY_PTR(a)[i])
79
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
80
+ #define pg_compact_callback(x) (x)
81
+ #define pg_gc_location(x) x = rb_gc_location(x)
82
+ #else
83
+ #define rb_gc_mark_movable(x) rb_gc_mark(x)
84
+ #define pg_compact_callback(x) {(x)}
85
+ #define pg_gc_location(x) UNUSED(x)
86
+ #endif
87
+
88
+ /* For compatibility with ruby < 3.0 */
89
+ #ifndef RUBY_TYPED_FROZEN_SHAREABLE
90
+ #define PG_RUBY_TYPED_FROZEN_SHAREABLE 0
91
+ #else
92
+ #define PG_RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
79
93
  #endif
94
+ #define PG_ENC_IDX_BITS 28
80
95
 
81
96
  /* The data behind each PG::Connection object */
82
97
  typedef struct {
@@ -84,6 +99,9 @@ typedef struct {
84
99
 
85
100
  /* Cached IO object for the socket descriptor */
86
101
  VALUE socket_io;
102
+ /* function pointers of the original libpq notice receivers */
103
+ PQnoticeReceiver default_notice_receiver;
104
+ PQnoticeProcessor default_notice_processor;
87
105
  /* Proc object that receives notices as PG::Result objects */
88
106
  VALUE notice_receiver;
89
107
  /* Proc object that receives notices as String objects */
@@ -94,15 +112,21 @@ typedef struct {
94
112
  VALUE type_map_for_results;
95
113
  /* IO object internally used for the trace stream */
96
114
  VALUE trace_stream;
97
- /* Cached Encoding object */
98
- VALUE external_encoding;
99
115
  /* Kind of PG::Coder object for casting ruby values to COPY rows */
100
116
  VALUE encoder_for_put_copy_data;
101
117
  /* Kind of PG::Coder object for casting COPY rows to ruby values */
102
118
  VALUE decoder_for_get_copy_data;
119
+ /* Ruby encoding index of the client/internal encoding */
120
+ int enc_idx : PG_ENC_IDX_BITS;
121
+ /* flags controlling Symbol/String field names */
122
+ unsigned int flags : 2;
123
+ /* enable automatic flushing of send data at the end of send_query calls */
124
+ unsigned int flush_data : 1;
103
125
 
104
- /* enable/disable guessing size of PGresult's allocated memory */
105
- int guess_result_memsize;
126
+ #if defined(_WIN32)
127
+ /* File descriptor to be used for rb_w32_unwrap_io_handle() */
128
+ int ruby_sd;
129
+ #endif
106
130
  } t_pg_connection;
107
131
 
108
132
  typedef struct pg_coder t_pg_coder;
@@ -123,10 +147,16 @@ typedef struct {
123
147
  */
124
148
  t_typemap *p_typemap;
125
149
 
150
+ /* Ruby encoding index of the client/internal encoding */
151
+ int enc_idx : PG_ENC_IDX_BITS;
152
+
126
153
  /* 0 = PGresult is cleared by PG::Result#clear or by the GC
127
154
  * 1 = PGresult is cleared internally by libpq
128
155
  */
129
- int autoclear;
156
+ unsigned int autoclear : 1;
157
+
158
+ /* flags controlling Symbol/String field names */
159
+ unsigned int flags : 2;
130
160
 
131
161
  /* Number of fields in fnames[] .
132
162
  * Set to -1 if fnames[] is not yet initialized.
@@ -142,7 +172,7 @@ typedef struct {
142
172
  /* Hash with fnames[] to field number mapping. */
143
173
  VALUE field_map;
144
174
 
145
- /* List of field names as frozen String objects.
175
+ /* List of field names as frozen String or Symbol objects.
146
176
  * Only valid if nfields != -1
147
177
  */
148
178
  VALUE fnames[0];
@@ -158,6 +188,10 @@ typedef VALUE (* t_pg_typecast_result)(t_typemap *, VALUE, int, int);
158
188
  typedef t_pg_coder *(* t_pg_typecast_query_param)(t_typemap *, VALUE, int);
159
189
  typedef VALUE (* t_pg_typecast_copy_get)( t_typemap *, VALUE, int, int, int );
160
190
 
191
+ #define PG_RESULT_FIELD_NAMES_MASK 0x03
192
+ #define PG_RESULT_FIELD_NAMES_SYMBOL 0x01
193
+ #define PG_RESULT_FIELD_NAMES_STATIC_SYMBOL 0x02
194
+
161
195
  #define PG_CODER_TIMESTAMP_DB_UTC 0x0
162
196
  #define PG_CODER_TIMESTAMP_DB_LOCAL 0x1
163
197
  #define PG_CODER_TIMESTAMP_APP_UTC 0x0
@@ -204,6 +238,8 @@ typedef struct {
204
238
  } convs[0];
205
239
  } t_tmbc;
206
240
 
241
+ extern const rb_data_type_t pg_typemap_type;
242
+ extern const rb_data_type_t pg_coder_type;
207
243
 
208
244
  #include "gvl_wrappers.h"
209
245
 
@@ -270,6 +306,7 @@ void init_pg_type_map_by_oid _(( void ));
270
306
  void init_pg_type_map_in_ruby _(( void ));
271
307
  void init_pg_coder _(( void ));
272
308
  void init_pg_copycoder _(( void ));
309
+ void init_pg_recordcoder _(( void ));
273
310
  void init_pg_text_encoder _(( void ));
274
311
  void init_pg_text_decoder _(( void ));
275
312
  void init_pg_binary_encoder _(( void ));
@@ -282,11 +319,12 @@ int pg_coder_enc_to_s _(( t_pg_coder*, VALUE, c
282
319
  int pg_text_enc_identifier _(( t_pg_coder*, VALUE, char *, VALUE *, int));
283
320
  t_pg_coder_enc_func pg_coder_enc_func _(( t_pg_coder* ));
284
321
  t_pg_coder_dec_func pg_coder_dec_func _(( t_pg_coder*, int ));
285
- void pg_define_coder _(( const char *, void *, VALUE, VALUE ));
322
+ VALUE pg_define_coder _(( const char *, void *, VALUE, VALUE ));
286
323
  VALUE pg_obj_to_i _(( VALUE ));
287
324
  VALUE pg_tmbc_allocate _(( void ));
288
325
  void pg_coder_init_encoder _(( VALUE ));
289
326
  void pg_coder_init_decoder _(( VALUE ));
327
+ void pg_coder_compact _(( void * ));
290
328
  char *pg_rb_str_ensure_capa _(( VALUE, long, char *, char ** ));
291
329
 
292
330
  #define PG_RB_STR_ENSURE_CAPA( str, expand_len, curr_ptr, end_ptr ) \
@@ -300,20 +338,19 @@ char *pg_rb_str_ensure_capa _(( VALUE, long, char *,
300
338
  (curr_ptr) = (end_ptr) = RSTRING_PTR(str) \
301
339
  )
302
340
 
303
- #define PG_RB_TAINTED_STR_NEW( str, curr_ptr, end_ptr ) ( \
304
- (str) = rb_tainted_str_new( NULL, 0 ), \
305
- (curr_ptr) = (end_ptr) = RSTRING_PTR(str) \
306
- )
307
-
308
341
  VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
309
342
  VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
310
343
  int pg_typemap_fit_to_copy_get _(( VALUE ));
311
344
  VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
312
345
  t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
313
346
  VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
347
+ void pg_typemap_mark _(( void * ));
348
+ size_t pg_typemap_memsize _(( const void * ));
349
+ void pg_typemap_compact _(( void * ));
314
350
 
315
351
  PGconn *pg_get_pgconn _(( VALUE ));
316
352
  t_pg_connection *pg_get_connection _(( VALUE ));
353
+ VALUE pgconn_block _(( int, VALUE *, VALUE ));
317
354
 
318
355
  VALUE pg_new_result _(( PGresult *, VALUE ));
319
356
  VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
@@ -328,16 +365,10 @@ VALUE pg_tuple_new _(( VALUE, int ));
328
365
  static inline t_pg_result *
329
366
  pgresult_get_this( VALUE self )
330
367
  {
331
- t_pg_result *this = RTYPEDDATA_DATA(self);
332
-
333
- if( this == NULL )
334
- rb_raise(rb_ePGerror, "result has been cleared");
335
-
336
- return this;
368
+ return RTYPEDDATA_DATA(self);
337
369
  }
338
370
 
339
371
 
340
- rb_encoding * pg_get_pg_encoding_as_rb_encoding _(( int ));
341
372
  rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
342
373
  const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
343
374
  rb_encoding *pg_conn_enc_get _(( PGconn * ));
@@ -1,24 +1,26 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id: pg_binary_decoder.c,v 5d166a4d0441 2018/07/29 12:03:00 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
7
7
  #include "ruby/version.h"
8
8
  #include "pg.h"
9
- #include "util.h"
9
+ #include "pg_util.h"
10
10
  #ifdef HAVE_INTTYPES_H
11
11
  #include <inttypes.h>
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
  /*
18
20
  * Document-class: PG::BinaryDecoder::Boolean < PG::SimpleDecoder
19
21
  *
20
- * This is a decoder class for conversion of PostgreSQL binary bool type
21
- * to Ruby true or false objects.
22
+ * This is a decoder class for conversion of PostgreSQL binary +bool+ type
23
+ * to Ruby +true+ or +false+ objects.
22
24
  *
23
25
  */
24
26
  static VALUE
@@ -33,7 +35,7 @@ pg_bin_dec_boolean(t_pg_coder *conv, const char *val, int len, int tuple, int fi
33
35
  /*
34
36
  * Document-class: PG::BinaryDecoder::Integer < PG::SimpleDecoder
35
37
  *
36
- * This is a decoder class for conversion of PostgreSQL binary int2, int4 and int8 types
38
+ * This is a decoder class for conversion of PostgreSQL binary +int2+, +int4+ and +int8+ types
37
39
  * to Ruby Integer objects.
38
40
  *
39
41
  */
@@ -55,7 +57,7 @@ pg_bin_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int fi
55
57
  /*
56
58
  * Document-class: PG::BinaryDecoder::Float < PG::SimpleDecoder
57
59
  *
58
- * This is a decoder class for conversion of PostgreSQL binary float4 and float8 types
60
+ * This is a decoder class for conversion of PostgreSQL binary +float4+ and +float8+ types
59
61
  * to Ruby Float objects.
60
62
  *
61
63
  */
@@ -87,7 +89,7 @@ pg_bin_dec_float(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
87
89
  * Document-class: PG::BinaryDecoder::Bytea < PG::SimpleDecoder
88
90
  *
89
91
  * This decoder class delivers the data received from the server as binary String object.
90
- * It is therefore suitable for conversion of PostgreSQL bytea data as well as any other
92
+ * It is therefore suitable for conversion of PostgreSQL +bytea+ data as well as any other
91
93
  * data in binary format.
92
94
  *
93
95
  */
@@ -95,7 +97,7 @@ VALUE
95
97
  pg_bin_dec_bytea(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
96
98
  {
97
99
  VALUE ret;
98
- ret = rb_tainted_str_new( val, len );
100
+ ret = rb_str_new( val, len );
99
101
  PG_ENCODING_SET_NOCHECK( ret, rb_ascii8bit_encindex() );
100
102
  return ret;
101
103
  }
@@ -103,7 +105,7 @@ pg_bin_dec_bytea(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
103
105
  /*
104
106
  * Document-class: PG::BinaryDecoder::ToBase64 < PG::CompositeDecoder
105
107
  *
106
- * This is a decoder class for conversion of binary (bytea) to base64 data.
108
+ * This is a decoder class for conversion of binary +bytea+ to base64 data.
107
109
  *
108
110
  */
109
111
  static VALUE
@@ -113,7 +115,7 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int
113
115
  t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
114
116
  int encoded_len = BASE64_ENCODED_SIZE(len);
115
117
  /* create a buffer of the encoded length */
116
- VALUE out_value = rb_tainted_str_new(NULL, encoded_len);
118
+ VALUE out_value = rb_str_new(NULL, encoded_len);
117
119
 
118
120
  base64_encode( RSTRING_PTR(out_value), val, len );
119
121
 
@@ -154,7 +156,8 @@ static VALUE
154
156
  pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
155
157
  {
156
158
  int64_t timestamp;
157
- struct timespec ts;
159
+ int64_t sec;
160
+ int64_t nsec;
158
161
  VALUE t;
159
162
 
160
163
  if( len != sizeof(timestamp) ){
@@ -171,14 +174,17 @@ pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int
171
174
  default:
172
175
  /* PostgreSQL's timestamp is based on year 2000 and Ruby's time is based on 1970.
173
176
  * Adjust the 30 years difference. */
174
- ts.tv_sec = (timestamp / 1000000) + 10957L * 24L * 3600L;
175
- ts.tv_nsec = (timestamp % 1000000) * 1000;
177
+ sec = (timestamp / 1000000) + 10957L * 24L * 3600L;
178
+ nsec = (timestamp % 1000000) * 1000;
176
179
 
177
- #if (RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 3)) && defined(NEGATIVE_TIME_T)
180
+ #if (RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 3)) && defined(NEGATIVE_TIME_T) && defined(SIZEOF_TIME_T) && SIZEOF_TIME_T >= 8
178
181
  /* Fast path for time conversion */
179
- t = rb_time_timespec_new(&ts, conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL ? INT_MAX : INT_MAX-1);
182
+ {
183
+ struct timespec ts = {sec, nsec};
184
+ t = rb_time_timespec_new(&ts, conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL ? INT_MAX : INT_MAX-1);
185
+ }
180
186
  #else
181
- t = rb_funcall(rb_cTime, rb_intern("at"), 2, LL2NUM(ts.tv_sec), LL2NUM(ts.tv_nsec / 1000));
187
+ t = rb_funcall(rb_cTime, rb_intern("at"), 2, LL2NUM(sec), LL2NUM(nsec / 1000));
182
188
  if( !(conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL) ) {
183
189
  t = rb_funcall(t, rb_intern("utc"), 0);
184
190
  }
@@ -191,6 +197,82 @@ pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int
191
197
  }
192
198
  }
193
199
 
200
+ #define PG_INT32_MIN (-0x7FFFFFFF-1)
201
+ #define PG_INT32_MAX (0x7FFFFFFF)
202
+ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
203
+ #define MONTHS_PER_YEAR 12
204
+
205
+ /* taken from PostgreSQL sources at src/backend/utils/adt/datetime.c */
206
+ void
207
+ j2date(int jd, int *year, int *month, int *day)
208
+ {
209
+ unsigned int julian;
210
+ unsigned int quad;
211
+ unsigned int extra;
212
+ int y;
213
+
214
+ julian = jd;
215
+ julian += 32044;
216
+ quad = julian / 146097;
217
+ extra = (julian - quad * 146097) * 4 + 3;
218
+ julian += 60 + quad * 3 + extra / 146097;
219
+ quad = julian / 1461;
220
+ julian -= quad * 1461;
221
+ y = julian * 4 / 1461;
222
+ julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
223
+ + 123;
224
+ y += quad * 4;
225
+ *year = y - 4800;
226
+ quad = julian * 2141 / 65536;
227
+ *day = julian - 7834 * quad / 256;
228
+ *month = (quad + 10) % MONTHS_PER_YEAR + 1;
229
+ } /* j2date() */
230
+
231
+ /*
232
+ * Document-class: PG::BinaryDecoder::Date < PG::SimpleDecoder
233
+ *
234
+ * This is a decoder class for conversion of PostgreSQL binary date
235
+ * to Ruby Date objects.
236
+ */
237
+ static VALUE
238
+ pg_bin_dec_date(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
239
+ {
240
+ int year, month, day;
241
+ int date;
242
+
243
+ if (len != 4) {
244
+ rb_raise(rb_eTypeError, "unexpected date format != 4 bytes");
245
+ }
246
+
247
+ date = read_nbo32(val);
248
+ switch(date){
249
+ case PG_INT32_MAX:
250
+ return rb_str_new2("infinity");
251
+ case PG_INT32_MIN:
252
+ return rb_str_new2("-infinity");
253
+ default:
254
+ j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
255
+
256
+ return rb_funcall(s_Date, s_id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
257
+ }
258
+ }
259
+
260
+ /* called per autoload when BinaryDecoder::Date is used */
261
+ static VALUE
262
+ init_pg_bin_decoder_date(VALUE rb_mPG_BinaryDecoder)
263
+ {
264
+ rb_require("date");
265
+ s_Date = rb_const_get(rb_cObject, rb_intern("Date"));
266
+ rb_gc_register_mark_object(s_Date);
267
+ s_id_new = rb_intern("new");
268
+
269
+ /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Date", rb_cPG_SimpleDecoder ); */
270
+ pg_define_coder( "Date", pg_bin_dec_date, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
271
+
272
+ return Qnil;
273
+ }
274
+
275
+
194
276
  /*
195
277
  * Document-class: PG::BinaryDecoder::String < PG::SimpleDecoder
196
278
  *
@@ -201,10 +283,11 @@ pg_bin_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int
201
283
  */
202
284
 
203
285
  void
204
- init_pg_binary_decoder()
286
+ init_pg_binary_decoder(void)
205
287
  {
206
288
  /* This module encapsulates all decoder classes with binary input format */
207
289
  rb_mPG_BinaryDecoder = rb_define_module_under( rb_mPG, "BinaryDecoder" );
290
+ rb_define_private_method(rb_singleton_class(rb_mPG_BinaryDecoder), "init_date", init_pg_bin_decoder_date, 0);
208
291
 
209
292
  /* Make RDoc aware of the decoder classes... */
210
293
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Boolean", rb_cPG_SimpleDecoder ); */