pg 0.18.4 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/BSDL +2 -2
  5. data/ChangeLog +0 -5911
  6. data/History.rdoc +240 -0
  7. data/Manifest.txt +8 -20
  8. data/README-Windows.rdoc +4 -4
  9. data/README.ja.rdoc +1 -2
  10. data/README.rdoc +64 -15
  11. data/Rakefile +20 -21
  12. data/Rakefile.cross +67 -69
  13. data/ext/errorcodes.def +101 -0
  14. data/ext/errorcodes.rb +1 -1
  15. data/ext/errorcodes.txt +33 -2
  16. data/ext/extconf.rb +26 -36
  17. data/ext/gvl_wrappers.c +4 -0
  18. data/ext/gvl_wrappers.h +27 -39
  19. data/ext/pg.c +156 -145
  20. data/ext/pg.h +74 -98
  21. data/ext/pg_binary_decoder.c +82 -15
  22. data/ext/pg_binary_encoder.c +20 -19
  23. data/ext/pg_coder.c +103 -21
  24. data/ext/pg_connection.c +917 -523
  25. data/ext/pg_copy_coder.c +50 -12
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +590 -208
  28. data/ext/pg_text_decoder.c +606 -40
  29. data/ext/pg_text_encoder.c +245 -94
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +14 -7
  32. data/ext/pg_type_map_all_strings.c +4 -4
  33. data/ext/pg_type_map_by_class.c +9 -4
  34. data/ext/pg_type_map_by_column.c +7 -6
  35. data/ext/pg_type_map_by_mri_type.c +1 -1
  36. data/ext/pg_type_map_by_oid.c +3 -2
  37. data/ext/pg_type_map_in_ruby.c +1 -1
  38. data/ext/{util.c → pg_util.c} +10 -10
  39. data/ext/{util.h → pg_util.h} +2 -2
  40. data/lib/pg.rb +23 -13
  41. data/lib/pg/basic_type_mapping.rb +155 -32
  42. data/lib/pg/binary_decoder.rb +23 -0
  43. data/lib/pg/coder.rb +23 -2
  44. data/lib/pg/connection.rb +73 -13
  45. data/lib/pg/constants.rb +2 -1
  46. data/lib/pg/exceptions.rb +2 -1
  47. data/lib/pg/result.rb +24 -7
  48. data/lib/pg/text_decoder.rb +24 -22
  49. data/lib/pg/text_encoder.rb +40 -8
  50. data/lib/pg/tuple.rb +30 -0
  51. data/lib/pg/type_map_by_column.rb +3 -2
  52. data/spec/helpers.rb +61 -36
  53. data/spec/pg/basic_type_mapping_spec.rb +415 -36
  54. data/spec/pg/connection_spec.rb +732 -327
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +253 -21
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +4 -4
  59. data/spec/pg/type_map_by_column_spec.rb +6 -2
  60. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  61. data/spec/pg/type_map_by_oid_spec.rb +3 -3
  62. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  63. data/spec/pg/type_map_spec.rb +1 -1
  64. data/spec/pg/type_spec.rb +446 -20
  65. data/spec/pg_spec.rb +2 -2
  66. metadata +63 -72
  67. metadata.gz.sig +0 -0
  68. data/sample/array_insert.rb +0 -20
  69. data/sample/async_api.rb +0 -106
  70. data/sample/async_copyto.rb +0 -39
  71. data/sample/async_mixed.rb +0 -56
  72. data/sample/check_conn.rb +0 -21
  73. data/sample/copyfrom.rb +0 -81
  74. data/sample/copyto.rb +0 -19
  75. data/sample/cursor.rb +0 -21
  76. data/sample/disk_usage_report.rb +0 -186
  77. data/sample/issue-119.rb +0 -94
  78. data/sample/losample.rb +0 -69
  79. data/sample/minimal-testcase.rb +0 -17
  80. data/sample/notify_wait.rb +0 -72
  81. data/sample/pg_statistics.rb +0 -294
  82. data/sample/replication_monitor.rb +0 -231
  83. data/sample/test_binary_values.rb +0 -33
  84. data/sample/wal_shipper.rb +0 -434
  85. data/sample/warehouse_partitions.rb +0 -320
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_decoder.c - PG::TextDecoder module
3
- * $Id: pg_text_decoder.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -12,13 +12,13 @@
12
12
  * assigns a decoder function.
13
13
  *
14
14
  * Signature of all type cast decoders is:
15
- * VALUE decoder_function(t_pg_coder *this, char *val, int len, int tuple, int field, int enc_idx)
15
+ * VALUE decoder_function(t_pg_coder *this, const char *val, int len, int tuple, int field, int enc_idx)
16
16
  *
17
17
  * Params:
18
18
  * this - The data part of the coder object that belongs to the decoder function.
19
- * val, len - The text or binary data to decode. The caller ensures, that the data is
20
- * zero terminated ( that is val[len] = 0 ). The memory should be used read
21
- * only by the callee.
19
+ * val, len - The text or binary data to decode.
20
+ * The caller ensures, that text data (format=0) is zero terminated so that val[len]=0.
21
+ * The memory should be used read-only by the callee.
22
22
  * tuple - Row of the value within the result set.
23
23
  * field - Column of the value within the result set.
24
24
  * enc_idx - Index of the Encoding that any output String should get assigned.
@@ -28,15 +28,39 @@
28
28
  *
29
29
  */
30
30
 
31
+ #include "ruby/version.h"
31
32
  #include "pg.h"
32
- #include "util.h"
33
+ #include "pg_util.h"
33
34
  #ifdef HAVE_INTTYPES_H
34
35
  #include <inttypes.h>
35
36
  #endif
37
+ #include <ctype.h>
38
+ #include <time.h>
39
+ #if !defined(_WIN32)
40
+ #include <arpa/inet.h>
41
+ #include <sys/socket.h>
42
+ #endif
43
+ #include <string.h>
36
44
 
37
45
  VALUE rb_mPG_TextDecoder;
38
46
  static ID s_id_decode;
39
-
47
+ static ID s_id_Rational;
48
+ static ID s_id_new;
49
+ static ID s_id_utc;
50
+ static ID s_id_getlocal;
51
+ static ID s_id_BigDecimal;
52
+
53
+ static VALUE s_IPAddr;
54
+ static VALUE s_vmasks4;
55
+ static VALUE s_vmasks6;
56
+ static VALUE s_nan, s_pos_inf, s_neg_inf;
57
+ static int use_ipaddr_alloc;
58
+ static ID s_id_lshift;
59
+ static ID s_id_add;
60
+ static ID s_id_mask;
61
+ static ID s_ivar_family;
62
+ static ID s_ivar_addr;
63
+ static ID s_ivar_mask_addr;
40
64
 
41
65
  /*
42
66
  * Document-class: PG::TextDecoder::Boolean < PG::SimpleDecoder
@@ -46,7 +70,7 @@ static ID s_id_decode;
46
70
  *
47
71
  */
48
72
  static VALUE
49
- pg_text_dec_boolean(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
73
+ pg_text_dec_boolean(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
50
74
  {
51
75
  if (len < 1) {
52
76
  rb_raise( rb_eTypeError, "wrong data for text boolean converter in tuple %d field %d", tuple, field);
@@ -63,9 +87,9 @@ pg_text_dec_boolean(t_pg_coder *conv, char *val, int len, int tuple, int field,
63
87
  *
64
88
  */
65
89
  VALUE
66
- pg_text_dec_string(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
90
+ pg_text_dec_string(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
67
91
  {
68
- VALUE ret = rb_tainted_str_new( val, len );
92
+ VALUE ret = rb_str_new( val, len );
69
93
  PG_ENCODING_SET_NOCHECK( ret, enc_idx );
70
94
  return ret;
71
95
  }
@@ -78,7 +102,7 @@ pg_text_dec_string(t_pg_coder *conv, char *val, int len, int tuple, int field, i
78
102
  *
79
103
  */
80
104
  static VALUE
81
- pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
105
+ pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
82
106
  {
83
107
  long i;
84
108
  int max_len;
@@ -103,7 +127,7 @@ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field,
103
127
  * conn.exec("select generate_series(1,1000000)").values }
104
128
  * end
105
129
  */
106
- char *val_pos = val;
130
+ const char *val_pos = val;
107
131
  char digit = *val_pos;
108
132
  int neg;
109
133
  int error = 0;
@@ -134,6 +158,19 @@ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field,
134
158
  return rb_cstr2inum(val, 10);
135
159
  }
136
160
 
161
+ /*
162
+ * Document-class: PG::TextDecoder::Numeric < PG::SimpleDecoder
163
+ *
164
+ * This is a decoder class for conversion of PostgreSQL numeric types
165
+ * to Ruby BigDecimal objects.
166
+ *
167
+ */
168
+ static VALUE
169
+ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
170
+ {
171
+ return rb_funcall(rb_cObject, s_id_BigDecimal, 1, rb_str_new(val, len));
172
+ }
173
+
137
174
  /*
138
175
  * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
139
176
  *
@@ -142,9 +179,37 @@ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field,
142
179
  *
143
180
  */
144
181
  static VALUE
145
- pg_text_dec_float(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
182
+ pg_text_dec_float(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
146
183
  {
147
- return rb_float_new(strtod(val, NULL));
184
+ switch(*val) {
185
+ case 'N':
186
+ return s_nan;
187
+ case 'I':
188
+ return s_pos_inf;
189
+ case '-':
190
+ if (val[1] == 'I') {
191
+ return s_neg_inf;
192
+ } else {
193
+ return rb_float_new(rb_cstr_to_dbl(val, Qfalse));
194
+ }
195
+ default:
196
+ return rb_float_new(rb_cstr_to_dbl(val, Qfalse));
197
+ }
198
+ }
199
+
200
+ struct pg_blob_initialization {
201
+ char *blob_string;
202
+ size_t length;
203
+ };
204
+
205
+ static VALUE pg_create_blob(VALUE v) {
206
+ struct pg_blob_initialization *bi = (struct pg_blob_initialization *)v;
207
+ return rb_str_new(bi->blob_string, bi->length);
208
+ }
209
+
210
+ static VALUE pg_pq_freemem(VALUE mem) {
211
+ PQfreemem((void *)mem);
212
+ return Qfalse;
148
213
  }
149
214
 
150
215
  /*
@@ -155,18 +220,54 @@ pg_text_dec_float(t_pg_coder *conv, char *val, int len, int tuple, int field, in
155
220
  *
156
221
  */
157
222
  static VALUE
158
- pg_text_dec_bytea(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
223
+ pg_text_dec_bytea(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
159
224
  {
160
- unsigned char *to;
161
- size_t to_len;
162
- VALUE ret;
225
+ struct pg_blob_initialization bi;
163
226
 
164
- to = PQunescapeBytea( (unsigned char *)val, &to_len);
227
+ bi.blob_string = (char *)PQunescapeBytea((unsigned char*)val, &bi.length);
228
+ if (bi.blob_string == NULL) {
229
+ rb_raise(rb_eNoMemError, "PQunescapeBytea failure: probably not enough memory");
230
+ }
231
+ return rb_ensure(pg_create_blob, (VALUE)&bi, pg_pq_freemem, (VALUE)bi.blob_string);
232
+ }
165
233
 
166
- ret = rb_tainted_str_new((char*)to, to_len);
167
- PQfreemem(to);
234
+ /*
235
+ * array_isspace() --- a non-locale-dependent isspace()
236
+ *
237
+ * We used to use isspace() for parsing array values, but that has
238
+ * undesirable results: an array value might be silently interpreted
239
+ * differently depending on the locale setting. Now we just hard-wire
240
+ * the traditional ASCII definition of isspace().
241
+ */
242
+ static int
243
+ array_isspace(char ch)
244
+ {
245
+ if (ch == ' ' ||
246
+ ch == '\t' ||
247
+ ch == '\n' ||
248
+ ch == '\r' ||
249
+ ch == '\v' ||
250
+ ch == '\f')
251
+ return 1;
252
+ return 0;
253
+ }
168
254
 
169
- return ret;
255
+ static int
256
+ array_isdim(char ch)
257
+ {
258
+ if ( (ch >= '0' && ch <= '9') ||
259
+ (ch == '-') ||
260
+ (ch == '+') ||
261
+ (ch == ':') )
262
+ return 1;
263
+ return 0;
264
+ }
265
+
266
+ static void
267
+ array_parser_error(t_pg_composite_coder *this, const char *text){
268
+ if( (this->comp.flags & PG_CODER_FORMAT_ERROR_MASK) == PG_CODER_FORMAT_ERROR_TO_RAISE ){
269
+ rb_raise( rb_eTypeError, "%s", text );
270
+ }
170
271
  }
171
272
 
172
273
  /*
@@ -174,7 +275,7 @@ pg_text_dec_bytea(t_pg_coder *conv, char *val, int len, int tuple, int field, in
174
275
  * https://github.com/dockyard/pg_array_parser
175
276
  */
176
277
  static VALUE
177
- read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int array_string_length, char *word, int enc_idx, int tuple, int field, t_pg_coder_dec_func dec_func)
278
+ read_array_without_dim(t_pg_composite_coder *this, int *index, const char *c_pg_array_string, int array_string_length, char *word, int enc_idx, int tuple, int field, t_pg_coder_dec_func dec_func)
178
279
  {
179
280
  /* Return value: array */
180
281
  VALUE array;
@@ -198,7 +299,7 @@ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int
198
299
 
199
300
  /* Special case the empty array, so it doesn't need to be handled manually inside
200
301
  * the loop. */
201
- if(((*index) < array_string_length) && c_pg_array_string[(*index)] == '}')
302
+ if(((*index) < array_string_length) && c_pg_array_string[*index] == '}')
202
303
  {
203
304
  return array;
204
305
  }
@@ -238,10 +339,17 @@ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int
238
339
  }
239
340
  else if(c == '{')
240
341
  {
342
+ VALUE subarray;
241
343
  (*index)++;
242
- rb_ary_push(array, read_array(this, index, c_pg_array_string, array_string_length, word, enc_idx, tuple, field, dec_func));
344
+ subarray = read_array_without_dim(this, index, c_pg_array_string, array_string_length, word, enc_idx, tuple, field, dec_func);
345
+ rb_ary_push(array, subarray);
243
346
  escapeNext = 1;
244
347
  }
348
+ else if(c == 0)
349
+ {
350
+ array_parser_error( this, "premature end of the array string" );
351
+ return array;
352
+ }
245
353
  else
246
354
  {
247
355
  word[word_index] = c;
@@ -268,30 +376,112 @@ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int
268
376
  }
269
377
  }
270
378
 
379
+ array_parser_error( this, "premature end of the array string" );
271
380
  return array;
272
381
  }
273
382
 
274
383
  /*
275
384
  * Document-class: PG::TextDecoder::Array < PG::CompositeDecoder
276
385
  *
277
- * This is the decoder class for PostgreSQL array types.
386
+ * This is a decoder class for PostgreSQL array types.
387
+ *
388
+ * It returns an Array with possibly an arbitrary number of sub-Arrays.
389
+ * All values are decoded according to the #elements_type accessor.
390
+ * Sub-arrays are decoded recursively.
278
391
  *
279
- * All values are decoded according to the #elements_type
280
- * accessor. Sub-arrays are decoded recursively.
392
+ * This decoder simply ignores any dimension decorations preceding the array values.
393
+ * It returns all array values as regular ruby Array with a zero based index, regardless of the index given in the dimension decoration.
394
+ *
395
+ * An array decoder which respects dimension decorations is waiting to be implemented.
281
396
  *
282
397
  */
283
398
  static VALUE
284
- pg_text_dec_array(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
399
+ pg_text_dec_array(t_pg_coder *conv, const char *c_pg_array_string, int array_string_length, int tuple, int field, int enc_idx)
285
400
  {
401
+ int index = 0;
402
+ int ndim = 0;
403
+ VALUE ret;
286
404
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
287
- t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
288
- /* create a buffer of the same length, as that will be the worst case */
289
- char *word = xmalloc(len + 1);
290
- int index = 1;
291
-
292
- VALUE return_value = read_array(this, &index, val, len, word, enc_idx, tuple, field, dec_func);
293
- free(word);
294
- return return_value;
405
+
406
+ /*
407
+ * If the input string starts with dimension info, read and use that.
408
+ * Otherwise, we require the input to be in curly-brace style, and we
409
+ * prescan the input to determine dimensions.
410
+ *
411
+ * Dimension info takes the form of one or more [n] or [m:n] items. The
412
+ * outer loop iterates once per dimension item.
413
+ */
414
+ for (;;)
415
+ {
416
+ /*
417
+ * Note: we currently allow whitespace between, but not within,
418
+ * dimension items.
419
+ */
420
+ while (array_isspace(c_pg_array_string[index]))
421
+ index++;
422
+ if (c_pg_array_string[index] != '[')
423
+ break; /* no more dimension items */
424
+ index++;
425
+
426
+ while (array_isdim(c_pg_array_string[index]))
427
+ index++;
428
+
429
+ if (c_pg_array_string[index] != ']'){
430
+ array_parser_error( this, "missing \"]\" in array dimensions");
431
+ break;
432
+ }
433
+ index++;
434
+
435
+ ndim++;
436
+ }
437
+
438
+ if (ndim == 0)
439
+ {
440
+ /* No array dimensions */
441
+ }
442
+ else
443
+ {
444
+ /* If array dimensions are given, expect '=' operator */
445
+ if (c_pg_array_string[index] != '=') {
446
+ array_parser_error( this, "missing assignment operator");
447
+ index-=2; /* jump back to before "]" so that we don't break behavior to pg < 1.1 */
448
+ }
449
+ index++;
450
+
451
+ while (array_isspace(c_pg_array_string[index]))
452
+ index++;
453
+ }
454
+
455
+ if (c_pg_array_string[index] != '{')
456
+ array_parser_error( this, "array value must start with \"{\" or dimension information");
457
+ index++;
458
+
459
+ if ( index < array_string_length && c_pg_array_string[index] == '}' ) {
460
+ /* avoid buffer allocation for empty array */
461
+ ret = rb_ary_new();
462
+ } else {
463
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
464
+ /* create a buffer of the same length, as that will be the worst case */
465
+ VALUE buf = rb_str_new(NULL, array_string_length);
466
+ char *word = RSTRING_PTR(buf);
467
+
468
+ ret = read_array_without_dim(this, &index, c_pg_array_string, array_string_length, word, enc_idx, tuple, field, dec_func);
469
+
470
+ RB_GC_GUARD(buf);
471
+ }
472
+
473
+ if (c_pg_array_string[index] != '}' )
474
+ array_parser_error( this, "array value must end with \"}\"");
475
+ index++;
476
+
477
+ /* only whitespace is allowed after the closing brace */
478
+ for(;index < array_string_length; ++index)
479
+ {
480
+ if (!array_isspace(c_pg_array_string[index]))
481
+ array_parser_error( this, "malformed array literal: Junk after closing right brace.");
482
+ }
483
+
484
+ return ret;
295
485
  }
296
486
 
297
487
  /*
@@ -305,7 +495,7 @@ pg_text_dec_array(t_pg_coder *conv, char *val, int len, int tuple, int field, in
305
495
  *
306
496
  */
307
497
  static VALUE
308
- pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
498
+ pg_text_dec_identifier(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
309
499
  {
310
500
  /* Return value: array */
311
501
  VALUE array;
@@ -367,13 +557,13 @@ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int fiel
367
557
  *
368
558
  */
369
559
  static VALUE
370
- pg_text_dec_from_base64(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
560
+ pg_text_dec_from_base64(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
371
561
  {
372
562
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
373
563
  t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
374
564
  int decoded_len;
375
565
  /* create a buffer of the expected decoded length */
376
- VALUE out_value = rb_tainted_str_new(NULL, BASE64_DECODED_SIZE(len));
566
+ VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
377
567
 
378
568
  decoded_len = base64_decode( RSTRING_PTR(out_value), val, len );
379
569
  rb_str_set_len(out_value, decoded_len);
@@ -392,10 +582,380 @@ pg_text_dec_from_base64(t_pg_coder *conv, char *val, int len, int tuple, int fie
392
582
  return out_value;
393
583
  }
394
584
 
585
+ static inline int char_to_digit(char c)
586
+ {
587
+ return c - '0';
588
+ }
589
+
590
+ static int str2_to_int(const char *str)
591
+ {
592
+ return char_to_digit(str[0]) * 10
593
+ + char_to_digit(str[1]);
594
+ }
595
+
596
+ static int parse_year(const char **str) {
597
+ int year = 0;
598
+ int i;
599
+ const char * p = *str;
600
+
601
+ for(i = 0; isdigit(*p) && i < 7; i++, p++) {
602
+ year = 10 * year + char_to_digit(*p);
603
+ }
604
+
605
+ *str = p;
606
+ return year;
607
+ }
608
+
609
+ #define TZ_NEG 1
610
+ #define TZ_POS 2
611
+
612
+ /*
613
+ * Document-class: PG::TextDecoder::Timestamp < PG::SimpleDecoder
614
+ *
615
+ * This is a decoder class for conversion of PostgreSQL text timestamps
616
+ * to Ruby Time objects.
617
+ *
618
+ * The following flags can be used to specify time interpretation when no timezone is given:
619
+ * * +PG::Coder::TIMESTAMP_DB_UTC+ : Interpret timestamp as UTC time (default)
620
+ * * +PG::Coder::TIMESTAMP_DB_LOCAL+ : Interpret timestamp as local time
621
+ * * +PG::Coder::TIMESTAMP_APP_UTC+ : Return timestamp as UTC time (default)
622
+ * * +PG::Coder::TIMESTAMP_APP_LOCAL+ : Return timestamp as local time
623
+ *
624
+ * Example:
625
+ * deco = PG::TextDecoder::Timestamp.new(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL)
626
+ * deco.decode("2000-01-01 00:00:00") # => 2000-01-01 01:00:00 +0100
627
+ * deco.decode("2000-01-01 00:00:00.123-06") # => 2000-01-01 00:00:00 -0600
628
+ */
629
+ static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
630
+ {
631
+ const char *str = val;
632
+ int year, mon, day;
633
+ int hour, min, sec;
634
+ int nsec = 0;
635
+ int tz_given = 0;
636
+ int tz_hour = 0;
637
+ int tz_min = 0;
638
+ int tz_sec = 0;
639
+
640
+ year = parse_year(&str);
641
+
642
+ if ( year > 0
643
+ && str[0] == '-' && isdigit(str[1]) && isdigit(str[2])
644
+ && str[3] == '-' && isdigit(str[4]) && isdigit(str[5])
645
+ && str[6] == ' ' && isdigit(str[7]) && isdigit(str[8])
646
+ && str[9] == ':' && isdigit(str[10]) && isdigit(str[11])
647
+ && str[12] == ':' && isdigit(str[13]) && isdigit(str[14])
648
+ ) {
649
+
650
+ mon = str2_to_int(str+1); str += 3;
651
+ day = str2_to_int(str+1); str += 3;
652
+ hour = str2_to_int(str+1); str += 3;
653
+ min = str2_to_int(str+1); str += 3;
654
+ sec = str2_to_int(str+1); str += 3;
655
+
656
+ if (str[0] == '.' && isdigit(str[1])) {
657
+ /* nano second part, up to 9 digits */
658
+ static const int coef[9] = {
659
+ 100000000, 10000000, 1000000,
660
+ 100000, 10000, 1000, 100, 10, 1
661
+ };
662
+ int i;
663
+
664
+ str++;
665
+ for (i = 0; i < 9 && isdigit(*str); i++)
666
+ {
667
+ nsec += coef[i] * char_to_digit(*str++);
668
+ }
669
+ /* consume digits smaller than nsec */
670
+ while(isdigit(*str)) str++;
671
+ }
672
+
673
+ if ((str[0] == '+' || str[0] == '-') && isdigit(str[1]) && isdigit(str[2])) {
674
+ tz_given = str[0] == '-' ? TZ_NEG : TZ_POS;
675
+ tz_hour = str2_to_int(str+1); str += 3;
676
+
677
+ if (str[0] == ':' && isdigit(str[1]) && isdigit(str[2]))
678
+ {
679
+ tz_min = str2_to_int(str+1); str += 3;
680
+ }
681
+ if (str[0] == ':' && isdigit(str[1]) && isdigit(str[2]))
682
+ {
683
+ tz_sec = str2_to_int(str+1); str += 3;
684
+ }
685
+ }
686
+
687
+ if (str[0] == ' ' && str[1] == 'B' && str[2] == 'C') {
688
+ year = -year + 1;
689
+ str += 3;
690
+ }
691
+
692
+ if (*str == '\0') { /* must have consumed all the string */
693
+ VALUE sec_value;
694
+ VALUE gmt_offset_value;
695
+ VALUE res;
696
+
697
+ #if (RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 3)) && defined(HAVE_TIMEGM)
698
+ /* Fast path for time conversion */
699
+ struct tm tm;
700
+ struct timespec ts;
701
+ tm.tm_year = year - 1900;
702
+ tm.tm_mon = mon - 1;
703
+ tm.tm_mday = day;
704
+ tm.tm_hour = hour;
705
+ tm.tm_min = min;
706
+ tm.tm_sec = sec;
707
+ tm.tm_isdst = -1;
708
+
709
+ if (tz_given) {
710
+ /* with timezone */
711
+ time_t time = timegm(&tm);
712
+ if (time != -1){
713
+ int gmt_offset;
714
+
715
+ gmt_offset = tz_hour * 3600 + tz_min * 60 + tz_sec;
716
+ if (tz_given == TZ_NEG)
717
+ {
718
+ gmt_offset = - gmt_offset;
719
+ }
720
+ ts.tv_sec = time - gmt_offset;
721
+ ts.tv_nsec = nsec;
722
+ return rb_time_timespec_new(&ts, gmt_offset);
723
+ }
724
+ } else {
725
+ /* without timezone */
726
+ time_t time;
727
+
728
+ if( conv->flags & PG_CODER_TIMESTAMP_DB_LOCAL ) {
729
+ time = mktime(&tm);
730
+ } else {
731
+ time = timegm(&tm);
732
+ }
733
+ if (time != -1){
734
+ ts.tv_sec = time;
735
+ ts.tv_nsec = nsec;
736
+ return rb_time_timespec_new(&ts, conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL ? INT_MAX : INT_MAX-1);
737
+ }
738
+ }
739
+ /* Some libc implementations fail to convert certain values,
740
+ * so that we fall through to the slow path.
741
+ */
742
+ #endif
743
+ if (nsec) {
744
+ int sec_numerator = sec * 1000000 + nsec / 1000;
745
+ int sec_denominator = 1000000;
746
+ sec_value = rb_funcall(Qnil, s_id_Rational, 2,
747
+ INT2NUM(sec_numerator), INT2NUM(sec_denominator));
748
+ } else {
749
+ sec_value = INT2NUM(sec);
750
+ }
751
+
752
+ if (tz_given) {
753
+ /* with timezone */
754
+ int gmt_offset;
755
+
756
+ gmt_offset = tz_hour * 3600 + tz_min * 60 + tz_sec;
757
+ if (tz_given == TZ_NEG)
758
+ {
759
+ gmt_offset = - gmt_offset;
760
+ }
761
+ gmt_offset_value = INT2NUM(gmt_offset);
762
+ } else {
763
+ /* without timezone */
764
+ gmt_offset_value = conv->flags & PG_CODER_TIMESTAMP_DB_LOCAL ? Qnil : INT2NUM(0);
765
+ }
766
+
767
+ res = rb_funcall(rb_cTime, s_id_new, 7,
768
+ INT2NUM(year),
769
+ INT2NUM(mon),
770
+ INT2NUM(day),
771
+ INT2NUM(hour),
772
+ INT2NUM(min),
773
+ sec_value,
774
+ gmt_offset_value);
775
+
776
+ if (tz_given) {
777
+ /* with timezone */
778
+ return res;
779
+ } else {
780
+ /* without timezone */
781
+ if( (conv->flags & PG_CODER_TIMESTAMP_DB_LOCAL) && (conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL) ) {
782
+ return res;
783
+ } else if( conv->flags & PG_CODER_TIMESTAMP_APP_LOCAL ) {
784
+ return rb_funcall(res, s_id_getlocal, 0);
785
+ } else {
786
+ return rb_funcall(res, s_id_utc, 0);
787
+ }
788
+ }
789
+ }
790
+ }
791
+
792
+ /* fall through to string conversion */
793
+ return pg_text_dec_string(conv, val, len, tuple, field, enc_idx);
794
+ }
795
+
796
+ /*
797
+ * Document-class: PG::TextDecoder::Inet < PG::SimpleDecoder
798
+ *
799
+ * This is a decoder class for conversion of PostgreSQL inet type
800
+ * to Ruby IPAddr values.
801
+ *
802
+ */
803
+ static VALUE
804
+ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
805
+ {
806
+ VALUE ip;
807
+ #if defined(_WIN32)
808
+ ip = rb_str_new(val, len);
809
+ ip = rb_class_new_instance(1, &ip, s_IPAddr);
810
+ #else
811
+ VALUE ip_int;
812
+ VALUE vmasks;
813
+ char dst[16];
814
+ char buf[64];
815
+ int af = strchr(val, '.') ? AF_INET : AF_INET6;
816
+ int mask = -1;
817
+
818
+ if (len >= 64) {
819
+ rb_raise(rb_eTypeError, "too long data for text inet converter in tuple %d field %d", tuple, field);
820
+ }
821
+
822
+ if (len >= 4) {
823
+ if (val[len-2] == '/') {
824
+ mask = val[len-1] - '0';
825
+ memcpy(buf, val, len-2);
826
+ buf[len-2] = '\0';
827
+ val = buf;
828
+ } else if (val[len-3] == '/') {
829
+ mask = (val[len-2]- '0')*10 + val[len-1] - '0';
830
+ memcpy(buf, val, len-3);
831
+ buf[len-3] = '\0';
832
+ val = buf;
833
+ } else if (val[len-4] == '/') {
834
+ mask = (val[len-3]- '0')*100 + (val[len-2]- '0')*10 + val[len-1] - '0';
835
+ memcpy(buf, val, len-4);
836
+ buf[len-4] = '\0';
837
+ val = buf;
838
+ }
839
+ }
840
+
841
+ if (1 != inet_pton(af, val, dst)) {
842
+ rb_raise(rb_eTypeError, "wrong data for text inet converter in tuple %d field %d val", tuple, field);
843
+ }
844
+
845
+ if (af == AF_INET) {
846
+ unsigned int ip_int_native;
847
+
848
+ if (mask == -1) {
849
+ mask = 32;
850
+ } else if (mask < 0 || mask > 32) {
851
+ rb_raise(rb_eTypeError, "invalid mask for IPv4: %d", mask);
852
+ }
853
+ vmasks = s_vmasks4;
854
+
855
+ ip_int_native = read_nbo32(dst);
856
+
857
+ /* Work around broken IPAddr behavior of convering portion
858
+ of address after netmask to 0 */
859
+ switch (mask) {
860
+ case 0:
861
+ ip_int_native = 0;
862
+ break;
863
+ case 32:
864
+ /* nothing to do */
865
+ break;
866
+ default:
867
+ ip_int_native &= ~((1UL<<(32-mask))-1);
868
+ break;
869
+ }
870
+
871
+ ip_int = UINT2NUM(ip_int_native);
872
+ } else {
873
+ unsigned long long * dstllp = (unsigned long long *)dst;
874
+ unsigned long long ip_int_native1;
875
+ unsigned long long ip_int_native2;
876
+
877
+ if (mask == -1) {
878
+ mask = 128;
879
+ } else if (mask < 0 || mask > 128) {
880
+ rb_raise(rb_eTypeError, "invalid mask for IPv6: %d", mask);
881
+ }
882
+ vmasks = s_vmasks6;
883
+
884
+ ip_int_native1 = read_nbo64(dstllp);
885
+ dstllp++;
886
+ ip_int_native2 = read_nbo64(dstllp);
887
+
888
+ if (mask == 128) {
889
+ /* nothing to do */
890
+ } else if (mask == 64) {
891
+ ip_int_native2 = 0;
892
+ } else if (mask == 0) {
893
+ ip_int_native1 = 0;
894
+ ip_int_native2 = 0;
895
+ } else if (mask < 64) {
896
+ ip_int_native1 &= ~((1ULL<<(64-mask))-1);
897
+ ip_int_native2 = 0;
898
+ } else {
899
+ ip_int_native2 &= ~((1ULL<<(128-mask))-1);
900
+ }
901
+
902
+ /* 4 Bignum allocations */
903
+ ip_int = ULL2NUM(ip_int_native1);
904
+ ip_int = rb_funcall(ip_int, s_id_lshift, 1, INT2NUM(64));
905
+ ip_int = rb_funcall(ip_int, s_id_add, 1, ULL2NUM(ip_int_native2));
906
+ }
907
+
908
+ if (use_ipaddr_alloc) {
909
+ ip = rb_obj_alloc(s_IPAddr);
910
+ rb_ivar_set(ip, s_ivar_family, INT2NUM(af));
911
+ rb_ivar_set(ip, s_ivar_addr, ip_int);
912
+ rb_ivar_set(ip, s_ivar_mask_addr, RARRAY_AREF(vmasks, mask));
913
+ } else {
914
+ VALUE ip_args[2];
915
+ ip_args[0] = ip_int;
916
+ ip_args[1] = INT2NUM(af);
917
+ ip = rb_class_new_instance(2, ip_args, s_IPAddr);
918
+ ip = rb_funcall(ip, s_id_mask, 1, INT2NUM(mask));
919
+ }
920
+
921
+ #endif
922
+ return ip;
923
+ }
924
+
395
925
  void
396
926
  init_pg_text_decoder()
397
927
  {
928
+ rb_require("ipaddr");
929
+ s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
930
+ rb_global_variable(&s_IPAddr);
931
+ s_ivar_family = rb_intern("@family");
932
+ s_ivar_addr = rb_intern("@addr");
933
+ s_ivar_mask_addr = rb_intern("@mask_addr");
934
+ s_id_lshift = rb_intern("<<");
935
+ s_id_add = rb_intern("+");
936
+ s_id_mask = rb_intern("mask");
937
+
938
+ use_ipaddr_alloc = RTEST(rb_eval_string("IPAddr.new.instance_variables.sort == [:@addr, :@family, :@mask_addr]"));
939
+
940
+ s_vmasks4 = rb_eval_string("a = [0]*33; a[0] = 0; a[32] = 0xffffffff; 31.downto(1){|i| a[i] = a[i+1] - (1 << (31 - i))}; a.freeze");
941
+ rb_global_variable(&s_vmasks4);
942
+ 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
+ rb_global_variable(&s_vmasks6);
944
+
398
945
  s_id_decode = rb_intern("decode");
946
+ s_id_Rational = rb_intern("Rational");
947
+ s_id_new = rb_intern("new");
948
+ s_id_utc = rb_intern("utc");
949
+ s_id_getlocal = rb_intern("getlocal");
950
+
951
+ rb_require("bigdecimal");
952
+ s_id_BigDecimal = rb_intern("BigDecimal");
953
+ s_nan = rb_eval_string("0.0/0.0");
954
+ rb_global_variable(&s_nan);
955
+ s_pos_inf = rb_eval_string("1.0/0.0");
956
+ rb_global_variable(&s_pos_inf);
957
+ s_neg_inf = rb_eval_string("-1.0/0.0");
958
+ rb_global_variable(&s_neg_inf);
399
959
 
400
960
  /* This module encapsulates all decoder classes with text input format */
401
961
  rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
@@ -407,12 +967,18 @@ init_pg_text_decoder()
407
967
  pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
408
968
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
409
969
  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 );
410
972
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
411
973
  pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
412
974
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
413
975
  pg_define_coder( "Bytea", pg_text_dec_bytea, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
414
976
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_SimpleDecoder ); */
415
977
  pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
978
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
979
+ 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);
416
982
 
417
983
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
418
984
  pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );