pg 0.18.4 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/BSDL +2 -2
  5. data/ChangeLog +0 -5911
  6. data/History.rdoc +240 -0
  7. data/Manifest.txt +8 -20
  8. data/README-Windows.rdoc +4 -4
  9. data/README.ja.rdoc +1 -2
  10. data/README.rdoc +64 -15
  11. data/Rakefile +20 -21
  12. data/Rakefile.cross +67 -69
  13. data/ext/errorcodes.def +101 -0
  14. data/ext/errorcodes.rb +1 -1
  15. data/ext/errorcodes.txt +33 -2
  16. data/ext/extconf.rb +26 -36
  17. data/ext/gvl_wrappers.c +4 -0
  18. data/ext/gvl_wrappers.h +27 -39
  19. data/ext/pg.c +156 -145
  20. data/ext/pg.h +74 -98
  21. data/ext/pg_binary_decoder.c +82 -15
  22. data/ext/pg_binary_encoder.c +20 -19
  23. data/ext/pg_coder.c +103 -21
  24. data/ext/pg_connection.c +917 -523
  25. data/ext/pg_copy_coder.c +50 -12
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +590 -208
  28. data/ext/pg_text_decoder.c +606 -40
  29. data/ext/pg_text_encoder.c +245 -94
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +14 -7
  32. data/ext/pg_type_map_all_strings.c +4 -4
  33. data/ext/pg_type_map_by_class.c +9 -4
  34. data/ext/pg_type_map_by_column.c +7 -6
  35. data/ext/pg_type_map_by_mri_type.c +1 -1
  36. data/ext/pg_type_map_by_oid.c +3 -2
  37. data/ext/pg_type_map_in_ruby.c +1 -1
  38. data/ext/{util.c → pg_util.c} +10 -10
  39. data/ext/{util.h → pg_util.h} +2 -2
  40. data/lib/pg.rb +23 -13
  41. data/lib/pg/basic_type_mapping.rb +155 -32
  42. data/lib/pg/binary_decoder.rb +23 -0
  43. data/lib/pg/coder.rb +23 -2
  44. data/lib/pg/connection.rb +73 -13
  45. data/lib/pg/constants.rb +2 -1
  46. data/lib/pg/exceptions.rb +2 -1
  47. data/lib/pg/result.rb +24 -7
  48. data/lib/pg/text_decoder.rb +24 -22
  49. data/lib/pg/text_encoder.rb +40 -8
  50. data/lib/pg/tuple.rb +30 -0
  51. data/lib/pg/type_map_by_column.rb +3 -2
  52. data/spec/helpers.rb +61 -36
  53. data/spec/pg/basic_type_mapping_spec.rb +415 -36
  54. data/spec/pg/connection_spec.rb +732 -327
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +253 -21
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +4 -4
  59. data/spec/pg/type_map_by_column_spec.rb +6 -2
  60. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  61. data/spec/pg/type_map_by_oid_spec.rb +3 -3
  62. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  63. data/spec/pg/type_map_spec.rb +1 -1
  64. data/spec/pg/type_spec.rb +446 -20
  65. data/spec/pg_spec.rb +2 -2
  66. metadata +63 -72
  67. metadata.gz.sig +0 -0
  68. data/sample/array_insert.rb +0 -20
  69. data/sample/async_api.rb +0 -106
  70. data/sample/async_copyto.rb +0 -39
  71. data/sample/async_mixed.rb +0 -56
  72. data/sample/check_conn.rb +0 -21
  73. data/sample/copyfrom.rb +0 -81
  74. data/sample/copyto.rb +0 -19
  75. data/sample/cursor.rb +0 -21
  76. data/sample/disk_usage_report.rb +0 -186
  77. data/sample/issue-119.rb +0 -94
  78. data/sample/losample.rb +0 -69
  79. data/sample/minimal-testcase.rb +0 -17
  80. data/sample/notify_wait.rb +0 -72
  81. data/sample/pg_statistics.rb +0 -294
  82. data/sample/replication_monitor.rb +0 -231
  83. data/sample/test_binary_values.rb +0 -33
  84. data/sample/wal_shipper.rb +0 -434
  85. data/sample/warehouse_partitions.rb +0 -320
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_encoder.c - PG::TextEncoder module
3
- * $Id: pg_text_encoder.c,v 4f0bd19e9111 2015/09/24 18:55:11 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -28,6 +28,7 @@
28
28
  * intermediate - Pointer to a VALUE that might be set by the encoding function to some
29
29
  * value in the first call that can be retrieved later in the second call.
30
30
  * This VALUE is not yet initialized by the caller.
31
+ * enc_idx - Index of the output Encoding that strings should be converted to.
31
32
  *
32
33
  * Returns:
33
34
  * >= 0 - If out==NULL the encoder function must return the expected output buffer size.
@@ -40,7 +41,7 @@
40
41
 
41
42
 
42
43
  #include "pg.h"
43
- #include "util.h"
44
+ #include "pg_util.h"
44
45
  #ifdef HAVE_INTTYPES_H
45
46
  #include <inttypes.h>
46
47
  #endif
@@ -49,8 +50,11 @@
49
50
  VALUE rb_mPG_TextEncoder;
50
51
  static ID s_id_encode;
51
52
  static ID s_id_to_i;
53
+ static ID s_id_to_s;
54
+ static ID s_cBigDecimal;
55
+ static VALUE s_str_F;
52
56
 
53
- static int pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate);
57
+ static int pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx);
54
58
 
55
59
  VALUE
56
60
  pg_obj_to_i( VALUE value )
@@ -76,7 +80,7 @@ pg_obj_to_i( VALUE value )
76
80
  *
77
81
  */
78
82
  static int
79
- pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
83
+ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
80
84
  {
81
85
  switch( TYPE(value) ){
82
86
  case T_FALSE:
@@ -94,10 +98,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
94
98
  if(out) *out = '1';
95
99
  return 1;
96
100
  } else {
97
- return pg_text_enc_integer(this, value, out, intermediate);
101
+ return pg_text_enc_integer(this, value, out, intermediate, enc_idx);
98
102
  }
99
103
  default:
100
- return pg_coder_enc_to_s(this, value, out, intermediate);
104
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
101
105
  }
102
106
  /* never reached */
103
107
  return 0;
@@ -113,45 +117,71 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
113
117
  *
114
118
  */
115
119
  int
116
- pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
120
+ pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
117
121
  {
118
- *intermediate = rb_obj_as_string(value);
122
+ VALUE str = rb_obj_as_string(value);
123
+ if( ENCODING_GET(str) == enc_idx ){
124
+ *intermediate = str;
125
+ }else{
126
+ *intermediate = rb_str_export_to_enc(str, rb_enc_from_index(enc_idx));
127
+ }
119
128
  return -1;
120
129
  }
121
130
 
131
+ static int
132
+ count_leading_zero_bits(unsigned long long x)
133
+ {
134
+ #if defined(__GNUC__) || defined(__clang__)
135
+ return __builtin_clzll(x);
136
+ #elif defined(_MSC_VER)
137
+ DWORD r = 0;
138
+ _BitScanForward64(&r, x);
139
+ return (int)r;
140
+ #else
141
+ unsigned int a;
142
+ for(a=0; a < sizeof(unsigned long long) * 8; a++){
143
+ if( x & (1 << (sizeof(unsigned long long) * 8 - 1))) return a;
144
+ x <<= 1;
145
+ }
146
+ return a;
147
+ #endif
148
+ }
122
149
 
123
150
  /*
124
151
  * Document-class: PG::TextEncoder::Integer < PG::SimpleEncoder
125
152
  *
126
- * This is the encoder class for the PostgreSQL int types.
153
+ * This is the encoder class for the PostgreSQL integer types.
127
154
  *
128
155
  * Non-Integer values are expected to have method +to_i+ defined.
129
156
  *
130
157
  */
131
158
  static int
132
- pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
159
+ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
133
160
  {
134
161
  if(out){
135
162
  if(TYPE(*intermediate) == T_STRING){
136
- return pg_coder_enc_to_s(this, value, out, intermediate);
163
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
137
164
  }else{
138
165
  char *start = out;
139
166
  int len;
140
167
  int neg = 0;
141
- long long ll = NUM2LL(*intermediate);
168
+ long long sll = NUM2LL(*intermediate);
169
+ unsigned long long ll;
142
170
 
143
- if (ll < 0) {
144
- /* We don't expect problems with the most negative integer not being representable
145
- * as a positive integer, because Fixnum is only up to 63 bits.
171
+ if (sll < 0) {
172
+ /* Avoid problems with the most negative integer not being representable
173
+ * as a positive integer, by using unsigned long long for encoding.
146
174
  */
147
- ll = -ll;
175
+ ll = -sll;
148
176
  neg = 1;
177
+ } else {
178
+ ll = sll;
149
179
  }
150
180
 
151
181
  /* Compute the result string backwards. */
152
182
  do {
153
- long long remainder;
154
- long long oldval = ll;
183
+ unsigned long long remainder;
184
+ unsigned long long oldval = ll;
155
185
 
156
186
  ll /= 10;
157
187
  remainder = oldval - ll * 10;
@@ -178,45 +208,17 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
178
208
  }else{
179
209
  *intermediate = pg_obj_to_i(value);
180
210
  if(TYPE(*intermediate) == T_FIXNUM){
181
- int len;
182
211
  long long sll = NUM2LL(*intermediate);
183
- long long ll = sll < 0 ? -sll : sll;
184
- if( ll < 100000000 ){
185
- if( ll < 10000 ){
186
- if( ll < 100 ){
187
- len = ll < 10 ? 1 : 2;
188
- }else{
189
- len = ll < 1000 ? 3 : 4;
190
- }
191
- }else{
192
- if( ll < 1000000 ){
193
- len = ll < 100000 ? 5 : 6;
194
- }else{
195
- len = ll < 10000000 ? 7 : 8;
196
- }
197
- }
198
- }else{
199
- if( ll < 1000000000000LL ){
200
- if( ll < 10000000000LL ){
201
- len = ll < 1000000000LL ? 9 : 10;
202
- }else{
203
- len = ll < 100000000000LL ? 11 : 12;
204
- }
205
- }else{
206
- if( ll < 100000000000000LL ){
207
- len = ll < 10000000000000LL ? 13 : 14;
208
- }else{
209
- return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
210
- }
211
- }
212
- }
213
- return sll < 0 ? len+1 : len;
212
+ unsigned long long ll = sll < 0 ? -sll : sll;
213
+ int len = (sizeof(unsigned long long) * 8 - count_leading_zero_bits(ll)) / 3;
214
+ return sll < 0 ? len+2 : len+1;
214
215
  }else{
215
- return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
216
+ return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
216
217
  }
217
218
  }
218
219
  }
219
220
 
221
+ #define MAX_DOUBLE_DIGITS 16
220
222
 
221
223
  /*
222
224
  * Document-class: PG::TextEncoder::Float < PG::SimpleEncoder
@@ -225,10 +227,16 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
225
227
  *
226
228
  */
227
229
  static int
228
- pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
230
+ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
229
231
  {
230
232
  if(out){
231
233
  double dvalue = NUM2DBL(value);
234
+ int len = 0;
235
+ int neg = 0;
236
+ int exp2i, exp10i, i;
237
+ unsigned long long ll, remainder, oldval;
238
+ VALUE intermediate;
239
+
232
240
  /* Cast to the same strings as value.to_s . */
233
241
  if( isinf(dvalue) ){
234
242
  if( dvalue < 0 ){
@@ -242,12 +250,128 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
242
250
  memcpy( out, "NaN", 3);
243
251
  return 3;
244
252
  }
245
- return sprintf( out, "%.16E", dvalue);
253
+
254
+ /*
255
+ * The following computaion is roughly a conversion kind of
256
+ * sprintf( out, "%.16E", dvalue);
257
+ */
258
+
259
+ /* write the algebraic sign */
260
+ if( dvalue < 0 ) {
261
+ dvalue = -dvalue;
262
+ *out++ = '-';
263
+ neg++;
264
+ }
265
+
266
+ /* retrieve the power of 2 exponent */
267
+ frexp(dvalue, &exp2i);
268
+ /* compute the power of 10 exponent */
269
+ exp10i = (int)floor(exp2i * 0.30102999566398114); /* Math.log(2)/Math.log(10) */
270
+ /* move the decimal point, so that we get an integer of MAX_DOUBLE_DIGITS decimal digits */
271
+ ll = (unsigned long long)(dvalue * pow(10, MAX_DOUBLE_DIGITS - 1 - exp10i) + 0.5);
272
+
273
+ /* avoid leading zeros due to inaccuracy of deriving exp10i from exp2i */
274
+ /* otherwise we would print "09.0" instead of "9.0" */
275
+ if( ll < 1000000000000000 ){ /* pow(10, MAX_DOUBLE_DIGITS-1) */
276
+ exp10i--;
277
+ ll *= 10;
278
+ }
279
+
280
+ if( exp10i <= -5 || exp10i >= 15 ) {
281
+ /* Write the float in exponent format (1.23e45) */
282
+
283
+ /* write fraction digits from right to left */
284
+ for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
285
+ oldval = ll;
286
+ ll /= 10;
287
+ remainder = oldval - ll * 10;
288
+ /* omit trailing zeros */
289
+ if(remainder != 0 || len ) {
290
+ out[i] = '0' + remainder;
291
+ len++;
292
+ }
293
+ }
294
+
295
+ /* write decimal point */
296
+ if( len ){
297
+ out[1] = '.';
298
+ len++;
299
+ }
300
+
301
+ /* write remaining single digit left to the decimal point */
302
+ oldval = ll;
303
+ ll /= 10;
304
+ remainder = oldval - ll * 10;
305
+ out[0] = '0' + remainder;
306
+ len++;
307
+
308
+ /* write exponent */
309
+ out[len++] = 'e';
310
+ intermediate = INT2NUM(exp10i);
311
+
312
+ return neg + len + pg_text_enc_integer(conv, Qnil, out + len, &intermediate, enc_idx);
313
+ } else {
314
+ /* write the float in non exponent format (0.001234 or 123450.0) */
315
+
316
+ /* write digits from right to left */
317
+ int lz = exp10i < 0 ? 0 : exp10i;
318
+ for( i = MAX_DOUBLE_DIGITS - (exp10i < 0 ? exp10i : 0); i >= 0; i-- ){
319
+ oldval = ll;
320
+ ll /= 10;
321
+ remainder = oldval - ll * 10;
322
+ /* write decimal point */
323
+ if( i - 1 == lz ){
324
+ out[i--] = '.';
325
+ len++;
326
+ }
327
+ /* if possible then omit trailing zeros */
328
+ if(remainder != 0 || len || i - 2 == lz) {
329
+ out[i] = '0' + remainder;
330
+ len++;
331
+ }
332
+ }
333
+ return neg + len;
334
+ }
246
335
  }else{
247
- return 23;
336
+ return 1 /*sign*/ + MAX_DOUBLE_DIGITS + 1 /*dot*/ + 1 /*e*/ + 1 /*exp sign*/ + 3 /*exp digits*/;
248
337
  }
249
338
  }
250
339
 
340
+
341
+ /*
342
+ * Document-class: PG::TextEncoder::Numeric < PG::SimpleEncoder
343
+ *
344
+ * This is the encoder class for the PostgreSQL numeric types.
345
+ *
346
+ * It converts Integer, Float and BigDecimal objects.
347
+ * All other objects are expected to respond to +to_s+.
348
+ */
349
+ static int
350
+ pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
351
+ {
352
+ switch(TYPE(value)){
353
+ case T_FIXNUM:
354
+ case T_BIGNUM:
355
+ return pg_text_enc_integer(this, value, out, intermediate, enc_idx);
356
+ case T_FLOAT:
357
+ return pg_text_enc_float(this, value, out, intermediate, enc_idx);
358
+ default:
359
+ if(out){ /* second pass */
360
+ rb_bug("unexpected value type: %d", TYPE(value));
361
+ } else { /* first pass */
362
+ if( rb_obj_is_kind_of(value, s_cBigDecimal) ){
363
+ /* value.to_s('F') */
364
+ *intermediate = rb_funcall(value, s_id_to_s, 1, s_str_F);
365
+ return -1; /* no second pass */
366
+ } else {
367
+ return pg_coder_enc_to_s(this, value, NULL, intermediate, enc_idx);
368
+ /* no second pass */
369
+ }
370
+ }
371
+ }
372
+ }
373
+
374
+
251
375
  static const char hextab[] = {
252
376
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
253
377
  };
@@ -255,8 +379,7 @@ static const char hextab[] = {
255
379
  /*
256
380
  * Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
257
381
  *
258
- * This is an encoder class for the PostgreSQL bytea type for server version 9.0
259
- * or newer.
382
+ * This is an encoder class for the PostgreSQL +bytea+ type.
260
383
  *
261
384
  * The binary String is converted to hexadecimal representation for transmission
262
385
  * in text format. For query bind parameters it is recommended to use
@@ -265,7 +388,7 @@ static const char hextab[] = {
265
388
  *
266
389
  */
267
390
  static int
268
- pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
391
+ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
269
392
  {
270
393
  if(out){
271
394
  size_t strlen = RSTRING_LEN(*intermediate);
@@ -344,13 +467,13 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
344
467
  }
345
468
 
346
469
  static char *
347
- quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data)
470
+ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data, int enc_idx)
348
471
  {
349
472
  int strlen;
350
473
  VALUE subint;
351
474
  t_pg_coder_enc_func enc_func = pg_coder_enc_func(this);
352
475
 
353
- strlen = enc_func(this, value, NULL, &subint);
476
+ strlen = enc_func(this, value, NULL, &subint, enc_idx);
354
477
 
355
478
  if( strlen == -1 ){
356
479
  /* we can directly use String value in subint */
@@ -376,20 +499,20 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
376
499
  current_out = pg_rb_str_ensure_capa( string, 2 * strlen + 2, current_out, NULL );
377
500
 
378
501
  /* Place the unescaped string at current output position. */
379
- strlen = enc_func(this, value, current_out, &subint);
502
+ strlen = enc_func(this, value, current_out, &subint, enc_idx);
380
503
 
381
504
  current_out += quote_buffer( func_data, current_out, strlen, current_out );
382
505
  }else{
383
506
  /* size of the unquoted string */
384
507
  current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
385
- current_out += enc_func(this, value, current_out, &subint);
508
+ current_out += enc_func(this, value, current_out, &subint, enc_idx);
386
509
  }
387
510
  }
388
511
  return current_out;
389
512
  }
390
513
 
391
514
  static char *
392
- write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote)
515
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
393
516
  {
394
517
  int i;
395
518
 
@@ -407,7 +530,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
407
530
 
408
531
  switch(TYPE(entry)){
409
532
  case T_ARRAY:
410
- current_out = write_array(this, entry, current_out, string, quote);
533
+ current_out = write_array(this, entry, current_out, string, quote, enc_idx);
411
534
  break;
412
535
  case T_NIL:
413
536
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
@@ -417,7 +540,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
417
540
  *current_out++ = 'L';
418
541
  break;
419
542
  default:
420
- current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this );
543
+ current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
421
544
  }
422
545
  }
423
546
  current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
@@ -439,41 +562,42 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
439
562
  *
440
563
  */
441
564
  static int
442
- pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
565
+ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
443
566
  {
444
567
  char *end_ptr;
445
568
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
446
569
 
447
570
  if( TYPE(value) == T_ARRAY){
448
- *intermediate = rb_str_new(NULL, 0);
571
+ VALUE out_str = rb_str_new(NULL, 0);
572
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
449
573
 
450
- end_ptr = write_array(this, value, RSTRING_PTR(*intermediate), *intermediate, this->needs_quotation);
574
+ end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
451
575
 
452
- rb_str_set_len( *intermediate, end_ptr - RSTRING_PTR(*intermediate) );
576
+ rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
577
+ *intermediate = out_str;
453
578
 
454
579
  return -1;
455
580
  } else {
456
- return pg_coder_enc_to_s( conv, value, out, intermediate );
581
+ return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
457
582
  }
458
583
  }
459
584
 
460
585
  static char *
461
586
  quote_identifier( VALUE value, VALUE out_string, char *current_out ){
462
587
  char *p_in = RSTRING_PTR(value);
463
- char *ptr1;
464
588
  size_t strlen = RSTRING_LEN(value);
589
+ char *p_inend = p_in + strlen;
465
590
  char *end_capa = current_out;
466
591
 
467
592
  PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
468
593
  *current_out++ = '"';
469
- for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
470
- char c = *ptr1;
594
+ for(; p_in != p_inend; p_in++) {
595
+ char c = *p_in;
471
596
  if (c == '"'){
472
- strlen++;
473
- PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
597
+ PG_RB_STR_ENSURE_CAPA( out_string, p_inend - p_in + 2, current_out, end_capa );
474
598
  *current_out++ = '"';
475
599
  } else if (c == 0){
476
- break;
600
+ rb_raise(rb_eArgError, "string contains null byte");
477
601
  }
478
602
  *current_out++ = c;
479
603
  }
@@ -484,7 +608,7 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
484
608
  }
485
609
 
486
610
  static char *
487
- pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
611
+ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
488
612
  {
489
613
  int i;
490
614
  int nr_elems;
@@ -496,6 +620,9 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
496
620
  VALUE entry = rb_ary_entry(value, i);
497
621
 
498
622
  StringValue(entry);
623
+ if( ENCODING_GET(entry) != enc_idx ){
624
+ entry = rb_str_export_to_enc(entry, rb_enc_from_index(enc_idx));
625
+ }
499
626
  out = quote_identifier(entry, string, out);
500
627
  if( i < nr_elems-1 ){
501
628
  out = pg_rb_str_ensure_capa( string, 1, out, NULL );
@@ -510,27 +637,34 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
510
637
  *
511
638
  * This is the encoder class for PostgreSQL identifiers.
512
639
  *
513
- * An Array value can be used for "schema.table.column" type identifiers:
640
+ * An Array value can be used for identifiers of the kind "schema.table.column".
641
+ * This ensures that each element is properly quoted:
514
642
  * PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
515
643
  * => '"schema"."table"."column"'
516
644
  *
517
645
  * This encoder can also be used per PG::Connection#quote_ident .
518
646
  */
519
647
  int
520
- pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
648
+ pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
521
649
  {
650
+ VALUE out_str;
522
651
  UNUSED( this );
523
652
  if( TYPE(value) == T_ARRAY){
524
- *intermediate = rb_str_new(NULL, 0);
525
- out = RSTRING_PTR(*intermediate);
526
- out = pg_text_enc_array_identifier(value, *intermediate, out);
653
+ out_str = rb_str_new(NULL, 0);
654
+ out = RSTRING_PTR(out_str);
655
+ out = pg_text_enc_array_identifier(value, out_str, out, enc_idx);
527
656
  } else {
528
657
  StringValue(value);
529
- *intermediate = rb_str_new(NULL, RSTRING_LEN(value) + 2);
530
- out = RSTRING_PTR(*intermediate);
531
- out = quote_identifier(value, *intermediate, out);
658
+ if( ENCODING_GET(value) != enc_idx ){
659
+ value = rb_str_export_to_enc(value, rb_enc_from_index(enc_idx));
660
+ }
661
+ out_str = rb_str_new(NULL, RSTRING_LEN(value) + 2);
662
+ out = RSTRING_PTR(out_str);
663
+ out = quote_identifier(value, out_str, out);
532
664
  }
533
- rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
665
+ rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
666
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
667
+ *intermediate = out_str;
534
668
  return -1;
535
669
  }
536
670
 
@@ -572,18 +706,26 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
572
706
  *
573
707
  * This is the encoder class for PostgreSQL literals.
574
708
  *
575
- * A literal is quoted and escaped by the +'+ character.
709
+ * A literal is quoted and escaped by the <tt>'</tt> character, so that it can be inserted into SQL queries.
710
+ * It works equal to PG::Connection#escape_literal, but integrates into the type cast system of ruby-pg.
711
+ *
712
+ * Both expressions have the same result:
713
+ * conn.escape_literal(PG::TextEncoder::Array.new.encode(["v1","v2"])) # => "'{v1,v2}'"
714
+ * PG::TextEncoder::QuotedLiteral.new(elements_type: PG::TextEncoder::Array.new).encode(["v1","v2"]) # => "'{v1,v2}'"
715
+ * While escape_literal requires a intermediate ruby string allocation, QuotedLiteral encodes the values directly to the result string.
576
716
  *
577
717
  */
578
718
  static int
579
- pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
719
+ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
580
720
  {
581
721
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
722
+ VALUE out_str = rb_str_new(NULL, 0);
723
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
582
724
 
583
- *intermediate = rb_str_new(NULL, 0);
584
- out = RSTRING_PTR(*intermediate);
585
- out = quote_string(this->elem, value, *intermediate, out, this->needs_quotation, quote_literal_buffer, this);
586
- rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
725
+ out = RSTRING_PTR(out_str);
726
+ out = quote_string(this->elem, value, out_str, out, this->needs_quotation, quote_literal_buffer, this, enc_idx);
727
+ rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
728
+ *intermediate = out_str;
587
729
  return -1;
588
730
  }
589
731
 
@@ -594,7 +736,7 @@ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *inte
594
736
  *
595
737
  */
596
738
  static int
597
- pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
739
+ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
598
740
  {
599
741
  int strlen;
600
742
  VALUE subint;
@@ -603,13 +745,13 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
603
745
 
604
746
  if(out){
605
747
  /* Second encoder pass, if required */
606
- strlen = enc_func(this->elem, value, out, intermediate);
748
+ strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
607
749
  base64_encode( out, out, strlen );
608
750
 
609
751
  return BASE64_ENCODED_SIZE(strlen);
610
752
  } else {
611
753
  /* First encoder pass */
612
- strlen = enc_func(this->elem, value, NULL, &subint);
754
+ strlen = enc_func(this->elem, value, NULL, &subint, enc_idx);
613
755
 
614
756
  if( strlen == -1 ){
615
757
  /* Encoded string is returned in subint */
@@ -617,6 +759,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
617
759
 
618
760
  strlen = RSTRING_LENINT(subint);
619
761
  out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
762
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
620
763
 
621
764
  base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
622
765
  *intermediate = out_str;
@@ -636,6 +779,12 @@ init_pg_text_encoder()
636
779
  {
637
780
  s_id_encode = rb_intern("encode");
638
781
  s_id_to_i = rb_intern("to_i");
782
+ s_id_to_s = rb_intern("to_s");
783
+ s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
784
+ rb_global_variable(&s_str_F);
785
+ rb_require("bigdecimal");
786
+ s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
787
+
639
788
 
640
789
  /* This module encapsulates all encoder classes with text output format */
641
790
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
@@ -647,6 +796,8 @@ init_pg_text_encoder()
647
796
  pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
648
797
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
649
798
  pg_define_coder( "Float", pg_text_enc_float, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
799
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
800
+ pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
650
801
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
651
802
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
652
803
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */