pg 1.1.0 → 1.2.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 (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +0 -6595
  5. data/History.rdoc +110 -1
  6. data/Manifest.txt +3 -2
  7. data/README-Windows.rdoc +4 -4
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +44 -9
  10. data/Rakefile +8 -6
  11. data/Rakefile.cross +57 -56
  12. data/ext/errorcodes.def +68 -0
  13. data/ext/errorcodes.txt +19 -2
  14. data/ext/extconf.rb +6 -6
  15. data/ext/pg.c +138 -99
  16. data/ext/pg.h +34 -26
  17. data/ext/pg_binary_decoder.c +20 -16
  18. data/ext/pg_binary_encoder.c +13 -12
  19. data/ext/pg_coder.c +21 -9
  20. data/ext/pg_connection.c +413 -321
  21. data/ext/pg_copy_coder.c +6 -3
  22. data/ext/pg_record_coder.c +491 -0
  23. data/ext/pg_result.c +282 -128
  24. data/ext/pg_text_decoder.c +14 -8
  25. data/ext/pg_text_encoder.c +180 -48
  26. data/ext/pg_tuple.c +14 -6
  27. data/ext/pg_type_map.c +1 -1
  28. data/ext/pg_type_map_all_strings.c +4 -4
  29. data/ext/pg_type_map_by_class.c +9 -4
  30. data/ext/pg_type_map_by_column.c +7 -6
  31. data/ext/pg_type_map_by_mri_type.c +1 -1
  32. data/ext/pg_type_map_by_oid.c +3 -2
  33. data/ext/pg_type_map_in_ruby.c +1 -1
  34. data/ext/{util.c → pg_util.c} +5 -5
  35. data/ext/{util.h → pg_util.h} +0 -0
  36. data/lib/pg.rb +5 -5
  37. data/lib/pg/basic_type_mapping.rb +81 -18
  38. data/lib/pg/binary_decoder.rb +1 -0
  39. data/lib/pg/coder.rb +22 -1
  40. data/lib/pg/connection.rb +2 -2
  41. data/lib/pg/constants.rb +1 -0
  42. data/lib/pg/exceptions.rb +1 -0
  43. data/lib/pg/result.rb +13 -1
  44. data/lib/pg/text_decoder.rb +2 -3
  45. data/lib/pg/text_encoder.rb +8 -18
  46. data/lib/pg/type_map_by_column.rb +2 -1
  47. data/spec/helpers.rb +19 -19
  48. data/spec/pg/basic_type_mapping_spec.rb +141 -19
  49. data/spec/pg/connection_spec.rb +239 -93
  50. data/spec/pg/result_spec.rb +194 -4
  51. data/spec/pg/tuple_spec.rb +55 -2
  52. data/spec/pg/type_map_by_class_spec.rb +1 -1
  53. data/spec/pg/type_map_by_column_spec.rb +5 -1
  54. data/spec/pg/type_map_by_oid_spec.rb +2 -2
  55. data/spec/pg/type_spec.rb +180 -6
  56. metadata +41 -47
  57. metadata.gz.sig +0 -0
@@ -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,7 +101,29 @@ pgresult_approx_size(PGresult *result)
94
101
 
95
102
  return size;
96
103
  }
104
+ #endif
97
105
 
106
+ /*
107
+ * GC Mark function
108
+ */
109
+ static void
110
+ pgresult_gc_mark( t_pg_result *this )
111
+ {
112
+ int i;
113
+
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 );
118
+
119
+ for( i=0; i < this->nfields; i++ ){
120
+ rb_gc_mark( this->fnames[i] );
121
+ }
122
+ }
123
+
124
+ /*
125
+ * GC Free function
126
+ */
98
127
  static void
99
128
  pgresult_clear( t_pg_result *this )
100
129
  {
@@ -104,15 +133,46 @@ pgresult_clear( t_pg_result *this )
104
133
  rb_gc_adjust_memory_usage(-this->result_size);
105
134
  #endif
106
135
  }
136
+ this->result_size = 0;
137
+ this->nfields = -1;
107
138
  this->pgresult = NULL;
108
139
  }
109
140
 
141
+ static void
142
+ pgresult_gc_free( t_pg_result *this )
143
+ {
144
+ pgresult_clear( this );
145
+ xfree(this);
146
+ }
147
+
110
148
  static size_t
111
149
  pgresult_memsize( t_pg_result *this )
112
150
  {
151
+ /* Ideally the memory 'this' is pointing to should be taken into account as well.
152
+ * However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
153
+ */
113
154
  return this->result_size;
114
155
  }
115
156
 
157
+ static const rb_data_type_t pgresult_type = {
158
+ "pg",
159
+ {
160
+ (void (*)(void*))pgresult_gc_mark,
161
+ (void (*)(void*))pgresult_gc_free,
162
+ (size_t (*)(const void *))pgresult_memsize,
163
+ },
164
+ 0, 0,
165
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
166
+ RUBY_TYPED_FREE_IMMEDIATELY,
167
+ #endif
168
+ };
169
+
170
+ /* Needed by sequel_pg gem, do not delete */
171
+ int pg_get_result_enc_idx(VALUE self)
172
+ {
173
+ return pgresult_get_this(self)->enc_idx;
174
+ }
175
+
116
176
  /*
117
177
  * Global functions
118
178
  */
@@ -124,12 +184,10 @@ static VALUE
124
184
  pg_new_result2(PGresult *result, VALUE rb_pgconn)
125
185
  {
126
186
  int nfields = result ? PQnfields(result) : 0;
127
- VALUE self = pgresult_s_allocate( rb_cPGresult );
187
+ VALUE self;
128
188
  t_pg_result *this;
129
189
 
130
190
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
131
- RTYPEDDATA_DATA(self) = this;
132
-
133
191
  this->pgresult = result;
134
192
  this->connection = rb_pgconn;
135
193
  this->typemap = pg_typemap_all_strings;
@@ -137,18 +195,21 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
137
195
  this->nfields = -1;
138
196
  this->tuple_hash = Qnil;
139
197
  this->field_map = Qnil;
140
-
141
- PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
198
+ this->flags = 0;
199
+ self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
142
200
 
143
201
  if( result ){
144
202
  t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
145
203
  VALUE typemap = p_conn->type_map_for_results;
146
-
147
204
  /* Type check is done when assigned to PG::Connection. */
148
205
  t_typemap *p_typemap = DATA_PTR(typemap);
149
206
 
207
+ this->enc_idx = p_conn->enc_idx;
150
208
  this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
151
209
  this->p_typemap = DATA_PTR( this->typemap );
210
+ this->flags = p_conn->flags;
211
+ } else {
212
+ this->enc_idx = rb_locale_encindex();
152
213
  }
153
214
 
154
215
  return self;
@@ -159,22 +220,38 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
159
220
  {
160
221
  VALUE self = pg_new_result2(result, rb_pgconn);
161
222
  t_pg_result *this = pgresult_get_this(self);
162
- t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
163
223
 
164
224
  this->autoclear = 0;
165
225
 
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);
226
+ /* Estimate size of underlying pgresult memory storage and account to ruby GC.
227
+ * There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
228
+ */
229
+ /* 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.
230
+ * For now the memory savings don't justify the ifdefs necessary to support both cases.
231
+ */
232
+ this->result_size = pgresult_approx_size(result);
169
233
 
170
234
  #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
171
- rb_gc_adjust_memory_usage(this->result_size);
235
+ rb_gc_adjust_memory_usage(this->result_size);
172
236
  #endif
173
- }
174
237
 
175
238
  return self;
176
239
  }
177
240
 
241
+ static VALUE
242
+ pg_copy_result(t_pg_result *this)
243
+ {
244
+ int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
245
+ size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
246
+ t_pg_result *copy;
247
+
248
+ copy = (t_pg_result *)xmalloc(len);
249
+ memcpy(copy, this, len);
250
+ this->result_size = 0;
251
+
252
+ return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
253
+ }
254
+
178
255
  VALUE
179
256
  pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
180
257
  {
@@ -229,7 +306,7 @@ pg_result_check( VALUE self )
229
306
  }
230
307
  }
231
308
 
232
- PG_ENCODING_SET_NOCHECK( error, ENCODING_GET(self) );
309
+ PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
233
310
 
234
311
  sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
235
312
  klass = lookup_error_class( sqlstate );
@@ -261,8 +338,7 @@ pg_result_check( VALUE self )
261
338
  * Special care must be taken when PG::Tuple objects are used.
262
339
  * In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
263
340
  *
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.
341
+ * 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
342
  *
267
343
  */
268
344
  VALUE
@@ -290,8 +366,10 @@ pgresult_cleared_p( VALUE self )
290
366
  * call-seq:
291
367
  * res.autoclear? -> boolean
292
368
  *
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.
369
+ * 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 .
371
+ *
372
+ * 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
373
  *
296
374
  */
297
375
  VALUE
@@ -305,37 +383,6 @@ pgresult_autoclear_p( VALUE self )
305
383
  * DATA pointer functions
306
384
  */
307
385
 
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
386
  /*
340
387
  * Fetch the PG::Result object data pointer and check it's
341
388
  * PGresult data pointer for sanity.
@@ -365,32 +412,30 @@ pgresult_get(VALUE self)
365
412
  return this->pgresult;
366
413
  }
367
414
 
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 )
415
+ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
390
416
  {
391
- VALUE self = TypedData_Wrap_Struct( klass, &pgresult_type, NULL );
392
-
393
- return self;
417
+ VALUE fname;
418
+ #ifdef TRUFFLERUBY
419
+ if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
420
+ #else
421
+ if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
422
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
423
+ fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
424
+ if( fname == Qnil ){
425
+ fname = rb_str_new2(cstr);
426
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
427
+ fname = rb_str_intern(fname);
428
+ }
429
+ } else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
430
+ #endif
431
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
432
+ fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
433
+ } else {
434
+ fname = rb_str_new2(cstr);
435
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
436
+ fname = rb_obj_freeze(fname);
437
+ }
438
+ return fname;
394
439
  }
395
440
 
396
441
  static void pgresult_init_fnames(VALUE self)
@@ -402,12 +447,9 @@ static void pgresult_init_fnames(VALUE self)
402
447
  int nfields = PQnfields(this->pgresult);
403
448
 
404
449
  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);
450
+ char *cfname = PQfname(this->pgresult, i);
451
+ this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
408
452
  this->nfields = i + 1;
409
-
410
- RB_GC_GUARD(fname);
411
453
  }
412
454
  this->nfields = nfields;
413
455
  }
@@ -419,6 +461,8 @@ static void pgresult_init_fnames(VALUE self)
419
461
  *
420
462
  * The class to represent the query result tuples (rows).
421
463
  * An instance of this class is created as the result of every query.
464
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
465
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
422
466
  *
423
467
  * 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
468
  * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
@@ -468,8 +512,9 @@ pgresult_result_status(VALUE self)
468
512
  static VALUE
469
513
  pgresult_res_status(VALUE self, VALUE status)
470
514
  {
471
- VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
472
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
515
+ t_pg_result *this = pgresult_get_this_safe(self);
516
+ VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
517
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
473
518
  return ret;
474
519
  }
475
520
 
@@ -482,11 +527,40 @@ pgresult_res_status(VALUE self, VALUE status)
482
527
  static VALUE
483
528
  pgresult_error_message(VALUE self)
484
529
  {
485
- VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
486
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
530
+ t_pg_result *this = pgresult_get_this_safe(self);
531
+ VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
532
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
487
533
  return ret;
488
534
  }
489
535
 
536
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
537
+ /*
538
+ * call-seq:
539
+ * res.verbose_error_message( verbosity, show_context ) -> String
540
+ *
541
+ * Returns a reformatted version of the error message associated with a PGresult object.
542
+ *
543
+ * Available since PostgreSQL-9.6
544
+ */
545
+ static VALUE
546
+ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
547
+ {
548
+ t_pg_result *this = pgresult_get_this_safe(self);
549
+ VALUE ret;
550
+ char *c_str;
551
+
552
+ c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
553
+ if(!c_str)
554
+ rb_raise(rb_eNoMemError, "insufficient memory to format error message");
555
+
556
+ ret = rb_str_new2(c_str);
557
+ PQfreemem(c_str);
558
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
559
+
560
+ return ret;
561
+ }
562
+ #endif
563
+
490
564
  /*
491
565
  * call-seq:
492
566
  * res.error_field(fieldcode) -> String
@@ -536,14 +610,14 @@ pgresult_error_message(VALUE self)
536
610
  static VALUE
537
611
  pgresult_error_field(VALUE self, VALUE field)
538
612
  {
539
- PGresult *result = pgresult_get( self );
613
+ t_pg_result *this = pgresult_get_this_safe(self);
540
614
  int fieldcode = NUM2INT( field );
541
- char * fieldstr = PQresultErrorField( result, fieldcode );
615
+ char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
542
616
  VALUE ret = Qnil;
543
617
 
544
618
  if ( fieldstr ) {
545
- ret = rb_tainted_str_new2( fieldstr );
546
- PG_ENCODING_SET_NOCHECK( ret, ENCODING_GET(self ));
619
+ ret = rb_str_new2( fieldstr );
620
+ PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
547
621
  }
548
622
 
549
623
  return ret;
@@ -581,24 +655,25 @@ pgresult_nfields(VALUE self)
581
655
 
582
656
  /*
583
657
  * call-seq:
584
- * res.fname( index ) -> String
658
+ * res.fname( index ) -> String or Symbol
585
659
  *
586
660
  * Returns the name of the column corresponding to _index_.
661
+ * Depending on #field_name_type= it's a String or Symbol.
662
+ *
587
663
  */
588
664
  static VALUE
589
665
  pgresult_fname(VALUE self, VALUE index)
590
666
  {
591
- VALUE fname;
592
- PGresult *result = pgresult_get(self);
667
+ t_pg_result *this = pgresult_get_this_safe(self);
593
668
  int i = NUM2INT(index);
669
+ char *cfname;
594
670
 
595
- if (i < 0 || i >= PQnfields(result)) {
671
+ if (i < 0 || i >= PQnfields(this->pgresult)) {
596
672
  rb_raise(rb_eArgError,"invalid field number %d", i);
597
673
  }
598
674
 
599
- fname = rb_tainted_str_new2(PQfname(result, i));
600
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
601
- return rb_obj_freeze(fname);
675
+ cfname = PQfname(this->pgresult, i);
676
+ return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
602
677
  }
603
678
 
604
679
  /*
@@ -897,8 +972,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
897
972
  static VALUE
898
973
  pgresult_cmd_status(VALUE self)
899
974
  {
900
- VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
901
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
975
+ t_pg_result *this = pgresult_get_this_safe(self);
976
+ VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
977
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
902
978
  return ret;
903
979
  }
904
980
 
@@ -1098,8 +1174,12 @@ static VALUE
1098
1174
  pgresult_field_values( VALUE self, VALUE field )
1099
1175
  {
1100
1176
  PGresult *result = pgresult_get( self );
1101
- const char *fieldname = StringValueCStr( field );
1102
- int fnum = PQfnumber( result, fieldname );
1177
+ const char *fieldname;
1178
+ int fnum;
1179
+
1180
+ if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
1181
+ fieldname = StringValueCStr( field );
1182
+ fnum = PQfnumber( result, fieldname );
1103
1183
 
1104
1184
  if ( fnum < 0 )
1105
1185
  rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
@@ -1142,6 +1222,25 @@ pgresult_tuple_values(VALUE self, VALUE index)
1142
1222
  }
1143
1223
  }
1144
1224
 
1225
+ static void ensure_init_for_tuple(VALUE self)
1226
+ {
1227
+ t_pg_result *this = pgresult_get_this_safe(self);
1228
+
1229
+ if( this->field_map == Qnil ){
1230
+ int i;
1231
+ VALUE field_map = rb_hash_new();
1232
+
1233
+ if( this->nfields == -1 )
1234
+ pgresult_init_fnames( self );
1235
+
1236
+ for( i = 0; i < this->nfields; i++ ){
1237
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1238
+ }
1239
+ rb_obj_freeze(field_map);
1240
+ this->field_map = field_map;
1241
+ }
1242
+ }
1243
+
1145
1244
  /*
1146
1245
  * call-seq:
1147
1246
  * res.tuple( n ) -> PG::Tuple
@@ -1162,19 +1261,7 @@ pgresult_tuple(VALUE self, VALUE index)
1162
1261
  if ( tuple_num < 0 || tuple_num >= num_tuples )
1163
1262
  rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1164
1263
 
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
- }
1264
+ ensure_init_for_tuple(self);
1178
1265
 
1179
1266
  return pg_tuple_new(self, tuple_num);
1180
1267
  }
@@ -1206,7 +1293,7 @@ pgresult_each(VALUE self)
1206
1293
  * call-seq:
1207
1294
  * res.fields() -> Array
1208
1295
  *
1209
- * Returns an array of Strings representing the names of the fields in the result.
1296
+ * Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
1210
1297
  */
1211
1298
  static VALUE
1212
1299
  pgresult_fields(VALUE self)
@@ -1305,11 +1392,17 @@ yield_tuple(VALUE self, int ntuples, int nfields)
1305
1392
  {
1306
1393
  int tuple_num;
1307
1394
  t_pg_result *this = pgresult_get_this(self);
1308
- VALUE result = pg_new_result(this->pgresult, this->connection);
1395
+ VALUE copy;
1309
1396
  UNUSED(nfields);
1310
1397
 
1398
+ /* make a copy of the base result, that is bound to the PG::Tuple */
1399
+ copy = pg_copy_result(this);
1400
+ /* The copy is now owner of the PGresult and is responsible to PQclear it.
1401
+ * We clear the pgresult here, so that it's not double freed on error within yield. */
1402
+ this->pgresult = NULL;
1403
+
1311
1404
  for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1312
- VALUE tuple = pgresult_tuple(result, INT2FIX(tuple_num));
1405
+ VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1313
1406
  rb_yield( tuple );
1314
1407
  }
1315
1408
  }
@@ -1390,8 +1483,6 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1390
1483
  * # do something with each received row of the second query
1391
1484
  * end
1392
1485
  * conn.get_result # => nil (no more results)
1393
- *
1394
- * Available since PostgreSQL-9.2
1395
1486
  */
1396
1487
  static VALUE
1397
1488
  pgresult_stream_each(VALUE self)
@@ -1408,8 +1499,6 @@ pgresult_stream_each(VALUE self)
1408
1499
  *
1409
1500
  * This method works equally to #stream_each , but yields an Array of
1410
1501
  * values.
1411
- *
1412
- * Available since PostgreSQL-9.2
1413
1502
  */
1414
1503
  static VALUE
1415
1504
  pgresult_stream_each_row(VALUE self)
@@ -1424,21 +1513,81 @@ pgresult_stream_each_row(VALUE self)
1424
1513
  * Yields each row of the result set in single row mode.
1425
1514
  *
1426
1515
  * This method works equally to #stream_each , but yields a PG::Tuple object.
1427
- *
1428
- * Available since PostgreSQL-9.2
1429
1516
  */
1430
1517
  static VALUE
1431
1518
  pgresult_stream_each_tuple(VALUE self)
1432
1519
  {
1520
+ /* allocate VALUEs that are shared between all streamed tuples */
1521
+ ensure_init_for_tuple(self);
1522
+
1433
1523
  return pgresult_stream_any(self, yield_tuple);
1434
1524
  }
1435
1525
 
1526
+ /*
1527
+ * call-seq:
1528
+ * res.field_name_type = Symbol
1529
+ *
1530
+ * Set type of field names specific to this result.
1531
+ * It can be set to one of:
1532
+ * * +:string+ to use String based field names
1533
+ * * +: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.
1535
+ *
1536
+ * The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
1537
+ *
1538
+ * This setting affects several result methods:
1539
+ * * keys of Hash returned by #[] , #each and #stream_each
1540
+ * * #fields
1541
+ * * #fname
1542
+ * * field names used by #tuple and #stream_each_tuple
1543
+ *
1544
+ * The type of field names can only be changed before any of the affected methods have been called.
1545
+ *
1546
+ */
1547
+ static VALUE
1548
+ pgresult_field_name_type_set(VALUE self, VALUE sym)
1549
+ {
1550
+ t_pg_result *this = pgresult_get_this(self);
1551
+ if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1552
+
1553
+ this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
1554
+ if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
1555
+ else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
1556
+ else if ( sym == sym_string );
1557
+ else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
1558
+
1559
+ return sym;
1560
+ }
1561
+
1562
+ /*
1563
+ * call-seq:
1564
+ * res.field_name_type -> Symbol
1565
+ *
1566
+ * Get type of field names.
1567
+ *
1568
+ * See description at #field_name_type=
1569
+ */
1570
+ static VALUE
1571
+ pgresult_field_name_type_get(VALUE self)
1572
+ {
1573
+ t_pg_result *this = pgresult_get_this(self);
1574
+ if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
1575
+ return sym_symbol;
1576
+ } else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
1577
+ return sym_static_symbol;
1578
+ } else {
1579
+ return sym_string;
1580
+ }
1581
+ }
1436
1582
 
1437
1583
  void
1438
1584
  init_pg_result()
1439
1585
  {
1440
- rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
1441
- rb_define_alloc_func( rb_cPGresult, pgresult_s_allocate );
1586
+ sym_string = ID2SYM(rb_intern("string"));
1587
+ sym_symbol = ID2SYM(rb_intern("symbol"));
1588
+ sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
1589
+
1590
+ rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cData );
1442
1591
  rb_include_module(rb_cPGresult, rb_mEnumerable);
1443
1592
  rb_include_module(rb_cPGresult, rb_mPGconstants);
1444
1593
 
@@ -1447,6 +1596,10 @@ init_pg_result()
1447
1596
  rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1448
1597
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1449
1598
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1599
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1600
+ rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1601
+ rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1602
+ #endif
1450
1603
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1451
1604
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1452
1605
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
@@ -1494,6 +1647,7 @@ init_pg_result()
1494
1647
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1495
1648
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1496
1649
  rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1497
- }
1498
-
1499
1650
 
1651
+ rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
1652
+ rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
1653
+ }