pg 0.17.1-x64-mingw32 → 0.18.0.pre20141017160319-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +1885 -169
  5. data/History.rdoc +6 -0
  6. data/Manifest.txt +25 -1
  7. data/README.rdoc +47 -0
  8. data/Rakefile +21 -12
  9. data/Rakefile.cross +39 -33
  10. data/ext/extconf.rb +27 -26
  11. data/ext/pg.c +73 -19
  12. data/ext/pg.h +194 -6
  13. data/ext/pg_binary_decoder.c +160 -0
  14. data/ext/pg_binary_encoder.c +160 -0
  15. data/ext/pg_coder.c +473 -0
  16. data/ext/pg_connection.c +872 -534
  17. data/ext/pg_copy_coder.c +557 -0
  18. data/ext/pg_result.c +266 -111
  19. data/ext/pg_text_decoder.c +424 -0
  20. data/ext/pg_text_encoder.c +631 -0
  21. data/ext/pg_type_map.c +113 -0
  22. data/ext/pg_type_map_all_strings.c +113 -0
  23. data/ext/pg_type_map_by_column.c +254 -0
  24. data/ext/pg_type_map_by_mri_type.c +266 -0
  25. data/ext/pg_type_map_by_oid.c +341 -0
  26. data/ext/util.c +149 -0
  27. data/ext/util.h +65 -0
  28. data/lib/2.0/pg_ext.so +0 -0
  29. data/lib/2.1/pg_ext.so +0 -0
  30. data/lib/pg.rb +11 -1
  31. data/lib/pg/basic_type_mapping.rb +377 -0
  32. data/lib/pg/coder.rb +74 -0
  33. data/lib/pg/connection.rb +43 -1
  34. data/lib/pg/result.rb +13 -3
  35. data/lib/pg/text_decoder.rb +42 -0
  36. data/lib/pg/text_encoder.rb +27 -0
  37. data/lib/pg/type_map_by_column.rb +15 -0
  38. data/lib/x64-mingw32/libpq.dll +0 -0
  39. data/spec/{lib/helpers.rb → helpers.rb} +95 -35
  40. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  41. data/spec/pg/connection_spec.rb +416 -214
  42. data/spec/pg/result_spec.rb +146 -116
  43. data/spec/pg/type_map_by_column_spec.rb +135 -0
  44. data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
  45. data/spec/pg/type_map_by_oid_spec.rb +133 -0
  46. data/spec/pg/type_map_spec.rb +39 -0
  47. data/spec/pg/type_spec.rb +649 -0
  48. data/spec/pg_spec.rb +10 -18
  49. metadata +129 -50
  50. metadata.gz.sig +0 -0
@@ -0,0 +1,424 @@
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, 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. The caller ensures, that the data is
20
+ * zero terminated ( that is val[len] = 0 ). The memory should be used read
21
+ * only by the callee.
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 "pg.h"
32
+ #include "util.h"
33
+ #include <inttypes.h>
34
+
35
+ VALUE rb_mPG_TextDecoder;
36
+ static ID s_id_decode;
37
+
38
+
39
+ /*
40
+ * Document-class: PG::TextDecoder::Boolean < PG::SimpleDecoder
41
+ *
42
+ * This is a decoder class for conversion of PostgreSQL boolean type
43
+ * to Ruby true or false values.
44
+ *
45
+ */
46
+ static VALUE
47
+ pg_text_dec_boolean(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
48
+ {
49
+ if (len < 1) {
50
+ rb_raise( rb_eTypeError, "wrong data for text boolean converter in tuple %d field %d", tuple, field);
51
+ }
52
+ return *val == 't' ? Qtrue : Qfalse;
53
+ }
54
+
55
+ /*
56
+ * Document-class: PG::TextDecoder::String < PG::SimpleDecoder
57
+ *
58
+ * This is a decoder class for conversion of PostgreSQL text output to
59
+ * to Ruby String object. The output value will have the character encoding
60
+ * set with PG::Connection#internal_encoding= .
61
+ *
62
+ */
63
+ VALUE
64
+ pg_text_dec_string(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
65
+ {
66
+ VALUE ret = rb_tainted_str_new( val, len );
67
+ PG_ENCODING_SET_NOCHECK( ret, enc_idx );
68
+ return ret;
69
+ }
70
+
71
+ /*
72
+ * Document-class: PG::TextDecoder::Integer < PG::SimpleDecoder
73
+ *
74
+ * This is a decoder class for conversion of PostgreSQL integer types
75
+ * to Ruby Integer objects.
76
+ *
77
+ */
78
+ static VALUE
79
+ pg_text_dec_integer(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
80
+ {
81
+ long i;
82
+ int max_len;
83
+
84
+ if( sizeof(i) >= 8 && FIXNUM_MAX >= 1000000000000000000LL ){
85
+ /* 64 bit system can safely handle all numbers up to 18 digits as Fixnum */
86
+ max_len = 18;
87
+ } else if( sizeof(i) >= 4 && FIXNUM_MAX >= 1000000000LL ){
88
+ /* 32 bit system can safely handle all numbers up to 9 digits as Fixnum */
89
+ max_len = 9;
90
+ } else {
91
+ /* unknown -> don't use fast path for int conversion */
92
+ max_len = 0;
93
+ }
94
+
95
+ if( len <= max_len ){
96
+ /* rb_cstr2inum() seems to be slow, so we do the int conversion by hand.
97
+ * This proved to be 40% faster by the following benchmark:
98
+ *
99
+ * conn.type_mapping_for_results = PG::BasicTypeMapForResults.new conn
100
+ * Benchmark.measure do
101
+ * conn.exec("select generate_series(1,1000000)").values }
102
+ * end
103
+ */
104
+ char *val_pos = val;
105
+ char digit = *val_pos;
106
+ int neg;
107
+ int error = 0;
108
+
109
+ if( digit=='-' ){
110
+ neg = 1;
111
+ i = 0;
112
+ }else if( digit>='0' && digit<='9' ){
113
+ neg = 0;
114
+ i = digit - '0';
115
+ } else {
116
+ error = 1;
117
+ }
118
+
119
+ while (!error && (digit=*++val_pos)) {
120
+ if( digit>='0' && digit<='9' ){
121
+ i = i * 10 + (digit - '0');
122
+ } else {
123
+ error = 1;
124
+ }
125
+ }
126
+
127
+ if( !error ){
128
+ return LONG2FIX(neg ? -i : i);
129
+ }
130
+ }
131
+ /* Fallback to ruby method if number too big or unrecognized. */
132
+ return rb_cstr2inum(val, 10);
133
+ }
134
+
135
+ /*
136
+ * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
137
+ *
138
+ * This is a decoder class for conversion of PostgreSQL float4 and float8 types
139
+ * to Ruby Float objects.
140
+ *
141
+ */
142
+ static VALUE
143
+ pg_text_dec_float(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
144
+ {
145
+ return rb_float_new(strtod(val, NULL));
146
+ }
147
+
148
+ /*
149
+ * Document-class: PG::TextDecoder::Bytea < PG::SimpleDecoder
150
+ *
151
+ * This is a decoder class for conversion of PostgreSQL bytea type
152
+ * to binary String objects.
153
+ *
154
+ */
155
+ static VALUE
156
+ pg_text_dec_bytea(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
157
+ {
158
+ unsigned char *to;
159
+ size_t to_len;
160
+ VALUE ret;
161
+
162
+ to = PQunescapeBytea( (unsigned char *)val, &to_len);
163
+
164
+ ret = rb_tainted_str_new((char*)to, to_len);
165
+ PQfreemem(to);
166
+
167
+ return ret;
168
+ }
169
+
170
+ /*
171
+ * Array parser functions are thankfully borrowed from here:
172
+ * https://github.com/dockyard/pg_array_parser
173
+ */
174
+ static VALUE
175
+ read_array(t_pg_composite_coder *this, int *index, char *c_pg_array_string, int array_string_length, char *word, int enc_idx, int tuple, int field, t_pg_coder_dec_func dec_func)
176
+ {
177
+ /* Return value: array */
178
+ VALUE array;
179
+ int word_index = 0;
180
+
181
+ /* The current character in the input string. */
182
+ char c;
183
+
184
+ /* 0: Currently outside a quoted string, current word never quoted
185
+ * 1: Currently inside a quoted string
186
+ * -1: Currently outside a quoted string, current word previously quoted */
187
+ int openQuote = 0;
188
+
189
+ /* Inside quoted input means the next character should be treated literally,
190
+ * instead of being treated as a metacharacter.
191
+ * Outside of quoted input, means that the word shouldn't be pushed to the array,
192
+ * used when the last entry was a subarray (which adds to the array itself). */
193
+ int escapeNext = 0;
194
+
195
+ array = rb_ary_new();
196
+
197
+ /* Special case the empty array, so it doesn't need to be handled manually inside
198
+ * the loop. */
199
+ if(((*index) < array_string_length) && c_pg_array_string[(*index)] == '}')
200
+ {
201
+ return array;
202
+ }
203
+
204
+ for(;(*index) < array_string_length; ++(*index))
205
+ {
206
+ c = c_pg_array_string[*index];
207
+ if(openQuote < 1)
208
+ {
209
+ if(c == this->delimiter || c == '}')
210
+ {
211
+ if(!escapeNext)
212
+ {
213
+ if(openQuote == 0 && word_index == 4 && !strncmp(word, "NULL", word_index))
214
+ {
215
+ rb_ary_push(array, Qnil);
216
+ }
217
+ else
218
+ {
219
+ VALUE val;
220
+ word[word_index] = 0;
221
+ val = dec_func(this->elem, word, word_index, tuple, field, enc_idx);
222
+ rb_ary_push(array, val);
223
+ }
224
+ }
225
+ if(c == '}')
226
+ {
227
+ return array;
228
+ }
229
+ escapeNext = 0;
230
+ openQuote = 0;
231
+ word_index = 0;
232
+ }
233
+ else if(c == '"')
234
+ {
235
+ openQuote = 1;
236
+ }
237
+ else if(c == '{')
238
+ {
239
+ (*index)++;
240
+ rb_ary_push(array, read_array(this, index, c_pg_array_string, array_string_length, word, enc_idx, tuple, field, dec_func));
241
+ escapeNext = 1;
242
+ }
243
+ else
244
+ {
245
+ word[word_index] = c;
246
+ word_index++;
247
+ }
248
+ }
249
+ else if (escapeNext) {
250
+ word[word_index] = c;
251
+ word_index++;
252
+ escapeNext = 0;
253
+ }
254
+ else if (c == '\\')
255
+ {
256
+ escapeNext = 1;
257
+ }
258
+ else if (c == '"')
259
+ {
260
+ openQuote = -1;
261
+ }
262
+ else
263
+ {
264
+ word[word_index] = c;
265
+ word_index++;
266
+ }
267
+ }
268
+
269
+ return array;
270
+ }
271
+
272
+ /*
273
+ * Document-class: PG::TextDecoder::Array < PG::CompositeDecoder
274
+ *
275
+ * This is the decoder class for PostgreSQL array types.
276
+ *
277
+ * All values are decoded according to the #elements_type
278
+ * accessor. Sub-arrays are decoded recursively.
279
+ *
280
+ */
281
+ static VALUE
282
+ pg_text_dec_array(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
283
+ {
284
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
285
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
286
+ /* create a buffer of the same length, as that will be the worst case */
287
+ char *word = xmalloc(len + 1);
288
+ int index = 1;
289
+
290
+ VALUE return_value = read_array(this, &index, val, len, word, enc_idx, tuple, field, dec_func);
291
+ free(word);
292
+ return return_value;
293
+ }
294
+
295
+ /*
296
+ * Document-class: PG::TextDecoder::Identifier < PG::CompositeDecoder
297
+ *
298
+ * This is the decoder class for PostgreSQL identifiers.
299
+ *
300
+ * Returns an Array of identifiers:
301
+ * PG::TextDecoder::Identifier.new.decode('schema."table"."column"')
302
+ * => ["schema", "table", "column"]
303
+ *
304
+ */
305
+ static VALUE
306
+ pg_text_dec_identifier(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
307
+ {
308
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
309
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, 0);
310
+
311
+ /* Return value: array */
312
+ VALUE array;
313
+ VALUE elem;
314
+ int word_index = 0;
315
+ int index;
316
+ /* create a buffer of the same length, as that will be the worst case */
317
+ char *word = xmalloc(len + 1);
318
+
319
+ /* The current character in the input string. */
320
+ char c;
321
+
322
+ /* 0: Currently outside a quoted string
323
+ * 1: Currently inside a quoted string, last char was a quote
324
+ * 2: Currently inside a quoted string, last char was no quote */
325
+ int openQuote = 0;
326
+
327
+ array = rb_ary_new();
328
+
329
+ for(index = 0; index < len; ++index) {
330
+ c = val[index];
331
+ if(c == '.' && openQuote < 2 ) {
332
+ word[word_index] = 0;
333
+
334
+ elem = dec_func(conv, word, word_index, tuple, field, enc_idx);
335
+ rb_ary_push(array, elem);
336
+
337
+ openQuote = 0;
338
+ word_index = 0;
339
+ } else if(c == '"') {
340
+ if (openQuote == 1) {
341
+ word[word_index] = c;
342
+ word_index++;
343
+ openQuote = 2;
344
+ } else if (openQuote == 2){
345
+ openQuote = 1;
346
+ } else {
347
+ openQuote = 2;
348
+ }
349
+ } else {
350
+ word[word_index] = c;
351
+ word_index++;
352
+ }
353
+ }
354
+
355
+ word[word_index] = 0;
356
+ elem = dec_func(conv, word, word_index, tuple, field, enc_idx);
357
+ rb_ary_push(array, elem);
358
+
359
+ free(word);
360
+
361
+ return array;
362
+ }
363
+
364
+ /*
365
+ * Document-class: PG::TextDecoder::FromBase64 < PG::CompositeDecoder
366
+ *
367
+ * This is a decoder class for conversion of base64 encoded data
368
+ * to it's binary representation. It outputs a binary Ruby String
369
+ * or some other Ruby object, if a #elements_type decoder was defined.
370
+ *
371
+ */
372
+ static VALUE
373
+ pg_text_dec_from_base64(t_pg_coder *conv, char *val, int len, int tuple, int field, int enc_idx)
374
+ {
375
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
376
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
377
+ int decoded_len;
378
+ /* create a buffer of the expected decoded length */
379
+ VALUE out_value = rb_tainted_str_new(NULL, BASE64_DECODED_SIZE(len));
380
+
381
+ decoded_len = base64_decode( RSTRING_PTR(out_value), val, len );
382
+ rb_str_set_len(out_value, decoded_len);
383
+
384
+ /* Is it a pure String conversion? Then we can directly send out_value to the user. */
385
+ if( this->comp.format == 0 && dec_func == pg_text_dec_string ){
386
+ PG_ENCODING_SET_NOCHECK( out_value, enc_idx );
387
+ return out_value;
388
+ }
389
+ if( this->comp.format == 1 && dec_func == pg_bin_dec_bytea ){
390
+ PG_ENCODING_SET_NOCHECK( out_value, rb_ascii8bit_encindex() );
391
+ return out_value;
392
+ }
393
+ out_value = dec_func(this->elem, RSTRING_PTR(out_value), decoded_len, tuple, field, enc_idx);
394
+
395
+ return out_value;
396
+ }
397
+
398
+ void
399
+ init_pg_text_decoder()
400
+ {
401
+ s_id_decode = rb_intern("decode");
402
+
403
+ /* This module encapsulates all decoder classes with text input format */
404
+ rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
405
+
406
+ /* Make RDoc aware of the decoder classes... */
407
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
408
+ pg_define_coder( "Boolean", pg_text_dec_boolean, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
409
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Integer", rb_cPG_SimpleDecoder ); */
410
+ pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
411
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
412
+ pg_define_coder( "Float", pg_text_dec_float, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
413
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
414
+ pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
415
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
416
+ pg_define_coder( "Bytea", pg_text_dec_bytea, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
417
+
418
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
419
+ pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
420
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Identifier", rb_cPG_CompositeDecoder ); */
421
+ pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
422
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "FromBase64", rb_cPG_CompositeDecoder ); */
423
+ pg_define_coder( "FromBase64", pg_text_dec_from_base64, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
424
+ }
@@ -0,0 +1,631 @@
1
+ /*
2
+ * pg_text_encoder.c - PG::TextEncoder module
3
+ * $Id$
4
+ *
5
+ */
6
+
7
+ /*
8
+ *
9
+ * Type casts for encoding Ruby objects to PostgreSQL string representations.
10
+ *
11
+ * Encoder classes are defined with pg_define_coder(). This creates a new coder class and
12
+ * assigns an encoder function. The encoder function can decide between two different options
13
+ * to return the encoded data. It can either return it as a Ruby String object or write the
14
+ * encoded data to a memory space provided by the caller. In the second case, the encoder
15
+ * function is called twice, once for deciding the encoding option and returning the expected
16
+ * data length, and a second time when the requested memory space was made available by the
17
+ * calling function, to do the actual conversion and writing. Parameter intermediate can be
18
+ * used to store data between these two calls.
19
+ *
20
+ * Signature of all type cast encoders is:
21
+ * int encoder_function(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
22
+ *
23
+ * Params:
24
+ * this - The data part of the coder object that belongs to the encoder function.
25
+ * value - The Ruby object to cast.
26
+ * out - NULL for the first call,
27
+ * pointer to a buffer with the requested size for the second call.
28
+ * intermediate - Pointer to a VALUE that might be set by the encoding function to some
29
+ * value in the first call that can be retrieved later in the second call.
30
+ * This VALUE is not yet initialized by the caller.
31
+ *
32
+ * Returns:
33
+ * >= 0 - If out==NULL the encoder function must return the expected output buffer size.
34
+ * This can be larger than the size of the second call, but may not be smaller.
35
+ * If out!=NULL the encoder function must return the actually used output buffer size
36
+ * without a termination character.
37
+ * -1 - The encoder function can alternatively return -1 to indicate that no second call
38
+ * is required, but the String value in *intermediate should be used instead.
39
+ */
40
+
41
+
42
+ #include "pg.h"
43
+ #include "util.h"
44
+ #include <inttypes.h>
45
+
46
+ VALUE rb_mPG_TextEncoder;
47
+ static ID s_id_encode;
48
+ static ID s_id_to_i;
49
+ static VALUE hash_false_values;
50
+
51
+
52
+ VALUE
53
+ pg_obj_to_i( VALUE value )
54
+ {
55
+ switch (TYPE(value)) {
56
+ case T_FIXNUM:
57
+ case T_FLOAT:
58
+ case T_BIGNUM:
59
+ return value;
60
+ default:
61
+ return rb_funcall(value, s_id_to_i, 0);
62
+ }
63
+ }
64
+
65
+ /*
66
+ * Document-class: PG::TextEncoder::Boolean < PG::SimpleEncoder
67
+ *
68
+ * This is the encoder class for the PostgreSQL bool type.
69
+ *
70
+ * Ruby values false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off' and 'OFF'
71
+ * are encoded as SQL +FALSE+ value. nil is sent as SQL +NULL+.
72
+ * Any other values are encoded as SQL +TRUE+ .
73
+ *
74
+ */
75
+ static int
76
+ pg_text_enc_boolean(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
77
+ {
78
+ if(out){
79
+ switch( TYPE(value) ){
80
+ case T_FALSE:
81
+ *out = 'f';
82
+ break;
83
+ case T_FIXNUM:
84
+ case T_BIGNUM:
85
+ if( NUM2LONG(value) == 0 ){
86
+ *out = 'f';
87
+ } else {
88
+ *out = 't';
89
+ }
90
+ break;
91
+ case T_STRING:
92
+ if( rb_hash_lookup(hash_false_values, value) == Qtrue ){
93
+ *out = 'f';
94
+ break;
95
+ }
96
+ /* fall through */
97
+ default:
98
+ *out = 't';
99
+ }
100
+ }
101
+ return 1;
102
+ }
103
+
104
+
105
+ /*
106
+ * Document-class: PG::TextEncoder::String < PG::SimpleEncoder
107
+ *
108
+ * This is the encoder class for the PostgreSQL text types.
109
+ *
110
+ * Non-String values are expected to have method +to_s+ defined.
111
+ *
112
+ */
113
+ int
114
+ pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
115
+ {
116
+ *intermediate = rb_obj_as_string(value);
117
+ return -1;
118
+ }
119
+
120
+
121
+ /*
122
+ * Document-class: PG::TextEncoder::Integer < PG::SimpleEncoder
123
+ *
124
+ * This is the encoder class for the PostgreSQL int types.
125
+ *
126
+ * Non-Integer values are expected to have method +to_i+ defined.
127
+ *
128
+ */
129
+ static int
130
+ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
131
+ {
132
+ if(out){
133
+ if(TYPE(*intermediate) == T_STRING){
134
+ return pg_coder_enc_to_s(this, value, out, intermediate);
135
+ }else{
136
+ char *start = out;
137
+ int len;
138
+ int neg = 0;
139
+ long long ll = NUM2LL(*intermediate);
140
+
141
+ if (ll < 0) {
142
+ /* We don't expect problems with the most negative integer not being representable
143
+ * as a positive integer, because Fixnum is only up to 63 bits.
144
+ */
145
+ ll = -ll;
146
+ neg = 1;
147
+ }
148
+
149
+ /* Compute the result string backwards. */
150
+ do {
151
+ long long remainder;
152
+ long long oldval = ll;
153
+
154
+ ll /= 10;
155
+ remainder = oldval - ll * 10;
156
+ *out++ = '0' + remainder;
157
+ } while (ll != 0);
158
+
159
+ if (neg)
160
+ *out++ = '-';
161
+
162
+ len = out - start;
163
+
164
+ /* Reverse string. */
165
+ out--;
166
+ while (start < out)
167
+ {
168
+ char swap = *start;
169
+
170
+ *start++ = *out;
171
+ *out-- = swap;
172
+ }
173
+
174
+ return len;
175
+ }
176
+ }else{
177
+ *intermediate = pg_obj_to_i(value);
178
+ if(TYPE(*intermediate) == T_FIXNUM){
179
+ int len;
180
+ long long sll = NUM2LL(*intermediate);
181
+ long long ll = sll < 0 ? -sll : sll;
182
+ if( ll < 100000000 ){
183
+ if( ll < 10000 ){
184
+ if( ll < 100 ){
185
+ len = ll < 10 ? 1 : 2;
186
+ }else{
187
+ len = ll < 1000 ? 3 : 4;
188
+ }
189
+ }else{
190
+ if( ll < 1000000 ){
191
+ len = ll < 100000 ? 5 : 6;
192
+ }else{
193
+ len = ll < 10000000 ? 7 : 8;
194
+ }
195
+ }
196
+ }else{
197
+ if( ll < 1000000000000LL ){
198
+ if( ll < 10000000000LL ){
199
+ len = ll < 1000000000LL ? 9 : 10;
200
+ }else{
201
+ len = ll < 100000000000LL ? 11 : 12;
202
+ }
203
+ }else{
204
+ if( ll < 100000000000000LL ){
205
+ len = ll < 10000000000000LL ? 13 : 14;
206
+ }else{
207
+ return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
208
+ }
209
+ }
210
+ }
211
+ return sll < 0 ? len+1 : len;
212
+ }else{
213
+ return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
214
+ }
215
+ }
216
+ }
217
+
218
+
219
+ /*
220
+ * Document-class: PG::TextEncoder::Float < PG::SimpleEncoder
221
+ *
222
+ * This is the encoder class for the PostgreSQL float types.
223
+ *
224
+ */
225
+ static int
226
+ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
227
+ {
228
+ if(out){
229
+ double dvalue = NUM2DBL(value);
230
+ /* Cast to the same strings as value.to_s . */
231
+ if( isinf(dvalue) ){
232
+ if( dvalue < 0 ){
233
+ memcpy( out, "-Infinity", 9);
234
+ return 9;
235
+ } else {
236
+ memcpy( out, "Infinity", 8);
237
+ return 8;
238
+ }
239
+ } else if (isnan(dvalue)) {
240
+ memcpy( out, "NaN", 3);
241
+ return 3;
242
+ }
243
+ return sprintf( out, "%.16E", dvalue);
244
+ }else{
245
+ return 23;
246
+ }
247
+ }
248
+
249
+ typedef int (*t_quote_func)( void *_this, char *p_in, int strlen, char *p_out );
250
+
251
+ static int
252
+ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
253
+ t_pg_composite_coder *this = _this;
254
+ char *ptr1;
255
+ char *ptr2;
256
+ int backslashs = 0;
257
+ int needquote;
258
+
259
+ /* count data plus backslashes; detect chars needing quotes */
260
+ if (strlen == 0)
261
+ needquote = 1; /* force quotes for empty string */
262
+ else if (strlen == 4 && pg_strncasecmp(p_in, "NULL", strlen) == 0)
263
+ needquote = 1; /* force quotes for literal NULL */
264
+ else
265
+ needquote = 0;
266
+
267
+ /* count required backlashs */
268
+ for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
269
+ char ch = *ptr1;
270
+
271
+ if (ch == '"' || ch == '\\'){
272
+ needquote = 1;
273
+ backslashs++;
274
+ } else if (ch == '{' || ch == '}' || ch == this->delimiter ||
275
+ ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
276
+ needquote = 1;
277
+ }
278
+ }
279
+
280
+ if( needquote ){
281
+ ptr1 = p_in + strlen;
282
+ ptr2 = p_out + strlen + backslashs + 2;
283
+ /* Write end quote */
284
+ *--ptr2 = '"';
285
+
286
+ /* Then store the escaped string on the final position, walking
287
+ * right to left, until all backslashs are placed. */
288
+ while( ptr1 != p_in ) {
289
+ *--ptr2 = *--ptr1;
290
+ if(*ptr2 == '"' || *ptr2 == '\\'){
291
+ *--ptr2 = '\\';
292
+ }
293
+ }
294
+ /* Write start quote */
295
+ *p_out = '"';
296
+ return strlen + backslashs + 2;
297
+ } else {
298
+ if( p_in != p_out )
299
+ memcpy( p_out, p_in, strlen );
300
+ return strlen;
301
+ }
302
+ }
303
+
304
+ static char *
305
+ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data)
306
+ {
307
+ int strlen;
308
+ VALUE subint;
309
+ t_pg_coder_enc_func enc_func = pg_coder_enc_func(this);
310
+
311
+ strlen = enc_func(this, value, NULL, &subint);
312
+
313
+ if( strlen == -1 ){
314
+ /* we can directly use String value in subint */
315
+ strlen = RSTRING_LENINT(subint);
316
+
317
+ if(with_quote){
318
+ /* size of string assuming the worst case, that every character must be escaped. */
319
+ current_out = pg_rb_str_ensure_capa( string, strlen * 2 + 2, current_out, NULL );
320
+
321
+ current_out += quote_buffer( func_data, RSTRING_PTR(subint), strlen, current_out );
322
+ } else {
323
+ current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
324
+ memcpy( current_out, RSTRING_PTR(subint), strlen );
325
+ current_out += strlen;
326
+ }
327
+
328
+ } else {
329
+
330
+ if(with_quote){
331
+ /* size of string assuming the worst case, that every character must be escaped
332
+ * plus two bytes for quotation.
333
+ */
334
+ current_out = pg_rb_str_ensure_capa( string, 2 * strlen + 2, current_out, NULL );
335
+
336
+ /* Place the unescaped string at current output position. */
337
+ strlen = enc_func(this, value, current_out, &subint);
338
+
339
+ current_out += quote_buffer( func_data, current_out, strlen, current_out );
340
+ }else{
341
+ /* size of the unquoted string */
342
+ current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
343
+ current_out += enc_func(this, value, current_out, &subint);
344
+ }
345
+ }
346
+ return current_out;
347
+ }
348
+
349
+ static char *
350
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote)
351
+ {
352
+ int i;
353
+
354
+ /* size of "{}" */
355
+ current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
356
+ *current_out++ = '{';
357
+
358
+ for( i=0; i<RARRAY_LEN(value); i++){
359
+ VALUE entry = rb_ary_entry(value, i);
360
+
361
+ if( i > 0 ){
362
+ current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
363
+ *current_out++ = this->delimiter;
364
+ }
365
+
366
+ switch(TYPE(entry)){
367
+ case T_ARRAY:
368
+ current_out = write_array(this, entry, current_out, string, quote);
369
+ break;
370
+ case T_NIL:
371
+ current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
372
+ *current_out++ = 'N';
373
+ *current_out++ = 'U';
374
+ *current_out++ = 'L';
375
+ *current_out++ = 'L';
376
+ break;
377
+ default:
378
+ current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this );
379
+ }
380
+ }
381
+ current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
382
+ *current_out++ = '}';
383
+ return current_out;
384
+ }
385
+
386
+
387
+ /*
388
+ * Document-class: PG::TextEncoder::Array < PG::CompositeEncoder
389
+ *
390
+ * This is the encoder class for PostgreSQL array types.
391
+ *
392
+ * All values are encoded according to the #elements_type
393
+ * accessor. Sub-arrays are encoded recursively.
394
+ *
395
+ * This encoder expects an Array of values or sub-arrays as input.
396
+ * Other values are passed through as text without interpretation.
397
+ *
398
+ */
399
+ static int
400
+ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
401
+ {
402
+ char *end_ptr;
403
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
404
+
405
+ if( TYPE(value) == T_ARRAY){
406
+ *intermediate = rb_str_new(NULL, 0);
407
+
408
+ end_ptr = write_array(this, value, RSTRING_PTR(*intermediate), *intermediate, this->needs_quotation);
409
+
410
+ rb_str_set_len( *intermediate, end_ptr - RSTRING_PTR(*intermediate) );
411
+
412
+ return -1;
413
+ } else {
414
+ return pg_coder_enc_to_s( conv, value, out, intermediate );
415
+ }
416
+ }
417
+
418
+ static int
419
+ quote_identifier_buffer( void *_this, char *p_in, int strlen, char *p_out ){
420
+ char *ptr1;
421
+ char *ptr2;
422
+ int backslashs = 0;
423
+
424
+ /* count required backlashs */
425
+ for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
426
+ if (*ptr1 == '"'){
427
+ backslashs++;
428
+ }
429
+ }
430
+
431
+ ptr1 = p_in + strlen;
432
+ ptr2 = p_out + strlen + backslashs + 2;
433
+ /* Write end quote */
434
+ *--ptr2 = '"';
435
+
436
+ /* Then store the escaped string on the final position, walking
437
+ * right to left, until all backslashs are placed. */
438
+ while( ptr1 != p_in ) {
439
+ *--ptr2 = *--ptr1;
440
+ if(*ptr2 == '"'){
441
+ *--ptr2 = '"';
442
+ }
443
+ }
444
+ /* Write start quote */
445
+ *p_out = '"';
446
+ return strlen + backslashs + 2;
447
+ }
448
+
449
+ static char *
450
+ pg_text_enc_array_identifier(t_pg_composite_coder *this, VALUE value, VALUE string, char *out)
451
+ {
452
+ int i;
453
+ int nr_elems;
454
+
455
+ Check_Type(value, T_ARRAY);
456
+ nr_elems = RARRAY_LEN(value);
457
+
458
+ for( i=0; i<nr_elems; i++){
459
+ VALUE entry = rb_ary_entry(value, i);
460
+
461
+ out = quote_string(this->elem, entry, string, out, this->needs_quotation, quote_identifier_buffer, this);
462
+ if( i < nr_elems-1 ){
463
+ out = pg_rb_str_ensure_capa( string, 1, out, NULL );
464
+ *out++ = '.';
465
+ }
466
+ }
467
+ return out;
468
+ }
469
+
470
+ /*
471
+ * Document-class: PG::TextEncoder::Identifier < PG::CompositeEncoder
472
+ *
473
+ * This is the encoder class for PostgreSQL identifiers.
474
+ *
475
+ * An Array value can be used for "schema.table.column" type identifiers:
476
+ * PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
477
+ * => "schema"."table"."column"
478
+ *
479
+ */
480
+ static int
481
+ pg_text_enc_identifier(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
482
+ {
483
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
484
+
485
+ *intermediate = rb_str_new(NULL, 0);
486
+ out = RSTRING_PTR(*intermediate);
487
+
488
+ if( TYPE(value) == T_ARRAY){
489
+ out = pg_text_enc_array_identifier(this, value, *intermediate, out);
490
+ } else {
491
+ out = quote_string(this->elem, value, *intermediate, out, this->needs_quotation, quote_identifier_buffer, this);
492
+ }
493
+ rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
494
+ return -1;
495
+ }
496
+
497
+
498
+ static int
499
+ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
500
+ char *ptr1;
501
+ char *ptr2;
502
+ int backslashs = 0;
503
+
504
+ /* count required backlashs */
505
+ for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
506
+ if (*ptr1 == '\''){
507
+ backslashs++;
508
+ }
509
+ }
510
+
511
+ ptr1 = p_in + strlen;
512
+ ptr2 = p_out + strlen + backslashs + 2;
513
+ /* Write end quote */
514
+ *--ptr2 = '\'';
515
+
516
+ /* Then store the escaped string on the final position, walking
517
+ * right to left, until all backslashs are placed. */
518
+ while( ptr1 != p_in ) {
519
+ *--ptr2 = *--ptr1;
520
+ if(*ptr2 == '\''){
521
+ *--ptr2 = '\'';
522
+ }
523
+ }
524
+ /* Write start quote */
525
+ *p_out = '\'';
526
+ return strlen + backslashs + 2;
527
+ }
528
+
529
+
530
+ /*
531
+ * Document-class: PG::TextEncoder::QuotedLiteral < PG::CompositeEncoder
532
+ *
533
+ * This is the encoder class for PostgreSQL literals.
534
+ *
535
+ * A literal is quoted and escaped by the +'+ character.
536
+ *
537
+ */
538
+ static int
539
+ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
540
+ {
541
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
542
+
543
+ *intermediate = rb_str_new(NULL, 0);
544
+ out = RSTRING_PTR(*intermediate);
545
+ out = quote_string(this->elem, value, *intermediate, out, this->needs_quotation, quote_literal_buffer, this);
546
+ rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
547
+ return -1;
548
+ }
549
+
550
+ /*
551
+ * Document-class: PG::TextEncoder::ToBase64 < PG::CompositeEncoder
552
+ *
553
+ * This is an encoder class for conversion of binary to base64 data.
554
+ *
555
+ */
556
+ static int
557
+ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
558
+ {
559
+ int strlen;
560
+ VALUE subint;
561
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
562
+ t_pg_coder_enc_func enc_func = pg_coder_enc_func(this->elem);
563
+
564
+ if(out){
565
+ /* Second encoder pass, if required */
566
+ strlen = enc_func(this->elem, value, out, intermediate);
567
+ base64_encode( out, out, strlen );
568
+
569
+ return BASE64_ENCODED_SIZE(strlen);
570
+ } else {
571
+ /* First encoder pass */
572
+ strlen = enc_func(this->elem, value, NULL, &subint);
573
+
574
+ if( strlen == -1 ){
575
+ /* Encoded string is returned in subint */
576
+ VALUE out_str;
577
+
578
+ strlen = RSTRING_LENINT(subint);
579
+ out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
580
+
581
+ base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
582
+ *intermediate = out_str;
583
+
584
+ return -1;
585
+ } else {
586
+ *intermediate = subint;
587
+
588
+ return BASE64_ENCODED_SIZE(strlen);
589
+ }
590
+ }
591
+ }
592
+
593
+
594
+ void
595
+ init_pg_text_encoder()
596
+ {
597
+ s_id_encode = rb_intern("encode");
598
+ s_id_to_i = rb_intern("to_i");
599
+
600
+ hash_false_values = rb_hash_new();
601
+ rb_gc_register_address( &hash_false_values );
602
+ rb_hash_aset( hash_false_values, rb_str_new2( "0" ), Qtrue );
603
+ rb_hash_aset( hash_false_values, rb_str_new2( "f" ), Qtrue );
604
+ rb_hash_aset( hash_false_values, rb_str_new2( "F" ), Qtrue );
605
+ rb_hash_aset( hash_false_values, rb_str_new2( "false" ), Qtrue );
606
+ rb_hash_aset( hash_false_values, rb_str_new2( "FALSE" ), Qtrue );
607
+ rb_hash_aset( hash_false_values, rb_str_new2( "off" ), Qtrue );
608
+ rb_hash_aset( hash_false_values, rb_str_new2( "OFF" ), Qtrue );
609
+
610
+ /* This module encapsulates all encoder classes with text output format */
611
+ rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
612
+
613
+ /* Make RDoc aware of the encoder classes... */
614
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
615
+ pg_define_coder( "Boolean", pg_text_enc_boolean, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
616
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Integer", rb_cPG_SimpleEncoder ); */
617
+ pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
618
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
619
+ pg_define_coder( "Float", pg_text_enc_float, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
620
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
621
+ pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
622
+
623
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Array", rb_cPG_CompositeEncoder ); */
624
+ pg_define_coder( "Array", pg_text_enc_array, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
625
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Identifier", rb_cPG_CompositeEncoder ); */
626
+ pg_define_coder( "Identifier", pg_text_enc_identifier, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
627
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "QuotedLiteral", rb_cPG_CompositeEncoder ); */
628
+ pg_define_coder( "QuotedLiteral", pg_text_enc_quoted_literal, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
629
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "ToBase64", rb_cPG_CompositeEncoder ); */
630
+ pg_define_coder( "ToBase64", pg_text_enc_to_base64, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
631
+ }