pg 1.1.3 → 1.5.4

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 (140) hide show
  1. checksums.yaml +4 -4
  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 +141 -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/Gemfile +14 -0
  15. data/History.md +884 -0
  16. data/Manifest.txt +3 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.md +300 -0
  19. data/README.md +286 -0
  20. data/Rakefile +37 -137
  21. data/Rakefile.cross +62 -62
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/certs/larskanis-2023.pem +24 -0
  25. data/ext/errorcodes.def +80 -0
  26. data/ext/errorcodes.rb +0 -0
  27. data/ext/errorcodes.txt +22 -2
  28. data/ext/extconf.rb +105 -26
  29. data/ext/gvl_wrappers.c +4 -0
  30. data/ext/gvl_wrappers.h +23 -0
  31. data/ext/pg.c +204 -152
  32. data/ext/pg.h +52 -21
  33. data/ext/pg_binary_decoder.c +100 -17
  34. data/ext/pg_binary_encoder.c +238 -13
  35. data/ext/pg_coder.c +109 -34
  36. data/ext/pg_connection.c +1383 -983
  37. data/ext/pg_copy_coder.c +356 -35
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +522 -0
  40. data/ext/pg_result.c +439 -172
  41. data/ext/pg_text_decoder.c +42 -18
  42. data/ext/pg_text_encoder.c +201 -56
  43. data/ext/pg_tuple.c +97 -66
  44. data/ext/pg_type_map.c +45 -11
  45. data/ext/pg_type_map_all_strings.c +21 -7
  46. data/ext/pg_type_map_by_class.c +59 -27
  47. data/ext/pg_type_map_by_column.c +80 -37
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +62 -29
  50. data/ext/pg_type_map_in_ruby.c +56 -22
  51. data/ext/{util.c → pg_util.c} +7 -7
  52. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  53. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  54. data/lib/pg/basic_type_map_for_results.rb +104 -0
  55. data/lib/pg/basic_type_registry.rb +299 -0
  56. data/lib/pg/binary_decoder/date.rb +9 -0
  57. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  58. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  59. data/lib/pg/coder.rb +35 -12
  60. data/lib/pg/connection.rb +744 -84
  61. data/lib/pg/exceptions.rb +15 -1
  62. data/lib/pg/result.rb +13 -1
  63. data/lib/pg/text_decoder/date.rb +18 -0
  64. data/lib/pg/text_decoder/inet.rb +9 -0
  65. data/lib/pg/text_decoder/json.rb +14 -0
  66. data/lib/pg/text_decoder/numeric.rb +9 -0
  67. data/lib/pg/text_decoder/timestamp.rb +30 -0
  68. data/lib/pg/text_encoder/date.rb +12 -0
  69. data/lib/pg/text_encoder/inet.rb +28 -0
  70. data/lib/pg/text_encoder/json.rb +14 -0
  71. data/lib/pg/text_encoder/numeric.rb +9 -0
  72. data/lib/pg/text_encoder/timestamp.rb +24 -0
  73. data/lib/pg/type_map_by_column.rb +2 -1
  74. data/lib/pg/version.rb +4 -0
  75. data/lib/pg.rb +94 -39
  76. data/misc/openssl-pg-segfault.rb +31 -0
  77. data/misc/postgres/History.txt +9 -0
  78. data/misc/postgres/Manifest.txt +5 -0
  79. data/misc/postgres/README.txt +21 -0
  80. data/misc/postgres/Rakefile +21 -0
  81. data/misc/postgres/lib/postgres.rb +16 -0
  82. data/misc/ruby-pg/History.txt +9 -0
  83. data/misc/ruby-pg/Manifest.txt +5 -0
  84. data/misc/ruby-pg/README.txt +21 -0
  85. data/misc/ruby-pg/Rakefile +21 -0
  86. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  87. data/pg.gemspec +34 -0
  88. data/rakelib/task_extension.rb +46 -0
  89. data/sample/array_insert.rb +20 -0
  90. data/sample/async_api.rb +102 -0
  91. data/sample/async_copyto.rb +39 -0
  92. data/sample/async_mixed.rb +56 -0
  93. data/sample/check_conn.rb +21 -0
  94. data/sample/copydata.rb +71 -0
  95. data/sample/copyfrom.rb +81 -0
  96. data/sample/copyto.rb +19 -0
  97. data/sample/cursor.rb +21 -0
  98. data/sample/disk_usage_report.rb +177 -0
  99. data/sample/issue-119.rb +94 -0
  100. data/sample/losample.rb +69 -0
  101. data/sample/minimal-testcase.rb +17 -0
  102. data/sample/notify_wait.rb +72 -0
  103. data/sample/pg_statistics.rb +285 -0
  104. data/sample/replication_monitor.rb +222 -0
  105. data/sample/test_binary_values.rb +33 -0
  106. data/sample/wal_shipper.rb +434 -0
  107. data/sample/warehouse_partitions.rb +311 -0
  108. data/translation/.po4a-version +7 -0
  109. data/translation/po/all.pot +936 -0
  110. data/translation/po/ja.po +1036 -0
  111. data/translation/po4a.cfg +12 -0
  112. data.tar.gz.sig +0 -0
  113. metadata +144 -222
  114. metadata.gz.sig +0 -0
  115. data/ChangeLog +0 -6595
  116. data/History.rdoc +0 -485
  117. data/README.ja.rdoc +0 -14
  118. data/README.rdoc +0 -178
  119. data/lib/pg/basic_type_mapping.rb +0 -459
  120. data/lib/pg/binary_decoder.rb +0 -22
  121. data/lib/pg/constants.rb +0 -11
  122. data/lib/pg/text_decoder.rb +0 -47
  123. data/lib/pg/text_encoder.rb +0 -69
  124. data/spec/data/expected_trace.out +0 -26
  125. data/spec/data/random_binary_data +0 -0
  126. data/spec/helpers.rb +0 -381
  127. data/spec/pg/basic_type_mapping_spec.rb +0 -508
  128. data/spec/pg/connection_spec.rb +0 -1849
  129. data/spec/pg/connection_sync_spec.rb +0 -41
  130. data/spec/pg/result_spec.rb +0 -491
  131. data/spec/pg/tuple_spec.rb +0 -280
  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 -949
  139. data/spec/pg_spec.rb +0 -50
  140. /data/ext/{util.h → pg_util.h} +0 -0
@@ -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
@@ -43,7 +43,6 @@
43
43
  #include <string.h>
44
44
 
45
45
  VALUE rb_mPG_TextDecoder;
46
- static ID s_id_decode;
47
46
  static ID s_id_Rational;
48
47
  static ID s_id_new;
49
48
  static ID s_id_utc;
@@ -89,7 +88,7 @@ pg_text_dec_boolean(t_pg_coder *conv, const char *val, int len, int tuple, int f
89
88
  VALUE
90
89
  pg_text_dec_string(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
91
90
  {
92
- VALUE ret = rb_tainted_str_new( val, len );
91
+ VALUE ret = rb_str_new( val, len );
93
92
  PG_ENCODING_SET_NOCHECK( ret, enc_idx );
94
93
  return ret;
95
94
  }
@@ -171,6 +170,19 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
171
170
  return rb_funcall(rb_cObject, s_id_BigDecimal, 1, rb_str_new(val, len));
172
171
  }
173
172
 
173
+ /* called per autoload when TextDecoder::Numeric is used */
174
+ static VALUE
175
+ init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
176
+ {
177
+ rb_require("bigdecimal");
178
+ s_id_BigDecimal = rb_intern("BigDecimal");
179
+
180
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
181
+ pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
182
+
183
+ return Qnil;
184
+ }
185
+
174
186
  /*
175
187
  * Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
176
188
  *
@@ -204,7 +216,12 @@ struct pg_blob_initialization {
204
216
 
205
217
  static VALUE pg_create_blob(VALUE v) {
206
218
  struct pg_blob_initialization *bi = (struct pg_blob_initialization *)v;
207
- return rb_tainted_str_new(bi->blob_string, bi->length);
219
+ return rb_str_new(bi->blob_string, bi->length);
220
+ }
221
+
222
+ static VALUE pg_pq_freemem(VALUE mem) {
223
+ PQfreemem((void *)mem);
224
+ return Qfalse;
208
225
  }
209
226
 
210
227
  /*
@@ -223,7 +240,7 @@ pg_text_dec_bytea(t_pg_coder *conv, const char *val, int len, int tuple, int fie
223
240
  if (bi.blob_string == NULL) {
224
241
  rb_raise(rb_eNoMemError, "PQunescapeBytea failure: probably not enough memory");
225
242
  }
226
- return rb_ensure(pg_create_blob, (VALUE)&bi, (VALUE(*)())PQfreemem, (VALUE)bi.blob_string);
243
+ return rb_ensure(pg_create_blob, (VALUE)&bi, pg_pq_freemem, (VALUE)bi.blob_string);
227
244
  }
228
245
 
229
246
  /*
@@ -558,7 +575,7 @@ pg_text_dec_from_base64(t_pg_coder *conv, const char *val, int len, int tuple, i
558
575
  t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
559
576
  int decoded_len;
560
577
  /* create a buffer of the expected decoded length */
561
- VALUE out_value = rb_tainted_str_new(NULL, BASE64_DECODED_SIZE(len));
578
+ VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
562
579
 
563
580
  decoded_len = base64_decode( RSTRING_PTR(out_value), val, len );
564
581
  rb_str_set_len(out_value, decoded_len);
@@ -610,7 +627,7 @@ static int parse_year(const char **str) {
610
627
  * This is a decoder class for conversion of PostgreSQL text timestamps
611
628
  * to Ruby Time objects.
612
629
  *
613
- * The following flags can be used to specify timezone interpretation:
630
+ * The following flags can be used to specify time interpretation when no timezone is given:
614
631
  * * +PG::Coder::TIMESTAMP_DB_UTC+ : Interpret timestamp as UTC time (default)
615
632
  * * +PG::Coder::TIMESTAMP_DB_LOCAL+ : Interpret timestamp as local time
616
633
  * * +PG::Coder::TIMESTAMP_APP_UTC+ : Return timestamp as UTC time (default)
@@ -619,6 +636,7 @@ static int parse_year(const char **str) {
619
636
  * Example:
620
637
  * deco = PG::TextDecoder::Timestamp.new(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL)
621
638
  * deco.decode("2000-01-01 00:00:00") # => 2000-01-01 01:00:00 +0100
639
+ * deco.decode("2000-01-01 00:00:00.123-06") # => 2000-01-01 00:00:00 -0600
622
640
  */
623
641
  static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
624
642
  {
@@ -848,7 +866,7 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
848
866
 
849
867
  ip_int_native = read_nbo32(dst);
850
868
 
851
- /* Work around broken IPAddr behavior of convering portion
869
+ /* Work around broken IPAddr behavior of converting portion
852
870
  of address after netmask to 0 */
853
871
  switch (mask) {
854
872
  case 0:
@@ -916,8 +934,9 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
916
934
  return ip;
917
935
  }
918
936
 
919
- void
920
- init_pg_text_decoder()
937
+ /* called per autoload when TextDecoder::Inet is used */
938
+ static VALUE
939
+ init_pg_text_decoder_inet(VALUE rb_mPG_TextDecoder)
921
940
  {
922
941
  rb_require("ipaddr");
923
942
  s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
@@ -936,14 +955,21 @@ init_pg_text_decoder()
936
955
  s_vmasks6 = rb_eval_string("a = [0]*129; a[0] = 0; a[128] = 0xffffffffffffffffffffffffffffffff; 127.downto(1){|i| a[i] = a[i+1] - (1 << (127 - i))}; a.freeze");
937
956
  rb_global_variable(&s_vmasks6);
938
957
 
939
- s_id_decode = rb_intern("decode");
958
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
959
+ pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
960
+
961
+ return Qnil;
962
+ }
963
+
964
+
965
+ void
966
+ init_pg_text_decoder(void)
967
+ {
940
968
  s_id_Rational = rb_intern("Rational");
941
969
  s_id_new = rb_intern("new");
942
970
  s_id_utc = rb_intern("utc");
943
971
  s_id_getlocal = rb_intern("getlocal");
944
972
 
945
- rb_require("bigdecimal");
946
- s_id_BigDecimal = rb_intern("BigDecimal");
947
973
  s_nan = rb_eval_string("0.0/0.0");
948
974
  rb_global_variable(&s_nan);
949
975
  s_pos_inf = rb_eval_string("1.0/0.0");
@@ -953,6 +979,8 @@ init_pg_text_decoder()
953
979
 
954
980
  /* This module encapsulates all decoder classes with text input format */
955
981
  rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
982
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_inet", init_pg_text_decoder_inet, 0);
983
+ rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_numeric", init_pg_text_decoder_numeric, 0);
956
984
 
957
985
  /* Make RDoc aware of the decoder classes... */
958
986
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
@@ -961,8 +989,6 @@ init_pg_text_decoder()
961
989
  pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
962
990
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
963
991
  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 ); */
965
- pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
966
992
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
967
993
  pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
968
994
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
@@ -971,8 +997,6 @@ init_pg_text_decoder()
971
997
  pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
972
998
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
973
999
  pg_define_coder( "Timestamp", pg_text_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
974
- /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
975
- pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
976
1000
 
977
1001
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
978
1002
  pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, 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,143 @@ 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
+ /* 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
+
257
390
  static const char hextab[] = {
258
391
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
259
392
  };
@@ -261,13 +394,16 @@ static const char hextab[] = {
261
394
  /*
262
395
  * Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
263
396
  *
264
- * This is an encoder class for the PostgreSQL bytea type for server version 9.0
265
- * or newer.
397
+ * This is an encoder class for the PostgreSQL +bytea+ type.
266
398
  *
267
399
  * The binary String is converted to hexadecimal representation for transmission
268
400
  * in text format. For query bind parameters it is recommended to use
269
- * PG::BinaryEncoder::Bytea instead, in order to decrease network traffic and
270
- * 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.
271
407
  *
272
408
  */
273
409
  static int
@@ -286,11 +422,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
286
422
  *optr++ = hextab[c >> 4];
287
423
  *optr++ = hextab[c & 0xf];
288
424
  }
289
- return optr - out;
425
+ return (int)(optr - out);
290
426
  }else{
291
427
  *intermediate = rb_obj_as_string(value);
292
428
  /* The output starts with "\x" and each character is converted to hex. */
293
- return 2 + RSTRING_LEN(*intermediate) * 2;
429
+ return 2 + RSTRING_LENINT(*intermediate) * 2;
294
430
  }
295
431
  }
296
432
 
@@ -493,8 +629,8 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
493
629
  static char *
494
630
  pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
495
631
  {
496
- int i;
497
- int nr_elems;
632
+ long i;
633
+ long nr_elems;
498
634
 
499
635
  Check_Type(value, T_ARRAY);
500
636
  nr_elems = RARRAY_LEN(value);
@@ -520,7 +656,8 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
520
656
  *
521
657
  * This is the encoder class for PostgreSQL identifiers.
522
658
  *
523
- * 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:
524
661
  * PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
525
662
  * => '"schema"."table"."column"'
526
663
  *
@@ -588,7 +725,13 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
588
725
  *
589
726
  * This is the encoder class for PostgreSQL literals.
590
727
  *
591
- * 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.
592
735
  *
593
736
  */
594
737
  static int
@@ -651,13 +794,15 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
651
794
 
652
795
 
653
796
  void
654
- init_pg_text_encoder()
797
+ init_pg_text_encoder(void)
655
798
  {
656
799
  s_id_encode = rb_intern("encode");
657
800
  s_id_to_i = rb_intern("to_i");
801
+ s_id_to_s = rb_intern("to_s");
658
802
 
659
803
  /* This module encapsulates all encoder classes with text output format */
660
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);
661
806
 
662
807
  /* Make RDoc aware of the encoder classes... */
663
808
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */