pg 1.0.0 → 1.5.9

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