pg 0.18.1 → 1.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) 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 +141 -0
  7. data/.gitignore +22 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/BSDL +2 -2
  15. data/Gemfile +17 -0
  16. data/History.md +901 -0
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.md +300 -0
  20. data/README.md +286 -0
  21. data/Rakefile +43 -131
  22. data/Rakefile.cross +89 -70
  23. data/certs/ged.pem +24 -0
  24. data/certs/kanis@comcard.de.pem +20 -0
  25. data/certs/larskanis-2022.pem +26 -0
  26. data/certs/larskanis-2023.pem +24 -0
  27. data/certs/larskanis-2024.pem +24 -0
  28. data/ext/errorcodes.def +113 -0
  29. data/ext/errorcodes.rb +1 -1
  30. data/ext/errorcodes.txt +36 -2
  31. data/ext/extconf.rb +128 -55
  32. data/ext/gvl_wrappers.c +8 -0
  33. data/ext/gvl_wrappers.h +44 -33
  34. data/ext/pg.c +228 -202
  35. data/ext/pg.h +108 -99
  36. data/ext/pg_binary_decoder.c +164 -16
  37. data/ext/pg_binary_encoder.c +249 -22
  38. data/ext/pg_coder.c +189 -44
  39. data/ext/pg_connection.c +1889 -1195
  40. data/ext/pg_copy_coder.c +398 -42
  41. data/ext/pg_errors.c +1 -1
  42. data/ext/pg_record_coder.c +522 -0
  43. data/ext/pg_result.c +729 -234
  44. data/ext/pg_text_decoder.c +635 -52
  45. data/ext/pg_text_encoder.c +294 -130
  46. data/ext/pg_tuple.c +572 -0
  47. data/ext/pg_type_map.c +64 -23
  48. data/ext/pg_type_map_all_strings.c +21 -7
  49. data/ext/pg_type_map_by_class.c +59 -27
  50. data/ext/pg_type_map_by_column.c +86 -43
  51. data/ext/pg_type_map_by_mri_type.c +50 -21
  52. data/ext/pg_type_map_by_oid.c +62 -29
  53. data/ext/pg_type_map_in_ruby.c +59 -28
  54. data/ext/{util.c → pg_util.c} +13 -13
  55. data/ext/{util.h → pg_util.h} +3 -3
  56. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  57. data/lib/pg/basic_type_map_for_queries.rb +202 -0
  58. data/lib/pg/basic_type_map_for_results.rb +104 -0
  59. data/lib/pg/basic_type_registry.rb +303 -0
  60. data/lib/pg/binary_decoder/date.rb +9 -0
  61. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  62. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  63. data/lib/pg/coder.rb +36 -13
  64. data/lib/pg/connection.rb +813 -74
  65. data/lib/pg/exceptions.rb +16 -2
  66. data/lib/pg/result.rb +24 -7
  67. data/lib/pg/text_decoder/date.rb +18 -0
  68. data/lib/pg/text_decoder/inet.rb +9 -0
  69. data/lib/pg/text_decoder/json.rb +14 -0
  70. data/lib/pg/text_decoder/numeric.rb +9 -0
  71. data/lib/pg/text_decoder/timestamp.rb +30 -0
  72. data/lib/pg/text_encoder/date.rb +12 -0
  73. data/lib/pg/text_encoder/inet.rb +28 -0
  74. data/lib/pg/text_encoder/json.rb +14 -0
  75. data/lib/pg/text_encoder/numeric.rb +9 -0
  76. data/lib/pg/text_encoder/timestamp.rb +24 -0
  77. data/lib/pg/tuple.rb +30 -0
  78. data/lib/pg/type_map_by_column.rb +3 -2
  79. data/lib/pg/version.rb +4 -0
  80. data/lib/pg.rb +106 -41
  81. data/misc/openssl-pg-segfault.rb +31 -0
  82. data/misc/postgres/History.txt +9 -0
  83. data/misc/postgres/Manifest.txt +5 -0
  84. data/misc/postgres/README.txt +21 -0
  85. data/misc/postgres/Rakefile +21 -0
  86. data/misc/postgres/lib/postgres.rb +16 -0
  87. data/misc/ruby-pg/History.txt +9 -0
  88. data/misc/ruby-pg/Manifest.txt +5 -0
  89. data/misc/ruby-pg/README.txt +21 -0
  90. data/misc/ruby-pg/Rakefile +21 -0
  91. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  92. data/pg.gemspec +34 -0
  93. data/rakelib/task_extension.rb +46 -0
  94. data/sample/array_insert.rb +1 -1
  95. data/sample/async_api.rb +4 -8
  96. data/sample/async_copyto.rb +1 -1
  97. data/sample/async_mixed.rb +1 -1
  98. data/sample/check_conn.rb +1 -1
  99. data/sample/copydata.rb +71 -0
  100. data/sample/copyfrom.rb +1 -1
  101. data/sample/copyto.rb +1 -1
  102. data/sample/cursor.rb +1 -1
  103. data/sample/disk_usage_report.rb +6 -15
  104. data/sample/issue-119.rb +2 -2
  105. data/sample/losample.rb +1 -1
  106. data/sample/minimal-testcase.rb +2 -2
  107. data/sample/notify_wait.rb +1 -1
  108. data/sample/pg_statistics.rb +6 -15
  109. data/sample/replication_monitor.rb +9 -18
  110. data/sample/test_binary_values.rb +1 -1
  111. data/sample/wal_shipper.rb +2 -2
  112. data/sample/warehouse_partitions.rb +8 -17
  113. data.tar.gz.sig +0 -0
  114. metadata +135 -207
  115. metadata.gz.sig +0 -0
  116. data/ChangeLog +0 -5378
  117. data/History.rdoc +0 -297
  118. data/README.ja.rdoc +0 -14
  119. data/README.rdoc +0 -161
  120. data/lib/pg/basic_type_mapping.rb +0 -399
  121. data/lib/pg/constants.rb +0 -11
  122. data/lib/pg/text_decoder.rb +0 -42
  123. data/lib/pg/text_encoder.rb +0 -27
  124. data/spec/data/expected_trace.out +0 -26
  125. data/spec/data/random_binary_data +0 -0
  126. data/spec/helpers.rb +0 -355
  127. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  128. data/spec/pg/connection_spec.rb +0 -1459
  129. data/spec/pg/result_spec.rb +0 -449
  130. data/spec/pg/type_map_by_class_spec.rb +0 -138
  131. data/spec/pg/type_map_by_column_spec.rb +0 -222
  132. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  133. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  134. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  135. data/spec/pg/type_map_spec.rb +0 -22
  136. data/spec/pg/type_spec.rb +0 -665
  137. 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 b7f9daeeba29 2014/11/21 19:53:47 lars $
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,13 +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"
34
+ #ifdef HAVE_INTTYPES_H
33
35
  #include <inttypes.h>
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>
34
44
 
35
45
  VALUE rb_mPG_TextDecoder;
36
- static ID s_id_decode;
37
-
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;
38
63
 
39
64
  /*
40
65
  * Document-class: PG::TextDecoder::Boolean < PG::SimpleDecoder
@@ -44,7 +69,7 @@ static ID s_id_decode;
44
69
  *
45
70
  */
46
71
  static VALUE
47
- 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)
48
73
  {
49
74
  if (len < 1) {
50
75
  rb_raise( rb_eTypeError, "wrong data for text boolean converter in tuple %d field %d", tuple, field);
@@ -61,9 +86,9 @@ pg_text_dec_boolean(t_pg_coder *conv, char *val, int len, int tuple, int field,
61
86
  *
62
87
  */
63
88
  VALUE
64
- 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)
65
90
  {
66
- VALUE ret = rb_tainted_str_new( val, len );
91
+ VALUE ret = rb_str_new( val, len );
67
92
  PG_ENCODING_SET_NOCHECK( ret, enc_idx );
68
93
  return ret;
69
94
  }
@@ -76,7 +101,7 @@ pg_text_dec_string(t_pg_coder *conv, char *val, int len, int tuple, int field, i
76
101
  *
77
102
  */
78
103
  static VALUE
79
- 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)
80
105
  {
81
106
  long i;
82
107
  int max_len;
@@ -101,7 +126,7 @@ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field,
101
126
  * conn.exec("select generate_series(1,1000000)").values }
102
127
  * end
103
128
  */
104
- char *val_pos = val;
129
+ const char *val_pos = val;
105
130
  char digit = *val_pos;
106
131
  int neg;
107
132
  int error = 0;
@@ -132,6 +157,32 @@ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field,
132
157
  return rb_cstr2inum(val, 10);
133
158
  }
134
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
+
135
186
  /*
136
187
  * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
137
188
  *
@@ -140,9 +191,37 @@ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field,
140
191
  *
141
192
  */
142
193
  static VALUE
143
- 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)
144
195
  {
145
- 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;
146
225
  }
147
226
 
148
227
  /*
@@ -153,18 +232,54 @@ pg_text_dec_float(t_pg_coder *conv, char *val, int len, int tuple, int field, in
153
232
  *
154
233
  */
155
234
  static VALUE
156
- 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)
157
236
  {
158
- unsigned char *to;
159
- size_t to_len;
160
- VALUE ret;
237
+ struct pg_blob_initialization bi;
238
+
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
+ }
161
245
 
162
- to = PQunescapeBytea( (unsigned char *)val, &to_len);
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
+ }
163
266
 
164
- ret = rb_tainted_str_new((char*)to, to_len);
165
- 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
+ }
166
277
 
167
- 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
+ }
168
283
  }
169
284
 
170
285
  /*
@@ -172,7 +287,7 @@ pg_text_dec_bytea(t_pg_coder *conv, char *val, int len, int tuple, int field, in
172
287
  * https://github.com/dockyard/pg_array_parser
173
288
  */
174
289
  static VALUE
175
- 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)
176
291
  {
177
292
  /* Return value: array */
178
293
  VALUE array;
@@ -196,7 +311,7 @@ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int
196
311
 
197
312
  /* Special case the empty array, so it doesn't need to be handled manually inside
198
313
  * the loop. */
199
- if(((*index) < array_string_length) && c_pg_array_string[(*index)] == '}')
314
+ if(((*index) < array_string_length) && c_pg_array_string[*index] == '}')
200
315
  {
201
316
  return array;
202
317
  }
@@ -236,10 +351,17 @@ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int
236
351
  }
237
352
  else if(c == '{')
238
353
  {
354
+ VALUE subarray;
239
355
  (*index)++;
240
- 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);
241
358
  escapeNext = 1;
242
359
  }
360
+ else if(c == 0)
361
+ {
362
+ array_parser_error( this, "premature end of the array string" );
363
+ return array;
364
+ }
243
365
  else
244
366
  {
245
367
  word[word_index] = c;
@@ -266,34 +388,116 @@ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int
266
388
  }
267
389
  }
268
390
 
391
+ array_parser_error( this, "premature end of the array string" );
269
392
  return array;
270
393
  }
271
394
 
272
395
  /*
273
396
  * Document-class: PG::TextDecoder::Array < PG::CompositeDecoder
274
397
  *
275
- * 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.
403
+ *
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.
276
406
  *
277
- * All values are decoded according to the #elements_type
278
- * accessor. Sub-arrays are decoded recursively.
407
+ * An array decoder which respects dimension decorations is waiting to be implemented.
279
408
  *
280
409
  */
281
410
  static VALUE
282
- 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)
283
412
  {
413
+ int index = 0;
414
+ int ndim = 0;
415
+ VALUE ret;
284
416
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
285
- t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
286
- /* create a buffer of the same length, as that will be the worst case */
287
- char *word = xmalloc(len + 1);
288
- int index = 1;
289
-
290
- VALUE return_value = read_array(this, &index, val, len, word, enc_idx, tuple, field, dec_func);
291
- free(word);
292
- 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;
293
497
  }
294
498
 
295
499
  /*
296
- * Document-class: PG::TextDecoder::Identifier < PG::CompositeDecoder
500
+ * Document-class: PG::TextDecoder::Identifier < PG::SimpleDecoder
297
501
  *
298
502
  * This is the decoder class for PostgreSQL identifiers.
299
503
  *
@@ -303,18 +507,15 @@ pg_text_dec_array(t_pg_coder *conv, char *val, int len, int tuple, int field, in
303
507
  *
304
508
  */
305
509
  static VALUE
306
- 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)
307
511
  {
308
- t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
309
- t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
310
-
311
512
  /* Return value: array */
312
513
  VALUE array;
313
514
  VALUE elem;
314
515
  int word_index = 0;
315
516
  int index;
316
517
  /* Use a buffer of the same length, as that will be the worst case */
317
- char word[len + 1];
518
+ PG_VARIABLE_LENGTH_ARRAY(char, word, len + 1, NAMEDATALEN)
318
519
 
319
520
  /* The current character in the input string. */
320
521
  char c;
@@ -331,7 +532,7 @@ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int fiel
331
532
  if(c == '.' && openQuote < 2 ) {
332
533
  word[word_index] = 0;
333
534
 
334
- elem = dec_func(conv, word, word_index, tuple, field, enc_idx);
535
+ elem = pg_text_dec_string(conv, word, word_index, tuple, field, enc_idx);
335
536
  rb_ary_push(array, elem);
336
537
 
337
538
  openQuote = 0;
@@ -353,7 +554,7 @@ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int fiel
353
554
  }
354
555
 
355
556
  word[word_index] = 0;
356
- elem = dec_func(conv, word, word_index, tuple, field, enc_idx);
557
+ elem = pg_text_dec_string(conv, word, word_index, tuple, field, enc_idx);
357
558
  rb_ary_push(array, elem);
358
559
 
359
560
  return array;
@@ -368,13 +569,13 @@ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int fiel
368
569
  *
369
570
  */
370
571
  static VALUE
371
- 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)
372
573
  {
373
574
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
374
575
  t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
375
576
  int decoded_len;
376
577
  /* create a buffer of the expected decoded length */
377
- VALUE out_value = rb_tainted_str_new(NULL, BASE64_DECODED_SIZE(len));
578
+ VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
378
579
 
379
580
  decoded_len = base64_decode( RSTRING_PTR(out_value), val, len );
380
581
  rb_str_set_len(out_value, decoded_len);
@@ -393,13 +594,393 @@ pg_text_dec_from_base64(t_pg_coder *conv, char *val, int len, int tuple, int fie
393
594
  return out_value;
394
595
  }
395
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
+
396
965
  void
397
- init_pg_text_decoder()
966
+ init_pg_text_decoder(void)
398
967
  {
399
- 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);
400
979
 
401
980
  /* This module encapsulates all decoder classes with text input format */
402
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);
403
984
 
404
985
  /* Make RDoc aware of the decoder classes... */
405
986
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
@@ -412,11 +993,13 @@ init_pg_text_decoder()
412
993
  pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
413
994
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
414
995
  pg_define_coder( "Bytea", pg_text_dec_bytea, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
996
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_SimpleDecoder ); */
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);
415
1000
 
416
1001
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
417
1002
  pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
418
- /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_CompositeDecoder ); */
419
- pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
420
1003
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "FromBase64", rb_cPG_CompositeDecoder ); */
421
1004
  pg_define_coder( "FromBase64", pg_text_dec_from_base64, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
422
1005
  }