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_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
|
@@ -12,6 +12,7 @@ static VALUE sym_symbol, sym_string, sym_static_symbol;
|
|
|
12
12
|
static VALUE pgresult_type_map_set( VALUE, VALUE );
|
|
13
13
|
static t_pg_result *pgresult_get_this( VALUE );
|
|
14
14
|
static t_pg_result *pgresult_get_this_safe( VALUE );
|
|
15
|
+
static void ensure_init_for_tuple(VALUE self);
|
|
15
16
|
|
|
16
17
|
#if defined(HAVE_PQRESULTMEMORYSIZE)
|
|
17
18
|
|
|
@@ -114,7 +115,6 @@ pgresult_gc_mark( void *_this )
|
|
|
114
115
|
|
|
115
116
|
rb_gc_mark_movable( this->connection );
|
|
116
117
|
rb_gc_mark_movable( this->typemap );
|
|
117
|
-
rb_gc_mark_movable( this->tuple_hash );
|
|
118
118
|
rb_gc_mark_movable( this->field_map );
|
|
119
119
|
|
|
120
120
|
for( i=0; i < this->nfields; i++ ){
|
|
@@ -130,7 +130,6 @@ pgresult_gc_compact( void *_this )
|
|
|
130
130
|
|
|
131
131
|
pg_gc_location( this->connection );
|
|
132
132
|
pg_gc_location( this->typemap );
|
|
133
|
-
pg_gc_location( this->tuple_hash );
|
|
134
133
|
pg_gc_location( this->field_map );
|
|
135
134
|
|
|
136
135
|
for( i=0; i < this->nfields; i++ ){
|
|
@@ -147,9 +146,7 @@ pgresult_clear( void *_this )
|
|
|
147
146
|
t_pg_result *this = (t_pg_result *)_this;
|
|
148
147
|
if( this->pgresult && !this->autoclear ){
|
|
149
148
|
PQclear(this->pgresult);
|
|
150
|
-
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
151
149
|
rb_gc_adjust_memory_usage(-this->result_size);
|
|
152
|
-
#endif
|
|
153
150
|
}
|
|
154
151
|
this->result_size = 0;
|
|
155
152
|
this->nfields = -1;
|
|
@@ -180,7 +177,7 @@ static const rb_data_type_t pgresult_type = {
|
|
|
180
177
|
pgresult_gc_mark,
|
|
181
178
|
pgresult_gc_free,
|
|
182
179
|
pgresult_memsize,
|
|
183
|
-
|
|
180
|
+
pgresult_gc_compact,
|
|
184
181
|
},
|
|
185
182
|
0, 0,
|
|
186
183
|
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
|
@@ -214,7 +211,6 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
|
214
211
|
this->typemap = pg_typemap_all_strings;
|
|
215
212
|
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
216
213
|
this->nfields = -1;
|
|
217
|
-
this->tuple_hash = Qnil;
|
|
218
214
|
this->field_map = Qnil;
|
|
219
215
|
this->flags = 0;
|
|
220
216
|
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
|
@@ -253,9 +249,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
|
253
249
|
*/
|
|
254
250
|
this->result_size = pgresult_approx_size(result);
|
|
255
251
|
|
|
256
|
-
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
257
252
|
rb_gc_adjust_memory_usage(this->result_size);
|
|
258
|
-
#endif
|
|
259
253
|
|
|
260
254
|
return self;
|
|
261
255
|
}
|
|
@@ -323,6 +317,9 @@ pg_result_check( VALUE self )
|
|
|
323
317
|
case PGRES_COMMAND_OK:
|
|
324
318
|
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
325
319
|
case PGRES_PIPELINE_SYNC:
|
|
320
|
+
#endif
|
|
321
|
+
#ifdef HAVE_PQSETCHUNKEDROWSMODE
|
|
322
|
+
case PGRES_TUPLES_CHUNK:
|
|
326
323
|
#endif
|
|
327
324
|
return self;
|
|
328
325
|
case PGRES_BAD_RESPONSE:
|
|
@@ -397,6 +394,7 @@ pg_result_freeze(VALUE self)
|
|
|
397
394
|
{
|
|
398
395
|
t_pg_result *this = pgresult_get_this(self);
|
|
399
396
|
|
|
397
|
+
ensure_init_for_tuple(self);
|
|
400
398
|
RB_OBJ_WRITE(self, &this->connection, Qnil);
|
|
401
399
|
return rb_call_super(0, NULL);
|
|
402
400
|
}
|
|
@@ -514,20 +512,46 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
514
512
|
*
|
|
515
513
|
* The class to represent the query result tuples (rows).
|
|
516
514
|
* An instance of this class is created as the result of every query.
|
|
517
|
-
* All result rows and columns are stored in
|
|
518
|
-
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
|
519
|
-
*
|
|
520
|
-
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
|
521
|
-
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
515
|
+
* All result rows and columns are stored in an immutable memory block attached to the PG::Result object unless streaming is used.
|
|
522
516
|
*
|
|
523
|
-
*
|
|
517
|
+
* A PG::Result has various ways to retrieve the result data:
|
|
524
518
|
* require 'pg'
|
|
525
|
-
* conn = PG.connect(:
|
|
526
|
-
* res
|
|
527
|
-
* res.
|
|
528
|
-
* res
|
|
529
|
-
* res
|
|
519
|
+
* conn = PG.connect(dbname: 'test')
|
|
520
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
521
|
+
* res.num_fields # 3
|
|
522
|
+
* res.num_tuples # 1
|
|
523
|
+
* res.fname(2) # "c"
|
|
524
|
+
* res.fields # ["a", "b", "c"]
|
|
525
|
+
* res.getvalue(0,0) # '1'
|
|
526
|
+
* res[0] # {"a" => "1", "b" => "2", "c" => "3"}
|
|
527
|
+
* res.tuple_values(0) # ["1", "2", nil]
|
|
528
|
+
* res.tuple(0) # #<PG::Tuple a: "1", b: "2", c: nil>
|
|
529
|
+
* res.values # [["1", "2", nil]]
|
|
530
|
+
* res.field_values(:a) # ["1"]
|
|
531
|
+
* res.column_values(1) # ["2"]
|
|
532
|
+
* res.each.first # {"a" => "1", "b" => "2", "c" => nil}
|
|
533
|
+
* res.each_row.first # ["1", "2", nil]
|
|
534
|
+
*
|
|
535
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map which is PG::TypeMapAllStrings by default.
|
|
536
|
+
* Similarly field names can be retrieved either as strings (default) or as symbols which can be switched per #field_name_type= .
|
|
537
|
+
*
|
|
538
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
539
|
+
* res.type_map = PG::TypeMapByColumn.new([PG::TextDecoder::Integer.new]*3)
|
|
540
|
+
* res.field_name_type = :symbol
|
|
541
|
+
* res.fname(2) # :c
|
|
542
|
+
* res.fields # [:a, :b, :c]
|
|
543
|
+
* res.getvalue(0,0) # 1
|
|
544
|
+
* res[0] # {a: 1, b: 2, c: nil}
|
|
545
|
+
* res.tuple_values(0) # [1, 2, nil]
|
|
546
|
+
* res.tuple(0) # #<PG::Tuple a: 1, b: 2, c: nil>
|
|
547
|
+
* res.values # [[1, 2, nil]]
|
|
548
|
+
* res.field_values(:a) # [1]
|
|
549
|
+
* res.column_values(1) # [2]
|
|
550
|
+
* res.each.first # {a: 1, b: 2, c: nil}
|
|
551
|
+
* res.each_row.first # [1, 2, nil]
|
|
530
552
|
*
|
|
553
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
|
554
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
531
555
|
*/
|
|
532
556
|
|
|
533
557
|
/**************************************************************************
|
|
@@ -549,6 +573,7 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
549
573
|
* * +PGRES_FATAL_ERROR+
|
|
550
574
|
* * +PGRES_COPY_BOTH+
|
|
551
575
|
* * +PGRES_SINGLE_TUPLE+
|
|
576
|
+
* * +PGRES_TUPLES_CHUNK+
|
|
552
577
|
* * +PGRES_PIPELINE_SYNC+
|
|
553
578
|
* * +PGRES_PIPELINE_ABORTED+
|
|
554
579
|
*
|
|
@@ -613,14 +638,12 @@ pgresult_error_message(VALUE self)
|
|
|
613
638
|
return ret;
|
|
614
639
|
}
|
|
615
640
|
|
|
616
|
-
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
617
641
|
/*
|
|
618
642
|
* call-seq:
|
|
619
643
|
* res.verbose_error_message( verbosity, show_context ) -> String
|
|
620
644
|
*
|
|
621
|
-
* Returns a reformatted version of the error message associated with
|
|
645
|
+
* Returns a reformatted version of the error message associated with the PG::Result object.
|
|
622
646
|
*
|
|
623
|
-
* Available since PostgreSQL-9.6
|
|
624
647
|
*/
|
|
625
648
|
static VALUE
|
|
626
649
|
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
@@ -639,7 +662,6 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
|
639
662
|
|
|
640
663
|
return ret;
|
|
641
664
|
}
|
|
642
|
-
#endif
|
|
643
665
|
|
|
644
666
|
/*
|
|
645
667
|
* call-seq:
|
|
@@ -664,7 +686,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
|
664
686
|
* An example:
|
|
665
687
|
*
|
|
666
688
|
* begin
|
|
667
|
-
* conn.exec( "SELECT * FROM
|
|
689
|
+
* conn.exec( "SELECT * FROM nonexistent_table" )
|
|
668
690
|
* rescue PG::Error => err
|
|
669
691
|
* p [
|
|
670
692
|
* err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
|
@@ -684,7 +706,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
|
684
706
|
*
|
|
685
707
|
* Outputs:
|
|
686
708
|
*
|
|
687
|
-
* ["ERROR", "42P01", "relation \"
|
|
709
|
+
* ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
|
|
688
710
|
* "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
|
|
689
711
|
*/
|
|
690
712
|
static VALUE
|
|
@@ -708,6 +730,10 @@ pgresult_error_field(VALUE self, VALUE field)
|
|
|
708
730
|
* res.ntuples() -> Integer
|
|
709
731
|
*
|
|
710
732
|
* Returns the number of tuples in the query result.
|
|
733
|
+
*
|
|
734
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
735
|
+
* res.ntuples # 1
|
|
736
|
+
* res.num_tuples # 1
|
|
711
737
|
*/
|
|
712
738
|
static VALUE
|
|
713
739
|
pgresult_ntuples(VALUE self)
|
|
@@ -726,6 +752,9 @@ pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
|
|
726
752
|
* res.nfields() -> Integer
|
|
727
753
|
*
|
|
728
754
|
* Returns the number of columns in the query result.
|
|
755
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
756
|
+
* res.nfields # 3
|
|
757
|
+
* res.num_fields # 3
|
|
729
758
|
*/
|
|
730
759
|
static VALUE
|
|
731
760
|
pgresult_nfields(VALUE self)
|
|
@@ -737,9 +766,9 @@ pgresult_nfields(VALUE self)
|
|
|
737
766
|
* call-seq:
|
|
738
767
|
* res.binary_tuples() -> Integer
|
|
739
768
|
*
|
|
740
|
-
* Returns 1 if the
|
|
769
|
+
* Returns 1 if the PG::Result contains binary data and 0 if it contains text data.
|
|
741
770
|
*
|
|
742
|
-
* This function is deprecated (except for its use in connection with COPY), because it is possible for a single
|
|
771
|
+
* This function is deprecated (except for its use in connection with COPY), because it is possible for a single PG::Result to contain text data in some columns and binary data in others.
|
|
743
772
|
* Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
|
|
744
773
|
*/
|
|
745
774
|
static VALUE
|
|
@@ -755,6 +784,8 @@ pgresult_binary_tuples(VALUE self)
|
|
|
755
784
|
* Returns the name of the column corresponding to _index_.
|
|
756
785
|
* Depending on #field_name_type= it's a String or Symbol.
|
|
757
786
|
*
|
|
787
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
788
|
+
* res.fname(2) # "c"
|
|
758
789
|
*/
|
|
759
790
|
static VALUE
|
|
760
791
|
pgresult_fname(VALUE self, VALUE index)
|
|
@@ -1125,6 +1156,9 @@ pgresult_oid_value(VALUE self)
|
|
|
1125
1156
|
* res[ n ] -> Hash
|
|
1126
1157
|
*
|
|
1127
1158
|
* Returns tuple _n_ as a hash.
|
|
1159
|
+
*
|
|
1160
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1161
|
+
* res[0] # {"a" => "1", "b" => "2", "c" => "3"}
|
|
1128
1162
|
*/
|
|
1129
1163
|
static VALUE
|
|
1130
1164
|
pgresult_aref(VALUE self, VALUE index)
|
|
@@ -1141,16 +1175,11 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
1141
1175
|
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1142
1176
|
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1143
1177
|
|
|
1144
|
-
|
|
1145
|
-
* This is somewhat faster than populating an empty Hash object. */
|
|
1146
|
-
tuple = NIL_P(this->tuple_hash) ? rb_hash_new() : this->tuple_hash;
|
|
1178
|
+
tuple = rb_hash_new_capa(this->nfields);
|
|
1147
1179
|
for ( field_num = 0; field_num < this->nfields; field_num++ ) {
|
|
1148
1180
|
VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field_num);
|
|
1149
1181
|
rb_hash_aset( tuple, this->fnames[field_num], val );
|
|
1150
1182
|
}
|
|
1151
|
-
/* Store a copy of the filled hash for use at the next row. */
|
|
1152
|
-
if( num_tuples > 10 )
|
|
1153
|
-
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
|
1154
1183
|
|
|
1155
1184
|
return tuple;
|
|
1156
1185
|
}
|
|
@@ -1159,7 +1188,10 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
1159
1188
|
* call-seq:
|
|
1160
1189
|
* res.each_row { |row| ... }
|
|
1161
1190
|
*
|
|
1162
|
-
* Yields
|
|
1191
|
+
* Yields an Array object for each row in the result.
|
|
1192
|
+
*
|
|
1193
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1194
|
+
* res.each_row.first # ["1", "2", nil]
|
|
1163
1195
|
*/
|
|
1164
1196
|
static VALUE
|
|
1165
1197
|
pgresult_each_row(VALUE self)
|
|
@@ -1194,6 +1226,9 @@ pgresult_each_row(VALUE self)
|
|
|
1194
1226
|
* res.values -> Array
|
|
1195
1227
|
*
|
|
1196
1228
|
* Returns all tuples as an array of arrays.
|
|
1229
|
+
*
|
|
1230
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1231
|
+
* res.values # [["1", "2", nil]]
|
|
1197
1232
|
*/
|
|
1198
1233
|
static VALUE
|
|
1199
1234
|
pgresult_values(VALUE self)
|
|
@@ -1243,12 +1278,13 @@ make_column_result_array( VALUE self, int col )
|
|
|
1243
1278
|
|
|
1244
1279
|
|
|
1245
1280
|
/*
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1281
|
+
* call-seq:
|
|
1282
|
+
* res.column_values( n ) -> array
|
|
1248
1283
|
*
|
|
1249
|
-
*
|
|
1250
|
-
* tuple in the result.
|
|
1284
|
+
* Returns an Array of the values from the nth column of each tuple in the result.
|
|
1251
1285
|
*
|
|
1286
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1287
|
+
* res.column_values(1) # ["2"]
|
|
1252
1288
|
*/
|
|
1253
1289
|
static VALUE
|
|
1254
1290
|
pgresult_column_values(VALUE self, VALUE index)
|
|
@@ -1259,11 +1295,13 @@ pgresult_column_values(VALUE self, VALUE index)
|
|
|
1259
1295
|
|
|
1260
1296
|
|
|
1261
1297
|
/*
|
|
1262
|
-
*
|
|
1298
|
+
* call-seq:
|
|
1263
1299
|
* res.field_values( field ) -> array
|
|
1264
1300
|
*
|
|
1265
|
-
*
|
|
1301
|
+
* Returns an Array of the values from the given _field_ of each tuple in the result.
|
|
1266
1302
|
*
|
|
1303
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1304
|
+
* res.field_values(:a) # ["1"]
|
|
1267
1305
|
*/
|
|
1268
1306
|
static VALUE
|
|
1269
1307
|
pgresult_field_values( VALUE self, VALUE field )
|
|
@@ -1284,11 +1322,13 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
|
1284
1322
|
|
|
1285
1323
|
|
|
1286
1324
|
/*
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1325
|
+
* call-seq:
|
|
1326
|
+
* res.tuple_values( n ) -> array
|
|
1289
1327
|
*
|
|
1290
|
-
*
|
|
1328
|
+
* Returns an Array of the field values from the nth row of the result.
|
|
1291
1329
|
*
|
|
1330
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1331
|
+
* res.tuple_values(0) # ["1", "2", nil]
|
|
1292
1332
|
*/
|
|
1293
1333
|
static VALUE
|
|
1294
1334
|
pgresult_tuple_values(VALUE self, VALUE index)
|
|
@@ -1323,11 +1363,12 @@ static void ensure_init_for_tuple(VALUE self)
|
|
|
1323
1363
|
|
|
1324
1364
|
if( this->field_map == Qnil ){
|
|
1325
1365
|
int i;
|
|
1326
|
-
VALUE field_map
|
|
1366
|
+
VALUE field_map;
|
|
1327
1367
|
|
|
1328
1368
|
if( this->nfields == -1 )
|
|
1329
1369
|
pgresult_init_fnames( self );
|
|
1330
1370
|
|
|
1371
|
+
field_map = rb_hash_new_capa(this->nfields);
|
|
1331
1372
|
for( i = 0; i < this->nfields; i++ ){
|
|
1332
1373
|
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
|
1333
1374
|
}
|
|
@@ -1337,11 +1378,13 @@ static void ensure_init_for_tuple(VALUE self)
|
|
|
1337
1378
|
}
|
|
1338
1379
|
|
|
1339
1380
|
/*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1381
|
+
* call-seq:
|
|
1382
|
+
* res.tuple( n ) -> PG::Tuple
|
|
1342
1383
|
*
|
|
1343
|
-
*
|
|
1384
|
+
* Returns a PG::Tuple from the nth row of the result.
|
|
1344
1385
|
*
|
|
1386
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1387
|
+
* res.tuple(0) # #<PG::Tuple a: "1", b: "2", c: nil>
|
|
1345
1388
|
*/
|
|
1346
1389
|
static VALUE
|
|
1347
1390
|
pgresult_tuple(VALUE self, VALUE index)
|
|
@@ -1366,7 +1409,10 @@ pgresult_tuple(VALUE self, VALUE index)
|
|
|
1366
1409
|
* call-seq:
|
|
1367
1410
|
* res.each{ |tuple| ... }
|
|
1368
1411
|
*
|
|
1369
|
-
*
|
|
1412
|
+
* Yields a Hash object for each row in the result.
|
|
1413
|
+
*
|
|
1414
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1415
|
+
* res.each.first # {"a" => "1", "b" => "2", "c" => nil}
|
|
1370
1416
|
*/
|
|
1371
1417
|
static VALUE
|
|
1372
1418
|
pgresult_each(VALUE self)
|
|
@@ -1389,6 +1435,9 @@ pgresult_each(VALUE self)
|
|
|
1389
1435
|
* res.fields() -> Array
|
|
1390
1436
|
*
|
|
1391
1437
|
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
1438
|
+
*
|
|
1439
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1440
|
+
* res.fields # ["a", "b", "c"]
|
|
1392
1441
|
*/
|
|
1393
1442
|
static VALUE
|
|
1394
1443
|
pgresult_fields(VALUE self)
|
|
@@ -1405,7 +1454,7 @@ pgresult_fields(VALUE self)
|
|
|
1405
1454
|
* call-seq:
|
|
1406
1455
|
* res.type_map = typemap
|
|
1407
1456
|
*
|
|
1408
|
-
* Set the TypeMap that is used for type casts of result values to ruby objects.
|
|
1457
|
+
* Set the PG::TypeMap that is used for type casts of result values to ruby objects.
|
|
1409
1458
|
*
|
|
1410
1459
|
* All value retrieval methods will respect the type map and will do the
|
|
1411
1460
|
* type casts from PostgreSQL's wire format to Ruby objects on the fly,
|
|
@@ -1413,6 +1462,7 @@ pgresult_fields(VALUE self)
|
|
|
1413
1462
|
*
|
|
1414
1463
|
* +typemap+ must be a kind of PG::TypeMap .
|
|
1415
1464
|
*
|
|
1465
|
+
* See also #map_types! and PG::BasicTypeMapForResults
|
|
1416
1466
|
*/
|
|
1417
1467
|
static VALUE
|
|
1418
1468
|
pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
@@ -1435,7 +1485,8 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1435
1485
|
* call-seq:
|
|
1436
1486
|
* res.type_map -> value
|
|
1437
1487
|
*
|
|
1438
|
-
* Returns the TypeMap that is currently set for type casts of result values to ruby objects.
|
|
1488
|
+
* Returns the PG::TypeMap that is currently set for type casts of result values to ruby objects.
|
|
1489
|
+
* The default is retrieved from PG::Connection#type_map_for_results , which defaults to PG::TypeMapAllStrings .
|
|
1439
1490
|
*
|
|
1440
1491
|
*/
|
|
1441
1492
|
static VALUE
|
|
@@ -1528,6 +1579,9 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
|
1528
1579
|
return self;
|
|
1529
1580
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
1530
1581
|
case PGRES_SINGLE_TUPLE:
|
|
1582
|
+
#ifdef HAVE_PQSETCHUNKEDROWSMODE
|
|
1583
|
+
case PGRES_TUPLES_CHUNK:
|
|
1584
|
+
#endif
|
|
1531
1585
|
break;
|
|
1532
1586
|
default:
|
|
1533
1587
|
pg_result_check( self );
|
|
@@ -1572,8 +1626,8 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
|
1572
1626
|
* wrapping each row into a dedicated result object, it delivers data in nearly
|
|
1573
1627
|
* the same speed as with ordinary results.
|
|
1574
1628
|
*
|
|
1575
|
-
* The base result must be in status PGRES_SINGLE_TUPLE
|
|
1576
|
-
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK
|
|
1629
|
+
* The base result must be in status +PGRES_SINGLE_TUPLE+ or +PGRES_TUPLES_CHUNK+.
|
|
1630
|
+
* It iterates over all tuples until the status changes to +PGRES_TUPLES_OK+.
|
|
1577
1631
|
* A PG::Error is raised for any errors from the server.
|
|
1578
1632
|
*
|
|
1579
1633
|
* Row description data does not change while the iteration. All value retrieval
|
|
@@ -1707,10 +1761,8 @@ init_pg_result(void)
|
|
|
1707
1761
|
rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
|
|
1708
1762
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
|
1709
1763
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
|
1710
|
-
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
1711
1764
|
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
|
1712
1765
|
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
|
1713
|
-
#endif
|
|
1714
1766
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
|
1715
1767
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
|
1716
1768
|
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 ); */
|
|
@@ -577,7 +579,7 @@ pg_text_dec_from_base64(t_pg_coder *conv, const char *val, int len, int tuple, i
|
|
|
577
579
|
/* create a buffer of the expected decoded length */
|
|
578
580
|
VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
|
|
579
581
|
|
|
580
|
-
decoded_len =
|
|
582
|
+
decoded_len = rbpg_base64_decode( RSTRING_PTR(out_value), val, len );
|
|
581
583
|
rb_str_set_len(out_value, decoded_len);
|
|
582
584
|
|
|
583
585
|
/* Is it a pure String conversion? Then we can directly send out_value to the user. */
|
|
@@ -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)
|