pg 1.5.4 → 1.6.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/{History.md → CHANGELOG.md} +132 -4
- data/Gemfile +15 -6
- data/README-Windows.rdoc +1 -1
- data/README.ja.md +4 -4
- data/README.md +66 -23
- data/Rakefile +81 -14
- data/certs/kanis@comcard.de.pem +20 -0
- data/certs/larskanis-2024.pem +24 -0
- data/ext/errorcodes.def +11 -3
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +9 -5
- data/ext/extconf.rb +192 -15
- data/ext/gvl_wrappers.c +13 -2
- data/ext/gvl_wrappers.h +33 -0
- data/ext/pg.c +17 -6
- data/ext/pg.h +15 -13
- data/ext/pg_binary_decoder.c +153 -1
- data/ext/pg_binary_encoder.c +213 -10
- data/ext/pg_cancel_connection.c +360 -0
- data/ext/pg_coder.c +54 -5
- data/ext/pg_connection.c +431 -169
- data/ext/pg_copy_coder.c +19 -15
- data/ext/pg_record_coder.c +7 -7
- data/ext/pg_result.c +106 -54
- data/ext/pg_text_decoder.c +5 -2
- data/ext/pg_text_encoder.c +39 -20
- data/ext/pg_tuple.c +8 -8
- data/ext/pg_type_map.c +4 -2
- data/ext/pg_type_map_all_strings.c +1 -1
- data/ext/pg_type_map_by_class.c +1 -1
- data/ext/pg_type_map_by_column.c +2 -1
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -1
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/pg_util.c +2 -2
- data/ext/pg_util.h +2 -2
- data/lib/pg/basic_type_map_for_queries.rb +15 -7
- data/lib/pg/basic_type_registry.rb +16 -4
- data/lib/pg/cancel_connection.rb +53 -0
- data/lib/pg/coder.rb +4 -2
- data/lib/pg/connection.rb +310 -167
- data/lib/pg/exceptions.rb +6 -0
- data/lib/pg/text_decoder/date.rb +3 -0
- data/lib/pg/text_decoder/json.rb +3 -0
- data/lib/pg/text_encoder/date.rb +1 -0
- data/lib/pg/text_encoder/inet.rb +3 -0
- data/lib/pg/text_encoder/json.rb +3 -0
- data/lib/pg/version.rb +2 -1
- data/lib/pg.rb +156 -120
- data/misc/glibc/Dockerfile +20 -0
- data/misc/glibc/docker-compose.yml +9 -0
- data/misc/glibc/glibc_spec.rb +5 -0
- data/misc/yugabyte/Dockerfile +9 -0
- data/misc/yugabyte/docker-compose.yml +28 -0
- data/misc/yugabyte/pg-test.rb +45 -0
- data/pg.gemspec +8 -4
- data/ports/patches/krb5/1.22.1/0001-Allow-static-linking-krb5-library.patch +30 -0
- data/ports/patches/krb5/1.22.1/0002-unknown-command-line-option-on-clang.patch +12 -0
- data/ports/patches/openssl/3.5.2/0001-aarch64-mingw.patch +21 -0
- data/ports/patches/postgresql/18.1/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
- data/ports/patches/postgresql/18.1/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
- data/rakelib/pg_gem_helper.rb +64 -0
- data.tar.gz.sig +0 -0
- metadata +49 -47
- metadata.gz.sig +0 -0
- data/.appveyor.yml +0 -42
- data/.gems +0 -6
- data/.gemtest +0 -0
- data/.github/workflows/binary-gems.yml +0 -117
- data/.github/workflows/source-gem.yml +0 -141
- data/.gitignore +0 -22
- data/.hgsigs +0 -34
- data/.hgtags +0 -41
- data/.irbrc +0 -23
- data/.pryrc +0 -23
- data/.tm_properties +0 -21
- data/.travis.yml +0 -49
- data/Manifest.txt +0 -72
- data/Rakefile.cross +0 -298
- data/translation/.po4a-version +0 -7
- data/translation/po/all.pot +0 -936
- data/translation/po/ja.po +0 -1036
- data/translation/po4a.cfg +0 -12
data/ext/pg_text_encoder.c
CHANGED
|
@@ -119,6 +119,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
|
119
119
|
int
|
|
120
120
|
pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
121
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
|
+
*/
|
|
122
126
|
VALUE str = rb_obj_as_string(value);
|
|
123
127
|
if( ENCODING_GET(str) == enc_idx ){
|
|
124
128
|
*intermediate = str;
|
|
@@ -227,7 +231,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
|
227
231
|
*
|
|
228
232
|
*/
|
|
229
233
|
static int
|
|
230
|
-
pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *
|
|
234
|
+
pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate1, int enc_idx)
|
|
231
235
|
{
|
|
232
236
|
if(out){
|
|
233
237
|
double dvalue = NUM2DBL(value);
|
|
@@ -235,7 +239,6 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
235
239
|
int neg = 0;
|
|
236
240
|
int exp2i, exp10i, i;
|
|
237
241
|
unsigned long long ll, remainder, oldval;
|
|
238
|
-
VALUE intermediate;
|
|
239
242
|
|
|
240
243
|
/* Cast to the same strings as value.to_s . */
|
|
241
244
|
if( isinf(dvalue) ){
|
|
@@ -279,6 +282,7 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
279
282
|
|
|
280
283
|
if( exp10i <= -5 || exp10i >= 15 ) {
|
|
281
284
|
/* Write the float in exponent format (1.23e45) */
|
|
285
|
+
VALUE intermediate;
|
|
282
286
|
|
|
283
287
|
/* write fraction digits from right to left */
|
|
284
288
|
for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
|
|
@@ -345,6 +349,8 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
345
349
|
*
|
|
346
350
|
* It converts Integer, Float and BigDecimal objects.
|
|
347
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.
|
|
348
354
|
*/
|
|
349
355
|
static int
|
|
350
356
|
pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
|
@@ -377,7 +383,7 @@ init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
|
|
|
377
383
|
{
|
|
378
384
|
s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
|
|
379
385
|
rb_global_variable(&s_str_F);
|
|
380
|
-
|
|
386
|
+
rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
|
|
381
387
|
s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
|
382
388
|
|
|
383
389
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
|
|
@@ -437,7 +443,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
437
443
|
t_pg_composite_coder *this = _this;
|
|
438
444
|
char *ptr1;
|
|
439
445
|
char *ptr2;
|
|
440
|
-
int
|
|
446
|
+
int backslashes = 0;
|
|
441
447
|
int needquote;
|
|
442
448
|
|
|
443
449
|
/* count data plus backslashes; detect chars needing quotes */
|
|
@@ -454,7 +460,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
454
460
|
|
|
455
461
|
if (ch == '"' || ch == '\\'){
|
|
456
462
|
needquote = 1;
|
|
457
|
-
|
|
463
|
+
backslashes++;
|
|
458
464
|
} else if (ch == '{' || ch == '}' || ch == this->delimiter ||
|
|
459
465
|
ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
|
|
460
466
|
needquote = 1;
|
|
@@ -463,12 +469,12 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
463
469
|
|
|
464
470
|
if( needquote ){
|
|
465
471
|
ptr1 = p_in + strlen;
|
|
466
|
-
ptr2 = p_out + strlen +
|
|
472
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
|
467
473
|
/* Write end quote */
|
|
468
474
|
*--ptr2 = '"';
|
|
469
475
|
|
|
470
476
|
/* Then store the escaped string on the final position, walking
|
|
471
|
-
* right to left, until all
|
|
477
|
+
* right to left, until all backslashes are placed. */
|
|
472
478
|
while( ptr1 != p_in ) {
|
|
473
479
|
*--ptr2 = *--ptr1;
|
|
474
480
|
if(*ptr2 == '"' || *ptr2 == '\\'){
|
|
@@ -477,7 +483,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
477
483
|
}
|
|
478
484
|
/* Write start quote */
|
|
479
485
|
*p_out = '"';
|
|
480
|
-
return strlen +
|
|
486
|
+
return strlen + backslashes + 2;
|
|
481
487
|
} else {
|
|
482
488
|
if( p_in != p_out )
|
|
483
489
|
memcpy( p_out, p_in, strlen );
|
|
@@ -531,7 +537,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
|
|
|
531
537
|
}
|
|
532
538
|
|
|
533
539
|
static char *
|
|
534
|
-
write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx)
|
|
540
|
+
write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE string, int quote, int enc_idx, int dimension)
|
|
535
541
|
{
|
|
536
542
|
int i;
|
|
537
543
|
|
|
@@ -539,6 +545,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
539
545
|
current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
|
|
540
546
|
*current_out++ = '{';
|
|
541
547
|
|
|
548
|
+
if( RARRAY_LEN(value) == 0 && this->dimensions >= 0 && dimension != this->dimensions ){
|
|
549
|
+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
|
|
550
|
+
}
|
|
551
|
+
|
|
542
552
|
for( i=0; i<RARRAY_LEN(value); i++){
|
|
543
553
|
VALUE entry = rb_ary_entry(value, i);
|
|
544
554
|
|
|
@@ -548,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
548
558
|
}
|
|
549
559
|
|
|
550
560
|
switch(TYPE(entry)){
|
|
551
|
-
case T_ARRAY:
|
|
552
|
-
current_out = write_array(this, entry, current_out, string, quote, enc_idx);
|
|
553
|
-
break;
|
|
554
561
|
case T_NIL:
|
|
562
|
+
if( this->dimensions >= 0 && dimension != this->dimensions ){
|
|
563
|
+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
|
|
564
|
+
}
|
|
555
565
|
current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
|
|
556
566
|
*current_out++ = 'N';
|
|
557
567
|
*current_out++ = 'U';
|
|
558
568
|
*current_out++ = 'L';
|
|
559
569
|
*current_out++ = 'L';
|
|
560
570
|
break;
|
|
571
|
+
case T_ARRAY:
|
|
572
|
+
if( this->dimensions < 0 || dimension < this->dimensions ){
|
|
573
|
+
current_out = write_array(this, entry, current_out, string, quote, enc_idx, dimension+1);
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
/* Number of dimensions reached -> handle array as normal value */
|
|
561
577
|
default:
|
|
578
|
+
if( this->dimensions >= 0 && dimension != this->dimensions ){
|
|
579
|
+
rb_raise(rb_eArgError, "less array dimensions to encode (%d) than expected (%d)", dimension, this->dimensions);
|
|
580
|
+
}
|
|
562
581
|
current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
|
|
563
582
|
}
|
|
564
583
|
}
|
|
@@ -590,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
590
609
|
VALUE out_str = rb_str_new(NULL, 0);
|
|
591
610
|
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
592
611
|
|
|
593
|
-
end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx);
|
|
612
|
+
end_ptr = write_array(this, value, RSTRING_PTR(out_str), out_str, this->needs_quotation, enc_idx, 1);
|
|
594
613
|
|
|
595
614
|
rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
|
|
596
615
|
*intermediate = out_str;
|
|
@@ -692,22 +711,22 @@ static int
|
|
|
692
711
|
quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
693
712
|
char *ptr1;
|
|
694
713
|
char *ptr2;
|
|
695
|
-
int
|
|
714
|
+
int backslashes = 0;
|
|
696
715
|
|
|
697
716
|
/* count required backlashs */
|
|
698
717
|
for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
|
|
699
718
|
if (*ptr1 == '\''){
|
|
700
|
-
|
|
719
|
+
backslashes++;
|
|
701
720
|
}
|
|
702
721
|
}
|
|
703
722
|
|
|
704
723
|
ptr1 = p_in + strlen;
|
|
705
|
-
ptr2 = p_out + strlen +
|
|
724
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
|
706
725
|
/* Write end quote */
|
|
707
726
|
*--ptr2 = '\'';
|
|
708
727
|
|
|
709
728
|
/* Then store the escaped string on the final position, walking
|
|
710
|
-
* right to left, until all
|
|
729
|
+
* right to left, until all backslashes are placed. */
|
|
711
730
|
while( ptr1 != p_in ) {
|
|
712
731
|
*--ptr2 = *--ptr1;
|
|
713
732
|
if(*ptr2 == '\''){
|
|
@@ -716,7 +735,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
716
735
|
}
|
|
717
736
|
/* Write start quote */
|
|
718
737
|
*p_out = '\'';
|
|
719
|
-
return strlen +
|
|
738
|
+
return strlen + backslashes + 2;
|
|
720
739
|
}
|
|
721
740
|
|
|
722
741
|
|
|
@@ -765,7 +784,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
765
784
|
if(out){
|
|
766
785
|
/* Second encoder pass, if required */
|
|
767
786
|
strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
|
|
768
|
-
|
|
787
|
+
rbpg_base64_encode( out, out, strlen );
|
|
769
788
|
|
|
770
789
|
return BASE64_ENCODED_SIZE(strlen);
|
|
771
790
|
} else {
|
|
@@ -780,7 +799,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
780
799
|
out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
|
|
781
800
|
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
782
801
|
|
|
783
|
-
|
|
802
|
+
rbpg_base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
|
|
784
803
|
*intermediate = out_str;
|
|
785
804
|
|
|
786
805
|
return -1;
|
data/ext/pg_tuple.c
CHANGED
|
@@ -125,7 +125,7 @@ static const rb_data_type_t pg_tuple_type = {
|
|
|
125
125
|
pg_tuple_gc_mark,
|
|
126
126
|
pg_tuple_gc_free,
|
|
127
127
|
pg_tuple_memsize,
|
|
128
|
-
|
|
128
|
+
pg_tuple_gc_compact,
|
|
129
129
|
},
|
|
130
130
|
0, 0,
|
|
131
131
|
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
|
@@ -135,7 +135,7 @@ static const rb_data_type_t pg_tuple_type = {
|
|
|
135
135
|
* Document-method: allocate
|
|
136
136
|
*
|
|
137
137
|
* call-seq:
|
|
138
|
-
* PG::
|
|
138
|
+
* PG::Tuple.allocate -> obj
|
|
139
139
|
*/
|
|
140
140
|
static VALUE
|
|
141
141
|
pg_tuple_s_allocate( VALUE klass )
|
|
@@ -242,10 +242,10 @@ pg_tuple_materialize(VALUE self)
|
|
|
242
242
|
* An integer +key+ is interpreted as column index.
|
|
243
243
|
* Negative values of index count from the end of the array.
|
|
244
244
|
*
|
|
245
|
-
* Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
245
|
+
* Depending on PG::Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
246
246
|
*
|
|
247
247
|
* If the key can't be found, there are several options:
|
|
248
|
-
* With no other arguments, it will raise a IndexError exception;
|
|
248
|
+
* With no other arguments, it will raise a +IndexError+ exception;
|
|
249
249
|
* if default is given, then that will be returned;
|
|
250
250
|
* if the optional code block is specified, then that will be run and its result returned.
|
|
251
251
|
*/
|
|
@@ -302,7 +302,7 @@ pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
|
|
|
302
302
|
* An integer +key+ is interpreted as column index.
|
|
303
303
|
* Negative values of index count from the end of the array.
|
|
304
304
|
*
|
|
305
|
-
* Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
305
|
+
* Depending on PG::Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
306
306
|
*
|
|
307
307
|
* If the key can't be found, it returns +nil+ .
|
|
308
308
|
*/
|
|
@@ -405,7 +405,7 @@ pg_tuple_each_value(VALUE self)
|
|
|
405
405
|
* tup.values -> Array
|
|
406
406
|
*
|
|
407
407
|
* Returns the values of this tuple as Array.
|
|
408
|
-
*
|
|
408
|
+
* <tt>res.tuple(i).values</tt> is equal to <tt>res.tuple_values(i)</tt> .
|
|
409
409
|
*/
|
|
410
410
|
static VALUE
|
|
411
411
|
pg_tuple_values(VALUE self)
|
|
@@ -474,7 +474,7 @@ pg_tuple_dump(VALUE self)
|
|
|
474
474
|
values = rb_ary_new4(this->num_fields, &this->values[0]);
|
|
475
475
|
a = rb_ary_new3(2, field_names, values);
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
rb_copy_generic_ivar(a, self);
|
|
478
478
|
|
|
479
479
|
return a;
|
|
480
480
|
}
|
|
@@ -510,7 +510,7 @@ pg_tuple_load(VALUE self, VALUE a)
|
|
|
510
510
|
if (RARRAY_LENINT(field_names) != num_fields)
|
|
511
511
|
rb_raise(rb_eTypeError, "different number of fields and values");
|
|
512
512
|
|
|
513
|
-
field_map =
|
|
513
|
+
field_map = rb_hash_new_capa(num_fields);
|
|
514
514
|
for( i = 0; i < num_fields; i++ ){
|
|
515
515
|
rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
|
|
516
516
|
}
|
data/ext/pg_type_map.c
CHANGED
|
@@ -33,7 +33,7 @@ const rb_data_type_t pg_typemap_type = {
|
|
|
33
33
|
pg_typemap_mark,
|
|
34
34
|
RUBY_TYPED_DEFAULT_FREE,
|
|
35
35
|
pg_typemap_memsize,
|
|
36
|
-
|
|
36
|
+
pg_typemap_compact,
|
|
37
37
|
},
|
|
38
38
|
0,
|
|
39
39
|
0,
|
|
@@ -187,7 +187,9 @@ init_pg_type_map(void)
|
|
|
187
187
|
*
|
|
188
188
|
* This is the base class for type maps.
|
|
189
189
|
* See derived classes for implementations of different type cast strategies
|
|
190
|
-
* ( PG::TypeMapByColumn, PG::TypeMapByOid ).
|
|
190
|
+
* ( PG::TypeMapByColumn, PG::TypeMapByOid, etc.).
|
|
191
|
+
*
|
|
192
|
+
* Find more type maps in the {README}[rdoc-ref:README.md@Type+Casts].
|
|
191
193
|
*
|
|
192
194
|
*/
|
|
193
195
|
rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
|
data/ext/pg_type_map_by_class.c
CHANGED
data/ext/pg_type_map_by_column.c
CHANGED
|
@@ -54,6 +54,7 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
|
|
|
54
54
|
t_tmbc *this = RTYPEDDATA_DATA( self );
|
|
55
55
|
t_typemap *default_tm;
|
|
56
56
|
|
|
57
|
+
Check_Type(params, T_ARRAY);
|
|
57
58
|
nfields = (int)RARRAY_LEN( params );
|
|
58
59
|
if ( this->nfields != nfields ) {
|
|
59
60
|
rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
|
|
@@ -228,7 +229,7 @@ static const rb_data_type_t pg_tmbc_type = {
|
|
|
228
229
|
pg_tmbc_mark,
|
|
229
230
|
pg_tmbc_free,
|
|
230
231
|
pg_tmbc_memsize,
|
|
231
|
-
|
|
232
|
+
pg_tmbc_compact,
|
|
232
233
|
},
|
|
233
234
|
&pg_typemap_type,
|
|
234
235
|
0,
|
data/ext/pg_type_map_by_oid.c
CHANGED
|
@@ -190,7 +190,7 @@ static const rb_data_type_t pg_tmbo_type = {
|
|
|
190
190
|
pg_tmbo_mark,
|
|
191
191
|
RUBY_TYPED_DEFAULT_FREE,
|
|
192
192
|
pg_tmbo_memsize,
|
|
193
|
-
|
|
193
|
+
pg_tmbo_compact,
|
|
194
194
|
},
|
|
195
195
|
&pg_typemap_type,
|
|
196
196
|
0,
|
|
@@ -315,6 +315,8 @@ pg_tmbo_coders( VALUE self )
|
|
|
315
315
|
* The type map will do Hash lookups for each result value, if the number of rows
|
|
316
316
|
* is below or equal +number+.
|
|
317
317
|
*
|
|
318
|
+
* Default is 10.
|
|
319
|
+
*
|
|
318
320
|
*/
|
|
319
321
|
static VALUE
|
|
320
322
|
pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
|
data/ext/pg_type_map_in_ruby.c
CHANGED
data/ext/pg_util.c
CHANGED
|
@@ -15,7 +15,7 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
|
|
|
15
15
|
* in-place (with _out_ == _in_).
|
|
16
16
|
*/
|
|
17
17
|
void
|
|
18
|
-
|
|
18
|
+
rbpg_base64_encode( char *out, const char *in, int len)
|
|
19
19
|
{
|
|
20
20
|
const unsigned char *in_ptr = (const unsigned char *)in + len;
|
|
21
21
|
char *out_ptr = out + BASE64_ENCODED_SIZE(len);
|
|
@@ -72,7 +72,7 @@ static const unsigned char base64_decode_table[] =
|
|
|
72
72
|
* It is possible to decode a string in-place (with _out_ == _in_).
|
|
73
73
|
*/
|
|
74
74
|
int
|
|
75
|
-
|
|
75
|
+
rbpg_base64_decode( char *out, const char *in, unsigned int len)
|
|
76
76
|
{
|
|
77
77
|
unsigned char a, b, c, d;
|
|
78
78
|
const unsigned char *in_ptr = (const unsigned char *)in;
|
data/ext/pg_util.h
CHANGED
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
#define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
|
|
58
58
|
#define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
|
|
59
59
|
|
|
60
|
-
void
|
|
61
|
-
int
|
|
60
|
+
void rbpg_base64_encode( char *out, const char *in, int len);
|
|
61
|
+
int rbpg_base64_decode( char *out, const char *in, unsigned int len);
|
|
62
62
|
|
|
63
63
|
int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
|
|
64
64
|
|
|
@@ -53,14 +53,18 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
53
53
|
@coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
|
|
54
54
|
@array_encoders_by_klass = array_encoders_by_klass
|
|
55
55
|
@encode_array_as = :array
|
|
56
|
-
@if_undefined = if_undefined ||
|
|
56
|
+
@if_undefined = if_undefined || UndefinedDefault
|
|
57
57
|
init_encoders
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
class UndefinedDefault
|
|
61
|
+
def self.call(oid_name, format)
|
|
62
|
+
raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
|
|
63
|
+
end
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
private_constant :UndefinedDefault
|
|
67
|
+
|
|
64
68
|
# Change the mechanism that is used to encode ruby array values
|
|
65
69
|
#
|
|
66
70
|
# Possible values:
|
|
@@ -166,6 +170,12 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
166
170
|
@textarray_encoder
|
|
167
171
|
end
|
|
168
172
|
|
|
173
|
+
begin
|
|
174
|
+
PG.require_bigdecimal_without_warning
|
|
175
|
+
has_bigdecimal = true
|
|
176
|
+
rescue LoadError
|
|
177
|
+
end
|
|
178
|
+
|
|
169
179
|
DEFAULT_TYPE_MAP = PG.make_shareable({
|
|
170
180
|
TrueClass => [1, 'bool', 'bool'],
|
|
171
181
|
FalseClass => [1, 'bool', 'bool'],
|
|
@@ -173,7 +183,6 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
173
183
|
# to unnecessary type conversions on server side.
|
|
174
184
|
Integer => [0, 'int8'],
|
|
175
185
|
Float => [0, 'float8'],
|
|
176
|
-
BigDecimal => [0, 'numeric'],
|
|
177
186
|
Time => [0, 'timestamptz'],
|
|
178
187
|
# We use text format and no type OID for IPAddr, because setting the OID can lead
|
|
179
188
|
# to unnecessary inet/cidr conversions on the server side.
|
|
@@ -181,7 +190,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
181
190
|
Hash => [0, 'json'],
|
|
182
191
|
Array => :get_array_type,
|
|
183
192
|
BinaryData => [1, 'bytea'],
|
|
184
|
-
})
|
|
193
|
+
}.merge(has_bigdecimal ? {BigDecimal => [0, 'numeric']} : {}))
|
|
185
194
|
private_constant :DEFAULT_TYPE_MAP
|
|
186
195
|
|
|
187
196
|
DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
|
|
@@ -190,9 +199,8 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
190
199
|
Integer => [0, '_int8'],
|
|
191
200
|
String => [0, '_text'],
|
|
192
201
|
Float => [0, '_float8'],
|
|
193
|
-
BigDecimal => [0, '_numeric'],
|
|
194
202
|
Time => [0, '_timestamptz'],
|
|
195
203
|
IPAddr => [0, '_inet'],
|
|
196
|
-
})
|
|
204
|
+
}.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
|
|
197
205
|
private_constant :DEFAULT_ARRAY_TYPE_MAP
|
|
198
206
|
end
|
|
@@ -127,8 +127,8 @@ class PG::BasicTypeRegistry
|
|
|
127
127
|
@maps = [
|
|
128
128
|
[0, :encoder, PG::TextEncoder::Array],
|
|
129
129
|
[0, :decoder, PG::TextDecoder::Array],
|
|
130
|
-
[1, :encoder,
|
|
131
|
-
[1, :decoder,
|
|
130
|
+
[1, :encoder, PG::BinaryEncoder::Array],
|
|
131
|
+
[1, :decoder, PG::BinaryDecoder::Array],
|
|
132
132
|
].inject([]) do |h, (format, direction, arraycoder)|
|
|
133
133
|
coders = registry.coders_for(format, direction) || {}
|
|
134
134
|
h[format] ||= {}
|
|
@@ -171,7 +171,14 @@ class PG::BasicTypeRegistry
|
|
|
171
171
|
include Checker
|
|
172
172
|
|
|
173
173
|
def initialize
|
|
174
|
-
#
|
|
174
|
+
# @coders_by_name has a content of
|
|
175
|
+
# Array< Hash< Symbol: Hash< String: Coder > > >
|
|
176
|
+
#
|
|
177
|
+
# The layers are:
|
|
178
|
+
# * index of Array is 0 (text) and 1 (binary)
|
|
179
|
+
# * Symbol key in the middle Hash is :encoder and :decoder
|
|
180
|
+
# * String key in the inner Hash corresponds to the `typname` column in the table pg_type
|
|
181
|
+
# * Coder value in the inner Hash is the associated coder object
|
|
175
182
|
@coders_by_name = []
|
|
176
183
|
end
|
|
177
184
|
|
|
@@ -225,7 +232,11 @@ class PG::BasicTypeRegistry
|
|
|
225
232
|
alias_type 0, 'int8', 'int2'
|
|
226
233
|
alias_type 0, 'oid', 'int2'
|
|
227
234
|
|
|
228
|
-
|
|
235
|
+
begin
|
|
236
|
+
PG.require_bigdecimal_without_warning
|
|
237
|
+
register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
|
|
238
|
+
rescue LoadError
|
|
239
|
+
end
|
|
229
240
|
register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
|
|
230
241
|
alias_type 0, 'varchar', 'text'
|
|
231
242
|
alias_type 0, 'char', 'text'
|
|
@@ -267,6 +278,7 @@ class PG::BasicTypeRegistry
|
|
|
267
278
|
register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
|
|
268
279
|
alias_type 0, 'cidr', 'inet'
|
|
269
280
|
|
|
281
|
+
register_type 0, 'record', PG::TextEncoder::Record, PG::TextDecoder::Record
|
|
270
282
|
|
|
271
283
|
|
|
272
284
|
register_type 1, 'int2', PG::BinaryEncoder::Int2, PG::BinaryDecoder::Integer
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'pg' unless defined?( PG )
|
|
5
|
+
|
|
6
|
+
if defined?(PG::CancelConnection)
|
|
7
|
+
class PG::CancelConnection
|
|
8
|
+
include PG::Connection::Pollable
|
|
9
|
+
|
|
10
|
+
alias c_initialize initialize
|
|
11
|
+
|
|
12
|
+
def initialize(conn)
|
|
13
|
+
c_initialize(conn)
|
|
14
|
+
|
|
15
|
+
# A cancel connection is always to one destination server only.
|
|
16
|
+
# Prepare conninfo_hash with just enough information to allow a shared polling_loop.
|
|
17
|
+
@host = conn.host
|
|
18
|
+
@hostaddr = conn.hostaddr
|
|
19
|
+
@port = conn.port
|
|
20
|
+
|
|
21
|
+
@conninfo_hash = {
|
|
22
|
+
host: @host,
|
|
23
|
+
hostaddr: @hostaddr,
|
|
24
|
+
port: @port.to_s,
|
|
25
|
+
connect_timeout: conn.conninfo_hash[:connect_timeout],
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# call-seq:
|
|
30
|
+
# conn.cancel
|
|
31
|
+
#
|
|
32
|
+
# Requests that the server abandons processing of the current command in a blocking manner.
|
|
33
|
+
#
|
|
34
|
+
# If the cancel request wasn't successfully dispatched an error message is raised.
|
|
35
|
+
#
|
|
36
|
+
# Successful dispatch of the cancellation is no guarantee that the request will have any effect, however.
|
|
37
|
+
# If the cancellation is effective, the command being canceled will terminate early and raises an error.
|
|
38
|
+
# If the cancellation fails (say, because the server was already done processing the command), then there will be no visible result at all.
|
|
39
|
+
#
|
|
40
|
+
def cancel
|
|
41
|
+
start
|
|
42
|
+
polling_loop(:poll)
|
|
43
|
+
end
|
|
44
|
+
alias async_cancel cancel
|
|
45
|
+
|
|
46
|
+
# These private methods are there to allow a shared polling_loop.
|
|
47
|
+
private
|
|
48
|
+
attr_reader :host
|
|
49
|
+
attr_reader :hostaddr
|
|
50
|
+
attr_reader :port
|
|
51
|
+
attr_reader :conninfo_hash
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/pg/coder.rb
CHANGED
|
@@ -72,16 +72,18 @@ module PG
|
|
|
72
72
|
|
|
73
73
|
class CompositeCoder < Coder
|
|
74
74
|
def to_h
|
|
75
|
-
{ **super,
|
|
75
|
+
h = { **super,
|
|
76
76
|
elements_type: elements_type,
|
|
77
77
|
needs_quotation: needs_quotation?,
|
|
78
78
|
delimiter: delimiter,
|
|
79
79
|
}
|
|
80
|
+
h[:dimensions] = dimensions if dimensions # Write only when set, for Marshal compat with pg<1.6
|
|
81
|
+
h
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
def inspect
|
|
83
85
|
str = super
|
|
84
|
-
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation"
|
|
86
|
+
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation#{dimensions && " #{dimensions} dimensions"}"
|
|
85
87
|
str
|
|
86
88
|
end
|
|
87
89
|
end
|