pg 1.1.3 → 1.3.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 (117) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +86 -0
  6. data/.github/workflows/source-gem.yml +129 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.rdoc +210 -6
  16. data/Manifest.txt +3 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.rdoc +1 -2
  19. data/README.rdoc +51 -15
  20. data/Rakefile +31 -140
  21. data/Rakefile.cross +60 -56
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/ext/errorcodes.def +76 -0
  25. data/ext/errorcodes.txt +21 -2
  26. data/ext/extconf.rb +101 -26
  27. data/ext/gvl_wrappers.c +4 -0
  28. data/ext/gvl_wrappers.h +23 -0
  29. data/ext/pg.c +190 -98
  30. data/ext/pg.h +42 -17
  31. data/ext/pg_binary_decoder.c +20 -16
  32. data/ext/pg_binary_encoder.c +13 -12
  33. data/ext/pg_coder.c +95 -29
  34. data/ext/pg_connection.c +1043 -769
  35. data/ext/pg_copy_coder.c +50 -18
  36. data/ext/pg_record_coder.c +519 -0
  37. data/ext/pg_result.c +326 -142
  38. data/ext/pg_text_decoder.c +15 -9
  39. data/ext/pg_text_encoder.c +185 -53
  40. data/ext/pg_tuple.c +61 -27
  41. data/ext/pg_type_map.c +42 -9
  42. data/ext/pg_type_map_all_strings.c +19 -5
  43. data/ext/pg_type_map_by_class.c +54 -24
  44. data/ext/pg_type_map_by_column.c +73 -34
  45. data/ext/pg_type_map_by_mri_type.c +48 -19
  46. data/ext/pg_type_map_by_oid.c +55 -25
  47. data/ext/pg_type_map_in_ruby.c +51 -20
  48. data/ext/{util.c → pg_util.c} +7 -7
  49. data/ext/{util.h → pg_util.h} +0 -0
  50. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  51. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  52. data/lib/pg/basic_type_map_for_results.rb +81 -0
  53. data/lib/pg/basic_type_registry.rb +296 -0
  54. data/lib/pg/binary_decoder.rb +1 -0
  55. data/lib/pg/coder.rb +23 -2
  56. data/lib/pg/connection.rb +589 -59
  57. data/lib/pg/constants.rb +1 -0
  58. data/lib/pg/exceptions.rb +1 -0
  59. data/lib/pg/result.rb +13 -1
  60. data/lib/pg/text_decoder.rb +2 -3
  61. data/lib/pg/text_encoder.rb +8 -18
  62. data/lib/pg/type_map_by_column.rb +2 -1
  63. data/lib/pg/version.rb +4 -0
  64. data/lib/pg.rb +48 -33
  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/pg.gemspec +32 -0
  77. data/rakelib/task_extension.rb +46 -0
  78. data/sample/array_insert.rb +20 -0
  79. data/sample/async_api.rb +106 -0
  80. data/sample/async_copyto.rb +39 -0
  81. data/sample/async_mixed.rb +56 -0
  82. data/sample/check_conn.rb +21 -0
  83. data/sample/copydata.rb +71 -0
  84. data/sample/copyfrom.rb +81 -0
  85. data/sample/copyto.rb +19 -0
  86. data/sample/cursor.rb +21 -0
  87. data/sample/disk_usage_report.rb +177 -0
  88. data/sample/issue-119.rb +94 -0
  89. data/sample/losample.rb +69 -0
  90. data/sample/minimal-testcase.rb +17 -0
  91. data/sample/notify_wait.rb +72 -0
  92. data/sample/pg_statistics.rb +285 -0
  93. data/sample/replication_monitor.rb +222 -0
  94. data/sample/test_binary_values.rb +33 -0
  95. data/sample/wal_shipper.rb +434 -0
  96. data/sample/warehouse_partitions.rb +311 -0
  97. data.tar.gz.sig +0 -0
  98. metadata +94 -237
  99. metadata.gz.sig +0 -0
  100. data/ChangeLog +0 -6595
  101. data/lib/pg/basic_type_mapping.rb +0 -459
  102. data/spec/data/expected_trace.out +0 -26
  103. data/spec/data/random_binary_data +0 -0
  104. data/spec/helpers.rb +0 -381
  105. data/spec/pg/basic_type_mapping_spec.rb +0 -508
  106. data/spec/pg/connection_spec.rb +0 -1849
  107. data/spec/pg/connection_sync_spec.rb +0 -41
  108. data/spec/pg/result_spec.rb +0 -491
  109. data/spec/pg/tuple_spec.rb +0 -280
  110. data/spec/pg/type_map_by_class_spec.rb +0 -138
  111. data/spec/pg/type_map_by_column_spec.rb +0 -222
  112. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  113. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  114. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  115. data/spec/pg/type_map_spec.rb +0 -22
  116. data/spec/pg/type_spec.rb +0 -949
  117. data/spec/pg_spec.rb +0 -50
data/ext/pg_result.c CHANGED
@@ -1,20 +1,27 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v a8b70c42b3e8 2018/07/30 14:09:38 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
7
7
  #include "pg.h"
8
8
 
9
-
10
9
  VALUE rb_cPGresult;
10
+ static VALUE sym_symbol, sym_string, sym_static_symbol;
11
11
 
12
- static void pgresult_gc_free( t_pg_result * );
13
12
  static VALUE pgresult_type_map_set( VALUE, VALUE );
14
- static VALUE pgresult_s_allocate( VALUE );
15
13
  static t_pg_result *pgresult_get_this( VALUE );
16
14
  static t_pg_result *pgresult_get_this_safe( VALUE );
17
15
 
16
+ #if defined(HAVE_PQRESULTMEMORYSIZE)
17
+
18
+ static ssize_t
19
+ pgresult_approx_size(const PGresult *result)
20
+ {
21
+ return PQresultMemorySize(result);
22
+ }
23
+
24
+ #else
18
25
 
19
26
  #define PGRESULT_DATA_BLOCKSIZE 2048
20
27
  typedef struct pgresAttValue
@@ -44,7 +51,7 @@ count_leading_zero_bits(unsigned int x)
44
51
  }
45
52
 
46
53
  static ssize_t
47
- pgresult_approx_size(PGresult *result)
54
+ pgresult_approx_size(const PGresult *result)
48
55
  {
49
56
  int num_fields = PQnfields(result);
50
57
  ssize_t size = 0;
@@ -94,25 +101,97 @@ pgresult_approx_size(PGresult *result)
94
101
 
95
102
  return size;
96
103
  }
104
+ #endif
105
+
106
+ /*
107
+ * GC Mark function
108
+ */
109
+ static void
110
+ pgresult_gc_mark( void *_this )
111
+ {
112
+ t_pg_result *this = (t_pg_result *)_this;
113
+ int i;
114
+
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 );
119
+
120
+ for( i=0; i < this->nfields; 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] );
138
+ }
139
+ }
97
140
 
141
+ /*
142
+ * GC Free function
143
+ */
98
144
  static void
99
- pgresult_clear( t_pg_result *this )
145
+ pgresult_clear( void *_this )
100
146
  {
147
+ t_pg_result *this = (t_pg_result *)_this;
101
148
  if( this->pgresult && !this->autoclear ){
102
149
  PQclear(this->pgresult);
103
150
  #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
104
151
  rb_gc_adjust_memory_usage(-this->result_size);
105
152
  #endif
106
153
  }
154
+ this->result_size = 0;
155
+ this->nfields = -1;
107
156
  this->pgresult = NULL;
108
157
  }
109
158
 
159
+ static void
160
+ pgresult_gc_free( void *_this )
161
+ {
162
+ t_pg_result *this = (t_pg_result *)_this;
163
+ pgresult_clear( this );
164
+ xfree(this);
165
+ }
166
+
110
167
  static size_t
111
- pgresult_memsize( t_pg_result *this )
168
+ pgresult_memsize( const void *_this )
112
169
  {
170
+ const t_pg_result *this = (const t_pg_result *)_this;
171
+ /* Ideally the memory 'this' is pointing to should be taken into account as well.
172
+ * However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
173
+ */
113
174
  return this->result_size;
114
175
  }
115
176
 
177
+ static const rb_data_type_t pgresult_type = {
178
+ "PG::Result",
179
+ {
180
+ pgresult_gc_mark,
181
+ pgresult_gc_free,
182
+ pgresult_memsize,
183
+ pg_compact_callback(pgresult_gc_compact),
184
+ },
185
+ 0, 0,
186
+ RUBY_TYPED_FREE_IMMEDIATELY,
187
+ };
188
+
189
+ /* Needed by sequel_pg gem, do not delete */
190
+ int pg_get_result_enc_idx(VALUE self)
191
+ {
192
+ return pgresult_get_this(self)->enc_idx;
193
+ }
194
+
116
195
  /*
117
196
  * Global functions
118
197
  */
@@ -124,31 +203,32 @@ static VALUE
124
203
  pg_new_result2(PGresult *result, VALUE rb_pgconn)
125
204
  {
126
205
  int nfields = result ? PQnfields(result) : 0;
127
- VALUE self = pgresult_s_allocate( rb_cPGresult );
206
+ VALUE self;
128
207
  t_pg_result *this;
129
208
 
130
209
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
131
- RTYPEDDATA_DATA(self) = this;
132
-
133
210
  this->pgresult = result;
134
211
  this->connection = rb_pgconn;
135
212
  this->typemap = pg_typemap_all_strings;
136
- this->p_typemap = DATA_PTR( this->typemap );
213
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
137
214
  this->nfields = -1;
138
215
  this->tuple_hash = Qnil;
139
216
  this->field_map = Qnil;
140
-
141
- PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
217
+ this->flags = 0;
218
+ self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
142
219
 
143
220
  if( result ){
144
221
  t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
145
222
  VALUE typemap = p_conn->type_map_for_results;
146
-
147
223
  /* Type check is done when assigned to PG::Connection. */
148
- t_typemap *p_typemap = DATA_PTR(typemap);
224
+ t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
149
225
 
226
+ this->enc_idx = p_conn->enc_idx;
150
227
  this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
151
- this->p_typemap = DATA_PTR( this->typemap );
228
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
229
+ this->flags = p_conn->flags;
230
+ } else {
231
+ this->enc_idx = rb_locale_encindex();
152
232
  }
153
233
 
154
234
  return self;
@@ -159,22 +239,38 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
159
239
  {
160
240
  VALUE self = pg_new_result2(result, rb_pgconn);
161
241
  t_pg_result *this = pgresult_get_this(self);
162
- t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
163
242
 
164
243
  this->autoclear = 0;
165
244
 
166
- if( p_conn->guess_result_memsize ){
167
- /* Approximate size of underlying pgresult memory storage and account to ruby GC */
168
- this->result_size = pgresult_approx_size(result);
245
+ /* Estimate size of underlying pgresult memory storage and account to ruby GC.
246
+ * There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
247
+ */
248
+ /* TODO: If someday most systems provide PQresultMemorySize(), it's questionable to store result_size in t_pg_result in addition to the value already stored in PGresult.
249
+ * For now the memory savings don't justify the ifdefs necessary to support both cases.
250
+ */
251
+ this->result_size = pgresult_approx_size(result);
169
252
 
170
253
  #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
171
- rb_gc_adjust_memory_usage(this->result_size);
254
+ rb_gc_adjust_memory_usage(this->result_size);
172
255
  #endif
173
- }
174
256
 
175
257
  return self;
176
258
  }
177
259
 
260
+ static VALUE
261
+ pg_copy_result(t_pg_result *this)
262
+ {
263
+ int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
264
+ size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
265
+ t_pg_result *copy;
266
+
267
+ copy = (t_pg_result *)xmalloc(len);
268
+ memcpy(copy, this, len);
269
+ this->result_size = 0;
270
+
271
+ return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
272
+ }
273
+
178
274
  VALUE
179
275
  pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
180
276
  {
@@ -193,7 +289,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
193
289
  * call-seq:
194
290
  * res.check -> nil
195
291
  *
196
- * Raises appropriate exception if PG::Result is in a bad state.
292
+ * Raises appropriate exception if PG::Result is in a bad state, which is:
293
+ * * +PGRES_BAD_RESPONSE+
294
+ * * +PGRES_FATAL_ERROR+
295
+ * * +PGRES_NONFATAL_ERROR+
296
+ * * +PGRES_PIPELINE_ABORTED+
197
297
  */
198
298
  VALUE
199
299
  pg_result_check( VALUE self )
@@ -218,10 +318,16 @@ pg_result_check( VALUE self )
218
318
  case PGRES_SINGLE_TUPLE:
219
319
  case PGRES_EMPTY_QUERY:
220
320
  case PGRES_COMMAND_OK:
321
+ #ifdef HAVE_PQENTERPIPELINEMODE
322
+ case PGRES_PIPELINE_SYNC:
323
+ #endif
221
324
  return self;
222
325
  case PGRES_BAD_RESPONSE:
223
326
  case PGRES_FATAL_ERROR:
224
327
  case PGRES_NONFATAL_ERROR:
328
+ #ifdef HAVE_PQENTERPIPELINEMODE
329
+ case PGRES_PIPELINE_ABORTED:
330
+ #endif
225
331
  error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
226
332
  break;
227
333
  default:
@@ -229,7 +335,7 @@ pg_result_check( VALUE self )
229
335
  }
230
336
  }
231
337
 
232
- PG_ENCODING_SET_NOCHECK( error, ENCODING_GET(self) );
338
+ PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
233
339
 
234
340
  sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
235
341
  klass = lookup_error_class( sqlstate );
@@ -261,8 +367,7 @@ pg_result_check( VALUE self )
261
367
  * Special care must be taken when PG::Tuple objects are used.
262
368
  * In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
263
369
  *
264
- * If PG::Result#autoclear? is true then the result is marked as cleared
265
- * and the underlying C struct will be cleared automatically by libpq.
370
+ * If PG::Result#autoclear? is +true+ then the result is only marked as cleared but clearing the underlying C struct will happen when the callback returns.
266
371
  *
267
372
  */
268
373
  VALUE
@@ -277,7 +382,7 @@ pg_result_clear(VALUE self)
277
382
  * call-seq:
278
383
  * res.cleared? -> boolean
279
384
  *
280
- * Returns +true+ if the backend result memory has been free'd.
385
+ * Returns +true+ if the backend result memory has been freed.
281
386
  */
282
387
  VALUE
283
388
  pgresult_cleared_p( VALUE self )
@@ -290,8 +395,10 @@ pgresult_cleared_p( VALUE self )
290
395
  * call-seq:
291
396
  * res.autoclear? -> boolean
292
397
  *
293
- * Returns +true+ if the underlying C struct will be cleared automatically by libpq.
294
- * Elsewise the result is cleared by PG::Result#clear or by the GC when it's no longer in use.
398
+ * Returns +true+ if the underlying C struct will be cleared at the end of a callback.
399
+ * This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
400
+ *
401
+ * All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
295
402
  *
296
403
  */
297
404
  VALUE
@@ -305,37 +412,6 @@ pgresult_autoclear_p( VALUE self )
305
412
  * DATA pointer functions
306
413
  */
307
414
 
308
- /*
309
- * GC Mark function
310
- */
311
- static void
312
- pgresult_gc_mark( t_pg_result *this )
313
- {
314
- int i;
315
-
316
- if( !this ) return;
317
- rb_gc_mark( this->connection );
318
- rb_gc_mark( this->typemap );
319
- rb_gc_mark( this->tuple_hash );
320
- rb_gc_mark( this->field_map );
321
-
322
- for( i=0; i < this->nfields; i++ ){
323
- rb_gc_mark( this->fnames[i] );
324
- }
325
- }
326
-
327
- /*
328
- * GC Free function
329
- */
330
- static void
331
- pgresult_gc_free( t_pg_result *this )
332
- {
333
- if( !this ) return;
334
- pgresult_clear( this );
335
-
336
- xfree(this);
337
- }
338
-
339
415
  /*
340
416
  * Fetch the PG::Result object data pointer and check it's
341
417
  * PGresult data pointer for sanity.
@@ -365,32 +441,30 @@ pgresult_get(VALUE self)
365
441
  return this->pgresult;
366
442
  }
367
443
 
368
-
369
- static const rb_data_type_t pgresult_type = {
370
- "pg",
371
- {
372
- (void (*)(void*))pgresult_gc_mark,
373
- (void (*)(void*))pgresult_gc_free,
374
- (size_t (*)(const void *))pgresult_memsize,
375
- },
376
- 0, 0,
377
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
378
- RUBY_TYPED_FREE_IMMEDIATELY,
379
- #endif
380
- };
381
-
382
- /*
383
- * Document-method: allocate
384
- *
385
- * call-seq:
386
- * PG::Result.allocate -> result
387
- */
388
- static VALUE
389
- pgresult_s_allocate( VALUE klass )
444
+ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
390
445
  {
391
- VALUE self = TypedData_Wrap_Struct( klass, &pgresult_type, NULL );
392
-
393
- return self;
446
+ VALUE fname;
447
+ #ifdef TRUFFLERUBY
448
+ if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
449
+ #else
450
+ if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
451
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
452
+ fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
453
+ if( fname == Qnil ){
454
+ fname = rb_str_new2(cstr);
455
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
456
+ fname = rb_str_intern(fname);
457
+ }
458
+ } else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
459
+ #endif
460
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
461
+ fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
462
+ } else {
463
+ fname = rb_str_new2(cstr);
464
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
465
+ fname = rb_obj_freeze(fname);
466
+ }
467
+ return fname;
394
468
  }
395
469
 
396
470
  static void pgresult_init_fnames(VALUE self)
@@ -402,12 +476,9 @@ static void pgresult_init_fnames(VALUE self)
402
476
  int nfields = PQnfields(this->pgresult);
403
477
 
404
478
  for( i=0; i<nfields; i++ ){
405
- VALUE fname = rb_tainted_str_new2(PQfname(this->pgresult, i));
406
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
407
- this->fnames[i] = rb_obj_freeze(fname);
479
+ char *cfname = PQfname(this->pgresult, i);
480
+ this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
408
481
  this->nfields = i + 1;
409
-
410
- RB_GC_GUARD(fname);
411
482
  }
412
483
  this->nfields = nfields;
413
484
  }
@@ -419,6 +490,8 @@ static void pgresult_init_fnames(VALUE self)
419
490
  *
420
491
  * The class to represent the query result tuples (rows).
421
492
  * An instance of this class is created as the result of every query.
493
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
494
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
422
495
  *
423
496
  * Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
424
497
  * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
@@ -451,6 +524,9 @@ static void pgresult_init_fnames(VALUE self)
451
524
  * * +PGRES_NONFATAL_ERROR+
452
525
  * * +PGRES_FATAL_ERROR+
453
526
  * * +PGRES_COPY_BOTH+
527
+ * * +PGRES_SINGLE_TUPLE+
528
+ * * +PGRES_PIPELINE_SYNC+
529
+ * * +PGRES_PIPELINE_ABORTED+
454
530
  */
455
531
  static VALUE
456
532
  pgresult_result_status(VALUE self)
@@ -462,14 +538,15 @@ pgresult_result_status(VALUE self)
462
538
  * call-seq:
463
539
  * res.res_status( status ) -> String
464
540
  *
465
- * Returns the string representation of status +status+.
541
+ * Returns the string representation of +status+.
466
542
  *
467
543
  */
468
544
  static VALUE
469
545
  pgresult_res_status(VALUE self, VALUE status)
470
546
  {
471
- VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
472
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
547
+ t_pg_result *this = pgresult_get_this_safe(self);
548
+ VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
549
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
473
550
  return ret;
474
551
  }
475
552
 
@@ -482,10 +559,39 @@ pgresult_res_status(VALUE self, VALUE status)
482
559
  static VALUE
483
560
  pgresult_error_message(VALUE self)
484
561
  {
485
- VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
486
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
562
+ t_pg_result *this = pgresult_get_this_safe(self);
563
+ VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
564
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
565
+ return ret;
566
+ }
567
+
568
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
569
+ /*
570
+ * call-seq:
571
+ * res.verbose_error_message( verbosity, show_context ) -> String
572
+ *
573
+ * Returns a reformatted version of the error message associated with a PGresult object.
574
+ *
575
+ * Available since PostgreSQL-9.6
576
+ */
577
+ static VALUE
578
+ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
579
+ {
580
+ t_pg_result *this = pgresult_get_this_safe(self);
581
+ VALUE ret;
582
+ char *c_str;
583
+
584
+ c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
585
+ if(!c_str)
586
+ rb_raise(rb_eNoMemError, "insufficient memory to format error message");
587
+
588
+ ret = rb_str_new2(c_str);
589
+ PQfreemem(c_str);
590
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
591
+
487
592
  return ret;
488
593
  }
594
+ #endif
489
595
 
490
596
  /*
491
597
  * call-seq:
@@ -536,14 +642,14 @@ pgresult_error_message(VALUE self)
536
642
  static VALUE
537
643
  pgresult_error_field(VALUE self, VALUE field)
538
644
  {
539
- PGresult *result = pgresult_get( self );
645
+ t_pg_result *this = pgresult_get_this_safe(self);
540
646
  int fieldcode = NUM2INT( field );
541
- char * fieldstr = PQresultErrorField( result, fieldcode );
647
+ char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
542
648
  VALUE ret = Qnil;
543
649
 
544
650
  if ( fieldstr ) {
545
- ret = rb_tainted_str_new2( fieldstr );
546
- PG_ENCODING_SET_NOCHECK( ret, ENCODING_GET(self ));
651
+ ret = rb_str_new2( fieldstr );
652
+ PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
547
653
  }
548
654
 
549
655
  return ret;
@@ -581,24 +687,25 @@ pgresult_nfields(VALUE self)
581
687
 
582
688
  /*
583
689
  * call-seq:
584
- * res.fname( index ) -> String
690
+ * res.fname( index ) -> String or Symbol
585
691
  *
586
692
  * Returns the name of the column corresponding to _index_.
693
+ * Depending on #field_name_type= it's a String or Symbol.
694
+ *
587
695
  */
588
696
  static VALUE
589
697
  pgresult_fname(VALUE self, VALUE index)
590
698
  {
591
- VALUE fname;
592
- PGresult *result = pgresult_get(self);
699
+ t_pg_result *this = pgresult_get_this_safe(self);
593
700
  int i = NUM2INT(index);
701
+ char *cfname;
594
702
 
595
- if (i < 0 || i >= PQnfields(result)) {
703
+ if (i < 0 || i >= PQnfields(this->pgresult)) {
596
704
  rb_raise(rb_eArgError,"invalid field number %d", i);
597
705
  }
598
706
 
599
- fname = rb_tainted_str_new2(PQfname(result, i));
600
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
601
- return rb_obj_freeze(fname);
707
+ cfname = PQfname(this->pgresult, i);
708
+ return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
602
709
  }
603
710
 
604
711
  /*
@@ -897,8 +1004,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
897
1004
  static VALUE
898
1005
  pgresult_cmd_status(VALUE self)
899
1006
  {
900
- VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
901
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
1007
+ t_pg_result *this = pgresult_get_this_safe(self);
1008
+ VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
1009
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
902
1010
  return ret;
903
1011
  }
904
1012
 
@@ -927,7 +1035,7 @@ pgresult_cmd_tuples(VALUE self)
927
1035
  {
928
1036
  long n;
929
1037
  n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
930
- return INT2NUM(n);
1038
+ return LONG2NUM(n);
931
1039
  }
932
1040
 
933
1041
  /*
@@ -1098,8 +1206,12 @@ static VALUE
1098
1206
  pgresult_field_values( VALUE self, VALUE field )
1099
1207
  {
1100
1208
  PGresult *result = pgresult_get( self );
1101
- const char *fieldname = StringValueCStr( field );
1102
- int fnum = PQfnumber( result, fieldname );
1209
+ const char *fieldname;
1210
+ int fnum;
1211
+
1212
+ if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
1213
+ fieldname = StringValueCStr( field );
1214
+ fnum = PQfnumber( result, fieldname );
1103
1215
 
1104
1216
  if ( fnum < 0 )
1105
1217
  rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
@@ -1142,6 +1254,25 @@ pgresult_tuple_values(VALUE self, VALUE index)
1142
1254
  }
1143
1255
  }
1144
1256
 
1257
+ static void ensure_init_for_tuple(VALUE self)
1258
+ {
1259
+ t_pg_result *this = pgresult_get_this_safe(self);
1260
+
1261
+ if( this->field_map == Qnil ){
1262
+ int i;
1263
+ VALUE field_map = rb_hash_new();
1264
+
1265
+ if( this->nfields == -1 )
1266
+ pgresult_init_fnames( self );
1267
+
1268
+ for( i = 0; i < this->nfields; i++ ){
1269
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1270
+ }
1271
+ rb_obj_freeze(field_map);
1272
+ this->field_map = field_map;
1273
+ }
1274
+ }
1275
+
1145
1276
  /*
1146
1277
  * call-seq:
1147
1278
  * res.tuple( n ) -> PG::Tuple
@@ -1162,19 +1293,7 @@ pgresult_tuple(VALUE self, VALUE index)
1162
1293
  if ( tuple_num < 0 || tuple_num >= num_tuples )
1163
1294
  rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1164
1295
 
1165
- if( this->field_map == Qnil ){
1166
- int i;
1167
- VALUE field_map = rb_hash_new();
1168
-
1169
- if( this->nfields == -1 )
1170
- pgresult_init_fnames( self );
1171
-
1172
- for( i = 0; i < this->nfields; i++ ){
1173
- rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1174
- }
1175
- rb_obj_freeze(field_map);
1176
- this->field_map = field_map;
1177
- }
1296
+ ensure_init_for_tuple(self);
1178
1297
 
1179
1298
  return pg_tuple_new(self, tuple_num);
1180
1299
  }
@@ -1206,7 +1325,7 @@ pgresult_each(VALUE self)
1206
1325
  * call-seq:
1207
1326
  * res.fields() -> Array
1208
1327
  *
1209
- * Returns an array of Strings representing the names of the fields in the result.
1328
+ * Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
1210
1329
  */
1211
1330
  static VALUE
1212
1331
  pgresult_fields(VALUE self)
@@ -1238,14 +1357,11 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1238
1357
  t_pg_result *this = pgresult_get_this(self);
1239
1358
  t_typemap *p_typemap;
1240
1359
 
1241
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
1242
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
1243
- rb_obj_classname( typemap ) );
1244
- }
1245
- Data_Get_Struct(typemap, t_typemap, p_typemap);
1360
+ /* Check type of method param */
1361
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
1246
1362
 
1247
1363
  this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1248
- this->p_typemap = DATA_PTR( this->typemap );
1364
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
1249
1365
 
1250
1366
  return typemap;
1251
1367
  }
@@ -1305,11 +1421,17 @@ yield_tuple(VALUE self, int ntuples, int nfields)
1305
1421
  {
1306
1422
  int tuple_num;
1307
1423
  t_pg_result *this = pgresult_get_this(self);
1308
- VALUE result = pg_new_result(this->pgresult, this->connection);
1424
+ VALUE copy;
1309
1425
  UNUSED(nfields);
1310
1426
 
1427
+ /* make a copy of the base result, that is bound to the PG::Tuple */
1428
+ copy = pg_copy_result(this);
1429
+ /* The copy is now owner of the PGresult and is responsible to PQclear it.
1430
+ * We clear the pgresult here, so that it's not double freed on error within yield. */
1431
+ this->pgresult = NULL;
1432
+
1311
1433
  for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1312
- VALUE tuple = pgresult_tuple(result, INT2FIX(tuple_num));
1434
+ VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1313
1435
  rb_yield( tuple );
1314
1436
  }
1315
1437
  }
@@ -1390,8 +1512,6 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1390
1512
  * # do something with each received row of the second query
1391
1513
  * end
1392
1514
  * conn.get_result # => nil (no more results)
1393
- *
1394
- * Available since PostgreSQL-9.2
1395
1515
  */
1396
1516
  static VALUE
1397
1517
  pgresult_stream_each(VALUE self)
@@ -1408,8 +1528,6 @@ pgresult_stream_each(VALUE self)
1408
1528
  *
1409
1529
  * This method works equally to #stream_each , but yields an Array of
1410
1530
  * values.
1411
- *
1412
- * Available since PostgreSQL-9.2
1413
1531
  */
1414
1532
  static VALUE
1415
1533
  pgresult_stream_each_row(VALUE self)
@@ -1424,21 +1542,82 @@ pgresult_stream_each_row(VALUE self)
1424
1542
  * Yields each row of the result set in single row mode.
1425
1543
  *
1426
1544
  * This method works equally to #stream_each , but yields a PG::Tuple object.
1427
- *
1428
- * Available since PostgreSQL-9.2
1429
1545
  */
1430
1546
  static VALUE
1431
1547
  pgresult_stream_each_tuple(VALUE self)
1432
1548
  {
1549
+ /* allocate VALUEs that are shared between all streamed tuples */
1550
+ ensure_init_for_tuple(self);
1551
+
1433
1552
  return pgresult_stream_any(self, yield_tuple);
1434
1553
  }
1435
1554
 
1555
+ /*
1556
+ * call-seq:
1557
+ * res.field_name_type = Symbol
1558
+ *
1559
+ * Set type of field names specific to this result.
1560
+ * It can be set to one of:
1561
+ * * +:string+ to use String based field names
1562
+ * * +:symbol+ to use Symbol based field names
1563
+ * * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
1564
+ *
1565
+ * The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
1566
+ *
1567
+ * This setting affects several result methods:
1568
+ * * keys of Hash returned by #[] , #each and #stream_each
1569
+ * * #fields
1570
+ * * #fname
1571
+ * * field names used by #tuple and #stream_each_tuple
1572
+ *
1573
+ * The type of field names can only be changed before any of the affected methods have been called.
1574
+ *
1575
+ */
1576
+ static VALUE
1577
+ pgresult_field_name_type_set(VALUE self, VALUE sym)
1578
+ {
1579
+ t_pg_result *this = pgresult_get_this(self);
1580
+ if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1581
+
1582
+ this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
1583
+ if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
1584
+ else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
1585
+ else if ( sym == sym_string );
1586
+ else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
1587
+
1588
+ return sym;
1589
+ }
1590
+
1591
+ /*
1592
+ * call-seq:
1593
+ * res.field_name_type -> Symbol
1594
+ *
1595
+ * Get type of field names.
1596
+ *
1597
+ * See description at #field_name_type=
1598
+ */
1599
+ static VALUE
1600
+ pgresult_field_name_type_get(VALUE self)
1601
+ {
1602
+ t_pg_result *this = pgresult_get_this(self);
1603
+ if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
1604
+ return sym_symbol;
1605
+ } else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
1606
+ return sym_static_symbol;
1607
+ } else {
1608
+ return sym_string;
1609
+ }
1610
+ }
1436
1611
 
1437
1612
  void
1438
1613
  init_pg_result()
1439
1614
  {
1615
+ sym_string = ID2SYM(rb_intern("string"));
1616
+ sym_symbol = ID2SYM(rb_intern("symbol"));
1617
+ sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
1618
+
1440
1619
  rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
1441
- rb_define_alloc_func( rb_cPGresult, pgresult_s_allocate );
1620
+ rb_undef_alloc_func(rb_cPGresult);
1442
1621
  rb_include_module(rb_cPGresult, rb_mEnumerable);
1443
1622
  rb_include_module(rb_cPGresult, rb_mPGconstants);
1444
1623
 
@@ -1447,6 +1626,10 @@ init_pg_result()
1447
1626
  rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1448
1627
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1449
1628
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1629
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1630
+ rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1631
+ rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1632
+ #endif
1450
1633
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1451
1634
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1452
1635
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
@@ -1494,6 +1677,7 @@ init_pg_result()
1494
1677
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1495
1678
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1496
1679
  rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1497
- }
1498
-
1499
1680
 
1681
+ rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
1682
+ rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
1683
+ }