pg 0.18.4 → 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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.appveyor.yml +42 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +117 -0
- data/.github/workflows/source-gem.yml +137 -0
- data/.gitignore +22 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/BSDL +2 -2
- data/Gemfile +14 -0
- data/History.md +876 -0
- data/Manifest.txt +8 -21
- data/README-Windows.rdoc +4 -4
- data/README.ja.md +276 -0
- data/README.md +286 -0
- data/Rakefile +38 -141
- data/Rakefile.cross +69 -72
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/certs/larskanis-2023.pem +24 -0
- data/ext/errorcodes.def +113 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +36 -2
- data/ext/extconf.rb +119 -54
- data/ext/gvl_wrappers.c +8 -0
- data/ext/gvl_wrappers.h +44 -33
- data/ext/pg.c +226 -200
- data/ext/pg.h +99 -99
- data/ext/pg_binary_decoder.c +162 -16
- data/ext/pg_binary_encoder.c +245 -20
- data/ext/pg_coder.c +189 -44
- data/ext/pg_connection.c +1865 -1172
- data/ext/pg_copy_coder.c +398 -42
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +522 -0
- data/ext/pg_result.c +727 -232
- data/ext/pg_text_decoder.c +627 -43
- data/ext/pg_text_encoder.c +266 -102
- data/ext/pg_tuple.c +572 -0
- data/ext/pg_type_map.c +58 -17
- data/ext/pg_type_map_all_strings.c +21 -7
- data/ext/pg_type_map_by_class.c +59 -27
- data/ext/pg_type_map_by_column.c +80 -37
- data/ext/pg_type_map_by_mri_type.c +49 -20
- data/ext/pg_type_map_by_oid.c +62 -29
- data/ext/pg_type_map_in_ruby.c +56 -22
- data/ext/{util.c → pg_util.c} +12 -12
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg/basic_type_map_based_on_result.rb +67 -0
- data/lib/pg/basic_type_map_for_queries.rb +198 -0
- data/lib/pg/basic_type_map_for_results.rb +104 -0
- data/lib/pg/basic_type_registry.rb +299 -0
- data/lib/pg/binary_decoder/date.rb +9 -0
- data/lib/pg/binary_decoder/timestamp.rb +26 -0
- data/lib/pg/binary_encoder/timestamp.rb +20 -0
- data/lib/pg/coder.rb +36 -13
- data/lib/pg/connection.rb +797 -77
- data/lib/pg/exceptions.rb +16 -2
- data/lib/pg/result.rb +24 -7
- data/lib/pg/text_decoder/date.rb +18 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +14 -0
- data/lib/pg/text_decoder/numeric.rb +9 -0
- data/lib/pg/text_decoder/timestamp.rb +30 -0
- data/lib/pg/text_encoder/date.rb +12 -0
- data/lib/pg/text_encoder/inet.rb +28 -0
- data/lib/pg/text_encoder/json.rb +14 -0
- data/lib/pg/text_encoder/numeric.rb +9 -0
- data/lib/pg/text_encoder/timestamp.rb +24 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +106 -41
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +34 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +1 -1
- data/sample/async_api.rb +4 -8
- data/sample/async_copyto.rb +1 -1
- data/sample/async_mixed.rb +1 -1
- data/sample/check_conn.rb +1 -1
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +1 -1
- data/sample/copyto.rb +1 -1
- data/sample/cursor.rb +1 -1
- data/sample/disk_usage_report.rb +6 -15
- data/sample/issue-119.rb +2 -2
- data/sample/losample.rb +1 -1
- data/sample/minimal-testcase.rb +2 -2
- data/sample/notify_wait.rb +1 -1
- data/sample/pg_statistics.rb +6 -15
- data/sample/replication_monitor.rb +9 -18
- data/sample/test_binary_values.rb +1 -1
- data/sample/wal_shipper.rb +2 -2
- data/sample/warehouse_partitions.rb +8 -17
- data/translation/.po4a-version +7 -0
- data/translation/po/all.pot +910 -0
- data/translation/po/ja.po +1047 -0
- data/translation/po4a.cfg +12 -0
- data.tar.gz.sig +0 -0
- metadata +136 -213
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -5911
- data/History.rdoc +0 -338
- data/README.ja.rdoc +0 -14
- data/README.rdoc +0 -164
- data/lib/pg/basic_type_mapping.rb +0 -399
- data/lib/pg/constants.rb +0 -11
- data/lib/pg/text_decoder.rb +0 -44
- data/lib/pg/text_encoder.rb +0 -27
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -355
- data/spec/pg/basic_type_mapping_spec.rb +0 -251
- data/spec/pg/connection_spec.rb +0 -1544
- data/spec/pg/result_spec.rb +0 -449
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -222
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -697
- data/spec/pg_spec.rb +0 -50
data/ext/pg_text_encoder.c
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* pg_text_encoder.c - PG::TextEncoder module
|
|
3
|
-
* $Id
|
|
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 "
|
|
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
|
-
|
|
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
|
|
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
|
|
168
|
+
long long sll = NUM2LL(*intermediate);
|
|
169
|
+
unsigned long long ll;
|
|
142
170
|
|
|
143
|
-
if (
|
|
144
|
-
/*
|
|
145
|
-
* as a positive integer,
|
|
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 = -
|
|
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;
|
|
@@ -161,7 +191,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
|
161
191
|
if (neg)
|
|
162
192
|
*out++ = '-';
|
|
163
193
|
|
|
164
|
-
len = out - start;
|
|
194
|
+
len = (int)(out - start);
|
|
165
195
|
|
|
166
196
|
/* Reverse string. */
|
|
167
197
|
out--;
|
|
@@ -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
|
-
|
|
185
|
-
|
|
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,143 @@ 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
|
-
|
|
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
|
+
}
|
|
246
335
|
}else{
|
|
247
|
-
return
|
|
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
|
+
}
|
|
248
371
|
}
|
|
249
372
|
}
|
|
250
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
|
+
|
|
251
390
|
static const char hextab[] = {
|
|
252
391
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
|
253
392
|
};
|
|
@@ -255,17 +394,20 @@ static const char hextab[] = {
|
|
|
255
394
|
/*
|
|
256
395
|
* Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
|
|
257
396
|
*
|
|
258
|
-
* This is an encoder class for the PostgreSQL bytea type
|
|
259
|
-
* or newer.
|
|
397
|
+
* This is an encoder class for the PostgreSQL +bytea+ type.
|
|
260
398
|
*
|
|
261
399
|
* The binary String is converted to hexadecimal representation for transmission
|
|
262
400
|
* in text format. For query bind parameters it is recommended to use
|
|
263
|
-
* PG::BinaryEncoder::Bytea
|
|
264
|
-
* 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.
|
|
265
407
|
*
|
|
266
408
|
*/
|
|
267
409
|
static int
|
|
268
|
-
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)
|
|
269
411
|
{
|
|
270
412
|
if(out){
|
|
271
413
|
size_t strlen = RSTRING_LEN(*intermediate);
|
|
@@ -280,11 +422,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
|
280
422
|
*optr++ = hextab[c >> 4];
|
|
281
423
|
*optr++ = hextab[c & 0xf];
|
|
282
424
|
}
|
|
283
|
-
return optr - out;
|
|
425
|
+
return (int)(optr - out);
|
|
284
426
|
}else{
|
|
285
427
|
*intermediate = rb_obj_as_string(value);
|
|
286
428
|
/* The output starts with "\x" and each character is converted to hex. */
|
|
287
|
-
return 2 +
|
|
429
|
+
return 2 + RSTRING_LENINT(*intermediate) * 2;
|
|
288
430
|
}
|
|
289
431
|
}
|
|
290
432
|
|
|
@@ -344,13 +486,13 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
344
486
|
}
|
|
345
487
|
|
|
346
488
|
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)
|
|
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)
|
|
348
490
|
{
|
|
349
491
|
int strlen;
|
|
350
492
|
VALUE subint;
|
|
351
493
|
t_pg_coder_enc_func enc_func = pg_coder_enc_func(this);
|
|
352
494
|
|
|
353
|
-
strlen = enc_func(this, value, NULL, &subint);
|
|
495
|
+
strlen = enc_func(this, value, NULL, &subint, enc_idx);
|
|
354
496
|
|
|
355
497
|
if( strlen == -1 ){
|
|
356
498
|
/* we can directly use String value in subint */
|
|
@@ -376,20 +518,20 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
|
|
|
376
518
|
current_out = pg_rb_str_ensure_capa( string, 2 * strlen + 2, current_out, NULL );
|
|
377
519
|
|
|
378
520
|
/* Place the unescaped string at current output position. */
|
|
379
|
-
strlen = enc_func(this, value, current_out, &subint);
|
|
521
|
+
strlen = enc_func(this, value, current_out, &subint, enc_idx);
|
|
380
522
|
|
|
381
523
|
current_out += quote_buffer( func_data, current_out, strlen, current_out );
|
|
382
524
|
}else{
|
|
383
525
|
/* size of the unquoted string */
|
|
384
526
|
current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
|
|
385
|
-
current_out += enc_func(this, value, current_out, &subint);
|
|
527
|
+
current_out += enc_func(this, value, current_out, &subint, enc_idx);
|
|
386
528
|
}
|
|
387
529
|
}
|
|
388
530
|
return current_out;
|
|
389
531
|
}
|
|
390
532
|
|
|
391
533
|
static char *
|
|
392
|
-
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)
|
|
393
535
|
{
|
|
394
536
|
int i;
|
|
395
537
|
|
|
@@ -407,7 +549,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
407
549
|
|
|
408
550
|
switch(TYPE(entry)){
|
|
409
551
|
case T_ARRAY:
|
|
410
|
-
current_out = write_array(this, entry, current_out, string, quote);
|
|
552
|
+
current_out = write_array(this, entry, current_out, string, quote, enc_idx);
|
|
411
553
|
break;
|
|
412
554
|
case T_NIL:
|
|
413
555
|
current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
|
|
@@ -417,7 +559,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
417
559
|
*current_out++ = 'L';
|
|
418
560
|
break;
|
|
419
561
|
default:
|
|
420
|
-
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 );
|
|
421
563
|
}
|
|
422
564
|
}
|
|
423
565
|
current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
|
|
@@ -439,41 +581,42 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
439
581
|
*
|
|
440
582
|
*/
|
|
441
583
|
static int
|
|
442
|
-
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)
|
|
443
585
|
{
|
|
444
586
|
char *end_ptr;
|
|
445
587
|
t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
|
|
446
588
|
|
|
447
589
|
if( TYPE(value) == T_ARRAY){
|
|
448
|
-
|
|
590
|
+
VALUE out_str = rb_str_new(NULL, 0);
|
|
591
|
+
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
449
592
|
|
|
450
|
-
end_ptr = write_array(this, value, RSTRING_PTR(
|
|
593
|
+
end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
|
|
451
594
|
|
|
452
|
-
rb_str_set_len(
|
|
595
|
+
rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
|
|
596
|
+
*intermediate = out_str;
|
|
453
597
|
|
|
454
598
|
return -1;
|
|
455
599
|
} else {
|
|
456
|
-
return pg_coder_enc_to_s( conv, value, out, intermediate );
|
|
600
|
+
return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
|
|
457
601
|
}
|
|
458
602
|
}
|
|
459
603
|
|
|
460
604
|
static char *
|
|
461
605
|
quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
|
462
606
|
char *p_in = RSTRING_PTR(value);
|
|
463
|
-
char *ptr1;
|
|
464
607
|
size_t strlen = RSTRING_LEN(value);
|
|
608
|
+
char *p_inend = p_in + strlen;
|
|
465
609
|
char *end_capa = current_out;
|
|
466
610
|
|
|
467
611
|
PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
|
|
468
612
|
*current_out++ = '"';
|
|
469
|
-
for(
|
|
470
|
-
char c = *
|
|
613
|
+
for(; p_in != p_inend; p_in++) {
|
|
614
|
+
char c = *p_in;
|
|
471
615
|
if (c == '"'){
|
|
472
|
-
|
|
473
|
-
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 );
|
|
474
617
|
*current_out++ = '"';
|
|
475
618
|
} else if (c == 0){
|
|
476
|
-
|
|
619
|
+
rb_raise(rb_eArgError, "string contains null byte");
|
|
477
620
|
}
|
|
478
621
|
*current_out++ = c;
|
|
479
622
|
}
|
|
@@ -484,10 +627,10 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
|
|
484
627
|
}
|
|
485
628
|
|
|
486
629
|
static char *
|
|
487
|
-
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)
|
|
488
631
|
{
|
|
489
|
-
|
|
490
|
-
|
|
632
|
+
long i;
|
|
633
|
+
long nr_elems;
|
|
491
634
|
|
|
492
635
|
Check_Type(value, T_ARRAY);
|
|
493
636
|
nr_elems = RARRAY_LEN(value);
|
|
@@ -496,6 +639,9 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
|
|
|
496
639
|
VALUE entry = rb_ary_entry(value, i);
|
|
497
640
|
|
|
498
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
|
+
}
|
|
499
645
|
out = quote_identifier(entry, string, out);
|
|
500
646
|
if( i < nr_elems-1 ){
|
|
501
647
|
out = pg_rb_str_ensure_capa( string, 1, out, NULL );
|
|
@@ -510,27 +656,34 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
|
|
|
510
656
|
*
|
|
511
657
|
* This is the encoder class for PostgreSQL identifiers.
|
|
512
658
|
*
|
|
513
|
-
* An Array value can be used for "schema.table.column"
|
|
659
|
+
* An Array value can be used for identifiers of the kind "schema.table.column".
|
|
660
|
+
* This ensures that each element is properly quoted:
|
|
514
661
|
* PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
|
|
515
662
|
* => '"schema"."table"."column"'
|
|
516
663
|
*
|
|
517
664
|
* This encoder can also be used per PG::Connection#quote_ident .
|
|
518
665
|
*/
|
|
519
666
|
int
|
|
520
|
-
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)
|
|
521
668
|
{
|
|
669
|
+
VALUE out_str;
|
|
522
670
|
UNUSED( this );
|
|
523
671
|
if( TYPE(value) == T_ARRAY){
|
|
524
|
-
|
|
525
|
-
out = RSTRING_PTR(
|
|
526
|
-
out = pg_text_enc_array_identifier(value,
|
|
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);
|
|
527
675
|
} else {
|
|
528
676
|
StringValue(value);
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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);
|
|
532
683
|
}
|
|
533
|
-
rb_str_set_len(
|
|
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;
|
|
534
687
|
return -1;
|
|
535
688
|
}
|
|
536
689
|
|
|
@@ -572,18 +725,26 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
572
725
|
*
|
|
573
726
|
* This is the encoder class for PostgreSQL literals.
|
|
574
727
|
*
|
|
575
|
-
* A literal is quoted and escaped by the
|
|
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.
|
|
576
735
|
*
|
|
577
736
|
*/
|
|
578
737
|
static int
|
|
579
|
-
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)
|
|
580
739
|
{
|
|
581
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);
|
|
582
743
|
|
|
583
|
-
|
|
584
|
-
out =
|
|
585
|
-
|
|
586
|
-
|
|
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;
|
|
587
748
|
return -1;
|
|
588
749
|
}
|
|
589
750
|
|
|
@@ -594,7 +755,7 @@ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *inte
|
|
|
594
755
|
*
|
|
595
756
|
*/
|
|
596
757
|
static int
|
|
597
|
-
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)
|
|
598
759
|
{
|
|
599
760
|
int strlen;
|
|
600
761
|
VALUE subint;
|
|
@@ -603,13 +764,13 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
603
764
|
|
|
604
765
|
if(out){
|
|
605
766
|
/* Second encoder pass, if required */
|
|
606
|
-
strlen = enc_func(this->elem, value, out, intermediate);
|
|
767
|
+
strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
|
|
607
768
|
base64_encode( out, out, strlen );
|
|
608
769
|
|
|
609
770
|
return BASE64_ENCODED_SIZE(strlen);
|
|
610
771
|
} else {
|
|
611
772
|
/* First encoder pass */
|
|
612
|
-
strlen = enc_func(this->elem, value, NULL, &subint);
|
|
773
|
+
strlen = enc_func(this->elem, value, NULL, &subint, enc_idx);
|
|
613
774
|
|
|
614
775
|
if( strlen == -1 ){
|
|
615
776
|
/* Encoded string is returned in subint */
|
|
@@ -617,6 +778,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
617
778
|
|
|
618
779
|
strlen = RSTRING_LENINT(subint);
|
|
619
780
|
out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
|
|
781
|
+
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
620
782
|
|
|
621
783
|
base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
|
|
622
784
|
*intermediate = out_str;
|
|
@@ -632,13 +794,15 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
632
794
|
|
|
633
795
|
|
|
634
796
|
void
|
|
635
|
-
init_pg_text_encoder()
|
|
797
|
+
init_pg_text_encoder(void)
|
|
636
798
|
{
|
|
637
799
|
s_id_encode = rb_intern("encode");
|
|
638
800
|
s_id_to_i = rb_intern("to_i");
|
|
801
|
+
s_id_to_s = rb_intern("to_s");
|
|
639
802
|
|
|
640
803
|
/* This module encapsulates all encoder classes with text output format */
|
|
641
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);
|
|
642
806
|
|
|
643
807
|
/* Make RDoc aware of the encoder classes... */
|
|
644
808
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
|