pg 1.1.4

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