pg 0.18.2 → 1.4.5
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 +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +86 -0
- data/.github/workflows/source-gem.yml +131 -0
- data/.gitignore +13 -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.rdoc +480 -4
- data/Manifest.txt +8 -21
- data/README-Windows.rdoc +17 -28
- data/README.ja.rdoc +1 -2
- data/README.rdoc +92 -20
- data/Rakefile +33 -133
- data/Rakefile.cross +89 -67
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/ext/errorcodes.def +113 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +36 -2
- data/ext/extconf.rb +120 -54
- data/ext/gvl_wrappers.c +8 -0
- data/ext/gvl_wrappers.h +44 -33
- data/ext/pg.c +216 -172
- data/ext/pg.h +93 -98
- data/ext/pg_binary_decoder.c +85 -16
- data/ext/pg_binary_encoder.c +25 -22
- data/ext/pg_coder.c +176 -40
- data/ext/pg_connection.c +1735 -1138
- data/ext/pg_copy_coder.c +95 -28
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +521 -0
- data/ext/pg_result.c +642 -221
- data/ext/pg_text_decoder.c +609 -41
- data/ext/pg_text_encoder.c +254 -100
- data/ext/pg_tuple.c +569 -0
- data/ext/pg_type_map.c +62 -22
- data/ext/pg_type_map_all_strings.c +20 -6
- data/ext/pg_type_map_by_class.c +55 -25
- data/ext/pg_type_map_by_column.c +81 -42
- data/ext/pg_type_map_by_mri_type.c +49 -20
- data/ext/pg_type_map_by_oid.c +56 -26
- data/ext/pg_type_map_in_ruby.c +52 -21
- 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 +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +301 -0
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +24 -3
- data/lib/pg/connection.rb +711 -64
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +9 -2
- data/lib/pg/result.rb +24 -7
- data/lib/pg/text_decoder.rb +27 -23
- data/lib/pg/text_encoder.rb +40 -8
- 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 +61 -36
- 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 +32 -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.tar.gz.sig +0 -0
- metadata +74 -216
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -5545
- data/lib/pg/basic_type_mapping.rb +0 -399
- 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 -1535
- 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 -688
- 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,15 +41,20 @@
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
#include "pg.h"
|
|
43
|
-
#include "
|
|
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
|
-
|
|
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
|
|
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
|
|
168
|
+
long long sll = NUM2LL(*intermediate);
|
|
169
|
+
unsigned long long ll;
|
|
140
170
|
|
|
141
|
-
if (
|
|
142
|
-
/*
|
|
143
|
-
* 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.
|
|
144
174
|
*/
|
|
145
|
-
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
|
-
|
|
183
|
-
|
|
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,128 @@ 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
|
-
|
|
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
|
|
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
|
+
|
|
249
375
|
static const char hextab[] = {
|
|
250
376
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
|
251
377
|
};
|
|
@@ -253,8 +379,7 @@ static const char hextab[] = {
|
|
|
253
379
|
/*
|
|
254
380
|
* Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
|
|
255
381
|
*
|
|
256
|
-
* This is an encoder class for the PostgreSQL bytea type
|
|
257
|
-
* or newer.
|
|
382
|
+
* This is an encoder class for the PostgreSQL +bytea+ type.
|
|
258
383
|
*
|
|
259
384
|
* The binary String is converted to hexadecimal representation for transmission
|
|
260
385
|
* in text format. For query bind parameters it is recommended to use
|
|
@@ -263,7 +388,7 @@ static const char hextab[] = {
|
|
|
263
388
|
*
|
|
264
389
|
*/
|
|
265
390
|
static int
|
|
266
|
-
pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
391
|
+
pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
267
392
|
{
|
|
268
393
|
if(out){
|
|
269
394
|
size_t strlen = RSTRING_LEN(*intermediate);
|
|
@@ -278,11 +403,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
|
278
403
|
*optr++ = hextab[c >> 4];
|
|
279
404
|
*optr++ = hextab[c & 0xf];
|
|
280
405
|
}
|
|
281
|
-
return optr - out;
|
|
406
|
+
return (int)(optr - out);
|
|
282
407
|
}else{
|
|
283
408
|
*intermediate = rb_obj_as_string(value);
|
|
284
409
|
/* The output starts with "\x" and each character is converted to hex. */
|
|
285
|
-
return 2 +
|
|
410
|
+
return 2 + RSTRING_LENINT(*intermediate) * 2;
|
|
286
411
|
}
|
|
287
412
|
}
|
|
288
413
|
|
|
@@ -342,13 +467,13 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
342
467
|
}
|
|
343
468
|
|
|
344
469
|
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)
|
|
470
|
+
quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int with_quote, t_quote_func quote_buffer, void *func_data, int enc_idx)
|
|
346
471
|
{
|
|
347
472
|
int strlen;
|
|
348
473
|
VALUE subint;
|
|
349
474
|
t_pg_coder_enc_func enc_func = pg_coder_enc_func(this);
|
|
350
475
|
|
|
351
|
-
strlen = enc_func(this, value, NULL, &subint);
|
|
476
|
+
strlen = enc_func(this, value, NULL, &subint, enc_idx);
|
|
352
477
|
|
|
353
478
|
if( strlen == -1 ){
|
|
354
479
|
/* we can directly use String value in subint */
|
|
@@ -374,20 +499,20 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
|
|
|
374
499
|
current_out = pg_rb_str_ensure_capa( string, 2 * strlen + 2, current_out, NULL );
|
|
375
500
|
|
|
376
501
|
/* Place the unescaped string at current output position. */
|
|
377
|
-
strlen = enc_func(this, value, current_out, &subint);
|
|
502
|
+
strlen = enc_func(this, value, current_out, &subint, enc_idx);
|
|
378
503
|
|
|
379
504
|
current_out += quote_buffer( func_data, current_out, strlen, current_out );
|
|
380
505
|
}else{
|
|
381
506
|
/* size of the unquoted string */
|
|
382
507
|
current_out = pg_rb_str_ensure_capa( string, strlen, current_out, NULL );
|
|
383
|
-
current_out += enc_func(this, value, current_out, &subint);
|
|
508
|
+
current_out += enc_func(this, value, current_out, &subint, enc_idx);
|
|
384
509
|
}
|
|
385
510
|
}
|
|
386
511
|
return current_out;
|
|
387
512
|
}
|
|
388
513
|
|
|
389
514
|
static char *
|
|
390
|
-
write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote)
|
|
515
|
+
write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
|
|
391
516
|
{
|
|
392
517
|
int i;
|
|
393
518
|
|
|
@@ -405,7 +530,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
405
530
|
|
|
406
531
|
switch(TYPE(entry)){
|
|
407
532
|
case T_ARRAY:
|
|
408
|
-
current_out = write_array(this, entry, current_out, string, quote);
|
|
533
|
+
current_out = write_array(this, entry, current_out, string, quote, enc_idx);
|
|
409
534
|
break;
|
|
410
535
|
case T_NIL:
|
|
411
536
|
current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
|
|
@@ -415,7 +540,7 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
415
540
|
*current_out++ = 'L';
|
|
416
541
|
break;
|
|
417
542
|
default:
|
|
418
|
-
current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this );
|
|
543
|
+
current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
|
|
419
544
|
}
|
|
420
545
|
}
|
|
421
546
|
current_out = pg_rb_str_ensure_capa( string, 1, current_out, NULL );
|
|
@@ -437,41 +562,42 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
437
562
|
*
|
|
438
563
|
*/
|
|
439
564
|
static int
|
|
440
|
-
pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
565
|
+
pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
441
566
|
{
|
|
442
567
|
char *end_ptr;
|
|
443
568
|
t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
|
|
444
569
|
|
|
445
570
|
if( TYPE(value) == T_ARRAY){
|
|
446
|
-
|
|
571
|
+
VALUE out_str = rb_str_new(NULL, 0);
|
|
572
|
+
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
447
573
|
|
|
448
|
-
end_ptr = write_array(this, value, RSTRING_PTR(
|
|
574
|
+
end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
|
|
449
575
|
|
|
450
|
-
rb_str_set_len(
|
|
576
|
+
rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
|
|
577
|
+
*intermediate = out_str;
|
|
451
578
|
|
|
452
579
|
return -1;
|
|
453
580
|
} else {
|
|
454
|
-
return pg_coder_enc_to_s( conv, value, out, intermediate );
|
|
581
|
+
return pg_coder_enc_to_s( conv, value, out, intermediate, enc_idx );
|
|
455
582
|
}
|
|
456
583
|
}
|
|
457
584
|
|
|
458
585
|
static char *
|
|
459
586
|
quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
|
460
587
|
char *p_in = RSTRING_PTR(value);
|
|
461
|
-
char *ptr1;
|
|
462
588
|
size_t strlen = RSTRING_LEN(value);
|
|
589
|
+
char *p_inend = p_in + strlen;
|
|
463
590
|
char *end_capa = current_out;
|
|
464
591
|
|
|
465
592
|
PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
|
|
466
593
|
*current_out++ = '"';
|
|
467
|
-
for(
|
|
468
|
-
char c = *
|
|
594
|
+
for(; p_in != p_inend; p_in++) {
|
|
595
|
+
char c = *p_in;
|
|
469
596
|
if (c == '"'){
|
|
470
|
-
|
|
471
|
-
PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
|
|
597
|
+
PG_RB_STR_ENSURE_CAPA( out_string, p_inend - p_in + 2, current_out, end_capa );
|
|
472
598
|
*current_out++ = '"';
|
|
473
599
|
} else if (c == 0){
|
|
474
|
-
|
|
600
|
+
rb_raise(rb_eArgError, "string contains null byte");
|
|
475
601
|
}
|
|
476
602
|
*current_out++ = c;
|
|
477
603
|
}
|
|
@@ -482,10 +608,10 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
|
|
482
608
|
}
|
|
483
609
|
|
|
484
610
|
static char *
|
|
485
|
-
pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
|
|
611
|
+
pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
|
|
486
612
|
{
|
|
487
|
-
|
|
488
|
-
|
|
613
|
+
long i;
|
|
614
|
+
long nr_elems;
|
|
489
615
|
|
|
490
616
|
Check_Type(value, T_ARRAY);
|
|
491
617
|
nr_elems = RARRAY_LEN(value);
|
|
@@ -493,6 +619,10 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
|
|
|
493
619
|
for( i=0; i<nr_elems; i++){
|
|
494
620
|
VALUE entry = rb_ary_entry(value, i);
|
|
495
621
|
|
|
622
|
+
StringValue(entry);
|
|
623
|
+
if( ENCODING_GET(entry) != enc_idx ){
|
|
624
|
+
entry = rb_str_export_to_enc(entry, rb_enc_from_index(enc_idx));
|
|
625
|
+
}
|
|
496
626
|
out = quote_identifier(entry, string, out);
|
|
497
627
|
if( i < nr_elems-1 ){
|
|
498
628
|
out = pg_rb_str_ensure_capa( string, 1, out, NULL );
|
|
@@ -507,27 +637,34 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out)
|
|
|
507
637
|
*
|
|
508
638
|
* This is the encoder class for PostgreSQL identifiers.
|
|
509
639
|
*
|
|
510
|
-
* An Array value can be used for "schema.table.column"
|
|
640
|
+
* An Array value can be used for identifiers of the kind "schema.table.column".
|
|
641
|
+
* This ensures that each element is properly quoted:
|
|
511
642
|
* PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
|
|
512
643
|
* => '"schema"."table"."column"'
|
|
513
644
|
*
|
|
514
645
|
* This encoder can also be used per PG::Connection#quote_ident .
|
|
515
646
|
*/
|
|
516
647
|
int
|
|
517
|
-
pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate)
|
|
648
|
+
pg_text_enc_identifier(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
518
649
|
{
|
|
650
|
+
VALUE out_str;
|
|
519
651
|
UNUSED( this );
|
|
520
652
|
if( TYPE(value) == T_ARRAY){
|
|
521
|
-
|
|
522
|
-
out = RSTRING_PTR(
|
|
523
|
-
out = pg_text_enc_array_identifier(value,
|
|
653
|
+
out_str = rb_str_new(NULL, 0);
|
|
654
|
+
out = RSTRING_PTR(out_str);
|
|
655
|
+
out = pg_text_enc_array_identifier(value, out_str, out, enc_idx);
|
|
524
656
|
} else {
|
|
525
657
|
StringValue(value);
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
658
|
+
if( ENCODING_GET(value) != enc_idx ){
|
|
659
|
+
value = rb_str_export_to_enc(value, rb_enc_from_index(enc_idx));
|
|
660
|
+
}
|
|
661
|
+
out_str = rb_str_new(NULL, RSTRING_LEN(value) + 2);
|
|
662
|
+
out = RSTRING_PTR(out_str);
|
|
663
|
+
out = quote_identifier(value, out_str, out);
|
|
529
664
|
}
|
|
530
|
-
rb_str_set_len(
|
|
665
|
+
rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
|
|
666
|
+
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
667
|
+
*intermediate = out_str;
|
|
531
668
|
return -1;
|
|
532
669
|
}
|
|
533
670
|
|
|
@@ -569,18 +706,26 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
569
706
|
*
|
|
570
707
|
* This is the encoder class for PostgreSQL literals.
|
|
571
708
|
*
|
|
572
|
-
* A literal is quoted and escaped by the
|
|
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.
|
|
573
716
|
*
|
|
574
717
|
*/
|
|
575
718
|
static int
|
|
576
|
-
pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
719
|
+
pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
577
720
|
{
|
|
578
721
|
t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
|
|
722
|
+
VALUE out_str = rb_str_new(NULL, 0);
|
|
723
|
+
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
579
724
|
|
|
580
|
-
|
|
581
|
-
out =
|
|
582
|
-
|
|
583
|
-
|
|
725
|
+
out = RSTRING_PTR(out_str);
|
|
726
|
+
out = quote_string(this->elem, value, out_str, out, this->needs_quotation, quote_literal_buffer, this, enc_idx);
|
|
727
|
+
rb_str_set_len( out_str, out - RSTRING_PTR(out_str) );
|
|
728
|
+
*intermediate = out_str;
|
|
584
729
|
return -1;
|
|
585
730
|
}
|
|
586
731
|
|
|
@@ -591,7 +736,7 @@ pg_text_enc_quoted_literal(t_pg_coder *conv, VALUE value, char *out, VALUE *inte
|
|
|
591
736
|
*
|
|
592
737
|
*/
|
|
593
738
|
static int
|
|
594
|
-
pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
739
|
+
pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
595
740
|
{
|
|
596
741
|
int strlen;
|
|
597
742
|
VALUE subint;
|
|
@@ -600,13 +745,13 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
600
745
|
|
|
601
746
|
if(out){
|
|
602
747
|
/* Second encoder pass, if required */
|
|
603
|
-
strlen = enc_func(this->elem, value, out, intermediate);
|
|
748
|
+
strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
|
|
604
749
|
base64_encode( out, out, strlen );
|
|
605
750
|
|
|
606
751
|
return BASE64_ENCODED_SIZE(strlen);
|
|
607
752
|
} else {
|
|
608
753
|
/* First encoder pass */
|
|
609
|
-
strlen = enc_func(this->elem, value, NULL, &subint);
|
|
754
|
+
strlen = enc_func(this->elem, value, NULL, &subint, enc_idx);
|
|
610
755
|
|
|
611
756
|
if( strlen == -1 ){
|
|
612
757
|
/* Encoded string is returned in subint */
|
|
@@ -614,6 +759,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
614
759
|
|
|
615
760
|
strlen = RSTRING_LENINT(subint);
|
|
616
761
|
out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
|
|
762
|
+
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
617
763
|
|
|
618
764
|
base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
|
|
619
765
|
*intermediate = out_str;
|
|
@@ -629,10 +775,16 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
629
775
|
|
|
630
776
|
|
|
631
777
|
void
|
|
632
|
-
init_pg_text_encoder()
|
|
778
|
+
init_pg_text_encoder(void)
|
|
633
779
|
{
|
|
634
780
|
s_id_encode = rb_intern("encode");
|
|
635
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
|
+
|
|
636
788
|
|
|
637
789
|
/* This module encapsulates all encoder classes with text output format */
|
|
638
790
|
rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
|
|
@@ -644,6 +796,8 @@ init_pg_text_encoder()
|
|
|
644
796
|
pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
|
645
797
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
|
|
646
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 );
|
|
647
801
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
|
|
648
802
|
pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
|
649
803
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
|