pg 1.5.9 → 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} +80 -0
- data/Gemfile +10 -7
- data/README-Windows.rdoc +1 -1
- data/README.ja.md +4 -4
- data/README.md +66 -23
- data/Rakefile +78 -14
- data/ext/errorcodes.def +9 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +8 -1
- data/ext/extconf.rb +189 -15
- data/ext/gvl_wrappers.c +13 -2
- data/ext/gvl_wrappers.h +33 -0
- data/ext/pg.c +16 -5
- data/ext/pg.h +15 -13
- data/ext/pg_binary_decoder.c +151 -1
- data/ext/pg_binary_encoder.c +212 -9
- data/ext/pg_cancel_connection.c +360 -0
- data/ext/pg_coder.c +54 -5
- data/ext/pg_connection.c +390 -160
- data/ext/pg_copy_coder.c +2 -2
- data/ext/pg_record_coder.c +1 -1
- data/ext/pg_result.c +104 -52
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +22 -9
- 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 +7 -3
- data/lib/pg/basic_type_registry.rb +2 -2
- data/lib/pg/cancel_connection.rb +53 -0
- data/lib/pg/coder.rb +4 -2
- data/lib/pg/connection.rb +254 -131
- data/lib/pg/version.rb +2 -1
- data/lib/pg.rb +156 -130
- 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 +5 -3
- 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 +37 -22
- metadata.gz.sig +0 -0
- data/Manifest.txt +0 -72
- data/Rakefile.cross +0 -303
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,
|
|
@@ -831,7 +831,6 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
|
831
831
|
|
|
832
832
|
for( fieldno = 0; fieldno < nfields; fieldno++){
|
|
833
833
|
long input_len;
|
|
834
|
-
VALUE field_value;
|
|
835
834
|
|
|
836
835
|
/* read field size */
|
|
837
836
|
if (line_end_ptr - cur_ptr < 4 ) goto length_error;
|
|
@@ -843,6 +842,7 @@ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tupl
|
|
|
843
842
|
/* NULL indicator */
|
|
844
843
|
rb_ary_push(array, Qnil);
|
|
845
844
|
} else {
|
|
845
|
+
VALUE field_value;
|
|
846
846
|
if (line_end_ptr - cur_ptr < input_len ) goto length_error;
|
|
847
847
|
|
|
848
848
|
/* copy input data to field_str */
|
data/ext/pg_record_coder.c
CHANGED
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:
|
|
@@ -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
|
@@ -579,7 +579,7 @@ pg_text_dec_from_base64(t_pg_coder *conv, const char *val, int len, int tuple, i
|
|
|
579
579
|
/* create a buffer of the expected decoded length */
|
|
580
580
|
VALUE out_value = rb_str_new(NULL, BASE64_DECODED_SIZE(len));
|
|
581
581
|
|
|
582
|
-
decoded_len =
|
|
582
|
+
decoded_len = rbpg_base64_decode( RSTRING_PTR(out_value), val, len );
|
|
583
583
|
rb_str_set_len(out_value, decoded_len);
|
|
584
584
|
|
|
585
585
|
/* Is it a pure String conversion? Then we can directly send out_value to the user. */
|
data/ext/pg_text_encoder.c
CHANGED
|
@@ -231,7 +231,7 @@ pg_text_enc_integer(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
|
231
231
|
*
|
|
232
232
|
*/
|
|
233
233
|
static int
|
|
234
|
-
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)
|
|
235
235
|
{
|
|
236
236
|
if(out){
|
|
237
237
|
double dvalue = NUM2DBL(value);
|
|
@@ -239,7 +239,6 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
239
239
|
int neg = 0;
|
|
240
240
|
int exp2i, exp10i, i;
|
|
241
241
|
unsigned long long ll, remainder, oldval;
|
|
242
|
-
VALUE intermediate;
|
|
243
242
|
|
|
244
243
|
/* Cast to the same strings as value.to_s . */
|
|
245
244
|
if( isinf(dvalue) ){
|
|
@@ -283,6 +282,7 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
283
282
|
|
|
284
283
|
if( exp10i <= -5 || exp10i >= 15 ) {
|
|
285
284
|
/* Write the float in exponent format (1.23e45) */
|
|
285
|
+
VALUE intermediate;
|
|
286
286
|
|
|
287
287
|
/* write fraction digits from right to left */
|
|
288
288
|
for( i = MAX_DOUBLE_DIGITS; i > 1; i--){
|
|
@@ -537,7 +537,7 @@ quote_string(t_pg_coder *this, VALUE value, VALUE string, char *current_out, int
|
|
|
537
537
|
}
|
|
538
538
|
|
|
539
539
|
static char *
|
|
540
|
-
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)
|
|
541
541
|
{
|
|
542
542
|
int i;
|
|
543
543
|
|
|
@@ -545,6 +545,10 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
545
545
|
current_out = pg_rb_str_ensure_capa( string, 2, current_out, NULL );
|
|
546
546
|
*current_out++ = '{';
|
|
547
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
|
+
|
|
548
552
|
for( i=0; i<RARRAY_LEN(value); i++){
|
|
549
553
|
VALUE entry = rb_ary_entry(value, i);
|
|
550
554
|
|
|
@@ -554,17 +558,26 @@ write_array(t_pg_composite_coder *this, VALUE value, char *current_out, VALUE st
|
|
|
554
558
|
}
|
|
555
559
|
|
|
556
560
|
switch(TYPE(entry)){
|
|
557
|
-
case T_ARRAY:
|
|
558
|
-
current_out = write_array(this, entry, current_out, string, quote, enc_idx);
|
|
559
|
-
break;
|
|
560
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
|
+
}
|
|
561
565
|
current_out = pg_rb_str_ensure_capa( string, 4, current_out, NULL );
|
|
562
566
|
*current_out++ = 'N';
|
|
563
567
|
*current_out++ = 'U';
|
|
564
568
|
*current_out++ = 'L';
|
|
565
569
|
*current_out++ = 'L';
|
|
566
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 */
|
|
567
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
|
+
}
|
|
568
581
|
current_out = quote_string( this->elem, entry, string, current_out, quote, quote_array_buffer, this, enc_idx );
|
|
569
582
|
}
|
|
570
583
|
}
|
|
@@ -596,7 +609,7 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
596
609
|
VALUE out_str = rb_str_new(NULL, 0);
|
|
597
610
|
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
598
611
|
|
|
599
|
-
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);
|
|
600
613
|
|
|
601
614
|
rb_str_set_len( out_str, end_ptr - RSTRING_PTR(out_str) );
|
|
602
615
|
*intermediate = out_str;
|
|
@@ -771,7 +784,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
771
784
|
if(out){
|
|
772
785
|
/* Second encoder pass, if required */
|
|
773
786
|
strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
|
|
774
|
-
|
|
787
|
+
rbpg_base64_encode( out, out, strlen );
|
|
775
788
|
|
|
776
789
|
return BASE64_ENCODED_SIZE(strlen);
|
|
777
790
|
} else {
|
|
@@ -786,7 +799,7 @@ pg_text_enc_to_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermedi
|
|
|
786
799
|
out_str = rb_str_new(NULL, BASE64_ENCODED_SIZE(strlen));
|
|
787
800
|
PG_ENCODING_SET_NOCHECK(out_str, enc_idx);
|
|
788
801
|
|
|
789
|
-
|
|
802
|
+
rbpg_base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
|
|
790
803
|
*intermediate = out_str;
|
|
791
804
|
|
|
792
805
|
return -1;
|
data/ext/pg_tuple.c
CHANGED
|
@@ -125,7 +125,7 @@ static const rb_data_type_t pg_tuple_type = {
|
|
|
125
125
|
pg_tuple_gc_mark,
|
|
126
126
|
pg_tuple_gc_free,
|
|
127
127
|
pg_tuple_memsize,
|
|
128
|
-
|
|
128
|
+
pg_tuple_gc_compact,
|
|
129
129
|
},
|
|
130
130
|
0, 0,
|
|
131
131
|
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
|
@@ -135,7 +135,7 @@ static const rb_data_type_t pg_tuple_type = {
|
|
|
135
135
|
* Document-method: allocate
|
|
136
136
|
*
|
|
137
137
|
* call-seq:
|
|
138
|
-
* PG::
|
|
138
|
+
* PG::Tuple.allocate -> obj
|
|
139
139
|
*/
|
|
140
140
|
static VALUE
|
|
141
141
|
pg_tuple_s_allocate( VALUE klass )
|
|
@@ -242,10 +242,10 @@ pg_tuple_materialize(VALUE self)
|
|
|
242
242
|
* An integer +key+ is interpreted as column index.
|
|
243
243
|
* Negative values of index count from the end of the array.
|
|
244
244
|
*
|
|
245
|
-
* Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
245
|
+
* Depending on PG::Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
246
246
|
*
|
|
247
247
|
* If the key can't be found, there are several options:
|
|
248
|
-
* With no other arguments, it will raise a IndexError exception;
|
|
248
|
+
* With no other arguments, it will raise a +IndexError+ exception;
|
|
249
249
|
* if default is given, then that will be returned;
|
|
250
250
|
* if the optional code block is specified, then that will be run and its result returned.
|
|
251
251
|
*/
|
|
@@ -302,7 +302,7 @@ pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
|
|
|
302
302
|
* An integer +key+ is interpreted as column index.
|
|
303
303
|
* Negative values of index count from the end of the array.
|
|
304
304
|
*
|
|
305
|
-
* Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
305
|
+
* Depending on PG::Result#field_name_type= a string or symbol +key+ is interpreted as column name.
|
|
306
306
|
*
|
|
307
307
|
* If the key can't be found, it returns +nil+ .
|
|
308
308
|
*/
|
|
@@ -405,7 +405,7 @@ pg_tuple_each_value(VALUE self)
|
|
|
405
405
|
* tup.values -> Array
|
|
406
406
|
*
|
|
407
407
|
* Returns the values of this tuple as Array.
|
|
408
|
-
*
|
|
408
|
+
* <tt>res.tuple(i).values</tt> is equal to <tt>res.tuple_values(i)</tt> .
|
|
409
409
|
*/
|
|
410
410
|
static VALUE
|
|
411
411
|
pg_tuple_values(VALUE self)
|
|
@@ -474,7 +474,7 @@ pg_tuple_dump(VALUE self)
|
|
|
474
474
|
values = rb_ary_new4(this->num_fields, &this->values[0]);
|
|
475
475
|
a = rb_ary_new3(2, field_names, values);
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
rb_copy_generic_ivar(a, self);
|
|
478
478
|
|
|
479
479
|
return a;
|
|
480
480
|
}
|
|
@@ -510,7 +510,7 @@ pg_tuple_load(VALUE self, VALUE a)
|
|
|
510
510
|
if (RARRAY_LENINT(field_names) != num_fields)
|
|
511
511
|
rb_raise(rb_eTypeError, "different number of fields and values");
|
|
512
512
|
|
|
513
|
-
field_map =
|
|
513
|
+
field_map = rb_hash_new_capa(num_fields);
|
|
514
514
|
for( i = 0; i < num_fields; i++ ){
|
|
515
515
|
rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
|
|
516
516
|
}
|
data/ext/pg_type_map.c
CHANGED
|
@@ -33,7 +33,7 @@ const rb_data_type_t pg_typemap_type = {
|
|
|
33
33
|
pg_typemap_mark,
|
|
34
34
|
RUBY_TYPED_DEFAULT_FREE,
|
|
35
35
|
pg_typemap_memsize,
|
|
36
|
-
|
|
36
|
+
pg_typemap_compact,
|
|
37
37
|
},
|
|
38
38
|
0,
|
|
39
39
|
0,
|
|
@@ -187,7 +187,9 @@ init_pg_type_map(void)
|
|
|
187
187
|
*
|
|
188
188
|
* This is the base class for type maps.
|
|
189
189
|
* See derived classes for implementations of different type cast strategies
|
|
190
|
-
* ( PG::TypeMapByColumn, PG::TypeMapByOid ).
|
|
190
|
+
* ( PG::TypeMapByColumn, PG::TypeMapByOid, etc.).
|
|
191
|
+
*
|
|
192
|
+
* Find more type maps in the {README}[rdoc-ref:README.md@Type+Casts].
|
|
191
193
|
*
|
|
192
194
|
*/
|
|
193
195
|
rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
|
data/ext/pg_type_map_by_class.c
CHANGED
data/ext/pg_type_map_by_column.c
CHANGED
|
@@ -54,6 +54,7 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
|
|
|
54
54
|
t_tmbc *this = RTYPEDDATA_DATA( self );
|
|
55
55
|
t_typemap *default_tm;
|
|
56
56
|
|
|
57
|
+
Check_Type(params, T_ARRAY);
|
|
57
58
|
nfields = (int)RARRAY_LEN( params );
|
|
58
59
|
if ( this->nfields != nfields ) {
|
|
59
60
|
rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
|
|
@@ -228,7 +229,7 @@ static const rb_data_type_t pg_tmbc_type = {
|
|
|
228
229
|
pg_tmbc_mark,
|
|
229
230
|
pg_tmbc_free,
|
|
230
231
|
pg_tmbc_memsize,
|
|
231
|
-
|
|
232
|
+
pg_tmbc_compact,
|
|
232
233
|
},
|
|
233
234
|
&pg_typemap_type,
|
|
234
235
|
0,
|