pg 1.5.3 → 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 +6 -0
- data/History.md +60 -4
- data/README.ja.md +54 -30
- data/Rakefile +4 -1
- data/Rakefile.cross +13 -8
- 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 +7 -0
- data/ext/pg.c +2 -2
- data/ext/pg.h +0 -1
- data/ext/pg_binary_decoder.c +2 -0
- data/ext/pg_binary_encoder.c +1 -1
- data/ext/pg_connection.c +60 -21
- data/ext/pg_copy_coder.c +17 -13
- data/ext/pg_record_coder.c +6 -6
- data/ext/pg_result.c +4 -4
- data/ext/pg_text_decoder.c +4 -1
- data/ext/pg_text_encoder.c +17 -11
- data/lib/pg/basic_type_map_for_queries.rb +8 -4
- data/lib/pg/basic_type_registry.rb +14 -2
- data/lib/pg/connection.rb +58 -38
- 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 +10 -0
- data/pg.gemspec +3 -1
- data.tar.gz.sig +0 -0
- metadata +17 -34
- 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 -137
- 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/translation/.po4a-version +0 -7
- data/translation/po/all.pot +0 -910
- data/translation/po/ja.po +0 -1047
- data/translation/po4a.cfg +0 -12
data/ext/pg_connection.c
CHANGED
|
@@ -33,8 +33,8 @@ static VALUE pgconn_async_flush(VALUE self);
|
|
|
33
33
|
#ifdef __GNUC__
|
|
34
34
|
__attribute__((format(printf, 3, 4)))
|
|
35
35
|
#endif
|
|
36
|
-
static void
|
|
37
|
-
pg_raise_conn_error( VALUE klass, VALUE self, const char *format, ...)
|
|
36
|
+
NORETURN( static void
|
|
37
|
+
pg_raise_conn_error( VALUE klass, VALUE self, const char *format, ...))
|
|
38
38
|
{
|
|
39
39
|
VALUE msg, error;
|
|
40
40
|
va_list ap;
|
|
@@ -264,6 +264,7 @@ pgconn_s_allocate( VALUE klass )
|
|
|
264
264
|
RB_OBJ_WRITE(self, &this->decoder_for_get_copy_data, Qnil);
|
|
265
265
|
RB_OBJ_WRITE(self, &this->trace_stream, Qnil);
|
|
266
266
|
rb_ivar_set(self, rb_intern("@calls_to_put_copy_data"), INT2FIX(0));
|
|
267
|
+
rb_ivar_set(self, rb_intern("@iopts_for_reset"), Qnil);
|
|
267
268
|
|
|
268
269
|
return self;
|
|
269
270
|
}
|
|
@@ -515,9 +516,9 @@ static VALUE
|
|
|
515
516
|
pgconn_connect_poll(VALUE self)
|
|
516
517
|
{
|
|
517
518
|
PostgresPollingStatusType status;
|
|
518
|
-
status = gvl_PQconnectPoll(pg_get_pgconn(self));
|
|
519
519
|
|
|
520
520
|
pgconn_close_socket_io(self);
|
|
521
|
+
status = gvl_PQconnectPoll(pg_get_pgconn(self));
|
|
521
522
|
|
|
522
523
|
return INT2FIX((int)status);
|
|
523
524
|
}
|
|
@@ -563,6 +564,27 @@ pgconn_sync_reset( VALUE self )
|
|
|
563
564
|
return self;
|
|
564
565
|
}
|
|
565
566
|
|
|
567
|
+
static VALUE
|
|
568
|
+
pgconn_reset_start2( VALUE self, VALUE conninfo )
|
|
569
|
+
{
|
|
570
|
+
t_pg_connection *this = pg_get_connection( self );
|
|
571
|
+
|
|
572
|
+
/* Close old connection */
|
|
573
|
+
pgconn_close_socket_io( self );
|
|
574
|
+
PQfinish( this->pgconn );
|
|
575
|
+
|
|
576
|
+
/* Start new connection */
|
|
577
|
+
this->pgconn = gvl_PQconnectStart( StringValueCStr(conninfo) );
|
|
578
|
+
|
|
579
|
+
if( this->pgconn == NULL )
|
|
580
|
+
rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate PGconn structure");
|
|
581
|
+
|
|
582
|
+
if ( PQstatus(this->pgconn) == CONNECTION_BAD )
|
|
583
|
+
pg_raise_conn_error( rb_eConnectionBad, self, "%s", PQerrorMessage(this->pgconn));
|
|
584
|
+
|
|
585
|
+
return Qnil;
|
|
586
|
+
}
|
|
587
|
+
|
|
566
588
|
/*
|
|
567
589
|
* call-seq:
|
|
568
590
|
* conn.reset_start() -> nil
|
|
@@ -594,9 +616,9 @@ static VALUE
|
|
|
594
616
|
pgconn_reset_poll(VALUE self)
|
|
595
617
|
{
|
|
596
618
|
PostgresPollingStatusType status;
|
|
597
|
-
status = gvl_PQresetPoll(pg_get_pgconn(self));
|
|
598
619
|
|
|
599
620
|
pgconn_close_socket_io(self);
|
|
621
|
+
status = gvl_PQresetPoll(pg_get_pgconn(self));
|
|
600
622
|
|
|
601
623
|
return INT2FIX((int)status);
|
|
602
624
|
}
|
|
@@ -2245,6 +2267,17 @@ pgconn_notifies(VALUE self)
|
|
|
2245
2267
|
return hash;
|
|
2246
2268
|
}
|
|
2247
2269
|
|
|
2270
|
+
#ifndef HAVE_RB_IO_DESCRIPTOR
|
|
2271
|
+
static int
|
|
2272
|
+
rb_io_descriptor(VALUE io)
|
|
2273
|
+
{
|
|
2274
|
+
Check_Type(io, T_FILE);
|
|
2275
|
+
rb_io_t *fptr = RFILE(io)->fptr;
|
|
2276
|
+
rb_io_check_closed(fptr);
|
|
2277
|
+
return fptr->fd;
|
|
2278
|
+
}
|
|
2279
|
+
#endif
|
|
2280
|
+
|
|
2248
2281
|
#if defined(_WIN32)
|
|
2249
2282
|
|
|
2250
2283
|
/* We use a specialized implementation of rb_io_wait() on Windows.
|
|
@@ -2265,7 +2298,6 @@ int rb_w32_wait_events( HANDLE *events, int num, DWORD timeout );
|
|
|
2265
2298
|
|
|
2266
2299
|
static VALUE
|
|
2267
2300
|
pg_rb_thread_io_wait(VALUE io, VALUE events, VALUE timeout) {
|
|
2268
|
-
rb_io_t *fptr;
|
|
2269
2301
|
struct timeval ptimeout;
|
|
2270
2302
|
|
|
2271
2303
|
struct timeval aborttime={0,0}, currtime, waittime;
|
|
@@ -2276,7 +2308,6 @@ pg_rb_thread_io_wait(VALUE io, VALUE events, VALUE timeout) {
|
|
|
2276
2308
|
long w32_events = 0;
|
|
2277
2309
|
DWORD wait_ret;
|
|
2278
2310
|
|
|
2279
|
-
GetOpenFile((io), fptr);
|
|
2280
2311
|
if( !NIL_P(timeout) ){
|
|
2281
2312
|
ptimeout.tv_sec = (time_t)(NUM2DBL(timeout));
|
|
2282
2313
|
ptimeout.tv_usec = (time_t)((NUM2DBL(timeout) - (double)ptimeout.tv_sec) * 1e6);
|
|
@@ -2290,7 +2321,7 @@ pg_rb_thread_io_wait(VALUE io, VALUE events, VALUE timeout) {
|
|
|
2290
2321
|
if(rb_events & PG_RUBY_IO_PRIORITY) w32_events |= FD_OOB;
|
|
2291
2322
|
|
|
2292
2323
|
for(;;) {
|
|
2293
|
-
if ( WSAEventSelect(_get_osfhandle(
|
|
2324
|
+
if ( WSAEventSelect(_get_osfhandle(rb_io_descriptor(io)), hEvent, w32_events) == SOCKET_ERROR ) {
|
|
2294
2325
|
WSACloseEvent( hEvent );
|
|
2295
2326
|
rb_raise( rb_eConnectionBad, "WSAEventSelect socket error: %d", WSAGetLastError() );
|
|
2296
2327
|
}
|
|
@@ -2333,7 +2364,7 @@ static VALUE
|
|
|
2333
2364
|
pg_rb_io_wait(VALUE io, VALUE events, VALUE timeout) {
|
|
2334
2365
|
#if defined(HAVE_RUBY_FIBER_SCHEDULER_H)
|
|
2335
2366
|
/* We don't support Fiber.scheduler on Windows ruby-3.0 because there is no fast way to check whether a scheduler is active.
|
|
2336
|
-
*
|
|
2367
|
+
* Fortunately ruby-3.1 offers a C-API for it.
|
|
2337
2368
|
*/
|
|
2338
2369
|
VALUE scheduler = rb_fiber_scheduler_current();
|
|
2339
2370
|
|
|
@@ -2363,16 +2394,14 @@ typedef enum {
|
|
|
2363
2394
|
|
|
2364
2395
|
static VALUE
|
|
2365
2396
|
pg_rb_io_wait(VALUE io, VALUE events, VALUE timeout) {
|
|
2366
|
-
rb_io_t *fptr;
|
|
2367
2397
|
struct timeval waittime;
|
|
2368
2398
|
int res;
|
|
2369
2399
|
|
|
2370
|
-
GetOpenFile((io), fptr);
|
|
2371
2400
|
if( !NIL_P(timeout) ){
|
|
2372
2401
|
waittime.tv_sec = (time_t)(NUM2DBL(timeout));
|
|
2373
2402
|
waittime.tv_usec = (time_t)((NUM2DBL(timeout) - (double)waittime.tv_sec) * 1e6);
|
|
2374
2403
|
}
|
|
2375
|
-
res = rb_wait_for_single_fd(
|
|
2404
|
+
res = rb_wait_for_single_fd(rb_io_descriptor(io), NUM2UINT(events), NIL_P(timeout) ? NULL : &waittime);
|
|
2376
2405
|
|
|
2377
2406
|
return UINT2NUM(res);
|
|
2378
2407
|
}
|
|
@@ -3107,7 +3136,9 @@ pgconn_async_get_last_result(VALUE self)
|
|
|
3107
3136
|
for(;;) {
|
|
3108
3137
|
int status;
|
|
3109
3138
|
|
|
3110
|
-
/*
|
|
3139
|
+
/* Wait for input before reading each result.
|
|
3140
|
+
* That way we support the ruby-3.x IO scheduler and don't block other ruby threads.
|
|
3141
|
+
*/
|
|
3111
3142
|
wait_socket_readable(self, NULL, get_result_readable);
|
|
3112
3143
|
|
|
3113
3144
|
cur = gvl_PQgetResult(conn);
|
|
@@ -3141,7 +3172,7 @@ pgconn_async_get_last_result(VALUE self)
|
|
|
3141
3172
|
* Returns:
|
|
3142
3173
|
* * +nil+ when the connection is already idle
|
|
3143
3174
|
* * +true+ when some results have been discarded
|
|
3144
|
-
* * +false+ when a failure
|
|
3175
|
+
* * +false+ when a failure occurred and the connection was closed
|
|
3145
3176
|
*
|
|
3146
3177
|
*/
|
|
3147
3178
|
static VALUE
|
|
@@ -4171,16 +4202,23 @@ static VALUE
|
|
|
4171
4202
|
pgconn_set_default_encoding( VALUE self )
|
|
4172
4203
|
{
|
|
4173
4204
|
PGconn *conn = pg_get_pgconn( self );
|
|
4174
|
-
rb_encoding *
|
|
4175
|
-
const char *encname;
|
|
4205
|
+
rb_encoding *rb_enc;
|
|
4176
4206
|
|
|
4177
4207
|
rb_check_frozen(self);
|
|
4178
|
-
if ((
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4208
|
+
if (( rb_enc = rb_default_internal_encoding() )) {
|
|
4209
|
+
rb_encoding * conn_encoding = pg_conn_enc_get( conn );
|
|
4210
|
+
|
|
4211
|
+
/* Don't set the server encoding, if it's unnecessary.
|
|
4212
|
+
* This is important for connection proxies, who disallow configuration settings.
|
|
4213
|
+
*/
|
|
4214
|
+
if ( conn_encoding != rb_enc ) {
|
|
4215
|
+
const char *encname = pg_get_rb_encoding_as_pg_encoding( rb_enc );
|
|
4216
|
+
if ( pgconn_set_client_encoding_async(self, rb_str_new_cstr(encname)) != 0 )
|
|
4217
|
+
rb_warning( "Failed to set the default_internal encoding to %s: '%s'",
|
|
4218
|
+
encname, PQerrorMessage(conn) );
|
|
4219
|
+
}
|
|
4220
|
+
pgconn_set_internal_encoding_index( self );
|
|
4221
|
+
return rb_enc_from_encoding( rb_enc );
|
|
4184
4222
|
} else {
|
|
4185
4223
|
pgconn_set_internal_encoding_index( self );
|
|
4186
4224
|
return Qnil;
|
|
@@ -4461,6 +4499,7 @@ init_pg_connection(void)
|
|
|
4461
4499
|
rb_define_method(rb_cPGconn, "finished?", pgconn_finished_p, 0);
|
|
4462
4500
|
rb_define_method(rb_cPGconn, "sync_reset", pgconn_sync_reset, 0);
|
|
4463
4501
|
rb_define_method(rb_cPGconn, "reset_start", pgconn_reset_start, 0);
|
|
4502
|
+
rb_define_private_method(rb_cPGconn, "reset_start2", pgconn_reset_start2, 1);
|
|
4464
4503
|
rb_define_method(rb_cPGconn, "reset_poll", pgconn_reset_poll, 0);
|
|
4465
4504
|
rb_define_alias(rb_cPGconn, "close", "finish");
|
|
4466
4505
|
|
data/ext/pg_copy_coder.c
CHANGED
|
@@ -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
|
|
|
@@ -830,7 +834,7 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
|
830
834
|
VALUE field_value;
|
|
831
835
|
|
|
832
836
|
/* read field size */
|
|
833
|
-
if (cur_ptr
|
|
837
|
+
if (line_end_ptr - cur_ptr < 4 ) goto length_error;
|
|
834
838
|
input_len = read_nbo32(cur_ptr);
|
|
835
839
|
cur_ptr += 4;
|
|
836
840
|
|
|
@@ -839,7 +843,7 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
|
839
843
|
/* NULL indicator */
|
|
840
844
|
rb_ary_push(array, Qnil);
|
|
841
845
|
} else {
|
|
842
|
-
if (cur_ptr
|
|
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
|
@@ -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
|
@@ -392,8 +392,8 @@ pg_result_clear(VALUE self)
|
|
|
392
392
|
* It also denies setting a type_map or field_name_type.
|
|
393
393
|
*
|
|
394
394
|
*/
|
|
395
|
-
VALUE
|
|
396
|
-
|
|
395
|
+
static VALUE
|
|
396
|
+
pg_result_freeze(VALUE self)
|
|
397
397
|
{
|
|
398
398
|
t_pg_result *this = pgresult_get_this(self);
|
|
399
399
|
|
|
@@ -664,7 +664,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
|
664
664
|
* An example:
|
|
665
665
|
*
|
|
666
666
|
* begin
|
|
667
|
-
* conn.exec( "SELECT * FROM
|
|
667
|
+
* conn.exec( "SELECT * FROM nonexistent_table" )
|
|
668
668
|
* rescue PG::Error => err
|
|
669
669
|
* p [
|
|
670
670
|
* err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
|
@@ -684,7 +684,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
|
684
684
|
*
|
|
685
685
|
* Outputs:
|
|
686
686
|
*
|
|
687
|
-
* ["ERROR", "42P01", "relation \"
|
|
687
|
+
* ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
|
|
688
688
|
* "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
|
|
689
689
|
*/
|
|
690
690
|
static VALUE
|
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;
|
|
@@ -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 );
|
|
@@ -692,22 +698,22 @@ static int
|
|
|
692
698
|
quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
693
699
|
char *ptr1;
|
|
694
700
|
char *ptr2;
|
|
695
|
-
int
|
|
701
|
+
int backslashes = 0;
|
|
696
702
|
|
|
697
703
|
/* count required backlashs */
|
|
698
704
|
for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
|
|
699
705
|
if (*ptr1 == '\''){
|
|
700
|
-
|
|
706
|
+
backslashes++;
|
|
701
707
|
}
|
|
702
708
|
}
|
|
703
709
|
|
|
704
710
|
ptr1 = p_in + strlen;
|
|
705
|
-
ptr2 = p_out + strlen +
|
|
711
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
|
706
712
|
/* Write end quote */
|
|
707
713
|
*--ptr2 = '\'';
|
|
708
714
|
|
|
709
715
|
/* Then store the escaped string on the final position, walking
|
|
710
|
-
* right to left, until all
|
|
716
|
+
* right to left, until all backslashes are placed. */
|
|
711
717
|
while( ptr1 != p_in ) {
|
|
712
718
|
*--ptr2 = *--ptr1;
|
|
713
719
|
if(*ptr2 == '\''){
|
|
@@ -716,7 +722,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
|
716
722
|
}
|
|
717
723
|
/* Write start quote */
|
|
718
724
|
*p_out = '\'';
|
|
719
|
-
return strlen +
|
|
725
|
+
return strlen + backslashes + 2;
|
|
720
726
|
}
|
|
721
727
|
|
|
722
728
|
|
|
@@ -166,6 +166,12 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
166
166
|
@textarray_encoder
|
|
167
167
|
end
|
|
168
168
|
|
|
169
|
+
begin
|
|
170
|
+
PG.require_bigdecimal_without_warning
|
|
171
|
+
has_bigdecimal = true
|
|
172
|
+
rescue LoadError
|
|
173
|
+
end
|
|
174
|
+
|
|
169
175
|
DEFAULT_TYPE_MAP = PG.make_shareable({
|
|
170
176
|
TrueClass => [1, 'bool', 'bool'],
|
|
171
177
|
FalseClass => [1, 'bool', 'bool'],
|
|
@@ -173,7 +179,6 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
173
179
|
# to unnecessary type conversions on server side.
|
|
174
180
|
Integer => [0, 'int8'],
|
|
175
181
|
Float => [0, 'float8'],
|
|
176
|
-
BigDecimal => [0, 'numeric'],
|
|
177
182
|
Time => [0, 'timestamptz'],
|
|
178
183
|
# We use text format and no type OID for IPAddr, because setting the OID can lead
|
|
179
184
|
# to unnecessary inet/cidr conversions on the server side.
|
|
@@ -181,7 +186,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
181
186
|
Hash => [0, 'json'],
|
|
182
187
|
Array => :get_array_type,
|
|
183
188
|
BinaryData => [1, 'bytea'],
|
|
184
|
-
})
|
|
189
|
+
}.merge(has_bigdecimal ? {BigDecimal => [0, 'numeric']} : {}))
|
|
185
190
|
private_constant :DEFAULT_TYPE_MAP
|
|
186
191
|
|
|
187
192
|
DEFAULT_ARRAY_TYPE_MAP = PG.make_shareable({
|
|
@@ -190,9 +195,8 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
|
190
195
|
Integer => [0, '_int8'],
|
|
191
196
|
String => [0, '_text'],
|
|
192
197
|
Float => [0, '_float8'],
|
|
193
|
-
BigDecimal => [0, '_numeric'],
|
|
194
198
|
Time => [0, '_timestamptz'],
|
|
195
199
|
IPAddr => [0, '_inet'],
|
|
196
|
-
})
|
|
200
|
+
}.merge(has_bigdecimal ? {BigDecimal => [0, '_numeric']} : {}))
|
|
197
201
|
private_constant :DEFAULT_ARRAY_TYPE_MAP
|
|
198
202
|
end
|
|
@@ -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
|