pg 1.2.3 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +986 -0
  4. data/Gemfile +23 -0
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +300 -0
  7. data/README.md +327 -0
  8. data/Rakefile +123 -144
  9. data/certs/ged.pem +24 -0
  10. data/certs/kanis@comcard.de.pem +20 -0
  11. data/certs/larskanis-2022.pem +26 -0
  12. data/certs/larskanis-2023.pem +24 -0
  13. data/certs/larskanis-2024.pem +24 -0
  14. data/ext/errorcodes.def +16 -5
  15. data/ext/errorcodes.rb +0 -0
  16. data/ext/errorcodes.txt +5 -5
  17. data/ext/extconf.rb +259 -33
  18. data/ext/gvl_wrappers.c +17 -2
  19. data/ext/gvl_wrappers.h +56 -0
  20. data/ext/pg.c +89 -63
  21. data/ext/pg.h +31 -8
  22. data/ext/pg_binary_decoder.c +232 -1
  23. data/ext/pg_binary_encoder.c +428 -1
  24. data/ext/pg_cancel_connection.c +360 -0
  25. data/ext/pg_coder.c +148 -36
  26. data/ext/pg_connection.c +1365 -817
  27. data/ext/pg_copy_coder.c +360 -38
  28. data/ext/pg_errors.c +1 -1
  29. data/ext/pg_record_coder.c +56 -25
  30. data/ext/pg_result.c +187 -76
  31. data/ext/pg_text_decoder.c +32 -11
  32. data/ext/pg_text_encoder.c +65 -33
  33. data/ext/pg_tuple.c +84 -61
  34. data/ext/pg_type_map.c +44 -10
  35. data/ext/pg_type_map_all_strings.c +17 -3
  36. data/ext/pg_type_map_by_class.c +54 -27
  37. data/ext/pg_type_map_by_column.c +74 -31
  38. data/ext/pg_type_map_by_mri_type.c +48 -19
  39. data/ext/pg_type_map_by_oid.c +61 -27
  40. data/ext/pg_type_map_in_ruby.c +55 -21
  41. data/ext/pg_util.c +2 -2
  42. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  43. data/lib/pg/basic_type_map_for_queries.rb +206 -0
  44. data/lib/pg/basic_type_map_for_results.rb +104 -0
  45. data/lib/pg/basic_type_registry.rb +311 -0
  46. data/lib/pg/binary_decoder/date.rb +9 -0
  47. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  48. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  49. data/lib/pg/cancel_connection.rb +53 -0
  50. data/lib/pg/coder.rb +18 -14
  51. data/lib/pg/connection.rb +894 -91
  52. data/lib/pg/exceptions.rb +20 -1
  53. data/lib/pg/text_decoder/date.rb +21 -0
  54. data/lib/pg/text_decoder/inet.rb +9 -0
  55. data/lib/pg/text_decoder/json.rb +17 -0
  56. data/lib/pg/text_decoder/numeric.rb +9 -0
  57. data/lib/pg/text_decoder/timestamp.rb +30 -0
  58. data/lib/pg/text_encoder/date.rb +13 -0
  59. data/lib/pg/text_encoder/inet.rb +31 -0
  60. data/lib/pg/text_encoder/json.rb +17 -0
  61. data/lib/pg/text_encoder/numeric.rb +9 -0
  62. data/lib/pg/text_encoder/timestamp.rb +24 -0
  63. data/lib/pg/version.rb +4 -0
  64. data/lib/pg.rb +109 -39
  65. data/misc/openssl-pg-segfault.rb +31 -0
  66. data/misc/postgres/History.txt +9 -0
  67. data/misc/postgres/Manifest.txt +5 -0
  68. data/misc/postgres/README.txt +21 -0
  69. data/misc/postgres/Rakefile +21 -0
  70. data/misc/postgres/lib/postgres.rb +16 -0
  71. data/misc/ruby-pg/History.txt +9 -0
  72. data/misc/ruby-pg/Manifest.txt +5 -0
  73. data/misc/ruby-pg/README.txt +21 -0
  74. data/misc/ruby-pg/Rakefile +21 -0
  75. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  76. data/misc/yugabyte/Dockerfile +9 -0
  77. data/misc/yugabyte/docker-compose.yml +28 -0
  78. data/misc/yugabyte/pg-test.rb +45 -0
  79. data/pg.gemspec +38 -0
  80. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  81. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  82. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  83. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  84. data/rakelib/pg_gem_helper.rb +64 -0
  85. data/rakelib/task_extension.rb +46 -0
  86. data/sample/array_insert.rb +20 -0
  87. data/sample/async_api.rb +102 -0
  88. data/sample/async_copyto.rb +39 -0
  89. data/sample/async_mixed.rb +56 -0
  90. data/sample/check_conn.rb +21 -0
  91. data/sample/copydata.rb +71 -0
  92. data/sample/copyfrom.rb +81 -0
  93. data/sample/copyto.rb +19 -0
  94. data/sample/cursor.rb +21 -0
  95. data/sample/disk_usage_report.rb +177 -0
  96. data/sample/issue-119.rb +94 -0
  97. data/sample/losample.rb +69 -0
  98. data/sample/minimal-testcase.rb +17 -0
  99. data/sample/notify_wait.rb +72 -0
  100. data/sample/pg_statistics.rb +285 -0
  101. data/sample/replication_monitor.rb +222 -0
  102. data/sample/test_binary_values.rb +33 -0
  103. data/sample/wal_shipper.rb +434 -0
  104. data/sample/warehouse_partitions.rb +311 -0
  105. data.tar.gz.sig +0 -0
  106. metadata +139 -213
  107. metadata.gz.sig +0 -0
  108. data/.gemtest +0 -0
  109. data/ChangeLog +0 -0
  110. data/History.rdoc +0 -578
  111. data/Manifest.txt +0 -73
  112. data/README.ja.rdoc +0 -13
  113. data/README.rdoc +0 -213
  114. data/Rakefile.cross +0 -299
  115. data/lib/pg/basic_type_mapping.rb +0 -522
  116. data/lib/pg/binary_decoder.rb +0 -23
  117. data/lib/pg/constants.rb +0 -12
  118. data/lib/pg/text_decoder.rb +0 -46
  119. data/lib/pg/text_encoder.rb +0 -59
  120. data/spec/data/expected_trace.out +0 -26
  121. data/spec/data/random_binary_data +0 -0
  122. data/spec/helpers.rb +0 -380
  123. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  124. data/spec/pg/connection_spec.rb +0 -1949
  125. data/spec/pg/connection_sync_spec.rb +0 -41
  126. data/spec/pg/result_spec.rb +0 -681
  127. data/spec/pg/tuple_spec.rb +0 -333
  128. data/spec/pg/type_map_by_class_spec.rb +0 -138
  129. data/spec/pg/type_map_by_column_spec.rb +0 -226
  130. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  131. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  132. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  133. data/spec/pg/type_map_spec.rb +0 -22
  134. data/spec/pg/type_spec.rb +0 -1123
  135. data/spec/pg_spec.rb +0 -50
data/ext/pg_result.c CHANGED
@@ -107,17 +107,34 @@ pgresult_approx_size(const PGresult *result)
107
107
  * GC Mark function
108
108
  */
109
109
  static void
110
- pgresult_gc_mark( t_pg_result *this )
110
+ pgresult_gc_mark( void *_this )
111
111
  {
112
+ t_pg_result *this = (t_pg_result *)_this;
112
113
  int i;
113
114
 
114
- rb_gc_mark( this->connection );
115
- rb_gc_mark( this->typemap );
116
- rb_gc_mark( this->tuple_hash );
117
- rb_gc_mark( this->field_map );
115
+ rb_gc_mark_movable( this->connection );
116
+ rb_gc_mark_movable( this->typemap );
117
+ rb_gc_mark_movable( this->tuple_hash );
118
+ rb_gc_mark_movable( this->field_map );
118
119
 
119
120
  for( i=0; i < this->nfields; i++ ){
120
- rb_gc_mark( this->fnames[i] );
121
+ rb_gc_mark_movable( this->fnames[i] );
122
+ }
123
+ }
124
+
125
+ static void
126
+ pgresult_gc_compact( void *_this )
127
+ {
128
+ t_pg_result *this = (t_pg_result *)_this;
129
+ int i;
130
+
131
+ pg_gc_location( this->connection );
132
+ pg_gc_location( this->typemap );
133
+ pg_gc_location( this->tuple_hash );
134
+ pg_gc_location( this->field_map );
135
+
136
+ for( i=0; i < this->nfields; i++ ){
137
+ pg_gc_location( this->fnames[i] );
121
138
  }
122
139
  }
123
140
 
@@ -125,13 +142,12 @@ pgresult_gc_mark( t_pg_result *this )
125
142
  * GC Free function
126
143
  */
127
144
  static void
128
- pgresult_clear( t_pg_result *this )
145
+ pgresult_clear( void *_this )
129
146
  {
147
+ t_pg_result *this = (t_pg_result *)_this;
130
148
  if( this->pgresult && !this->autoclear ){
131
149
  PQclear(this->pgresult);
132
- #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
133
150
  rb_gc_adjust_memory_usage(-this->result_size);
134
- #endif
135
151
  }
136
152
  this->result_size = 0;
137
153
  this->nfields = -1;
@@ -139,15 +155,17 @@ pgresult_clear( t_pg_result *this )
139
155
  }
140
156
 
141
157
  static void
142
- pgresult_gc_free( t_pg_result *this )
158
+ pgresult_gc_free( void *_this )
143
159
  {
160
+ t_pg_result *this = (t_pg_result *)_this;
144
161
  pgresult_clear( this );
145
162
  xfree(this);
146
163
  }
147
164
 
148
165
  static size_t
149
- pgresult_memsize( t_pg_result *this )
166
+ pgresult_memsize( const void *_this )
150
167
  {
168
+ const t_pg_result *this = (const t_pg_result *)_this;
151
169
  /* Ideally the memory 'this' is pointing to should be taken into account as well.
152
170
  * However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
153
171
  */
@@ -155,16 +173,15 @@ pgresult_memsize( t_pg_result *this )
155
173
  }
156
174
 
157
175
  static const rb_data_type_t pgresult_type = {
158
- "pg",
176
+ "PG::Result",
159
177
  {
160
- (void (*)(void*))pgresult_gc_mark,
161
- (void (*)(void*))pgresult_gc_free,
162
- (size_t (*)(const void *))pgresult_memsize,
178
+ pgresult_gc_mark,
179
+ pgresult_gc_free,
180
+ pgresult_memsize,
181
+ pgresult_gc_compact,
163
182
  },
164
183
  0, 0,
165
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
166
- RUBY_TYPED_FREE_IMMEDIATELY,
167
- #endif
184
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
168
185
  };
169
186
 
170
187
  /* Needed by sequel_pg gem, do not delete */
@@ -189,9 +206,11 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
189
206
 
190
207
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
191
208
  this->pgresult = result;
209
+ /* Initialize connection and typemap prior to any object allocations,
210
+ * to make sure valid objects are marked. */
192
211
  this->connection = rb_pgconn;
193
212
  this->typemap = pg_typemap_all_strings;
194
- this->p_typemap = DATA_PTR( this->typemap );
213
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
195
214
  this->nfields = -1;
196
215
  this->tuple_hash = Qnil;
197
216
  this->field_map = Qnil;
@@ -202,11 +221,12 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
202
221
  t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
203
222
  VALUE typemap = p_conn->type_map_for_results;
204
223
  /* Type check is done when assigned to PG::Connection. */
205
- t_typemap *p_typemap = DATA_PTR(typemap);
224
+ t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
206
225
 
207
226
  this->enc_idx = p_conn->enc_idx;
208
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
209
- this->p_typemap = DATA_PTR( this->typemap );
227
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
228
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
229
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
210
230
  this->flags = p_conn->flags;
211
231
  } else {
212
232
  this->enc_idx = rb_locale_encindex();
@@ -231,9 +251,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
231
251
  */
232
252
  this->result_size = pgresult_approx_size(result);
233
253
 
234
- #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
235
254
  rb_gc_adjust_memory_usage(this->result_size);
236
- #endif
237
255
 
238
256
  return self;
239
257
  }
@@ -270,7 +288,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
270
288
  * call-seq:
271
289
  * res.check -> nil
272
290
  *
273
- * Raises appropriate exception if PG::Result is in a bad state.
291
+ * Raises appropriate exception if PG::Result is in a bad state, which is:
292
+ * * +PGRES_BAD_RESPONSE+
293
+ * * +PGRES_FATAL_ERROR+
294
+ * * +PGRES_NONFATAL_ERROR+
295
+ * * +PGRES_PIPELINE_ABORTED+
274
296
  */
275
297
  VALUE
276
298
  pg_result_check( VALUE self )
@@ -295,10 +317,19 @@ pg_result_check( VALUE self )
295
317
  case PGRES_SINGLE_TUPLE:
296
318
  case PGRES_EMPTY_QUERY:
297
319
  case PGRES_COMMAND_OK:
320
+ #ifdef HAVE_PQENTERPIPELINEMODE
321
+ case PGRES_PIPELINE_SYNC:
322
+ #endif
323
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
324
+ case PGRES_TUPLES_CHUNK:
325
+ #endif
298
326
  return self;
299
327
  case PGRES_BAD_RESPONSE:
300
328
  case PGRES_FATAL_ERROR:
301
329
  case PGRES_NONFATAL_ERROR:
330
+ #ifdef HAVE_PQENTERPIPELINEMODE
331
+ case PGRES_PIPELINE_ABORTED:
332
+ #endif
302
333
  error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
303
334
  break;
304
335
  default:
@@ -345,17 +376,37 @@ VALUE
345
376
  pg_result_clear(VALUE self)
346
377
  {
347
378
  t_pg_result *this = pgresult_get_this(self);
379
+ rb_check_frozen(self);
348
380
  pgresult_clear( this );
349
381
  return Qnil;
350
382
  }
351
383
 
384
+ /*
385
+ * call-seq:
386
+ * res.freeze
387
+ *
388
+ * Freeze the PG::Result object and unlink the result from the related PG::Connection.
389
+ *
390
+ * A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
391
+ * It also denies setting a type_map or field_name_type.
392
+ *
393
+ */
394
+ static VALUE
395
+ pg_result_freeze(VALUE self)
396
+ {
397
+ t_pg_result *this = pgresult_get_this(self);
398
+
399
+ RB_OBJ_WRITE(self, &this->connection, Qnil);
400
+ return rb_call_super(0, NULL);
401
+ }
402
+
352
403
  /*
353
404
  * call-seq:
354
405
  * res.cleared? -> boolean
355
406
  *
356
- * Returns +true+ if the backend result memory has been free'd.
407
+ * Returns +true+ if the backend result memory has been freed.
357
408
  */
358
- VALUE
409
+ static VALUE
359
410
  pgresult_cleared_p( VALUE self )
360
411
  {
361
412
  t_pg_result *this = pgresult_get_this(self);
@@ -367,12 +418,12 @@ pgresult_cleared_p( VALUE self )
367
418
  * res.autoclear? -> boolean
368
419
  *
369
420
  * Returns +true+ if the underlying C struct will be cleared at the end of a callback.
370
- * This applies only to Result objects received by the block to PG::Cinnection#set_notice_receiver .
421
+ * This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
371
422
  *
372
423
  * All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
373
424
  *
374
425
  */
375
- VALUE
426
+ static VALUE
376
427
  pgresult_autoclear_p( VALUE self )
377
428
  {
378
429
  t_pg_result *this = pgresult_get_this(self);
@@ -448,7 +499,8 @@ static void pgresult_init_fnames(VALUE self)
448
499
 
449
500
  for( i=0; i<nfields; i++ ){
450
501
  char *cfname = PQfname(this->pgresult, i);
451
- this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
502
+ VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
503
+ RB_OBJ_WRITE(self, &this->fnames[i], fname);
452
504
  this->nfields = i + 1;
453
505
  }
454
506
  this->nfields = nfields;
@@ -495,6 +547,12 @@ static void pgresult_init_fnames(VALUE self)
495
547
  * * +PGRES_NONFATAL_ERROR+
496
548
  * * +PGRES_FATAL_ERROR+
497
549
  * * +PGRES_COPY_BOTH+
550
+ * * +PGRES_SINGLE_TUPLE+
551
+ * * +PGRES_TUPLES_CHUNK+
552
+ * * +PGRES_PIPELINE_SYNC+
553
+ * * +PGRES_PIPELINE_ABORTED+
554
+ *
555
+ * Use <tt>res.res_status</tt> to retrieve the string representation.
498
556
  */
499
557
  static VALUE
500
558
  pgresult_result_status(VALUE self)
@@ -504,16 +562,38 @@ pgresult_result_status(VALUE self)
504
562
 
505
563
  /*
506
564
  * call-seq:
507
- * res.res_status( status ) -> String
565
+ * PG::Result.res_status( status ) -> String
508
566
  *
509
- * Returns the string representation of status +status+.
567
+ * Returns the string representation of +status+.
510
568
  *
511
569
  */
512
570
  static VALUE
513
- pgresult_res_status(VALUE self, VALUE status)
571
+ pgresult_s_res_status(VALUE self, VALUE status)
572
+ {
573
+ return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
574
+ }
575
+
576
+ /*
577
+ * call-seq:
578
+ * res.res_status -> String
579
+ * res.res_status( status ) -> String
580
+ *
581
+ * Returns the string representation of the status of the result or of the provided +status+.
582
+ *
583
+ */
584
+ static VALUE
585
+ pgresult_res_status(int argc, VALUE *argv, VALUE self)
514
586
  {
515
587
  t_pg_result *this = pgresult_get_this_safe(self);
516
- VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
588
+ VALUE ret;
589
+
590
+ if( argc == 0 ){
591
+ ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
592
+ }else if( argc == 1 ){
593
+ ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
594
+ }else{
595
+ rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
596
+ }
517
597
  PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
518
598
  return ret;
519
599
  }
@@ -533,14 +613,12 @@ pgresult_error_message(VALUE self)
533
613
  return ret;
534
614
  }
535
615
 
536
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
537
616
  /*
538
617
  * call-seq:
539
618
  * res.verbose_error_message( verbosity, show_context ) -> String
540
619
  *
541
620
  * Returns a reformatted version of the error message associated with a PGresult object.
542
621
  *
543
- * Available since PostgreSQL-9.6
544
622
  */
545
623
  static VALUE
546
624
  pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
@@ -559,7 +637,6 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
559
637
 
560
638
  return ret;
561
639
  }
562
- #endif
563
640
 
564
641
  /*
565
642
  * call-seq:
@@ -584,7 +661,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
584
661
  * An example:
585
662
  *
586
663
  * begin
587
- * conn.exec( "SELECT * FROM nonexistant_table" )
664
+ * conn.exec( "SELECT * FROM nonexistent_table" )
588
665
  * rescue PG::Error => err
589
666
  * p [
590
667
  * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
@@ -604,7 +681,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
604
681
  *
605
682
  * Outputs:
606
683
  *
607
- * ["ERROR", "42P01", "relation \"nonexistant_table\" does not exist", nil, nil,
684
+ * ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
608
685
  * "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
609
686
  */
610
687
  static VALUE
@@ -653,6 +730,21 @@ pgresult_nfields(VALUE self)
653
730
  return INT2NUM(PQnfields(pgresult_get(self)));
654
731
  }
655
732
 
733
+ /*
734
+ * call-seq:
735
+ * res.binary_tuples() -> Integer
736
+ *
737
+ * Returns 1 if the PGresult contains binary data and 0 if it contains text data.
738
+ *
739
+ * 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.
740
+ * Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
741
+ */
742
+ static VALUE
743
+ pgresult_binary_tuples(VALUE self)
744
+ {
745
+ return INT2NUM(PQbinaryTuples(pgresult_get(self)));
746
+ }
747
+
656
748
  /*
657
749
  * call-seq:
658
750
  * res.fname( index ) -> String or Symbol
@@ -1003,7 +1095,7 @@ pgresult_cmd_tuples(VALUE self)
1003
1095
  {
1004
1096
  long n;
1005
1097
  n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
1006
- return INT2NUM(n);
1098
+ return LONG2NUM(n);
1007
1099
  }
1008
1100
 
1009
1101
  /*
@@ -1055,7 +1147,7 @@ pgresult_aref(VALUE self, VALUE index)
1055
1147
  }
1056
1148
  /* Store a copy of the filled hash for use at the next row. */
1057
1149
  if( num_tuples > 10 )
1058
- this->tuple_hash = rb_hash_dup(tuple);
1150
+ RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
1059
1151
 
1060
1152
  return tuple;
1061
1153
  }
@@ -1237,7 +1329,7 @@ static void ensure_init_for_tuple(VALUE self)
1237
1329
  rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1238
1330
  }
1239
1331
  rb_obj_freeze(field_map);
1240
- this->field_map = field_map;
1332
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
1241
1333
  }
1242
1334
  }
1243
1335
 
@@ -1325,14 +1417,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1325
1417
  t_pg_result *this = pgresult_get_this(self);
1326
1418
  t_typemap *p_typemap;
1327
1419
 
1328
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
1329
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
1330
- rb_obj_classname( typemap ) );
1331
- }
1332
- Data_Get_Struct(typemap, t_typemap, p_typemap);
1420
+ rb_check_frozen(self);
1421
+ /* Check type of method param */
1422
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
1333
1423
 
1334
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1335
- this->p_typemap = DATA_PTR( this->typemap );
1424
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
1425
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
1426
+ this->p_typemap = RTYPEDDATA_DATA( typemap );
1336
1427
 
1337
1428
  return typemap;
1338
1429
  }
@@ -1353,22 +1444,21 @@ pgresult_type_map_get(VALUE self)
1353
1444
  }
1354
1445
 
1355
1446
 
1356
- static void
1357
- yield_hash(VALUE self, int ntuples, int nfields)
1447
+ static int
1448
+ yield_hash(VALUE self, int ntuples, int nfields, void *data)
1358
1449
  {
1359
1450
  int tuple_num;
1360
- t_pg_result *this = pgresult_get_this(self);
1361
1451
  UNUSED(nfields);
1362
1452
 
1363
1453
  for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1364
1454
  rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1365
1455
  }
1366
1456
 
1367
- pgresult_clear( this );
1457
+ return 1; /* clear the result */
1368
1458
  }
1369
1459
 
1370
- static void
1371
- yield_array(VALUE self, int ntuples, int nfields)
1460
+ static int
1461
+ yield_array(VALUE self, int ntuples, int nfields, void *data)
1372
1462
  {
1373
1463
  int row;
1374
1464
  t_pg_result *this = pgresult_get_this(self);
@@ -1384,11 +1474,11 @@ yield_array(VALUE self, int ntuples, int nfields)
1384
1474
  rb_yield( rb_ary_new4( nfields, row_values ));
1385
1475
  }
1386
1476
 
1387
- pgresult_clear( this );
1477
+ return 1; /* clear the result */
1388
1478
  }
1389
1479
 
1390
- static void
1391
- yield_tuple(VALUE self, int ntuples, int nfields)
1480
+ static int
1481
+ yield_tuple(VALUE self, int ntuples, int nfields, void *data)
1392
1482
  {
1393
1483
  int tuple_num;
1394
1484
  t_pg_result *this = pgresult_get_this(self);
@@ -1405,16 +1495,19 @@ yield_tuple(VALUE self, int ntuples, int nfields)
1405
1495
  VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1406
1496
  rb_yield( tuple );
1407
1497
  }
1498
+ return 0; /* don't clear the result */
1408
1499
  }
1409
1500
 
1410
- static VALUE
1411
- pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1501
+ /* Non-static, and data pointer for use by sequel_pg */
1502
+ VALUE
1503
+ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
1412
1504
  {
1413
1505
  t_pg_result *this;
1414
- int nfields;
1506
+ int nfields, nfields2;
1415
1507
  PGconn *pgconn;
1416
1508
  PGresult *pgresult;
1417
1509
 
1510
+ rb_check_frozen(self);
1418
1511
  RETURN_ENUMERATOR(self, 0, NULL);
1419
1512
 
1420
1513
  this = pgresult_get_this_safe(self);
@@ -1427,23 +1520,37 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1427
1520
 
1428
1521
  switch( PQresultStatus(pgresult) ){
1429
1522
  case PGRES_TUPLES_OK:
1523
+ case PGRES_COMMAND_OK:
1430
1524
  if( ntuples == 0 )
1431
1525
  return self;
1432
1526
  rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1433
1527
  case PGRES_SINGLE_TUPLE:
1528
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
1529
+ case PGRES_TUPLES_CHUNK:
1530
+ #endif
1434
1531
  break;
1435
1532
  default:
1436
1533
  pg_result_check( self );
1437
1534
  }
1438
1535
 
1439
- yielder( self, ntuples, nfields );
1536
+ nfields2 = PQnfields(pgresult);
1537
+ if( nfields != nfields2 ){
1538
+ pgresult_clear( this );
1539
+ rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, nfields2);
1540
+ }
1541
+
1542
+ if( yielder( self, ntuples, nfields, data ) ){
1543
+ pgresult_clear( this );
1544
+ }
1545
+
1546
+ if( gvl_PQisBusy(pgconn) ){
1547
+ /* wait for input (without blocking) before reading each result */
1548
+ pgconn_block( 0, NULL, this->connection );
1549
+ }
1440
1550
 
1441
1551
  pgresult = gvl_PQgetResult(pgconn);
1442
1552
  if( pgresult == NULL )
1443
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1444
-
1445
- if( nfields != PQnfields(pgresult) )
1446
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1553
+ rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
1447
1554
 
1448
1555
  this->pgresult = pgresult;
1449
1556
  }
@@ -1465,7 +1572,7 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1465
1572
  * wrapping each row into a dedicated result object, it delivers data in nearly
1466
1573
  * the same speed as with ordinary results.
1467
1574
  *
1468
- * The base result must be in status PGRES_SINGLE_TUPLE.
1575
+ * The base result must be in status PGRES_SINGLE_TUPLE or PGRES_TUPLES_CHUNK.
1469
1576
  * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1470
1577
  * A PG::Error is raised for any errors from the server.
1471
1578
  *
@@ -1487,7 +1594,7 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1487
1594
  static VALUE
1488
1595
  pgresult_stream_each(VALUE self)
1489
1596
  {
1490
- return pgresult_stream_any(self, yield_hash);
1597
+ return pgresult_stream_any(self, yield_hash, NULL);
1491
1598
  }
1492
1599
 
1493
1600
  /*
@@ -1503,7 +1610,7 @@ pgresult_stream_each(VALUE self)
1503
1610
  static VALUE
1504
1611
  pgresult_stream_each_row(VALUE self)
1505
1612
  {
1506
- return pgresult_stream_any(self, yield_array);
1613
+ return pgresult_stream_any(self, yield_array, NULL);
1507
1614
  }
1508
1615
 
1509
1616
  /*
@@ -1520,7 +1627,7 @@ pgresult_stream_each_tuple(VALUE self)
1520
1627
  /* allocate VALUEs that are shared between all streamed tuples */
1521
1628
  ensure_init_for_tuple(self);
1522
1629
 
1523
- return pgresult_stream_any(self, yield_tuple);
1630
+ return pgresult_stream_any(self, yield_tuple, NULL);
1524
1631
  }
1525
1632
 
1526
1633
  /*
@@ -1531,7 +1638,7 @@ pgresult_stream_each_tuple(VALUE self)
1531
1638
  * It can be set to one of:
1532
1639
  * * +:string+ to use String based field names
1533
1640
  * * +:symbol+ to use Symbol based field names
1534
- * * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably removed in future.
1641
+ * * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
1535
1642
  *
1536
1643
  * The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
1537
1644
  *
@@ -1548,6 +1655,8 @@ static VALUE
1548
1655
  pgresult_field_name_type_set(VALUE self, VALUE sym)
1549
1656
  {
1550
1657
  t_pg_result *this = pgresult_get_this(self);
1658
+
1659
+ rb_check_frozen(self);
1551
1660
  if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1552
1661
 
1553
1662
  this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
@@ -1581,34 +1690,36 @@ pgresult_field_name_type_get(VALUE self)
1581
1690
  }
1582
1691
 
1583
1692
  void
1584
- init_pg_result()
1693
+ init_pg_result(void)
1585
1694
  {
1586
1695
  sym_string = ID2SYM(rb_intern("string"));
1587
1696
  sym_symbol = ID2SYM(rb_intern("symbol"));
1588
1697
  sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
1589
1698
 
1590
- rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cData );
1699
+ rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
1700
+ rb_undef_alloc_func(rb_cPGresult);
1591
1701
  rb_include_module(rb_cPGresult, rb_mEnumerable);
1592
1702
  rb_include_module(rb_cPGresult, rb_mPGconstants);
1593
1703
 
1594
1704
  /****** PG::Result INSTANCE METHODS: libpq ******/
1595
1705
  rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
1596
- rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1706
+ rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, -1);
1707
+ rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
1597
1708
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1598
1709
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1599
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1600
1710
  rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1601
1711
  rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1602
- #endif
1603
1712
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1604
1713
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1605
1714
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
1715
+ rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
1606
1716
  rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
1607
1717
  rb_define_alias (rb_cPGresult, "check_result", "check");
1608
1718
  rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
1609
1719
  rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
1610
1720
  rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
1611
1721
  rb_define_alias(rb_cPGresult, "num_fields", "nfields");
1722
+ rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
1612
1723
  rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
1613
1724
  rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
1614
1725
  rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);