pg 1.1.4

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 (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
+ }