pg 0.18.2 → 1.5.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 (139) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +137 -0
  7. data/.gitignore +22 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/BSDL +2 -2
  15. data/Gemfile +14 -0
  16. data/History.md +876 -0
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.md +276 -0
  20. data/README.md +286 -0
  21. data/Rakefile +40 -131
  22. data/Rakefile.cross +88 -70
  23. data/certs/ged.pem +24 -0
  24. data/certs/larskanis-2022.pem +26 -0
  25. data/certs/larskanis-2023.pem +24 -0
  26. data/ext/errorcodes.def +113 -0
  27. data/ext/errorcodes.rb +1 -1
  28. data/ext/errorcodes.txt +36 -2
  29. data/ext/extconf.rb +120 -54
  30. data/ext/gvl_wrappers.c +8 -0
  31. data/ext/gvl_wrappers.h +44 -33
  32. data/ext/pg.c +226 -200
  33. data/ext/pg.h +99 -99
  34. data/ext/pg_binary_decoder.c +164 -16
  35. data/ext/pg_binary_encoder.c +249 -22
  36. data/ext/pg_coder.c +189 -44
  37. data/ext/pg_connection.c +1866 -1173
  38. data/ext/pg_copy_coder.c +398 -42
  39. data/ext/pg_errors.c +1 -1
  40. data/ext/pg_record_coder.c +522 -0
  41. data/ext/pg_result.c +727 -232
  42. data/ext/pg_text_decoder.c +629 -43
  43. data/ext/pg_text_encoder.c +269 -102
  44. data/ext/pg_tuple.c +572 -0
  45. data/ext/pg_type_map.c +64 -23
  46. data/ext/pg_type_map_all_strings.c +21 -7
  47. data/ext/pg_type_map_by_class.c +59 -27
  48. data/ext/pg_type_map_by_column.c +86 -43
  49. data/ext/pg_type_map_by_mri_type.c +49 -20
  50. data/ext/pg_type_map_by_oid.c +62 -29
  51. data/ext/pg_type_map_in_ruby.c +56 -22
  52. data/ext/{util.c → pg_util.c} +12 -12
  53. data/ext/{util.h → pg_util.h} +2 -2
  54. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  55. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  56. data/lib/pg/basic_type_map_for_results.rb +104 -0
  57. data/lib/pg/basic_type_registry.rb +299 -0
  58. data/lib/pg/binary_decoder/date.rb +9 -0
  59. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  60. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  61. data/lib/pg/coder.rb +36 -13
  62. data/lib/pg/connection.rb +797 -77
  63. data/lib/pg/exceptions.rb +16 -2
  64. data/lib/pg/result.rb +24 -7
  65. data/lib/pg/text_decoder/date.rb +18 -0
  66. data/lib/pg/text_decoder/inet.rb +9 -0
  67. data/lib/pg/text_decoder/json.rb +14 -0
  68. data/lib/pg/text_decoder/numeric.rb +9 -0
  69. data/lib/pg/text_decoder/timestamp.rb +30 -0
  70. data/lib/pg/text_encoder/date.rb +12 -0
  71. data/lib/pg/text_encoder/inet.rb +28 -0
  72. data/lib/pg/text_encoder/json.rb +14 -0
  73. data/lib/pg/text_encoder/numeric.rb +9 -0
  74. data/lib/pg/text_encoder/timestamp.rb +24 -0
  75. data/lib/pg/tuple.rb +30 -0
  76. data/lib/pg/type_map_by_column.rb +3 -2
  77. data/lib/pg/version.rb +4 -0
  78. data/lib/pg.rb +106 -41
  79. data/misc/openssl-pg-segfault.rb +31 -0
  80. data/misc/postgres/History.txt +9 -0
  81. data/misc/postgres/Manifest.txt +5 -0
  82. data/misc/postgres/README.txt +21 -0
  83. data/misc/postgres/Rakefile +21 -0
  84. data/misc/postgres/lib/postgres.rb +16 -0
  85. data/misc/ruby-pg/History.txt +9 -0
  86. data/misc/ruby-pg/Manifest.txt +5 -0
  87. data/misc/ruby-pg/README.txt +21 -0
  88. data/misc/ruby-pg/Rakefile +21 -0
  89. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  90. data/pg.gemspec +34 -0
  91. data/rakelib/task_extension.rb +46 -0
  92. data/sample/array_insert.rb +1 -1
  93. data/sample/async_api.rb +4 -8
  94. data/sample/async_copyto.rb +1 -1
  95. data/sample/async_mixed.rb +1 -1
  96. data/sample/check_conn.rb +1 -1
  97. data/sample/copydata.rb +71 -0
  98. data/sample/copyfrom.rb +1 -1
  99. data/sample/copyto.rb +1 -1
  100. data/sample/cursor.rb +1 -1
  101. data/sample/disk_usage_report.rb +6 -15
  102. data/sample/issue-119.rb +2 -2
  103. data/sample/losample.rb +1 -1
  104. data/sample/minimal-testcase.rb +2 -2
  105. data/sample/notify_wait.rb +1 -1
  106. data/sample/pg_statistics.rb +6 -15
  107. data/sample/replication_monitor.rb +9 -18
  108. data/sample/test_binary_values.rb +1 -1
  109. data/sample/wal_shipper.rb +2 -2
  110. data/sample/warehouse_partitions.rb +8 -17
  111. data/translation/.po4a-version +7 -0
  112. data/translation/po/all.pot +910 -0
  113. data/translation/po/ja.po +1047 -0
  114. data/translation/po4a.cfg +12 -0
  115. data.tar.gz.sig +0 -0
  116. metadata +137 -204
  117. metadata.gz.sig +0 -0
  118. data/ChangeLog +0 -5545
  119. data/History.rdoc +0 -313
  120. data/README.ja.rdoc +0 -14
  121. data/README.rdoc +0 -161
  122. data/lib/pg/basic_type_mapping.rb +0 -399
  123. data/lib/pg/constants.rb +0 -11
  124. data/lib/pg/text_decoder.rb +0 -42
  125. data/lib/pg/text_encoder.rb +0 -27
  126. data/spec/data/expected_trace.out +0 -26
  127. data/spec/data/random_binary_data +0 -0
  128. data/spec/helpers.rb +0 -355
  129. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  130. data/spec/pg/connection_spec.rb +0 -1535
  131. data/spec/pg/result_spec.rb +0 -449
  132. data/spec/pg/type_map_by_class_spec.rb +0 -138
  133. data/spec/pg/type_map_by_column_spec.rb +0 -222
  134. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  135. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  136. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  137. data/spec/pg/type_map_spec.rb +0 -22
  138. data/spec/pg/type_spec.rb +0 -688
  139. data/spec/pg_spec.rb +0 -50
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_encoder.c - PG::TextEncoder module
3
- * $Id: pg_text_encoder.c,v b859963462b2 2015/03/13 17:39:35 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,15 +41,20 @@
40
41
 
41
42
 
42
43
  #include "pg.h"
43
- #include "util.h"
44
+ #include "pg_util.h"
45
+ #ifdef HAVE_INTTYPES_H
44
46
  #include <inttypes.h>
47
+ #endif
45
48
  #include <math.h>
46
49
 
47
50
  VALUE rb_mPG_TextEncoder;
48
51
  static ID s_id_encode;
49
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;
50
56
 
51
- 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);
52
58
 
53
59
  VALUE
54
60
  pg_obj_to_i( VALUE value )
@@ -74,7 +80,7 @@ pg_obj_to_i( VALUE value )
74
80
  *
75
81
  */
76
82
  static int
77
- 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)
78
84
  {
79
85
  switch( TYPE(value) ){
80
86
  case T_FALSE:
@@ -92,10 +98,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
92
98
  if(out) *out = '1';
93
99
  return 1;
94
100
  } else {
95
- return pg_text_enc_integer(this, value, out, intermediate);
101
+ return pg_text_enc_integer(this, value, out, intermediate, enc_idx);
96
102
  }
97
103
  default:
98
- return pg_coder_enc_to_s(this, value, out, intermediate);
104
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
99
105
  }
100
106
  /* never reached */
101
107
  return 0;
@@ -111,45 +117,71 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
111
117
  *
112
118
  */
113
119
  int
114
- 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)
115
121
  {
116
- *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
+ }
117
128
  return -1;
118
129
  }
119
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
+ }
120
149
 
121
150
  /*
122
151
  * Document-class: PG::TextEncoder::Integer < PG::SimpleEncoder
123
152
  *
124
- * This is the encoder class for the PostgreSQL int types.
153
+ * This is the encoder class for the PostgreSQL integer types.
125
154
  *
126
155
  * Non-Integer values are expected to have method +to_i+ defined.
127
156
  *
128
157
  */
129
158
  static int
130
- 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)
131
160
  {
132
161
  if(out){
133
162
  if(TYPE(*intermediate) == T_STRING){
134
- return pg_coder_enc_to_s(this, value, out, intermediate);
163
+ return pg_coder_enc_to_s(this, value, out, intermediate, enc_idx);
135
164
  }else{
136
165
  char *start = out;
137
166
  int len;
138
167
  int neg = 0;
139
- long long ll = NUM2LL(*intermediate);
168
+ long long sll = NUM2LL(*intermediate);
169
+ unsigned long long ll;
140
170
 
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.
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.
144
174
  */
145
- ll = -ll;
175
+ ll = -sll;
146
176
  neg = 1;
177
+ } else {
178
+ ll = sll;
147
179
  }
148
180
 
149
181
  /* Compute the result string backwards. */
150
182
  do {
151
- long long remainder;
152
- long long oldval = ll;
183
+ unsigned long long remainder;
184
+ unsigned long long oldval = ll;
153
185
 
154
186
  ll /= 10;
155
187
  remainder = oldval - ll * 10;
@@ -159,7 +191,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
159
191
  if (neg)
160
192
  *out++ = '-';
161
193
 
162
- len = out - start;
194
+ len = (int)(out - start);
163
195
 
164
196
  /* Reverse string. */
165
197
  out--;
@@ -176,45 +208,17 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
176
208
  }else{
177
209
  *intermediate = pg_obj_to_i(value);
178
210
  if(TYPE(*intermediate) == T_FIXNUM){
179
- int len;
180
211
  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
+ 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;
212
215
  }else{
213
- return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate);
216
+ return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
214
217
  }
215
218
  }
216
219
  }
217
220
 
221
+ #define MAX_DOUBLE_DIGITS 16
218
222
 
219
223
  /*
220
224
  * Document-class: PG::TextEncoder::Float < PG::SimpleEncoder
@@ -223,10 +227,16 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
223
227
  *
224
228
  */
225
229
  static int
226
- 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)
227
231
  {
228
232
  if(out){
229
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
+
230
240
  /* Cast to the same strings as value.to_s . */
231
241
  if( isinf(dvalue) ){
232
242
  if( dvalue < 0 ){
@@ -240,12 +250,143 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
240
250
  memcpy( out, "NaN", 3);
241
251
  return 3;
242
252
  }
243
- return sprintf( out, "%.16E", dvalue);
253
+
254
+ /*
255
+ * The following computation 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
+ }
244
335
  }else{
245
- return 23;
336
+ return 1 /*sign*/ + MAX_DOUBLE_DIGITS + 1 /*dot*/ + 1 /*e*/ + 1 /*exp sign*/ + 3 /*exp digits*/;
246
337
  }
247
338
  }
248
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
+ /* called per autoload when TextEncoder::Numeric is used */
375
+ static VALUE
376
+ init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
377
+ {
378
+ s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
379
+ rb_global_variable(&s_str_F);
380
+ rb_require("bigdecimal");
381
+ s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
382
+
383
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
384
+ pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
385
+
386
+ return Qnil;
387
+ }
388
+
389
+
249
390
  static const char hextab[] = {
250
391
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
251
392
  };
@@ -253,17 +394,20 @@ static const char hextab[] = {
253
394
  /*
254
395
  * Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
255
396
  *
256
- * This is an encoder class for the PostgreSQL bytea type for server version 9.0
257
- * or newer.
397
+ * This is an encoder class for the PostgreSQL +bytea+ type.
258
398
  *
259
399
  * The binary String is converted to hexadecimal representation for transmission
260
400
  * in text format. For query bind parameters it is recommended to use
261
- * PG::BinaryEncoder::Bytea instead, in order to decrease network traffic and
262
- * CPU usage.
401
+ * PG::BinaryEncoder::Bytea or the hash form <tt>{value: binary_string, format: 1}</tt> instead,
402
+ * in order to decrease network traffic and CPU usage.
403
+ * See PG::Connection#exec_params for using the hash form.
404
+ *
405
+ * This encoder is particular useful when PG::TextEncoder::CopyRow is used with the COPY command.
406
+ * In this case there's no way to change the format of a single column to binary, so that the data have to be converted to bytea hex representation.
263
407
  *
264
408
  */
265
409
  static int
266
- pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
410
+ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
267
411
  {
268
412
  if(out){
269
413
  size_t strlen = RSTRING_LEN(*intermediate);
@@ -278,11 +422,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
278
422
  *optr++ = hextab[c >> 4];
279
423
  *optr++ = hextab[c & 0xf];
280
424
  }
281
- return optr - out;
425
+ return (int)(optr - out);
282
426
  }else{
283
427
  *intermediate = rb_obj_as_string(value);
284
428
  /* The output starts with "\x" and each character is converted to hex. */
285
- return 2 + RSTRING_LEN(*intermediate) * 2;
429
+ return 2 + RSTRING_LENINT(*intermediate) * 2;
286
430
  }
287
431
  }
288
432
 
@@ -342,13 +486,13 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
342
486
  }
343
487
 
344
488
  static char *
345
- quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data)
489
+ 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)
346
490
  {
347
491
  int strlen;
348
492
  VALUE subint;
349
493
  t_pg_coder_enc_func enc_func = pg_coder_enc_func(this);
350
494
 
351
- strlen = enc_func(this, value, NULL, &subint);
495
+ strlen = enc_func(this, value, NULL, &subint, enc_idx);
352
496
 
353
497
  if( strlen == -1 ){
354
498
  /* we can directly use String value in subint */
@@ -374,20 +518,20 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
374
518
  current_out = pg_rb_str_ensure_capa( string, 2 * strlen + 2, current_out, NULL );
375
519
 
376
520
  /* Place the unescaped string at current output position. */
377
- strlen = enc_func(this, value, current_out, &subint);
521
+ strlen = enc_func(this, value, current_out, &subint, enc_idx);
378
522
 
379
523
  current_out += quote_buffer( func_data, current_out, strlen, current_out );
380
524
  }else{
381
525
  /* size of the unquoted string */
382
526
  current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
383
- current_out += enc_func(this, value, current_out, &subint);
527
+ current_out += enc_func(this, value, current_out, &subint, enc_idx);
384
528
  }
385
529
  }
386
530
  return current_out;
387
531
  }
388
532
 
389
533
  static char *
390
- write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote)
534
+ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
391
535
  {
392
536
  int i;
393
537
 
@@ -405,7 +549,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
405
549
 
406
550
  switch(TYPE(entry)){
407
551
  case T_ARRAY:
408
- current_out = write_array(this, entry, current_out, string, quote);
552
+ current_out = write_array(this, entry, current_out, string, quote, enc_idx);
409
553
  break;
410
554
  case T_NIL:
411
555
  current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
@@ -415,7 +559,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
415
559
  *current_out++ = 'L';
416
560
  break;
417
561
  default:
418
- current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this );
562
+ current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
419
563
  }
420
564
  }
421
565
  current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
@@ -437,41 +581,42 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
437
581
  *
438
582
  */
439
583
  static int
440
- pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
584
+ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
441
585
  {
442
586
  char *end_ptr;
443
587
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
444
588
 
445
589
  if( TYPE(value) == T_ARRAY){
446
- *intermediate = rb_str_new(NULL, 0);
590
+ VALUE out_str = rb_str_new(NULL, 0);
591
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
447
592
 
448
- end_ptr = write_array(this, value, RSTRING_PTR(*intermediate), *intermediate, this->needs_quotation);
593
+ end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
449
594
 
450
- rb_str_set_len( *intermediate, end_ptr - RSTRING_PTR(*intermediate) );
595
+ rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
596
+ *intermediate = out_str;
451
597
 
452
598
  return -1;
453
599
  } else {
454
- return pg_coder_enc_to_s( conv, value, out, intermediate );
600
+ return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
455
601
  }
456
602
  }
457
603
 
458
604
  static char *
459
605
  quote_identifier( VALUE value, VALUE out_string, char *current_out ){
460
606
  char *p_in = RSTRING_PTR(value);
461
- char *ptr1;
462
607
  size_t strlen = RSTRING_LEN(value);
608
+ char *p_inend = p_in + strlen;
463
609
  char *end_capa = current_out;
464
610
 
465
611
  PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
466
612
  *current_out++ = '"';
467
- for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
468
- char c = *ptr1;
613
+ for(; p_in != p_inend; p_in++) {
614
+ char c = *p_in;
469
615
  if (c == '"'){
470
- strlen++;
471
- PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
616
+ PG_RB_STR_ENSURE_CAPA( out_string, p_inend - p_in + 2, current_out, end_capa );
472
617
  *current_out++ = '"';
473
618
  } else if (c == 0){
474
- break;
619
+ rb_raise(rb_eArgError, "string contains null byte");
475
620
  }
476
621
  *current_out++ = c;
477
622
  }
@@ -482,10 +627,10 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
482
627
  }
483
628
 
484
629
  static char *
485
- pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
630
+ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
486
631
  {
487
- int i;
488
- int nr_elems;
632
+ long i;
633
+ long nr_elems;
489
634
 
490
635
  Check_Type(value, T_ARRAY);
491
636
  nr_elems = RARRAY_LEN(value);
@@ -493,6 +638,10 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
493
638
  for( i=0; i<nr_elems; i++){
494
639
  VALUE entry = rb_ary_entry(value, i);
495
640
 
641
+ StringValue(entry);
642
+ if( ENCODING_GET(entry) != enc_idx ){
643
+ entry = rb_str_export_to_enc(entry, rb_enc_from_index(enc_idx));
644
+ }
496
645
  out = quote_identifier(entry, string, out);
497
646
  if( i < nr_elems-1 ){
498
647
  out = pg_rb_str_ensure_capa( string, 1, out, NULL );
@@ -507,27 +656,34 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
507
656
  *
508
657
  * This is the encoder class for PostgreSQL identifiers.
509
658
  *
510
- * An Array value can be used for "schema.table.column" type identifiers:
659
+ * An Array value can be used for identifiers of the kind "schema.table.column".
660
+ * This ensures that each element is properly quoted:
511
661
  * PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
512
662
  * => '"schema"."table"."column"'
513
663
  *
514
664
  * This encoder can also be used per PG::Connection#quote_ident .
515
665
  */
516
666
  int
517
- pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
667
+ pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
518
668
  {
669
+ VALUE out_str;
519
670
  UNUSED( this );
520
671
  if( TYPE(value) == T_ARRAY){
521
- *intermediate = rb_str_new(NULL, 0);
522
- out = RSTRING_PTR(*intermediate);
523
- out = pg_text_enc_array_identifier(value, *intermediate, out);
672
+ out_str = rb_str_new(NULL, 0);
673
+ out = RSTRING_PTR(out_str);
674
+ out = pg_text_enc_array_identifier(value, out_str, out, enc_idx);
524
675
  } else {
525
676
  StringValue(value);
526
- *intermediate = rb_str_new(NULL, RSTRING_LEN(value) + 2);
527
- out = RSTRING_PTR(*intermediate);
528
- out = quote_identifier(value, *intermediate, out);
677
+ if( ENCODING_GET(value) != enc_idx ){
678
+ value = rb_str_export_to_enc(value, rb_enc_from_index(enc_idx));
679
+ }
680
+ out_str = rb_str_new(NULL, RSTRING_LEN(value) + 2);
681
+ out = RSTRING_PTR(out_str);
682
+ out = quote_identifier(value, out_str, out);
529
683
  }
530
- rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
684
+ rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
685
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
686
+ *intermediate = out_str;
531
687
  return -1;
532
688
  }
533
689
 
@@ -569,18 +725,26 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
569
725
  *
570
726
  * This is the encoder class for PostgreSQL literals.
571
727
  *
572
- * A literal is quoted and escaped by the +'+ character.
728
+ * A literal is quoted and escaped by the <tt>'</tt> character, so that it can be inserted into SQL queries.
729
+ * It works equal to PG::Connection#escape_literal, but integrates into the type cast system of ruby-pg.
730
+ *
731
+ * Both expressions have the same result:
732
+ * conn.escape_literal(PG::TextEncoder::Array.new.encode(["v1","v2"])) # => "'{v1,v2}'"
733
+ * PG::TextEncoder::QuotedLiteral.new(elements_type: PG::TextEncoder::Array.new).encode(["v1","v2"]) # => "'{v1,v2}'"
734
+ * While escape_literal requires a intermediate ruby string allocation, QuotedLiteral encodes the values directly to the result string.
573
735
  *
574
736
  */
575
737
  static int
576
- pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
738
+ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
577
739
  {
578
740
  t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
741
+ VALUE out_str = rb_str_new(NULL, 0);
742
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
579
743
 
580
- *intermediate = rb_str_new(NULL, 0);
581
- out = RSTRING_PTR(*intermediate);
582
- out = quote_string(this->elem, value, *intermediate, out, this->needs_quotation, quote_literal_buffer, this);
583
- rb_str_set_len( *intermediate, out - RSTRING_PTR(*intermediate) );
744
+ out = RSTRING_PTR(out_str);
745
+ out = quote_string(this->elem, value, out_str, out, this->needs_quotation, quote_literal_buffer, this, enc_idx);
746
+ rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
747
+ *intermediate = out_str;
584
748
  return -1;
585
749
  }
586
750
 
@@ -591,7 +755,7 @@ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *inte
591
755
  *
592
756
  */
593
757
  static int
594
- pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
758
+ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
595
759
  {
596
760
  int strlen;
597
761
  VALUE subint;
@@ -600,13 +764,13 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
600
764
 
601
765
  if(out){
602
766
  /* Second encoder pass, if required */
603
- strlen = enc_func(this->elem, value, out, intermediate);
767
+ strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
604
768
  base64_encode( out, out, strlen );
605
769
 
606
770
  return BASE64_ENCODED_SIZE(strlen);
607
771
  } else {
608
772
  /* First encoder pass */
609
- strlen = enc_func(this->elem, value, NULL, &subint);
773
+ strlen = enc_func(this->elem, value, NULL, &subint, enc_idx);
610
774
 
611
775
  if( strlen == -1 ){
612
776
  /* Encoded string is returned in subint */
@@ -614,6 +778,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
614
778
 
615
779
  strlen = RSTRING_LENINT(subint);
616
780
  out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
781
+ PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
617
782
 
618
783
  base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
619
784
  *intermediate = out_str;
@@ -629,13 +794,15 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
629
794
 
630
795
 
631
796
  void
632
- init_pg_text_encoder()
797
+ init_pg_text_encoder(void)
633
798
  {
634
799
  s_id_encode = rb_intern("encode");
635
800
  s_id_to_i = rb_intern("to_i");
801
+ s_id_to_s = rb_intern("to_s");
636
802
 
637
803
  /* This module encapsulates all encoder classes with text output format */
638
804
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
805
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
639
806
 
640
807
  /* Make RDoc aware of the encoder classes... */
641
808
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */