pg 0.17.1 → 1.2.3

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