pg 0.18.4 → 1.5.3

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