pg 0.21.0 → 1.1.4

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