yugabyte_ysql 0.1

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