pg 1.1.3 → 1.3.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 (117) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +86 -0
  6. data/.github/workflows/source-gem.yml +129 -0
  7. data/.gitignore +13 -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/Gemfile +14 -0
  15. data/History.rdoc +210 -6
  16. data/Manifest.txt +3 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.rdoc +1 -2
  19. data/README.rdoc +51 -15
  20. data/Rakefile +31 -140
  21. data/Rakefile.cross +60 -56
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/ext/errorcodes.def +76 -0
  25. data/ext/errorcodes.txt +21 -2
  26. data/ext/extconf.rb +101 -26
  27. data/ext/gvl_wrappers.c +4 -0
  28. data/ext/gvl_wrappers.h +23 -0
  29. data/ext/pg.c +190 -98
  30. data/ext/pg.h +42 -17
  31. data/ext/pg_binary_decoder.c +20 -16
  32. data/ext/pg_binary_encoder.c +13 -12
  33. data/ext/pg_coder.c +95 -29
  34. data/ext/pg_connection.c +1043 -769
  35. data/ext/pg_copy_coder.c +50 -18
  36. data/ext/pg_record_coder.c +519 -0
  37. data/ext/pg_result.c +326 -142
  38. data/ext/pg_text_decoder.c +15 -9
  39. data/ext/pg_text_encoder.c +185 -53
  40. data/ext/pg_tuple.c +61 -27
  41. data/ext/pg_type_map.c +42 -9
  42. data/ext/pg_type_map_all_strings.c +19 -5
  43. data/ext/pg_type_map_by_class.c +54 -24
  44. data/ext/pg_type_map_by_column.c +73 -34
  45. data/ext/pg_type_map_by_mri_type.c +48 -19
  46. data/ext/pg_type_map_by_oid.c +55 -25
  47. data/ext/pg_type_map_in_ruby.c +51 -20
  48. data/ext/{util.c → pg_util.c} +7 -7
  49. data/ext/{util.h → pg_util.h} +0 -0
  50. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  51. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  52. data/lib/pg/basic_type_map_for_results.rb +81 -0
  53. data/lib/pg/basic_type_registry.rb +296 -0
  54. data/lib/pg/binary_decoder.rb +1 -0
  55. data/lib/pg/coder.rb +23 -2
  56. data/lib/pg/connection.rb +589 -59
  57. data/lib/pg/constants.rb +1 -0
  58. data/lib/pg/exceptions.rb +1 -0
  59. data/lib/pg/result.rb +13 -1
  60. data/lib/pg/text_decoder.rb +2 -3
  61. data/lib/pg/text_encoder.rb +8 -18
  62. data/lib/pg/type_map_by_column.rb +2 -1
  63. data/lib/pg/version.rb +4 -0
  64. data/lib/pg.rb +48 -33
  65. data/misc/openssl-pg-segfault.rb +31 -0
  66. data/misc/postgres/History.txt +9 -0
  67. data/misc/postgres/Manifest.txt +5 -0
  68. data/misc/postgres/README.txt +21 -0
  69. data/misc/postgres/Rakefile +21 -0
  70. data/misc/postgres/lib/postgres.rb +16 -0
  71. data/misc/ruby-pg/History.txt +9 -0
  72. data/misc/ruby-pg/Manifest.txt +5 -0
  73. data/misc/ruby-pg/README.txt +21 -0
  74. data/misc/ruby-pg/Rakefile +21 -0
  75. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  76. data/pg.gemspec +32 -0
  77. data/rakelib/task_extension.rb +46 -0
  78. data/sample/array_insert.rb +20 -0
  79. data/sample/async_api.rb +106 -0
  80. data/sample/async_copyto.rb +39 -0
  81. data/sample/async_mixed.rb +56 -0
  82. data/sample/check_conn.rb +21 -0
  83. data/sample/copydata.rb +71 -0
  84. data/sample/copyfrom.rb +81 -0
  85. data/sample/copyto.rb +19 -0
  86. data/sample/cursor.rb +21 -0
  87. data/sample/disk_usage_report.rb +177 -0
  88. data/sample/issue-119.rb +94 -0
  89. data/sample/losample.rb +69 -0
  90. data/sample/minimal-testcase.rb +17 -0
  91. data/sample/notify_wait.rb +72 -0
  92. data/sample/pg_statistics.rb +285 -0
  93. data/sample/replication_monitor.rb +222 -0
  94. data/sample/test_binary_values.rb +33 -0
  95. data/sample/wal_shipper.rb +434 -0
  96. data/sample/warehouse_partitions.rb +311 -0
  97. data.tar.gz.sig +0 -0
  98. metadata +94 -237
  99. metadata.gz.sig +0 -0
  100. data/ChangeLog +0 -6595
  101. data/lib/pg/basic_type_mapping.rb +0 -459
  102. data/spec/data/expected_trace.out +0 -26
  103. data/spec/data/random_binary_data +0 -0
  104. data/spec/helpers.rb +0 -381
  105. data/spec/pg/basic_type_mapping_spec.rb +0 -508
  106. data/spec/pg/connection_spec.rb +0 -1849
  107. data/spec/pg/connection_sync_spec.rb +0 -41
  108. data/spec/pg/result_spec.rb +0 -491
  109. data/spec/pg/tuple_spec.rb +0 -280
  110. data/spec/pg/type_map_by_class_spec.rb +0 -138
  111. data/spec/pg/type_map_by_column_spec.rb +0 -222
  112. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  113. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  114. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  115. data/spec/pg/type_map_spec.rb +0 -22
  116. data/spec/pg/type_spec.rb +0 -949
  117. data/spec/pg_spec.rb +0 -50
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_decoder.c - PG::TextDecoder module
3
- * $Id: pg_text_decoder.c,v cee615e0ea2c 2018/07/30 05:27:05 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -30,7 +30,7 @@
30
30
 
31
31
  #include "ruby/version.h"
32
32
  #include "pg.h"
33
- #include "util.h"
33
+ #include "pg_util.h"
34
34
  #ifdef HAVE_INTTYPES_H
35
35
  #include <inttypes.h>
36
36
  #endif
@@ -89,7 +89,7 @@ pg_text_dec_boolean(t_pg_coder *conv, const char *val, int len, int tuple, int f
89
89
  VALUE
90
90
  pg_text_dec_string(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
91
91
  {
92
- VALUE ret = rb_tainted_str_new( val, len );
92
+ VALUE ret = rb_str_new( val, len );
93
93
  PG_ENCODING_SET_NOCHECK( ret, enc_idx );
94
94
  return ret;
95
95
  }
@@ -204,7 +204,12 @@ struct pg_blob_initialization {
204
204
 
205
205
  static VALUE pg_create_blob(VALUE v) {
206
206
  struct pg_blob_initialization *bi = (struct pg_blob_initialization *)v;
207
- return rb_tainted_str_new(bi->blob_string, bi->length);
207
+ return rb_str_new(bi->blob_string, bi->length);
208
+ }
209
+
210
+ static VALUE pg_pq_freemem(VALUE mem) {
211
+ PQfreemem((void *)mem);
212
+ return Qfalse;
208
213
  }
209
214
 
210
215
  /*
@@ -223,7 +228,7 @@ pg_text_dec_bytea(t_pg_coder *conv, const char *val, int len, int tuple, int fie
223
228
  if (bi.blob_string == NULL) {
224
229
  rb_raise(rb_eNoMemError, "PQunescapeBytea failure: probably not enough memory");
225
230
  }
226
- return rb_ensure(pg_create_blob, (VALUE)&bi, (VALUE(*)())PQfreemem, (VALUE)bi.blob_string);
231
+ return rb_ensure(pg_create_blob, (VALUE)&bi, pg_pq_freemem, (VALUE)bi.blob_string);
227
232
  }
228
233
 
229
234
  /*
@@ -558,7 +563,7 @@ pg_text_dec_from_base64(t_pg_coder *conv, const char *val, int len, int tuple, i
558
563
  t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
559
564
  int decoded_len;
560
565
  /* create a buffer of the expected decoded length */
561
- VALUE out_value = rb_tainted_str_new(NULL, BASE64_DECODED_SIZE(len));
566
+ VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
562
567
 
563
568
  decoded_len = base64_decode( RSTRING_PTR(out_value), val, len );
564
569
  rb_str_set_len(out_value, decoded_len);
@@ -610,7 +615,7 @@ static int parse_year(const char **str) {
610
615
  * This is a decoder class for conversion of PostgreSQL text timestamps
611
616
  * to Ruby Time objects.
612
617
  *
613
- * The following flags can be used to specify timezone interpretation:
618
+ * The following flags can be used to specify time interpretation when no timezone is given:
614
619
  * * +PG::Coder::TIMESTAMP_DB_UTC+ : Interpret timestamp as UTC time (default)
615
620
  * * +PG::Coder::TIMESTAMP_DB_LOCAL+ : Interpret timestamp as local time
616
621
  * * +PG::Coder::TIMESTAMP_APP_UTC+ : Return timestamp as UTC time (default)
@@ -619,6 +624,7 @@ static int parse_year(const char **str) {
619
624
  * Example:
620
625
  * deco = PG::TextDecoder::Timestamp.new(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL)
621
626
  * deco.decode("2000-01-01 00:00:00") # => 2000-01-01 01:00:00 +0100
627
+ * deco.decode("2000-01-01 00:00:00.123-06") # => 2000-01-01 00:00:00 -0600
622
628
  */
623
629
  static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
624
630
  {
@@ -848,7 +854,7 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
848
854
 
849
855
  ip_int_native = read_nbo32(dst);
850
856
 
851
- /* Work around broken IPAddr behavior of convering portion
857
+ /* Work around broken IPAddr behavior of converting portion
852
858
  of address after netmask to 0 */
853
859
  switch (mask) {
854
860
  case 0:
@@ -961,7 +967,7 @@ init_pg_text_decoder()
961
967
  pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
962
968
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
963
969
  pg_define_coder( "Float", pg_text_dec_float, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
964
- /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "BigDecimal", rb_cPG_SimpleDecoder ); */
970
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
965
971
  pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
966
972
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
967
973
  pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_encoder.c - PG::TextEncoder module
3
- * $Id: pg_text_encoder.c,v e57f6b452eb3 2018/08/18 10:58:52 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -41,7 +41,7 @@
41
41
 
42
42
 
43
43
  #include "pg.h"
44
- #include "util.h"
44
+ #include "pg_util.h"
45
45
  #ifdef HAVE_INTTYPES_H
46
46
  #include <inttypes.h>
47
47
  #endif
@@ -50,6 +50,9 @@
50
50
  VALUE rb_mPG_TextEncoder;
51
51
  static ID s_id_encode;
52
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;
53
56
 
54
57
  static int pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx);
55
58
 
@@ -125,11 +128,29 @@ pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate,
125
128
  return -1;
126
129
  }
127
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
+ }
128
149
 
129
150
  /*
130
151
  * Document-class: PG::TextEncoder::Integer < PG::SimpleEncoder
131
152
  *
132
- * This is the encoder class for the PostgreSQL int types.
153
+ * This is the encoder class for the PostgreSQL integer types.
133
154
  *
134
155
  * Non-Integer values are expected to have method +to_i+ defined.
135
156
  *
@@ -144,20 +165,23 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
144
165
  char *start = out;
145
166
  int len;
146
167
  int neg = 0;
147
- long long ll = NUM2LL(*intermediate);
168
+ long long sll = NUM2LL(*intermediate);
169
+ unsigned long long ll;
148
170
 
149
- if (ll < 0) {
150
- /* We don't expect problems with the most negative integer not being representable
151
- * 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.
152
174
  */
153
- ll = -ll;
175
+ ll = -sll;
154
176
  neg = 1;
177
+ } else {
178
+ ll = sll;
155
179
  }
156
180
 
157
181
  /* Compute the result string backwards. */
158
182
  do {
159
- long long remainder;
160
- long long oldval = ll;
183
+ unsigned long long remainder;
184
+ unsigned long long oldval = ll;
161
185
 
162
186
  ll /= 10;
163
187
  remainder = oldval - ll * 10;
@@ -167,7 +191,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
167
191
  if (neg)
168
192
  *out++ = '-';
169
193
 
170
- len = out - start;
194
+ len = (int)(out - start);
171
195
 
172
196
  /* Reverse string. */
173
197
  out--;
@@ -184,45 +208,17 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
184
208
  }else{
185
209
  *intermediate = pg_obj_to_i(value);
186
210
  if(TYPE(*intermediate) == T_FIXNUM){
187
- int len;
188
211
  long long sll = NUM2LL(*intermediate);
189
- long long ll = sll < 0 ? -sll : sll;
190
- if( ll < 100000000 ){
191
- if( ll < 10000 ){
192
- if( ll < 100 ){
193
- len = ll < 10 ? 1 : 2;
194
- }else{
195
- len = ll < 1000 ? 3 : 4;
196
- }
197
- }else{
198
- if( ll < 1000000 ){
199
- len = ll < 100000 ? 5 : 6;
200
- }else{
201
- len = ll < 10000000 ? 7 : 8;
202
- }
203
- }
204
- }else{
205
- if( ll < 1000000000000LL ){
206
- if( ll < 10000000000LL ){
207
- len = ll < 1000000000LL ? 9 : 10;
208
- }else{
209
- len = ll < 100000000000LL ? 11 : 12;
210
- }
211
- }else{
212
- if( ll < 100000000000000LL ){
213
- len = ll < 10000000000000LL ? 13 : 14;
214
- }else{
215
- return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
216
- }
217
- }
218
- }
219
- 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;
220
215
  }else{
221
216
  return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
222
217
  }
223
218
  }
224
219
  }
225
220
 
221
+ #define MAX_DOUBLE_DIGITS 16
226
222
 
227
223
  /*
228
224
  * Document-class: PG::TextEncoder::Float < PG::SimpleEncoder
@@ -235,6 +231,12 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
235
231
  {
236
232
  if(out){
237
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
+
238
240
  /* Cast to the same strings as value.to_s . */
239
241
  if( isinf(dvalue) ){
240
242
  if( dvalue < 0 ){
@@ -248,12 +250,128 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
248
250
  memcpy( out, "NaN", 3);
249
251
  return 3;
250
252
  }
251
- 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
+ }
252
335
  }else{
253
- return 23;
336
+ return 1 /*sign*/ + MAX_DOUBLE_DIGITS + 1 /*dot*/ + 1 /*e*/ + 1 /*exp sign*/ + 3 /*exp digits*/;
337
+ }
338
+ }
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
+ }
254
371
  }
255
372
  }
256
373
 
374
+
257
375
  static const char hextab[] = {
258
376
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
259
377
  };
@@ -261,8 +379,7 @@ static const char hextab[] = {
261
379
  /*
262
380
  * Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
263
381
  *
264
- * This is an encoder class for the PostgreSQL bytea type for server version 9.0
265
- * or newer.
382
+ * This is an encoder class for the PostgreSQL +bytea+ type.
266
383
  *
267
384
  * The binary String is converted to hexadecimal representation for transmission
268
385
  * in text format. For query bind parameters it is recommended to use
@@ -286,11 +403,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
286
403
  *optr++ = hextab[c >> 4];
287
404
  *optr++ = hextab[c & 0xf];
288
405
  }
289
- return optr - out;
406
+ return (int)(optr - out);
290
407
  }else{
291
408
  *intermediate = rb_obj_as_string(value);
292
409
  /* The output starts with "\x" and each character is converted to hex. */
293
- return 2 + RSTRING_LEN(*intermediate) * 2;
410
+ return 2 + RSTRING_LENINT(*intermediate) * 2;
294
411
  }
295
412
  }
296
413
 
@@ -493,8 +610,8 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
493
610
  static char *
494
611
  pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
495
612
  {
496
- int i;
497
- int nr_elems;
613
+ long i;
614
+ long nr_elems;
498
615
 
499
616
  Check_Type(value, T_ARRAY);
500
617
  nr_elems = RARRAY_LEN(value);
@@ -520,7 +637,8 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
520
637
  *
521
638
  * This is the encoder class for PostgreSQL identifiers.
522
639
  *
523
- * 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:
524
642
  * PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
525
643
  * => '"schema"."table"."column"'
526
644
  *
@@ -588,7 +706,13 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
588
706
  *
589
707
  * This is the encoder class for PostgreSQL literals.
590
708
  *
591
- * 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.
592
716
  *
593
717
  */
594
718
  static int
@@ -655,6 +779,12 @@ init_pg_text_encoder()
655
779
  {
656
780
  s_id_encode = rb_intern("encode");
657
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
+
658
788
 
659
789
  /* This module encapsulates all encoder classes with text output format */
660
790
  rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
@@ -666,6 +796,8 @@ init_pg_text_encoder()
666
796
  pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
667
797
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
668
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 );
669
801
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
670
802
  pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
671
803
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
data/ext/pg_tuple.c CHANGED
@@ -52,57 +52,83 @@ typedef struct {
52
52
  VALUE values[0];
53
53
  } t_pg_tuple;
54
54
 
55
- static inline VALUE
56
- pg_tuple_get_field_names( t_pg_tuple *this )
55
+ static inline VALUE *
56
+ pg_tuple_get_field_names_ptr( t_pg_tuple *this )
57
57
  {
58
58
  if( this->num_fields != (int)RHASH_SIZE(this->field_map) ){
59
- return this->values[this->num_fields];
59
+ return &this->values[this->num_fields];
60
60
  } else {
61
- return Qfalse;
61
+ static VALUE f = Qfalse;
62
+ return &f;
63
+ }
64
+ }
65
+
66
+ static inline VALUE
67
+ pg_tuple_get_field_names( t_pg_tuple *this )
68
+ {
69
+ return *pg_tuple_get_field_names_ptr(this);
70
+ }
71
+
72
+ static void
73
+ pg_tuple_gc_mark( void *_this )
74
+ {
75
+ t_pg_tuple *this = (t_pg_tuple *)_this;
76
+ int i;
77
+
78
+ if( !this ) return;
79
+ rb_gc_mark_movable( this->result );
80
+ rb_gc_mark_movable( this->typemap );
81
+ rb_gc_mark_movable( this->field_map );
82
+
83
+ for( i = 0; i < this->num_fields; i++ ){
84
+ rb_gc_mark_movable( this->values[i] );
62
85
  }
86
+ rb_gc_mark_movable( pg_tuple_get_field_names(this) );
63
87
  }
64
88
 
65
89
  static void
66
- pg_tuple_gc_mark( t_pg_tuple *this )
90
+ pg_tuple_gc_compact( void *_this )
67
91
  {
92
+ t_pg_tuple *this = (t_pg_tuple *)_this;
68
93
  int i;
69
94
 
70
95
  if( !this ) return;
71
- rb_gc_mark( this->result );
72
- rb_gc_mark( this->typemap );
73
- rb_gc_mark( this->field_map );
96
+ pg_gc_location( this->result );
97
+ pg_gc_location( this->typemap );
98
+ pg_gc_location( this->field_map );
74
99
 
75
100
  for( i = 0; i < this->num_fields; i++ ){
76
- rb_gc_mark( this->values[i] );
101
+ pg_gc_location( this->values[i] );
77
102
  }
78
- rb_gc_mark( pg_tuple_get_field_names(this) );
103
+ pg_gc_location( *pg_tuple_get_field_names_ptr(this) );
79
104
  }
80
105
 
81
106
  static void
82
- pg_tuple_gc_free( t_pg_tuple *this )
107
+ pg_tuple_gc_free( void *_this )
83
108
  {
109
+ t_pg_tuple *this = (t_pg_tuple *)_this;
84
110
  if( !this ) return;
85
111
  xfree(this);
86
112
  }
87
113
 
88
114
  static size_t
89
- pg_tuple_memsize( t_pg_tuple *this )
115
+ pg_tuple_memsize( const void *_this )
90
116
  {
117
+ const t_pg_tuple *this = (const t_pg_tuple *)_this;
91
118
  if( this==NULL ) return 0;
92
119
  return sizeof(*this) + sizeof(*this->values) * this->num_fields;
93
120
  }
94
121
 
95
122
  static const rb_data_type_t pg_tuple_type = {
96
- "pg",
123
+ "PG::Tuple",
97
124
  {
98
- (void (*)(void*))pg_tuple_gc_mark,
99
- (void (*)(void*))pg_tuple_gc_free,
100
- (size_t (*)(const void *))pg_tuple_memsize,
125
+ pg_tuple_gc_mark,
126
+ pg_tuple_gc_free,
127
+ pg_tuple_memsize,
128
+ pg_compact_callback(pg_tuple_gc_compact),
101
129
  },
102
130
  0, 0,
103
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
104
131
  RUBY_TYPED_FREE_IMMEDIATELY,
105
- #endif
106
132
  };
107
133
 
108
134
  /*
@@ -132,7 +158,6 @@ pg_tuple_new(VALUE result, int row_num)
132
158
  sizeof(*this) +
133
159
  sizeof(*this->values) * num_fields +
134
160
  sizeof(*this->values) * (dup_names ? 1 : 0));
135
- RTYPEDDATA_DATA(self) = this;
136
161
 
137
162
  this->result = result;
138
163
  this->typemap = p_result->typemap;
@@ -151,6 +176,8 @@ pg_tuple_new(VALUE result, int row_num)
151
176
  this->values[num_fields] = rb_obj_freeze(rb_ary_new4(num_fields, p_result->fnames));
152
177
  }
153
178
 
179
+ RTYPEDDATA_DATA(self) = this;
180
+
154
181
  return self;
155
182
  }
156
183
 
@@ -171,7 +198,7 @@ pg_tuple_materialize_field(t_pg_tuple *this, int col)
171
198
  VALUE value = this->values[col];
172
199
 
173
200
  if( value == Qundef ){
174
- t_typemap *p_typemap = DATA_PTR( this->typemap );
201
+ t_typemap *p_typemap = RTYPEDDATA_DATA( this->typemap );
175
202
 
176
203
  pgresult_get(this->result); /* make sure we have a valid PGresult object */
177
204
  value = p_typemap->funcs.typecast_result_value(p_typemap, this->result, this->row_num, col);
@@ -211,7 +238,7 @@ pg_tuple_materialize(t_pg_tuple *this)
211
238
  * An integer +key+ is interpreted as column index.
212
239
  * Negative values of index count from the end of the array.
213
240
  *
214
- * A string +key+ is interpreted as column name.
241
+ * Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
215
242
  *
216
243
  * If the key can't be found, there are several options:
217
244
  * With no other arguments, it will raise a IndexError exception;
@@ -264,9 +291,16 @@ pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
264
291
 
265
292
  /*
266
293
  * call-seq:
267
- * res[ name ] -> value
294
+ * tup[ key ] -> value
295
+ *
296
+ * Returns a field value by either column index or column name.
297
+ *
298
+ * An integer +key+ is interpreted as column index.
299
+ * Negative values of index count from the end of the array.
268
300
  *
269
- * Returns field _name_.
301
+ * Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
302
+ *
303
+ * If the key can't be found, it returns +nil+ .
270
304
  */
271
305
  static VALUE
272
306
  pg_tuple_aref(VALUE self, VALUE key)
@@ -457,7 +491,6 @@ pg_tuple_load(VALUE self, VALUE a)
457
491
  int dup_names;
458
492
 
459
493
  rb_check_frozen(self);
460
- rb_check_trusted(self);
461
494
 
462
495
  TypedData_Get_Struct(self, t_pg_tuple, &pg_tuple_type, this);
463
496
  if (this)
@@ -472,9 +505,9 @@ pg_tuple_load(VALUE self, VALUE a)
472
505
  rb_obj_freeze(field_names);
473
506
  values = RARRAY_AREF(a, 1);
474
507
  Check_Type(values, T_ARRAY);
475
- num_fields = RARRAY_LEN(values);
508
+ num_fields = RARRAY_LENINT(values);
476
509
 
477
- if (RARRAY_LEN(field_names) != num_fields)
510
+ if (RARRAY_LENINT(field_names) != num_fields)
478
511
  rb_raise(rb_eTypeError, "different number of fields and values");
479
512
 
480
513
  field_map = rb_hash_new();
@@ -489,7 +522,6 @@ pg_tuple_load(VALUE self, VALUE a)
489
522
  sizeof(*this) +
490
523
  sizeof(*this->values) * num_fields +
491
524
  sizeof(*this->values) * (dup_names ? 1 : 0));
492
- RTYPEDDATA_DATA(self) = this;
493
525
 
494
526
  this->result = Qnil;
495
527
  this->typemap = Qnil;
@@ -508,6 +540,8 @@ pg_tuple_load(VALUE self, VALUE a)
508
540
  this->values[num_fields] = field_names;
509
541
  }
510
542
 
543
+ RTYPEDDATA_DATA(self) = this;
544
+
511
545
  if (FL_TEST(a, FL_EXIVAR)) {
512
546
  rb_copy_generic_ivar(self, a);
513
547
  FL_SET(self, FL_EXIVAR);