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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +80 -0
  4. data/Gemfile +10 -7
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +4 -4
  7. data/README.md +66 -23
  8. data/Rakefile +78 -14
  9. data/ext/errorcodes.def +9 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +8 -1
  12. data/ext/extconf.rb +189 -15
  13. data/ext/gvl_wrappers.c +13 -2
  14. data/ext/gvl_wrappers.h +33 -0
  15. data/ext/pg.c +16 -5
  16. data/ext/pg.h +15 -13
  17. data/ext/pg_binary_decoder.c +151 -1
  18. data/ext/pg_binary_encoder.c +212 -9
  19. data/ext/pg_cancel_connection.c +360 -0
  20. data/ext/pg_coder.c +54 -5
  21. data/ext/pg_connection.c +390 -160
  22. data/ext/pg_copy_coder.c +2 -2
  23. data/ext/pg_record_coder.c +1 -1
  24. data/ext/pg_result.c +104 -52
  25. data/ext/pg_text_decoder.c +1 -1
  26. data/ext/pg_text_encoder.c +22 -9
  27. data/ext/pg_tuple.c +8 -8
  28. data/ext/pg_type_map.c +4 -2
  29. data/ext/pg_type_map_all_strings.c +1 -1
  30. data/ext/pg_type_map_by_class.c +1 -1
  31. data/ext/pg_type_map_by_column.c +2 -1
  32. data/ext/pg_type_map_by_mri_type.c +1 -1
  33. data/ext/pg_type_map_by_oid.c +3 -1
  34. data/ext/pg_type_map_in_ruby.c +1 -1
  35. data/ext/pg_util.c +2 -2
  36. data/ext/pg_util.h +2 -2
  37. data/lib/pg/basic_type_map_for_queries.rb +7 -3
  38. data/lib/pg/basic_type_registry.rb +2 -2
  39. data/lib/pg/cancel_connection.rb +53 -0
  40. data/lib/pg/coder.rb +4 -2
  41. data/lib/pg/connection.rb +254 -131
  42. data/lib/pg/version.rb +2 -1
  43. data/lib/pg.rb +156 -130
  44. data/misc/glibc/Dockerfile +20 -0
  45. data/misc/glibc/docker-compose.yml +9 -0
  46. data/misc/glibc/glibc_spec.rb +5 -0
  47. data/misc/yugabyte/Dockerfile +9 -0
  48. data/misc/yugabyte/docker-compose.yml +28 -0
  49. data/misc/yugabyte/pg-test.rb +45 -0
  50. data/pg.gemspec +5 -3
  51. data/ports/patches/krb5/1.22.1/0001-Allow-static-linking-krb5-library.patch +30 -0
  52. data/ports/patches/krb5/1.22.1/0002-unknown-command-line-option-on-clang.patch +12 -0
  53. data/ports/patches/openssl/3.5.2/0001-aarch64-mingw.patch +21 -0
  54. data/ports/patches/postgresql/18.1/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  55. data/ports/patches/postgresql/18.1/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  56. data/rakelib/pg_gem_helper.rb +64 -0
  57. data.tar.gz.sig +0 -0
  58. metadata +37 -22
  59. metadata.gz.sig +0 -0
  60. data/Manifest.txt +0 -72
  61. 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
- pg_compact_callback(pg_copycoder_compact),
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 */
@@ -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
- pg_compact_callback(pg_recordcoder_compact),
46
+ pg_recordcoder_compact,
47
47
  },
48
48
  &pg_coder_type,
49
49
  0,
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
- pg_compact_callback(pgresult_gc_compact),
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 a memory block attached to the PG::Result object.
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
- * Example:
517
+ * A PG::Result has various ways to retrieve the result data:
524
518
  * require 'pg'
525
- * conn = PG.connect(:dbname => 'test')
526
- * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
527
- * res.getvalue(0,0) # '1'
528
- * res[0]['b'] # '2'
529
- * res[0]['c'] # nil
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 a PGresult object.
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 PGresult contains binary data and 0 if it contains text data.
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 PGresult to contain text data in some columns and binary data in others.
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
- /* We reuse the Hash of the previous output for larger row counts.
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 each row of the result. The row is a list of column values.
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
- * call-seq:
1247
- * res.column_values( n ) -> array
1281
+ * call-seq:
1282
+ * res.column_values( n ) -> array
1248
1283
  *
1249
- * Returns an Array of the values from the nth column of each
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
- * call-seq:
1298
+ * call-seq:
1263
1299
  * res.field_values( field ) -> array
1264
1300
  *
1265
- * Returns an Array of the values from the given _field_ of each tuple in the result.
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
- * call-seq:
1288
- * res.tuple_values( n ) -> array
1325
+ * call-seq:
1326
+ * res.tuple_values( n ) -> array
1289
1327
  *
1290
- * Returns an Array of the field values from the nth row of the result.
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 = rb_hash_new();
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
- * call-seq:
1341
- * res.tuple( n ) -> PG::Tuple
1381
+ * call-seq:
1382
+ * res.tuple( n ) -> PG::Tuple
1342
1383
  *
1343
- * Returns a PG::Tuple from the nth row of the result.
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
- * Invokes block for each tuple in the result set.
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);
@@ -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 = base64_decode( RSTRING_PTR(out_value), val, 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. */
@@ -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 *intermediate, int enc_idx)
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
- base64_encode( out, out, strlen );
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
- base64_encode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
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
- pg_compact_callback(pg_tuple_gc_compact),
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::VeryTuple.allocate -> obj
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
- * +res.tuple(i).values+ is equal to +res.tuple_values(i)+ .
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
- rb_copy_generic_ivar(a, self);
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 = rb_hash_new();
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
- pg_compact_callback(pg_typemap_compact),
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 );
@@ -14,7 +14,7 @@ static const rb_data_type_t pg_tmas_type = {
14
14
  pg_typemap_mark,
15
15
  RUBY_TYPED_DEFAULT_FREE,
16
16
  pg_typemap_memsize,
17
- pg_compact_callback(pg_typemap_compact),
17
+ pg_typemap_compact,
18
18
  },
19
19
  &pg_typemap_type,
20
20
  0,
@@ -153,7 +153,7 @@ static const rb_data_type_t pg_tmbk_type = {
153
153
  pg_tmbk_mark,
154
154
  RUBY_TYPED_DEFAULT_FREE,
155
155
  pg_tmbk_memsize,
156
- pg_compact_callback(pg_tmbk_compact),
156
+ pg_tmbk_compact,
157
157
  },
158
158
  &pg_typemap_type,
159
159
  0,
@@ -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
- pg_compact_callback(pg_tmbc_compact),
232
+ pg_tmbc_compact,
232
233
  },
233
234
  &pg_typemap_type,
234
235
  0,