pg 0.17.1-x86-mingw32 → 0.18.0.pre20141017160319-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +1885 -169
- data/History.rdoc +6 -0
- data/Manifest.txt +25 -1
- data/README.rdoc +47 -0
- data/Rakefile +21 -12
- data/Rakefile.cross +39 -33
- data/ext/extconf.rb +27 -26
- data/ext/pg.c +73 -19
- data/ext/pg.h +194 -6
- data/ext/pg_binary_decoder.c +160 -0
- data/ext/pg_binary_encoder.c +160 -0
- data/ext/pg_coder.c +473 -0
- data/ext/pg_connection.c +872 -534
- data/ext/pg_copy_coder.c +557 -0
- data/ext/pg_result.c +266 -111
- data/ext/pg_text_decoder.c +424 -0
- data/ext/pg_text_encoder.c +631 -0
- data/ext/pg_type_map.c +113 -0
- data/ext/pg_type_map_all_strings.c +113 -0
- data/ext/pg_type_map_by_column.c +254 -0
- data/ext/pg_type_map_by_mri_type.c +266 -0
- data/ext/pg_type_map_by_oid.c +341 -0
- data/ext/util.c +149 -0
- data/ext/util.h +65 -0
- data/lib/1.9/pg_ext.so +0 -0
- data/lib/2.0/pg_ext.so +0 -0
- data/lib/2.1/pg_ext.so +0 -0
- data/lib/i386-mingw32/libpq.dll +0 -0
- data/lib/pg.rb +11 -1
- data/lib/pg/basic_type_mapping.rb +377 -0
- data/lib/pg/coder.rb +74 -0
- data/lib/pg/connection.rb +43 -1
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +42 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/spec/{lib/helpers.rb → helpers.rb} +95 -35
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +416 -214
- data/spec/pg/result_spec.rb +146 -116
- data/spec/pg/type_map_by_column_spec.rb +135 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
- data/spec/pg/type_map_by_oid_spec.rb +133 -0
- data/spec/pg/type_map_spec.rb +39 -0
- data/spec/pg/type_spec.rb +649 -0
- data/spec/pg_spec.rb +10 -18
- metadata +130 -52
- metadata.gz.sig +0 -0
- data/lib/1.8/pg_ext.so +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
|
+
}
|