pg 1.5.4 → 1.6.1
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} +106 -4
- data/Gemfile +12 -3
- data/README-Windows.rdoc +1 -1
- data/README.ja.md +4 -4
- data/README.md +58 -17
- data/Rakefile +95 -14
- data/certs/kanis@comcard.de.pem +20 -0
- data/certs/larskanis-2024.pem +24 -0
- data/ext/errorcodes.def +4 -5
- data/ext/errorcodes.txt +2 -5
- data/ext/extconf.rb +161 -14
- data/ext/gvl_wrappers.c +13 -2
- data/ext/gvl_wrappers.h +33 -0
- data/ext/pg.c +17 -6
- data/ext/pg.h +9 -9
- data/ext/pg_binary_decoder.c +152 -0
- data/ext/pg_binary_encoder.c +211 -8
- data/ext/pg_cancel_connection.c +360 -0
- data/ext/pg_coder.c +54 -5
- data/ext/pg_connection.c +409 -167
- data/ext/pg_copy_coder.c +19 -15
- data/ext/pg_record_coder.c +7 -7
- data/ext/pg_result.c +11 -13
- data/ext/pg_text_decoder.c +4 -1
- data/ext/pg_text_encoder.c +37 -18
- data/ext/pg_tuple.c +2 -2
- data/ext/pg_type_map.c +1 -1
- 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/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 +1 -1
- data/lib/pg.rb +23 -8
- 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.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
- data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
- data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
- data/ports/patches/postgresql/17.5/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 +45 -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_copy_coder.c
CHANGED
@@ -51,7 +51,7 @@ static const rb_data_type_t pg_copycoder_type = {
|
|
51
51
|
pg_copycoder_mark,
|
52
52
|
RUBY_TYPED_DEFAULT_FREE,
|
53
53
|
pg_copycoder_memsize,
|
54
|
-
|
54
|
+
pg_copycoder_compact,
|
55
55
|
},
|
56
56
|
&pg_coder_type,
|
57
57
|
0,
|
@@ -212,6 +212,7 @@ pg_copycoder_type_map_get(VALUE self)
|
|
212
212
|
*
|
213
213
|
* See also PG::TextDecoder::CopyRow for the decoding direction with
|
214
214
|
* PG::Connection#get_copy_data .
|
215
|
+
* And see PG::BinaryEncoder::CopyRow for an encoder of the COPY binary format.
|
215
216
|
*/
|
216
217
|
static int
|
217
218
|
pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
@@ -235,7 +236,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
|
|
235
236
|
char *ptr1;
|
236
237
|
char *ptr2;
|
237
238
|
int strlen;
|
238
|
-
int
|
239
|
+
int backslashes;
|
239
240
|
VALUE subint;
|
240
241
|
VALUE entry;
|
241
242
|
|
@@ -286,19 +287,19 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
|
|
286
287
|
ptr2 = current_out + strlen;
|
287
288
|
|
288
289
|
/* count required backlashs */
|
289
|
-
for(
|
290
|
+
for(backslashes = 0; ptr1 != ptr2; ptr1++) {
|
290
291
|
/* Escape backslash itself, newline, carriage return, and the current delimiter character. */
|
291
292
|
if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
|
292
|
-
|
293
|
+
backslashes++;
|
293
294
|
}
|
294
295
|
}
|
295
296
|
|
296
297
|
ptr1 = current_out + strlen;
|
297
|
-
ptr2 = current_out + strlen +
|
298
|
+
ptr2 = current_out + strlen + backslashes;
|
298
299
|
current_out = ptr2;
|
299
300
|
|
300
301
|
/* Then store the escaped string on the final position, walking
|
301
|
-
* right to left, until all
|
302
|
+
* right to left, until all backslashes are placed. */
|
302
303
|
while( ptr1 != ptr2 ) {
|
303
304
|
*--ptr2 = *--ptr1;
|
304
305
|
if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
|
@@ -358,6 +359,7 @@ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedia
|
|
358
359
|
*
|
359
360
|
* See also PG::BinaryDecoder::CopyRow for the decoding direction with
|
360
361
|
* PG::Connection#get_copy_data .
|
362
|
+
* And see PG::TextEncoder::CopyRow for an encoder of the COPY text format.
|
361
363
|
*/
|
362
364
|
static int
|
363
365
|
pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
@@ -391,7 +393,7 @@ pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediat
|
|
391
393
|
|
392
394
|
switch(TYPE(entry)){
|
393
395
|
case T_NIL:
|
394
|
-
/* 4 bytes for -1
|
396
|
+
/* 4 bytes for -1 indicating a NULL value */
|
395
397
|
PG_RB_STR_ENSURE_CAPA( *intermediate, 4, current_out, end_capa_ptr );
|
396
398
|
write_nbo32(-1, current_out);
|
397
399
|
current_out += 4;
|
@@ -496,6 +498,7 @@ GetDecimalFromHex(char hex)
|
|
496
498
|
*
|
497
499
|
* See also PG::TextEncoder::CopyRow for the encoding direction with
|
498
500
|
* PG::Connection#put_copy_data .
|
501
|
+
* And see PG::BinaryDecoder::CopyRow for a decoder of the COPY binary format.
|
499
502
|
*/
|
500
503
|
/*
|
501
504
|
* Parse the current line into separate attributes (fields),
|
@@ -763,6 +766,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
|
|
763
766
|
*
|
764
767
|
* See also PG::BinaryEncoder::CopyRow for the encoding direction with
|
765
768
|
* PG::Connection#put_copy_data .
|
769
|
+
* And see PG::TextDecoder::CopyRow for a decoder of the COPY text format.
|
766
770
|
*/
|
767
771
|
static VALUE
|
768
772
|
pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tuple, int _field, int enc_idx)
|
@@ -795,26 +799,26 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
795
799
|
cur_ptr = input_line;
|
796
800
|
line_end_ptr = input_line + len;
|
797
801
|
|
798
|
-
if (cur_ptr
|
802
|
+
if (line_end_ptr - cur_ptr >= 11 && memcmp(cur_ptr, BinarySignature, 11) == 0){
|
799
803
|
/* binary COPY header signature detected -> just drop it */
|
800
804
|
int ext_bytes;
|
801
805
|
cur_ptr += 11;
|
802
806
|
|
803
807
|
/* read flags */
|
804
|
-
if (cur_ptr
|
808
|
+
if (line_end_ptr - cur_ptr < 4 ) goto length_error;
|
805
809
|
cur_ptr += 4;
|
806
810
|
|
807
811
|
/* read header extensions */
|
808
|
-
if (cur_ptr
|
812
|
+
if (line_end_ptr - cur_ptr < 4 ) goto length_error;
|
809
813
|
ext_bytes = read_nbo32(cur_ptr);
|
810
814
|
if (ext_bytes < 0) goto length_error;
|
811
815
|
cur_ptr += 4;
|
812
|
-
if (cur_ptr
|
816
|
+
if (line_end_ptr - cur_ptr < ext_bytes ) goto length_error;
|
813
817
|
cur_ptr += ext_bytes;
|
814
818
|
}
|
815
819
|
|
816
820
|
/* read row header */
|
817
|
-
if (cur_ptr
|
821
|
+
if (line_end_ptr - cur_ptr < 2 ) goto length_error;
|
818
822
|
nfields = read_nbo16(cur_ptr);
|
819
823
|
cur_ptr += 2;
|
820
824
|
|
@@ -827,10 +831,9 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
827
831
|
|
828
832
|
for( fieldno = 0; fieldno < nfields; fieldno++){
|
829
833
|
long input_len;
|
830
|
-
VALUE field_value;
|
831
834
|
|
832
835
|
/* read field size */
|
833
|
-
if (cur_ptr
|
836
|
+
if (line_end_ptr - cur_ptr < 4 ) goto length_error;
|
834
837
|
input_len = read_nbo32(cur_ptr);
|
835
838
|
cur_ptr += 4;
|
836
839
|
|
@@ -839,7 +842,8 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
839
842
|
/* NULL indicator */
|
840
843
|
rb_ary_push(array, Qnil);
|
841
844
|
} else {
|
842
|
-
|
845
|
+
VALUE field_value;
|
846
|
+
if (line_end_ptr - cur_ptr < input_len ) goto length_error;
|
843
847
|
|
844
848
|
/* copy input data to field_str */
|
845
849
|
PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
|
data/ext/pg_record_coder.c
CHANGED
@@ -43,7 +43,7 @@ static const rb_data_type_t pg_recordcoder_type = {
|
|
43
43
|
pg_recordcoder_mark,
|
44
44
|
RUBY_TYPED_DEFAULT_FREE,
|
45
45
|
pg_recordcoder_memsize,
|
46
|
-
|
46
|
+
pg_recordcoder_compact,
|
47
47
|
},
|
48
48
|
&pg_coder_type,
|
49
49
|
0,
|
@@ -198,7 +198,7 @@ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate
|
|
198
198
|
char *ptr1;
|
199
199
|
char *ptr2;
|
200
200
|
long strlen;
|
201
|
-
int
|
201
|
+
int backslashes;
|
202
202
|
VALUE subint;
|
203
203
|
VALUE entry;
|
204
204
|
|
@@ -249,19 +249,19 @@ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate
|
|
249
249
|
ptr2 = current_out + strlen;
|
250
250
|
|
251
251
|
/* count required backlashs */
|
252
|
-
for(
|
252
|
+
for(backslashes = 0; ptr1 != ptr2; ptr1++) {
|
253
253
|
/* Escape backslash itself, newline, carriage return, and the current delimiter character. */
|
254
254
|
if(*ptr1 == '"' || *ptr1 == '\\'){
|
255
|
-
|
255
|
+
backslashes++;
|
256
256
|
}
|
257
257
|
}
|
258
258
|
|
259
259
|
ptr1 = current_out + strlen;
|
260
|
-
ptr2 = current_out + strlen +
|
260
|
+
ptr2 = current_out + strlen + backslashes;
|
261
261
|
current_out = ptr2;
|
262
262
|
|
263
263
|
/* Then store the escaped string on the final position, walking
|
264
|
-
* right to left, until all
|
264
|
+
* right to left, until all backslashes are placed. */
|
265
265
|
while( ptr1 != ptr2 ) {
|
266
266
|
*--ptr2 = *--ptr1;
|
267
267
|
if(*ptr1 == '"' || *ptr1 == '\\'){
|
@@ -340,7 +340,7 @@ record_isspace(char ch)
|
|
340
340
|
* conn.exec("SELECT * FROM my_table").map_types!(PG::TypeMapByColumn.new([deco]*2)).to_a
|
341
341
|
* # => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]
|
342
342
|
*
|
343
|
-
* It's more
|
343
|
+
* It's more convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.
|
344
344
|
* # Fetch a NULL record of our type to retrieve the OIDs of the two fields "r" and "i"
|
345
345
|
* oids = conn.exec( "SELECT (NULL::complex).*" )
|
346
346
|
* # Build a type map (PG::TypeMapByColumn) for decoding the "complex" type
|
data/ext/pg_result.c
CHANGED
@@ -147,9 +147,7 @@ pgresult_clear( void *_this )
|
|
147
147
|
t_pg_result *this = (t_pg_result *)_this;
|
148
148
|
if( this->pgresult && !this->autoclear ){
|
149
149
|
PQclear(this->pgresult);
|
150
|
-
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
151
150
|
rb_gc_adjust_memory_usage(-this->result_size);
|
152
|
-
#endif
|
153
151
|
}
|
154
152
|
this->result_size = 0;
|
155
153
|
this->nfields = -1;
|
@@ -180,7 +178,7 @@ static const rb_data_type_t pgresult_type = {
|
|
180
178
|
pgresult_gc_mark,
|
181
179
|
pgresult_gc_free,
|
182
180
|
pgresult_memsize,
|
183
|
-
|
181
|
+
pgresult_gc_compact,
|
184
182
|
},
|
185
183
|
0, 0,
|
186
184
|
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
@@ -253,9 +251,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
253
251
|
*/
|
254
252
|
this->result_size = pgresult_approx_size(result);
|
255
253
|
|
256
|
-
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
257
254
|
rb_gc_adjust_memory_usage(this->result_size);
|
258
|
-
#endif
|
259
255
|
|
260
256
|
return self;
|
261
257
|
}
|
@@ -323,6 +319,9 @@ pg_result_check( VALUE self )
|
|
323
319
|
case PGRES_COMMAND_OK:
|
324
320
|
#ifdef HAVE_PQENTERPIPELINEMODE
|
325
321
|
case PGRES_PIPELINE_SYNC:
|
322
|
+
#endif
|
323
|
+
#ifdef HAVE_PQSETCHUNKEDROWSMODE
|
324
|
+
case PGRES_TUPLES_CHUNK:
|
326
325
|
#endif
|
327
326
|
return self;
|
328
327
|
case PGRES_BAD_RESPONSE:
|
@@ -549,6 +548,7 @@ static void pgresult_init_fnames(VALUE self)
|
|
549
548
|
* * +PGRES_FATAL_ERROR+
|
550
549
|
* * +PGRES_COPY_BOTH+
|
551
550
|
* * +PGRES_SINGLE_TUPLE+
|
551
|
+
* * +PGRES_TUPLES_CHUNK+
|
552
552
|
* * +PGRES_PIPELINE_SYNC+
|
553
553
|
* * +PGRES_PIPELINE_ABORTED+
|
554
554
|
*
|
@@ -613,14 +613,12 @@ pgresult_error_message(VALUE self)
|
|
613
613
|
return ret;
|
614
614
|
}
|
615
615
|
|
616
|
-
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
617
616
|
/*
|
618
617
|
* call-seq:
|
619
618
|
* res.verbose_error_message( verbosity, show_context ) -> String
|
620
619
|
*
|
621
620
|
* Returns a reformatted version of the error message associated with a PGresult object.
|
622
621
|
*
|
623
|
-
* Available since PostgreSQL-9.6
|
624
622
|
*/
|
625
623
|
static VALUE
|
626
624
|
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
@@ -639,7 +637,6 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
639
637
|
|
640
638
|
return ret;
|
641
639
|
}
|
642
|
-
#endif
|
643
640
|
|
644
641
|
/*
|
645
642
|
* call-seq:
|
@@ -664,7 +661,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
664
661
|
* An example:
|
665
662
|
*
|
666
663
|
* begin
|
667
|
-
* conn.exec( "SELECT * FROM
|
664
|
+
* conn.exec( "SELECT * FROM nonexistent_table" )
|
668
665
|
* rescue PG::Error => err
|
669
666
|
* p [
|
670
667
|
* err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
@@ -684,7 +681,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
684
681
|
*
|
685
682
|
* Outputs:
|
686
683
|
*
|
687
|
-
* ["ERROR", "42P01", "relation \"
|
684
|
+
* ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
|
688
685
|
* "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
|
689
686
|
*/
|
690
687
|
static VALUE
|
@@ -1528,6 +1525,9 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
1528
1525
|
return self;
|
1529
1526
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
1530
1527
|
case PGRES_SINGLE_TUPLE:
|
1528
|
+
#ifdef HAVE_PQSETCHUNKEDROWSMODE
|
1529
|
+
case PGRES_TUPLES_CHUNK:
|
1530
|
+
#endif
|
1531
1531
|
break;
|
1532
1532
|
default:
|
1533
1533
|
pg_result_check( self );
|
@@ -1572,7 +1572,7 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
1572
1572
|
* wrapping each row into a dedicated result object, it delivers data in nearly
|
1573
1573
|
* the same speed as with ordinary results.
|
1574
1574
|
*
|
1575
|
-
* The base result must be in status PGRES_SINGLE_TUPLE.
|
1575
|
+
* The base result must be in status PGRES_SINGLE_TUPLE or PGRES_TUPLES_CHUNK.
|
1576
1576
|
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
1577
1577
|
* A PG::Error is raised for any errors from the server.
|
1578
1578
|
*
|
@@ -1707,10 +1707,8 @@ init_pg_result(void)
|
|
1707
1707
|
rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
|
1708
1708
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
1709
1709
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
1710
|
-
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
1711
1710
|
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
1712
1711
|
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
1713
|
-
#endif
|
1714
1712
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
1715
1713
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
1716
1714
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
data/ext/pg_text_decoder.c
CHANGED
@@ -163,6 +163,8 @@ pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int f
|
|
163
163
|
* This is a decoder class for conversion of PostgreSQL numeric types
|
164
164
|
* to Ruby BigDecimal objects.
|
165
165
|
*
|
166
|
+
* As soon as this class is used, it requires the 'bigdecimal' gem.
|
167
|
+
*
|
166
168
|
*/
|
167
169
|
static VALUE
|
168
170
|
pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
|
@@ -174,7 +176,7 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
|
|
174
176
|
static VALUE
|
175
177
|
init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
|
176
178
|
{
|
177
|
-
|
179
|
+
rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
|
178
180
|
s_id_BigDecimal = rb_intern("BigDecimal");
|
179
181
|
|
180
182
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
|
@@ -811,6 +813,7 @@ static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, i
|
|
811
813
|
* This is a decoder class for conversion of PostgreSQL inet type
|
812
814
|
* to Ruby IPAddr values.
|
813
815
|
*
|
816
|
+
* As soon as this class is used, it requires the ruby standard library 'ipaddr'.
|
814
817
|
*/
|
815
818
|
static VALUE
|
816
819
|
pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
|
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
|
|
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 )
|
data/ext/pg_type_map.c
CHANGED
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
@@ -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
|