pg 1.0.0 → 1.5.9
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Gemfile +20 -0
- data/History.md +932 -0
- data/Manifest.txt +8 -3
- data/README-Windows.rdoc +4 -4
- data/README.ja.md +300 -0
- data/README.md +286 -0
- data/Rakefile +41 -138
- data/Rakefile.cross +71 -66
- data/certs/ged.pem +24 -0
- data/certs/kanis@comcard.de.pem +20 -0
- data/certs/larskanis-2022.pem +26 -0
- data/certs/larskanis-2023.pem +24 -0
- data/certs/larskanis-2024.pem +24 -0
- data/ext/errorcodes.def +84 -5
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +23 -6
- data/ext/extconf.rb +109 -25
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +213 -155
- data/ext/pg.h +89 -23
- data/ext/pg_binary_decoder.c +164 -16
- data/ext/pg_binary_encoder.c +238 -13
- data/ext/pg_coder.c +159 -35
- data/ext/pg_connection.c +1584 -967
- data/ext/pg_copy_coder.c +373 -43
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +522 -0
- data/ext/pg_result.c +710 -217
- data/ext/pg_text_decoder.c +630 -43
- data/ext/pg_text_encoder.c +222 -72
- data/ext/pg_tuple.c +572 -0
- data/ext/pg_type_map.c +45 -11
- 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 +202 -0
- data/lib/pg/basic_type_map_for_results.rb +104 -0
- data/lib/pg/basic_type_registry.rb +311 -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 +769 -70
- data/lib/pg/exceptions.rb +22 -2
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder/date.rb +21 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +17 -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 +13 -0
- data/lib/pg/text_encoder/inet.rb +31 -0
- data/lib/pg/text_encoder/json.rb +17 -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 -39
- 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 +36 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +102 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data.tar.gz.sig +0 -0
- metadata +138 -223
- metadata.gz.sig +0 -0
- data/.gemtest +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +0 -422
- data/README.ja.rdoc +0 -14
- data/README.rdoc +0 -167
- data/lib/pg/basic_type_mapping.rb +0 -426
- data/lib/pg/constants.rb +0 -11
- data/lib/pg/text_decoder.rb +0 -51
- data/lib/pg/text_encoder.rb +0 -35
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -348
- data/spec/pg/basic_type_mapping_spec.rb +0 -305
- data/spec/pg/connection_spec.rb +0 -1719
- data/spec/pg/result_spec.rb +0 -456
- 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 -777
- 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
|
|
@@ -41,7 +41,7 @@
|
|
41
41
|
|
42
42
|
|
43
43
|
#include "pg.h"
|
44
|
-
#include "
|
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
|
|
@@ -116,6 +119,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
116
119
|
int
|
117
120
|
pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
118
121
|
{
|
122
|
+
/* Attention:
|
123
|
+
* In contrast to all other encoders, the "this" pointer of this encoder can be NULL.
|
124
|
+
* This is because it is used as a fall-back if no encoder is defined.
|
125
|
+
*/
|
119
126
|
VALUE str = rb_obj_as_string(value);
|
120
127
|
if( ENCODING_GET(str) == enc_idx ){
|
121
128
|
*intermediate = str;
|
@@ -125,11 +132,29 @@ pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate,
|
|
125
132
|
return -1;
|
126
133
|
}
|
127
134
|
|
135
|
+
static int
|
136
|
+
count_leading_zero_bits(unsigned long long x)
|
137
|
+
{
|
138
|
+
#if defined(__GNUC__) || defined(__clang__)
|
139
|
+
return __builtin_clzll(x);
|
140
|
+
#elif defined(_MSC_VER)
|
141
|
+
DWORD r = 0;
|
142
|
+
_BitScanForward64(&r, x);
|
143
|
+
return (int)r;
|
144
|
+
#else
|
145
|
+
unsigned int a;
|
146
|
+
for(a=0; a < sizeof(unsigned long long) * 8; a++){
|
147
|
+
if( x & (1 << (sizeof(unsigned long long) * 8 - 1))) return a;
|
148
|
+
x <<= 1;
|
149
|
+
}
|
150
|
+
return a;
|
151
|
+
#endif
|
152
|
+
}
|
128
153
|
|
129
154
|
/*
|
130
155
|
* Document-class: PG::TextEncoder::Integer < PG::SimpleEncoder
|
131
156
|
*
|
132
|
-
* This is the encoder class for the PostgreSQL
|
157
|
+
* This is the encoder class for the PostgreSQL integer types.
|
133
158
|
*
|
134
159
|
* Non-Integer values are expected to have method +to_i+ defined.
|
135
160
|
*
|
@@ -144,20 +169,23 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
144
169
|
char *start = out;
|
145
170
|
int len;
|
146
171
|
int neg = 0;
|
147
|
-
long long
|
172
|
+
long long sll = NUM2LL(*intermediate);
|
173
|
+
unsigned long long ll;
|
148
174
|
|
149
|
-
if (
|
150
|
-
/*
|
151
|
-
* as a positive integer,
|
175
|
+
if (sll < 0) {
|
176
|
+
/* Avoid problems with the most negative integer not being representable
|
177
|
+
* as a positive integer, by using unsigned long long for encoding.
|
152
178
|
*/
|
153
|
-
ll = -
|
179
|
+
ll = -sll;
|
154
180
|
neg = 1;
|
181
|
+
} else {
|
182
|
+
ll = sll;
|
155
183
|
}
|
156
184
|
|
157
185
|
/* Compute the result string backwards. */
|
158
186
|
do {
|
159
|
-
long long remainder;
|
160
|
-
long long oldval = ll;
|
187
|
+
unsigned long long remainder;
|
188
|
+
unsigned long long oldval = ll;
|
161
189
|
|
162
190
|
ll /= 10;
|
163
191
|
remainder = oldval - ll * 10;
|
@@ -167,7 +195,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
167
195
|
if (neg)
|
168
196
|
*out++ = '-';
|
169
197
|
|
170
|
-
len = out - start;
|
198
|
+
len = (int)(out - start);
|
171
199
|
|
172
200
|
/* Reverse string. */
|
173
201
|
out--;
|
@@ -184,45 +212,17 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
184
212
|
}else{
|
185
213
|
*intermediate = pg_obj_to_i(value);
|
186
214
|
if(TYPE(*intermediate) == T_FIXNUM){
|
187
|
-
int len;
|
188
215
|
long long sll = NUM2LL(*intermediate);
|
189
|
-
long long ll = sll < 0 ? -sll : sll;
|
190
|
-
|
191
|
-
|
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;
|
216
|
+
unsigned long long ll = sll < 0 ? -sll : sll;
|
217
|
+
int len = (sizeof(unsigned long long) * 8 - count_leading_zero_bits(ll)) / 3;
|
218
|
+
return sll < 0 ? len+2 : len+1;
|
220
219
|
}else{
|
221
220
|
return pg_coder_enc_to_s(this, *intermediate, NULL, intermediate, enc_idx);
|
222
221
|
}
|
223
222
|
}
|
224
223
|
}
|
225
224
|
|
225
|
+
#define MAX_DOUBLE_DIGITS 16
|
226
226
|
|
227
227
|
/*
|
228
228
|
* Document-class: PG::TextEncoder::Float < PG::SimpleEncoder
|
@@ -235,6 +235,12 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
235
235
|
{
|
236
236
|
if(out){
|
237
237
|
double dvalue = NUM2DBL(value);
|
238
|
+
int len = 0;
|
239
|
+
int neg = 0;
|
240
|
+
int exp2i, exp10i, i;
|
241
|
+
unsigned long long ll, remainder, oldval;
|
242
|
+
VALUE intermediate;
|
243
|
+
|
238
244
|
/* Cast to the same strings as value.to_s . */
|
239
245
|
if( isinf(dvalue) ){
|
240
246
|
if( dvalue < 0 ){
|
@@ -248,12 +254,145 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
248
254
|
memcpy( out, "NaN", 3);
|
249
255
|
return 3;
|
250
256
|
}
|
251
|
-
|
257
|
+
|
258
|
+
/*
|
259
|
+
* The following computation is roughly a conversion kind of
|
260
|
+
* sprintf( out, "%.16E", dvalue);
|
261
|
+
*/
|
262
|
+
|
263
|
+
/* write the algebraic sign */
|
264
|
+
if( dvalue < 0 ) {
|
265
|
+
dvalue = -dvalue;
|
266
|
+
*out++ = '-';
|
267
|
+
neg++;
|
268
|
+
}
|
269
|
+
|
270
|
+
/* retrieve the power of 2 exponent */
|
271
|
+
frexp(dvalue, &exp2i);
|
272
|
+
/* compute the power of 10 exponent */
|
273
|
+
exp10i = (int)floor(exp2i * 0.30102999566398114); /* Math.log(2)/Math.log(10) */
|
274
|
+
/* move the decimal point, so that we get an integer of MAX_DOUBLE_DIGITS decimal digits */
|
275
|
+
ll = (unsigned long long)(dvalue * pow(10, MAX_DOUBLE_DIGITS - 1 - exp10i) + 0.5);
|
276
|
+
|
277
|
+
/* avoid leading zeros due to inaccuracy of deriving exp10i from exp2i */
|
278
|
+
/* otherwise we would print "09.0" instead of "9.0" */
|
279
|
+
if( ll < 1000000000000000 ){ /* pow(10, MAX_DOUBLE_DIGITS-1) */
|
280
|
+
exp10i--;
|
281
|
+
ll *= 10;
|
282
|
+
}
|
283
|
+
|
284
|
+
if( exp10i <= -5 || exp10i >= 15 ) {
|
285
|
+
/* Write the float in exponent format (1.23e45) */
|
286
|
+
|
287
|
+
/* write fraction digits from right to left */
|
288
|
+
for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
|
289
|
+
oldval = ll;
|
290
|
+
ll /= 10;
|
291
|
+
remainder = oldval - ll * 10;
|
292
|
+
/* omit trailing zeros */
|
293
|
+
if(remainder != 0 || len ) {
|
294
|
+
out[i] = '0' + remainder;
|
295
|
+
len++;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
/* write decimal point */
|
300
|
+
if( len ){
|
301
|
+
out[1] = '.';
|
302
|
+
len++;
|
303
|
+
}
|
304
|
+
|
305
|
+
/* write remaining single digit left to the decimal point */
|
306
|
+
oldval = ll;
|
307
|
+
ll /= 10;
|
308
|
+
remainder = oldval - ll * 10;
|
309
|
+
out[0] = '0' + remainder;
|
310
|
+
len++;
|
311
|
+
|
312
|
+
/* write exponent */
|
313
|
+
out[len++] = 'e';
|
314
|
+
intermediate = INT2NUM(exp10i);
|
315
|
+
|
316
|
+
return neg + len + pg_text_enc_integer(conv, Qnil, out + len, &intermediate, enc_idx);
|
317
|
+
} else {
|
318
|
+
/* write the float in non exponent format (0.001234 or 123450.0) */
|
319
|
+
|
320
|
+
/* write digits from right to left */
|
321
|
+
int lz = exp10i < 0 ? 0 : exp10i;
|
322
|
+
for( i = MAX_DOUBLE_DIGITS - (exp10i < 0 ? exp10i : 0); i >= 0; i-- ){
|
323
|
+
oldval = ll;
|
324
|
+
ll /= 10;
|
325
|
+
remainder = oldval - ll * 10;
|
326
|
+
/* write decimal point */
|
327
|
+
if( i - 1 == lz ){
|
328
|
+
out[i--] = '.';
|
329
|
+
len++;
|
330
|
+
}
|
331
|
+
/* if possible then omit trailing zeros */
|
332
|
+
if(remainder != 0 || len || i - 2 == lz) {
|
333
|
+
out[i] = '0' + remainder;
|
334
|
+
len++;
|
335
|
+
}
|
336
|
+
}
|
337
|
+
return neg + len;
|
338
|
+
}
|
252
339
|
}else{
|
253
|
-
return
|
340
|
+
return 1 /*sign*/ + MAX_DOUBLE_DIGITS + 1 /*dot*/ + 1 /*e*/ + 1 /*exp sign*/ + 3 /*exp digits*/;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
|
345
|
+
/*
|
346
|
+
* Document-class: PG::TextEncoder::Numeric < PG::SimpleEncoder
|
347
|
+
*
|
348
|
+
* This is the encoder class for the PostgreSQL numeric types.
|
349
|
+
*
|
350
|
+
* It converts Integer, Float and BigDecimal objects.
|
351
|
+
* All other objects are expected to respond to +to_s+.
|
352
|
+
*
|
353
|
+
* As soon as this class is used, it requires the 'bigdecimal' gem.
|
354
|
+
*/
|
355
|
+
static int
|
356
|
+
pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
357
|
+
{
|
358
|
+
switch(TYPE(value)){
|
359
|
+
case T_FIXNUM:
|
360
|
+
case T_BIGNUM:
|
361
|
+
return pg_text_enc_integer(this, value, out, intermediate, enc_idx);
|
362
|
+
case T_FLOAT:
|
363
|
+
return pg_text_enc_float(this, value, out, intermediate, enc_idx);
|
364
|
+
default:
|
365
|
+
if(out){ /* second pass */
|
366
|
+
rb_bug("unexpected value type: %d", TYPE(value));
|
367
|
+
} else { /* first pass */
|
368
|
+
if( rb_obj_is_kind_of(value, s_cBigDecimal) ){
|
369
|
+
/* value.to_s('F') */
|
370
|
+
*intermediate = rb_funcall(value, s_id_to_s, 1, s_str_F);
|
371
|
+
return -1; /* no second pass */
|
372
|
+
} else {
|
373
|
+
return pg_coder_enc_to_s(this, value, NULL, intermediate, enc_idx);
|
374
|
+
/* no second pass */
|
375
|
+
}
|
376
|
+
}
|
254
377
|
}
|
255
378
|
}
|
256
379
|
|
380
|
+
/* called per autoload when TextEncoder::Numeric is used */
|
381
|
+
static VALUE
|
382
|
+
init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
|
383
|
+
{
|
384
|
+
s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
|
385
|
+
rb_global_variable(&s_str_F);
|
386
|
+
rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
|
387
|
+
s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
388
|
+
|
389
|
+
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
|
390
|
+
pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
391
|
+
|
392
|
+
return Qnil;
|
393
|
+
}
|
394
|
+
|
395
|
+
|
257
396
|
static const char hextab[] = {
|
258
397
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
259
398
|
};
|
@@ -261,13 +400,16 @@ static const char hextab[] = {
|
|
261
400
|
/*
|
262
401
|
* Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
|
263
402
|
*
|
264
|
-
* This is an encoder class for the PostgreSQL bytea type
|
265
|
-
* or newer.
|
403
|
+
* This is an encoder class for the PostgreSQL +bytea+ type.
|
266
404
|
*
|
267
405
|
* The binary String is converted to hexadecimal representation for transmission
|
268
406
|
* in text format. For query bind parameters it is recommended to use
|
269
|
-
* PG::BinaryEncoder::Bytea
|
270
|
-
* CPU usage.
|
407
|
+
* PG::BinaryEncoder::Bytea or the hash form <tt>{value: binary_string, format: 1}</tt> instead,
|
408
|
+
* in order to decrease network traffic and CPU usage.
|
409
|
+
* See PG::Connection#exec_params for using the hash form.
|
410
|
+
*
|
411
|
+
* This encoder is particular useful when PG::TextEncoder::CopyRow is used with the COPY command.
|
412
|
+
* 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
413
|
*
|
272
414
|
*/
|
273
415
|
static int
|
@@ -286,11 +428,11 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
286
428
|
*optr++ = hextab[c >> 4];
|
287
429
|
*optr++ = hextab[c & 0xf];
|
288
430
|
}
|
289
|
-
return optr - out;
|
431
|
+
return (int)(optr - out);
|
290
432
|
}else{
|
291
433
|
*intermediate = rb_obj_as_string(value);
|
292
434
|
/* The output starts with "\x" and each character is converted to hex. */
|
293
|
-
return 2 +
|
435
|
+
return 2 + RSTRING_LENINT(*intermediate) * 2;
|
294
436
|
}
|
295
437
|
}
|
296
438
|
|
@@ -301,7 +443,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
301
443
|
t_pg_composite_coder *this = _this;
|
302
444
|
char *ptr1;
|
303
445
|
char *ptr2;
|
304
|
-
int
|
446
|
+
int backslashes = 0;
|
305
447
|
int needquote;
|
306
448
|
|
307
449
|
/* count data plus backslashes; detect chars needing quotes */
|
@@ -318,7 +460,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
318
460
|
|
319
461
|
if (ch == '"' || ch == '\\'){
|
320
462
|
needquote = 1;
|
321
|
-
|
463
|
+
backslashes++;
|
322
464
|
} else if (ch == '{' || ch == '}' || ch == this->delimiter ||
|
323
465
|
ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
|
324
466
|
needquote = 1;
|
@@ -327,12 +469,12 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
327
469
|
|
328
470
|
if( needquote ){
|
329
471
|
ptr1 = p_in + strlen;
|
330
|
-
ptr2 = p_out + strlen +
|
472
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
331
473
|
/* Write end quote */
|
332
474
|
*--ptr2 = '"';
|
333
475
|
|
334
476
|
/* Then store the escaped string on the final position, walking
|
335
|
-
* right to left, until all
|
477
|
+
* right to left, until all backslashes are placed. */
|
336
478
|
while( ptr1 != p_in ) {
|
337
479
|
*--ptr2 = *--ptr1;
|
338
480
|
if(*ptr2 == '"' || *ptr2 == '\\'){
|
@@ -341,7 +483,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
341
483
|
}
|
342
484
|
/* Write start quote */
|
343
485
|
*p_out = '"';
|
344
|
-
return strlen +
|
486
|
+
return strlen + backslashes + 2;
|
345
487
|
} else {
|
346
488
|
if( p_in != p_out )
|
347
489
|
memcpy( p_out, p_in, strlen );
|
@@ -468,20 +610,19 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
468
610
|
static char *
|
469
611
|
quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
470
612
|
char *p_in = RSTRING_PTR(value);
|
471
|
-
char *ptr1;
|
472
613
|
size_t strlen = RSTRING_LEN(value);
|
614
|
+
char *p_inend = p_in + strlen;
|
473
615
|
char *end_capa = current_out;
|
474
616
|
|
475
617
|
PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
|
476
618
|
*current_out++ = '"';
|
477
|
-
for(
|
478
|
-
char c = *
|
619
|
+
for(; p_in != p_inend; p_in++) {
|
620
|
+
char c = *p_in;
|
479
621
|
if (c == '"'){
|
480
|
-
|
481
|
-
PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
|
622
|
+
PG_RB_STR_ENSURE_CAPA( out_string, p_inend - p_in + 2, current_out, end_capa );
|
482
623
|
*current_out++ = '"';
|
483
624
|
} else if (c == 0){
|
484
|
-
|
625
|
+
rb_raise(rb_eArgError, "string contains null byte");
|
485
626
|
}
|
486
627
|
*current_out++ = c;
|
487
628
|
}
|
@@ -494,8 +635,8 @@ quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
|
494
635
|
static char *
|
495
636
|
pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
|
496
637
|
{
|
497
|
-
|
498
|
-
|
638
|
+
long i;
|
639
|
+
long nr_elems;
|
499
640
|
|
500
641
|
Check_Type(value, T_ARRAY);
|
501
642
|
nr_elems = RARRAY_LEN(value);
|
@@ -521,7 +662,8 @@ pg_text_enc_array_identifier(VALUE value, VALUE string, char *out, int enc_idx)
|
|
521
662
|
*
|
522
663
|
* This is the encoder class for PostgreSQL identifiers.
|
523
664
|
*
|
524
|
-
* An Array value can be used for "schema.table.column"
|
665
|
+
* An Array value can be used for identifiers of the kind "schema.table.column".
|
666
|
+
* This ensures that each element is properly quoted:
|
525
667
|
* PG::TextEncoder::Identifier.new.encode(['schema', 'table', 'column'])
|
526
668
|
* => '"schema"."table"."column"'
|
527
669
|
*
|
@@ -556,22 +698,22 @@ static int
|
|
556
698
|
quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
557
699
|
char *ptr1;
|
558
700
|
char *ptr2;
|
559
|
-
int
|
701
|
+
int backslashes = 0;
|
560
702
|
|
561
703
|
/* count required backlashs */
|
562
704
|
for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
|
563
705
|
if (*ptr1 == '\''){
|
564
|
-
|
706
|
+
backslashes++;
|
565
707
|
}
|
566
708
|
}
|
567
709
|
|
568
710
|
ptr1 = p_in + strlen;
|
569
|
-
ptr2 = p_out + strlen +
|
711
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
570
712
|
/* Write end quote */
|
571
713
|
*--ptr2 = '\'';
|
572
714
|
|
573
715
|
/* Then store the escaped string on the final position, walking
|
574
|
-
* right to left, until all
|
716
|
+
* right to left, until all backslashes are placed. */
|
575
717
|
while( ptr1 != p_in ) {
|
576
718
|
*--ptr2 = *--ptr1;
|
577
719
|
if(*ptr2 == '\''){
|
@@ -580,7 +722,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
580
722
|
}
|
581
723
|
/* Write start quote */
|
582
724
|
*p_out = '\'';
|
583
|
-
return strlen +
|
725
|
+
return strlen + backslashes + 2;
|
584
726
|
}
|
585
727
|
|
586
728
|
|
@@ -589,7 +731,13 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
589
731
|
*
|
590
732
|
* This is the encoder class for PostgreSQL literals.
|
591
733
|
*
|
592
|
-
* A literal is quoted and escaped by the
|
734
|
+
* A literal is quoted and escaped by the <tt>'</tt> character, so that it can be inserted into SQL queries.
|
735
|
+
* It works equal to PG::Connection#escape_literal, but integrates into the type cast system of ruby-pg.
|
736
|
+
*
|
737
|
+
* Both expressions have the same result:
|
738
|
+
* conn.escape_literal(PG::TextEncoder::Array.new.encode(["v1","v2"])) # => "'{v1,v2}'"
|
739
|
+
* PG::TextEncoder::QuotedLiteral.new(elements_type: PG::TextEncoder::Array.new).encode(["v1","v2"]) # => "'{v1,v2}'"
|
740
|
+
* While escape_literal requires a intermediate ruby string allocation, QuotedLiteral encodes the values directly to the result string.
|
593
741
|
*
|
594
742
|
*/
|
595
743
|
static int
|
@@ -652,13 +800,15 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
652
800
|
|
653
801
|
|
654
802
|
void
|
655
|
-
init_pg_text_encoder()
|
803
|
+
init_pg_text_encoder(void)
|
656
804
|
{
|
657
805
|
s_id_encode = rb_intern("encode");
|
658
806
|
s_id_to_i = rb_intern("to_i");
|
807
|
+
s_id_to_s = rb_intern("to_s");
|
659
808
|
|
660
809
|
/* This module encapsulates all encoder classes with text output format */
|
661
810
|
rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
|
811
|
+
rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
|
662
812
|
|
663
813
|
/* Make RDoc aware of the encoder classes... */
|
664
814
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
|