pg 1.1.3 → 1.2.0

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 (55) 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 +70 -0
  6. data/Manifest.txt +3 -2
  7. data/README-Windows.rdoc +4 -4
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +43 -8
  10. data/Rakefile +4 -4
  11. data/Rakefile.cross +7 -4
  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 +132 -95
  16. data/ext/pg.h +24 -17
  17. data/ext/pg_binary_decoder.c +20 -16
  18. data/ext/pg_binary_encoder.c +13 -12
  19. data/ext/pg_coder.c +5 -5
  20. data/ext/pg_connection.c +395 -301
  21. data/ext/pg_copy_coder.c +5 -3
  22. data/ext/pg_record_coder.c +490 -0
  23. data/ext/pg_result.c +272 -124
  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 +4 -3
  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 +2 -3
  37. data/lib/pg/basic_type_mapping.rb +79 -16
  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 +17 -16
  48. data/spec/pg/basic_type_mapping_spec.rb +151 -14
  49. data/spec/pg/connection_spec.rb +117 -55
  50. data/spec/pg/result_spec.rb +193 -3
  51. data/spec/pg/tuple_spec.rb +55 -2
  52. data/spec/pg/type_map_by_column_spec.rb +5 -1
  53. data/spec/pg/type_spec.rb +180 -6
  54. metadata +40 -45
  55. 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,41 @@ 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
+
116
171
  /*
117
172
  * Global functions
118
173
  */
@@ -124,12 +179,10 @@ static VALUE
124
179
  pg_new_result2(PGresult *result, VALUE rb_pgconn)
125
180
  {
126
181
  int nfields = result ? PQnfields(result) : 0;
127
- VALUE self = pgresult_s_allocate( rb_cPGresult );
182
+ VALUE self;
128
183
  t_pg_result *this;
129
184
 
130
185
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
131
- RTYPEDDATA_DATA(self) = this;
132
-
133
186
  this->pgresult = result;
134
187
  this->connection = rb_pgconn;
135
188
  this->typemap = pg_typemap_all_strings;
@@ -137,18 +190,21 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
137
190
  this->nfields = -1;
138
191
  this->tuple_hash = Qnil;
139
192
  this->field_map = Qnil;
140
-
141
- PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
193
+ this->flags = 0;
194
+ self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
142
195
 
143
196
  if( result ){
144
197
  t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
145
198
  VALUE typemap = p_conn->type_map_for_results;
146
-
147
199
  /* Type check is done when assigned to PG::Connection. */
148
200
  t_typemap *p_typemap = DATA_PTR(typemap);
149
201
 
202
+ this->enc_idx = p_conn->enc_idx;
150
203
  this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
151
204
  this->p_typemap = DATA_PTR( this->typemap );
205
+ this->flags = p_conn->flags;
206
+ } else {
207
+ this->enc_idx = rb_locale_encindex();
152
208
  }
153
209
 
154
210
  return self;
@@ -159,22 +215,38 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
159
215
  {
160
216
  VALUE self = pg_new_result2(result, rb_pgconn);
161
217
  t_pg_result *this = pgresult_get_this(self);
162
- t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
163
218
 
164
219
  this->autoclear = 0;
165
220
 
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);
221
+ /* Estimate size of underlying pgresult memory storage and account to ruby GC.
222
+ * There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
223
+ */
224
+ /* 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.
225
+ * For now the memory savings don't justify the ifdefs necessary to support both cases.
226
+ */
227
+ this->result_size = pgresult_approx_size(result);
169
228
 
170
229
  #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
171
- rb_gc_adjust_memory_usage(this->result_size);
230
+ rb_gc_adjust_memory_usage(this->result_size);
172
231
  #endif
173
- }
174
232
 
175
233
  return self;
176
234
  }
177
235
 
236
+ static VALUE
237
+ pg_copy_result(t_pg_result *this)
238
+ {
239
+ int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
240
+ size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
241
+ t_pg_result *copy;
242
+
243
+ copy = (t_pg_result *)xmalloc(len);
244
+ memcpy(copy, this, len);
245
+ this->result_size = 0;
246
+
247
+ return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
248
+ }
249
+
178
250
  VALUE
179
251
  pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
180
252
  {
@@ -229,7 +301,7 @@ pg_result_check( VALUE self )
229
301
  }
230
302
  }
231
303
 
232
- PG_ENCODING_SET_NOCHECK( error, ENCODING_GET(self) );
304
+ PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
233
305
 
234
306
  sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
235
307
  klass = lookup_error_class( sqlstate );
@@ -305,37 +377,6 @@ pgresult_autoclear_p( VALUE self )
305
377
  * DATA pointer functions
306
378
  */
307
379
 
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
380
  /*
340
381
  * Fetch the PG::Result object data pointer and check it's
341
382
  * PGresult data pointer for sanity.
@@ -365,32 +406,30 @@ pgresult_get(VALUE self)
365
406
  return this->pgresult;
366
407
  }
367
408
 
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 )
409
+ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
390
410
  {
391
- VALUE self = TypedData_Wrap_Struct( klass, &pgresult_type, NULL );
392
-
393
- return self;
411
+ VALUE fname;
412
+ #ifdef TRUFFLERUBY
413
+ if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
414
+ #else
415
+ if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
416
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
417
+ fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
418
+ if( fname == Qnil ){
419
+ fname = rb_str_new2(cstr);
420
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
421
+ fname = rb_str_intern(fname);
422
+ }
423
+ } else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
424
+ #endif
425
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
426
+ fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
427
+ } else {
428
+ fname = rb_str_new2(cstr);
429
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
430
+ fname = rb_obj_freeze(fname);
431
+ }
432
+ return fname;
394
433
  }
395
434
 
396
435
  static void pgresult_init_fnames(VALUE self)
@@ -402,12 +441,9 @@ static void pgresult_init_fnames(VALUE self)
402
441
  int nfields = PQnfields(this->pgresult);
403
442
 
404
443
  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);
444
+ char *cfname = PQfname(this->pgresult, i);
445
+ this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
408
446
  this->nfields = i + 1;
409
-
410
- RB_GC_GUARD(fname);
411
447
  }
412
448
  this->nfields = nfields;
413
449
  }
@@ -419,6 +455,8 @@ static void pgresult_init_fnames(VALUE self)
419
455
  *
420
456
  * The class to represent the query result tuples (rows).
421
457
  * An instance of this class is created as the result of every query.
458
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
459
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
422
460
  *
423
461
  * 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
462
  * 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 +506,9 @@ pgresult_result_status(VALUE self)
468
506
  static VALUE
469
507
  pgresult_res_status(VALUE self, VALUE status)
470
508
  {
471
- VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
472
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
509
+ t_pg_result *this = pgresult_get_this_safe(self);
510
+ VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
511
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
473
512
  return ret;
474
513
  }
475
514
 
@@ -482,10 +521,39 @@ pgresult_res_status(VALUE self, VALUE status)
482
521
  static VALUE
483
522
  pgresult_error_message(VALUE self)
484
523
  {
485
- VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
486
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
524
+ t_pg_result *this = pgresult_get_this_safe(self);
525
+ VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
526
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
527
+ return ret;
528
+ }
529
+
530
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
531
+ /*
532
+ * call-seq:
533
+ * res.verbose_error_message( verbosity, show_context ) -> String
534
+ *
535
+ * Returns a reformatted version of the error message associated with a PGresult object.
536
+ *
537
+ * Available since PostgreSQL-9.6
538
+ */
539
+ static VALUE
540
+ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
541
+ {
542
+ t_pg_result *this = pgresult_get_this_safe(self);
543
+ VALUE ret;
544
+ char *c_str;
545
+
546
+ c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
547
+ if(!c_str)
548
+ rb_raise(rb_eNoMemError, "insufficient memory to format error message");
549
+
550
+ ret = rb_str_new2(c_str);
551
+ PQfreemem(c_str);
552
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
553
+
487
554
  return ret;
488
555
  }
556
+ #endif
489
557
 
490
558
  /*
491
559
  * call-seq:
@@ -536,14 +604,14 @@ pgresult_error_message(VALUE self)
536
604
  static VALUE
537
605
  pgresult_error_field(VALUE self, VALUE field)
538
606
  {
539
- PGresult *result = pgresult_get( self );
607
+ t_pg_result *this = pgresult_get_this_safe(self);
540
608
  int fieldcode = NUM2INT( field );
541
- char * fieldstr = PQresultErrorField( result, fieldcode );
609
+ char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
542
610
  VALUE ret = Qnil;
543
611
 
544
612
  if ( fieldstr ) {
545
- ret = rb_tainted_str_new2( fieldstr );
546
- PG_ENCODING_SET_NOCHECK( ret, ENCODING_GET(self ));
613
+ ret = rb_str_new2( fieldstr );
614
+ PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
547
615
  }
548
616
 
549
617
  return ret;
@@ -581,24 +649,25 @@ pgresult_nfields(VALUE self)
581
649
 
582
650
  /*
583
651
  * call-seq:
584
- * res.fname( index ) -> String
652
+ * res.fname( index ) -> String or Symbol
585
653
  *
586
654
  * Returns the name of the column corresponding to _index_.
655
+ * Depending on #field_name_type= it's a String or Symbol.
656
+ *
587
657
  */
588
658
  static VALUE
589
659
  pgresult_fname(VALUE self, VALUE index)
590
660
  {
591
- VALUE fname;
592
- PGresult *result = pgresult_get(self);
661
+ t_pg_result *this = pgresult_get_this_safe(self);
593
662
  int i = NUM2INT(index);
663
+ char *cfname;
594
664
 
595
- if (i < 0 || i >= PQnfields(result)) {
665
+ if (i < 0 || i >= PQnfields(this->pgresult)) {
596
666
  rb_raise(rb_eArgError,"invalid field number %d", i);
597
667
  }
598
668
 
599
- fname = rb_tainted_str_new2(PQfname(result, i));
600
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
601
- return rb_obj_freeze(fname);
669
+ cfname = PQfname(this->pgresult, i);
670
+ return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
602
671
  }
603
672
 
604
673
  /*
@@ -897,8 +966,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
897
966
  static VALUE
898
967
  pgresult_cmd_status(VALUE self)
899
968
  {
900
- VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
901
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
969
+ t_pg_result *this = pgresult_get_this_safe(self);
970
+ VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
971
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
902
972
  return ret;
903
973
  }
904
974
 
@@ -1098,8 +1168,12 @@ static VALUE
1098
1168
  pgresult_field_values( VALUE self, VALUE field )
1099
1169
  {
1100
1170
  PGresult *result = pgresult_get( self );
1101
- const char *fieldname = StringValueCStr( field );
1102
- int fnum = PQfnumber( result, fieldname );
1171
+ const char *fieldname;
1172
+ int fnum;
1173
+
1174
+ if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
1175
+ fieldname = StringValueCStr( field );
1176
+ fnum = PQfnumber( result, fieldname );
1103
1177
 
1104
1178
  if ( fnum < 0 )
1105
1179
  rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
@@ -1142,6 +1216,25 @@ pgresult_tuple_values(VALUE self, VALUE index)
1142
1216
  }
1143
1217
  }
1144
1218
 
1219
+ static void ensure_init_for_tuple(VALUE self)
1220
+ {
1221
+ t_pg_result *this = pgresult_get_this_safe(self);
1222
+
1223
+ if( this->field_map == Qnil ){
1224
+ int i;
1225
+ VALUE field_map = rb_hash_new();
1226
+
1227
+ if( this->nfields == -1 )
1228
+ pgresult_init_fnames( self );
1229
+
1230
+ for( i = 0; i < this->nfields; i++ ){
1231
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1232
+ }
1233
+ rb_obj_freeze(field_map);
1234
+ this->field_map = field_map;
1235
+ }
1236
+ }
1237
+
1145
1238
  /*
1146
1239
  * call-seq:
1147
1240
  * res.tuple( n ) -> PG::Tuple
@@ -1162,19 +1255,7 @@ pgresult_tuple(VALUE self, VALUE index)
1162
1255
  if ( tuple_num < 0 || tuple_num >= num_tuples )
1163
1256
  rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1164
1257
 
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
- }
1258
+ ensure_init_for_tuple(self);
1178
1259
 
1179
1260
  return pg_tuple_new(self, tuple_num);
1180
1261
  }
@@ -1206,7 +1287,7 @@ pgresult_each(VALUE self)
1206
1287
  * call-seq:
1207
1288
  * res.fields() -> Array
1208
1289
  *
1209
- * Returns an array of Strings representing the names of the fields in the result.
1290
+ * Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
1210
1291
  */
1211
1292
  static VALUE
1212
1293
  pgresult_fields(VALUE self)
@@ -1305,11 +1386,17 @@ yield_tuple(VALUE self, int ntuples, int nfields)
1305
1386
  {
1306
1387
  int tuple_num;
1307
1388
  t_pg_result *this = pgresult_get_this(self);
1308
- VALUE result = pg_new_result(this->pgresult, this->connection);
1389
+ VALUE copy;
1309
1390
  UNUSED(nfields);
1310
1391
 
1392
+ /* make a copy of the base result, that is bound to the PG::Tuple */
1393
+ copy = pg_copy_result(this);
1394
+ /* The copy is now owner of the PGresult and is responsible to PQclear it.
1395
+ * We clear the pgresult here, so that it's not double freed on error within yield. */
1396
+ this->pgresult = NULL;
1397
+
1311
1398
  for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1312
- VALUE tuple = pgresult_tuple(result, INT2FIX(tuple_num));
1399
+ VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1313
1400
  rb_yield( tuple );
1314
1401
  }
1315
1402
  }
@@ -1390,8 +1477,6 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1390
1477
  * # do something with each received row of the second query
1391
1478
  * end
1392
1479
  * conn.get_result # => nil (no more results)
1393
- *
1394
- * Available since PostgreSQL-9.2
1395
1480
  */
1396
1481
  static VALUE
1397
1482
  pgresult_stream_each(VALUE self)
@@ -1408,8 +1493,6 @@ pgresult_stream_each(VALUE self)
1408
1493
  *
1409
1494
  * This method works equally to #stream_each , but yields an Array of
1410
1495
  * values.
1411
- *
1412
- * Available since PostgreSQL-9.2
1413
1496
  */
1414
1497
  static VALUE
1415
1498
  pgresult_stream_each_row(VALUE self)
@@ -1424,21 +1507,81 @@ pgresult_stream_each_row(VALUE self)
1424
1507
  * Yields each row of the result set in single row mode.
1425
1508
  *
1426
1509
  * This method works equally to #stream_each , but yields a PG::Tuple object.
1427
- *
1428
- * Available since PostgreSQL-9.2
1429
1510
  */
1430
1511
  static VALUE
1431
1512
  pgresult_stream_each_tuple(VALUE self)
1432
1513
  {
1514
+ /* allocate VALUEs that are shared between all streamed tuples */
1515
+ ensure_init_for_tuple(self);
1516
+
1433
1517
  return pgresult_stream_any(self, yield_tuple);
1434
1518
  }
1435
1519
 
1520
+ /*
1521
+ * call-seq:
1522
+ * res.field_name_type = Symbol
1523
+ *
1524
+ * Set type of field names specific to this result.
1525
+ * It can be set to one of:
1526
+ * * +:string+ to use String based field names
1527
+ * * +:symbol+ to use Symbol based field names
1528
+ * * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably removed in future.
1529
+ *
1530
+ * The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
1531
+ *
1532
+ * This setting affects several result methods:
1533
+ * * keys of Hash returned by #[] , #each and #stream_each
1534
+ * * #fields
1535
+ * * #fname
1536
+ * * field names used by #tuple and #stream_each_tuple
1537
+ *
1538
+ * The type of field names can only be changed before any of the affected methods have been called.
1539
+ *
1540
+ */
1541
+ static VALUE
1542
+ pgresult_field_name_type_set(VALUE self, VALUE sym)
1543
+ {
1544
+ t_pg_result *this = pgresult_get_this(self);
1545
+ if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1546
+
1547
+ this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
1548
+ if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
1549
+ else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
1550
+ else if ( sym == sym_string );
1551
+ else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
1552
+
1553
+ return sym;
1554
+ }
1555
+
1556
+ /*
1557
+ * call-seq:
1558
+ * res.field_name_type -> Symbol
1559
+ *
1560
+ * Get type of field names.
1561
+ *
1562
+ * See description at #field_name_type=
1563
+ */
1564
+ static VALUE
1565
+ pgresult_field_name_type_get(VALUE self)
1566
+ {
1567
+ t_pg_result *this = pgresult_get_this(self);
1568
+ if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
1569
+ return sym_symbol;
1570
+ } else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
1571
+ return sym_static_symbol;
1572
+ } else {
1573
+ return sym_string;
1574
+ }
1575
+ }
1436
1576
 
1437
1577
  void
1438
1578
  init_pg_result()
1439
1579
  {
1440
- rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
1441
- rb_define_alloc_func( rb_cPGresult, pgresult_s_allocate );
1580
+ sym_string = ID2SYM(rb_intern("string"));
1581
+ sym_symbol = ID2SYM(rb_intern("symbol"));
1582
+ sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
1583
+
1584
+ rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cData );
1442
1585
  rb_include_module(rb_cPGresult, rb_mEnumerable);
1443
1586
  rb_include_module(rb_cPGresult, rb_mPGconstants);
1444
1587
 
@@ -1447,6 +1590,10 @@ init_pg_result()
1447
1590
  rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1448
1591
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1449
1592
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1593
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1594
+ rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1595
+ rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1596
+ #endif
1450
1597
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1451
1598
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1452
1599
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
@@ -1494,6 +1641,7 @@ init_pg_result()
1494
1641
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1495
1642
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1496
1643
  rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1497
- }
1498
-
1499
1644
 
1645
+ rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
1646
+ rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
1647
+ }