pg 0.17.1 → 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 (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +0 -3506
  5. data/History.rdoc +308 -0
  6. data/Manifest.txt +35 -19
  7. data/README-Windows.rdoc +17 -28
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +113 -14
  10. data/Rakefile +67 -30
  11. data/Rakefile.cross +109 -83
  12. data/ext/errorcodes.def +101 -0
  13. data/ext/errorcodes.rb +1 -1
  14. data/ext/errorcodes.txt +33 -2
  15. data/ext/extconf.rb +55 -58
  16. data/ext/gvl_wrappers.c +4 -0
  17. data/ext/gvl_wrappers.h +27 -39
  18. data/ext/pg.c +262 -130
  19. data/ext/pg.h +266 -54
  20. data/ext/pg_binary_decoder.c +229 -0
  21. data/ext/pg_binary_encoder.c +163 -0
  22. data/ext/pg_coder.c +561 -0
  23. data/ext/pg_connection.c +1689 -990
  24. data/ext/pg_copy_coder.c +599 -0
  25. data/ext/pg_errors.c +6 -0
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +897 -164
  28. data/ext/pg_text_decoder.c +987 -0
  29. data/ext/pg_text_encoder.c +814 -0
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +166 -0
  32. data/ext/pg_type_map_all_strings.c +116 -0
  33. data/ext/pg_type_map_by_class.c +244 -0
  34. data/ext/pg_type_map_by_column.c +313 -0
  35. data/ext/pg_type_map_by_mri_type.c +284 -0
  36. data/ext/pg_type_map_by_oid.c +356 -0
  37. data/ext/pg_type_map_in_ruby.c +299 -0
  38. data/ext/pg_util.c +149 -0
  39. data/ext/pg_util.h +65 -0
  40. data/lib/pg/basic_type_mapping.rb +522 -0
  41. data/lib/pg/binary_decoder.rb +23 -0
  42. data/lib/pg/coder.rb +104 -0
  43. data/lib/pg/connection.rb +153 -41
  44. data/lib/pg/constants.rb +2 -1
  45. data/lib/pg/exceptions.rb +2 -1
  46. data/lib/pg/result.rb +33 -6
  47. data/lib/pg/text_decoder.rb +46 -0
  48. data/lib/pg/text_encoder.rb +59 -0
  49. data/lib/pg/tuple.rb +30 -0
  50. data/lib/pg/type_map_by_column.rb +16 -0
  51. data/lib/pg.rb +29 -9
  52. data/spec/{lib/helpers.rb → helpers.rb} +151 -64
  53. data/spec/pg/basic_type_mapping_spec.rb +630 -0
  54. data/spec/pg/connection_spec.rb +1180 -477
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +456 -120
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +138 -0
  59. data/spec/pg/type_map_by_column_spec.rb +226 -0
  60. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  61. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  62. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  63. data/spec/pg/type_map_spec.rb +22 -0
  64. data/spec/pg/type_spec.rb +1123 -0
  65. data/spec/pg_spec.rb +26 -20
  66. data.tar.gz.sig +0 -0
  67. metadata +148 -91
  68. metadata.gz.sig +0 -0
  69. data/sample/array_insert.rb +0 -20
  70. data/sample/async_api.rb +0 -106
  71. data/sample/async_copyto.rb +0 -39
  72. data/sample/async_mixed.rb +0 -56
  73. data/sample/check_conn.rb +0 -21
  74. data/sample/copyfrom.rb +0 -81
  75. data/sample/copyto.rb +0 -19
  76. data/sample/cursor.rb +0 -21
  77. data/sample/disk_usage_report.rb +0 -186
  78. data/sample/issue-119.rb +0 -94
  79. data/sample/losample.rb +0 -69
  80. data/sample/minimal-testcase.rb +0 -17
  81. data/sample/notify_wait.rb +0 -72
  82. data/sample/pg_statistics.rb +0 -294
  83. data/sample/replication_monitor.rb +0 -231
  84. data/sample/test_binary_values.rb +0 -33
  85. data/sample/wal_shipper.rb +0 -434
  86. data/sample/warehouse_partitions.rb +0 -320
data/ext/pg_result.c CHANGED
@@ -1,17 +1,177 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v 8255d4f73334 2013/08/18 20:40:08 lars $
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
+
12
+ static VALUE pgresult_type_map_set( VALUE, VALUE );
13
+ static t_pg_result *pgresult_get_this( VALUE );
14
+ static t_pg_result *pgresult_get_this_safe( VALUE );
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
25
+
26
+ #define PGRESULT_DATA_BLOCKSIZE 2048
27
+ typedef struct pgresAttValue
28
+ {
29
+ int len; /* length in bytes of the value */
30
+ char *value; /* actual value, plus terminating zero byte */
31
+ } PGresAttValue;
32
+
33
+
34
+ static int
35
+ count_leading_zero_bits(unsigned int x)
36
+ {
37
+ #if defined(__GNUC__) || defined(__clang__)
38
+ return __builtin_clz(x);
39
+ #elif defined(_MSC_VER)
40
+ DWORD r = 0;
41
+ _BitScanForward(&r, x);
42
+ return (int)r;
43
+ #else
44
+ unsigned int a;
45
+ for(a=0; a < sizeof(unsigned int) * 8; a++){
46
+ if( x & (1 << (sizeof(unsigned int) * 8 - 1))) return a;
47
+ x <<= 1;
48
+ }
49
+ return a;
50
+ #endif
51
+ }
52
+
53
+ static ssize_t
54
+ pgresult_approx_size(const PGresult *result)
55
+ {
56
+ int num_fields = PQnfields(result);
57
+ ssize_t size = 0;
58
+
59
+ if( num_fields > 0 ){
60
+ int num_tuples = PQntuples(result);
61
+
62
+ if( num_tuples > 0 ){
63
+ int pos;
64
+
65
+ /* This is a simple heuristic to determine the number of sample fields and subsequently to approximate the memory size taken by all field values of the result set.
66
+ * Since scanning of all field values is would have a severe performance impact, only a small subset of fields is retrieved and the result is extrapolated to the whole result set.
67
+ * The given algorithm has no real scientific background, but is made for speed and typical table layouts.
68
+ */
69
+ int num_samples =
70
+ (num_fields < 9 ? num_fields : 39 - count_leading_zero_bits(num_fields-8)) *
71
+ (num_tuples < 8 ? 1 : 30 - count_leading_zero_bits(num_tuples));
72
+
73
+ /* start with scanning very last fields, since they are most probably in the cache */
74
+ for( pos = 0; pos < (num_samples+1)/2; pos++ ){
75
+ size += PQgetlength(result, num_tuples - 1 - (pos / num_fields), num_fields - 1 - (pos % num_fields));
76
+ }
77
+ /* scan the very first fields */
78
+ for( pos = 0; pos < num_samples/2; pos++ ){
79
+ size += PQgetlength(result, pos / num_fields, pos % num_fields);
80
+ }
81
+ /* extrapolate sample size to whole result set */
82
+ size = size * num_tuples * num_fields / num_samples;
83
+ }
84
+
85
+ /* count metadata */
86
+ size += num_fields * (
87
+ sizeof(PGresAttDesc) + /* column description */
88
+ num_tuples * (
89
+ sizeof(PGresAttValue) + 1 /* ptr, len and zero termination of each value */
90
+ )
91
+ );
92
+
93
+ /* Account free space due to libpq's default block size */
94
+ size = (size + PGRESULT_DATA_BLOCKSIZE - 1) / PGRESULT_DATA_BLOCKSIZE * PGRESULT_DATA_BLOCKSIZE;
11
95
 
12
- static void pgresult_gc_free( PGresult * );
13
- static PGresult* pgresult_get( VALUE );
96
+ /* count tuple pointers */
97
+ size += sizeof(void*) * ((num_tuples + 128 - 1) / 128 * 128);
98
+ }
14
99
 
100
+ size += 216; /* add PGresult size */
101
+
102
+ return size;
103
+ }
104
+ #endif
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
+ */
127
+ static void
128
+ pgresult_clear( t_pg_result *this )
129
+ {
130
+ if( this->pgresult && !this->autoclear ){
131
+ PQclear(this->pgresult);
132
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
133
+ rb_gc_adjust_memory_usage(-this->result_size);
134
+ #endif
135
+ }
136
+ this->result_size = 0;
137
+ this->nfields = -1;
138
+ this->pgresult = NULL;
139
+ }
140
+
141
+ static void
142
+ pgresult_gc_free( t_pg_result *this )
143
+ {
144
+ pgresult_clear( this );
145
+ xfree(this);
146
+ }
147
+
148
+ static size_t
149
+ pgresult_memsize( t_pg_result *this )
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
+ */
154
+ return this->result_size;
155
+ }
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
+ }
15
175
 
16
176
  /*
17
177
  * Global functions
@@ -20,19 +180,90 @@ static PGresult* pgresult_get( VALUE );
20
180
  /*
21
181
  * Result constructor
22
182
  */
183
+ static VALUE
184
+ pg_new_result2(PGresult *result, VALUE rb_pgconn)
185
+ {
186
+ int nfields = result ? PQnfields(result) : 0;
187
+ VALUE self;
188
+ t_pg_result *this;
189
+
190
+ this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
191
+ this->pgresult = result;
192
+ this->connection = rb_pgconn;
193
+ this->typemap = pg_typemap_all_strings;
194
+ this->p_typemap = DATA_PTR( this->typemap );
195
+ this->nfields = -1;
196
+ this->tuple_hash = Qnil;
197
+ this->field_map = Qnil;
198
+ this->flags = 0;
199
+ self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
200
+
201
+ if( result ){
202
+ t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
203
+ VALUE typemap = p_conn->type_map_for_results;
204
+ /* Type check is done when assigned to PG::Connection. */
205
+ t_typemap *p_typemap = DATA_PTR(typemap);
206
+
207
+ 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 );
210
+ this->flags = p_conn->flags;
211
+ } else {
212
+ this->enc_idx = rb_locale_encindex();
213
+ }
214
+
215
+ return self;
216
+ }
217
+
23
218
  VALUE
24
219
  pg_new_result(PGresult *result, VALUE rb_pgconn)
25
220
  {
26
- PGconn *conn = pg_get_pgconn( rb_pgconn );
27
- VALUE val = Data_Wrap_Struct(rb_cPGresult, NULL, pgresult_gc_free, result);
28
- #ifdef M17N_SUPPORTED
29
- rb_encoding *enc = pg_conn_enc_get( conn );
30
- ENCODING_SET( val, rb_enc_to_index(enc) );
221
+ VALUE self = pg_new_result2(result, rb_pgconn);
222
+ t_pg_result *this = pgresult_get_this(self);
223
+
224
+ this->autoclear = 0;
225
+
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);
233
+
234
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
235
+ rb_gc_adjust_memory_usage(this->result_size);
31
236
  #endif
32
237
 
33
- rb_iv_set( val, "@connection", rb_pgconn );
238
+ return self;
239
+ }
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;
34
251
 
35
- return val;
252
+ return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
253
+ }
254
+
255
+ VALUE
256
+ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
257
+ {
258
+ VALUE self = pg_new_result2(result, rb_pgconn);
259
+ t_pg_result *this = pgresult_get_this(self);
260
+
261
+ /* Autocleared results are freed implicit instead of by PQclear().
262
+ * So it's not very useful to be accounted by ruby GC.
263
+ */
264
+ this->result_size = 0;
265
+ this->autoclear = 1;
266
+ return self;
36
267
  }
37
268
 
38
269
  /*
@@ -44,56 +275,44 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
44
275
  VALUE
45
276
  pg_result_check( VALUE self )
46
277
  {
278
+ t_pg_result *this = pgresult_get_this(self);
47
279
  VALUE error, exception, klass;
48
- VALUE rb_pgconn = rb_iv_get( self, "@connection" );
49
- PGconn *conn = pg_get_pgconn(rb_pgconn);
50
- PGresult *result;
51
- #ifdef M17N_SUPPORTED
52
- rb_encoding *enc = pg_conn_enc_get( conn );
53
- #endif
54
280
  char * sqlstate;
55
281
 
56
- Data_Get_Struct(self, PGresult, result);
57
-
58
- if(result == NULL)
282
+ if(this->pgresult == NULL)
59
283
  {
284
+ PGconn *conn = pg_get_pgconn(this->connection);
60
285
  error = rb_str_new2( PQerrorMessage(conn) );
61
286
  }
62
287
  else
63
288
  {
64
- switch (PQresultStatus(result))
289
+ switch (PQresultStatus(this->pgresult))
65
290
  {
66
291
  case PGRES_TUPLES_OK:
67
292
  case PGRES_COPY_OUT:
68
293
  case PGRES_COPY_IN:
69
- #ifdef HAVE_CONST_PGRES_COPY_BOTH
70
294
  case PGRES_COPY_BOTH:
71
- #endif
72
- #ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
73
295
  case PGRES_SINGLE_TUPLE:
74
- #endif
75
296
  case PGRES_EMPTY_QUERY:
76
297
  case PGRES_COMMAND_OK:
77
298
  return self;
78
299
  case PGRES_BAD_RESPONSE:
79
300
  case PGRES_FATAL_ERROR:
80
301
  case PGRES_NONFATAL_ERROR:
81
- error = rb_str_new2( PQresultErrorMessage(result) );
302
+ error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
82
303
  break;
83
304
  default:
84
305
  error = rb_str_new2( "internal error : unknown result status." );
85
306
  }
86
307
  }
87
308
 
88
- #ifdef M17N_SUPPORTED
89
- rb_enc_set_index( error, rb_enc_to_index(enc) );
90
- #endif
309
+ PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
91
310
 
92
- sqlstate = PQresultErrorField( result, PG_DIAG_SQLSTATE );
311
+ sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
93
312
  klass = lookup_error_class( sqlstate );
94
313
  exception = rb_exc_new3( klass, error );
95
- rb_iv_set( exception, "@connection", rb_pgconn );
96
- rb_iv_set( exception, "@result", result ? self : Qnil );
314
+ rb_iv_set( exception, "@connection", this->connection );
315
+ rb_iv_set( exception, "@result", this->pgresult ? self : Qnil );
97
316
  rb_exc_raise( exception );
98
317
 
99
318
  /* Not reached */
@@ -111,44 +330,130 @@ pg_result_check( VALUE self )
111
330
  * call-seq:
112
331
  * res.clear() -> nil
113
332
  *
114
- * Clears the PG::Result object as the result of the query.
333
+ * Clears the PG::Result object as the result of a query.
334
+ * This frees all underlying memory consumed by the result object.
335
+ * Afterwards access to result methods raises PG::Error "result has been cleared".
336
+ *
337
+ * Explicit calling #clear can lead to better memory performance, but is not generally necessary.
338
+ * Special care must be taken when PG::Tuple objects are used.
339
+ * In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
340
+ *
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.
342
+ *
115
343
  */
116
344
  VALUE
117
345
  pg_result_clear(VALUE self)
118
346
  {
119
- PQclear(pgresult_get(self));
120
- DATA_PTR(self) = NULL;
347
+ t_pg_result *this = pgresult_get_this(self);
348
+ pgresult_clear( this );
121
349
  return Qnil;
122
350
  }
123
351
 
352
+ /*
353
+ * call-seq:
354
+ * res.cleared? -> boolean
355
+ *
356
+ * Returns +true+ if the backend result memory has been free'd.
357
+ */
358
+ VALUE
359
+ pgresult_cleared_p( VALUE self )
360
+ {
361
+ t_pg_result *this = pgresult_get_this(self);
362
+ return this->pgresult ? Qfalse : Qtrue;
363
+ }
124
364
 
365
+ /*
366
+ * call-seq:
367
+ * res.autoclear? -> boolean
368
+ *
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 .
373
+ *
374
+ */
375
+ VALUE
376
+ pgresult_autoclear_p( VALUE self )
377
+ {
378
+ t_pg_result *this = pgresult_get_this(self);
379
+ return this->autoclear ? Qtrue : Qfalse;
380
+ }
125
381
 
126
382
  /*
127
383
  * DATA pointer functions
128
384
  */
129
385
 
130
386
  /*
131
- * GC Free function
387
+ * Fetch the PG::Result object data pointer and check it's
388
+ * PGresult data pointer for sanity.
132
389
  */
133
- static void
134
- pgresult_gc_free( PGresult *result )
390
+ static t_pg_result *
391
+ pgresult_get_this_safe( VALUE self )
135
392
  {
136
- if(result != NULL)
137
- PQclear(result);
393
+ t_pg_result *this = pgresult_get_this(self);
394
+
395
+ if (this->pgresult == NULL) rb_raise(rb_ePGerror, "result has been cleared");
396
+ return this;
138
397
  }
139
398
 
140
399
  /*
141
- * Fetch the data pointer for the result object
400
+ * Fetch the PGresult pointer for the result object and check validity
401
+ *
402
+ * Note: This function is used externally by the sequel_pg gem,
403
+ * so do changes carefully.
404
+ *
142
405
  */
143
- static PGresult*
406
+ PGresult*
144
407
  pgresult_get(VALUE self)
145
408
  {
146
- PGresult *result;
147
- Data_Get_Struct(self, PGresult, result);
148
- if (result == NULL) rb_raise(rb_ePGerror, "result has been cleared");
149
- return result;
409
+ t_pg_result *this = pgresult_get_this(self);
410
+
411
+ if (this->pgresult == NULL) rb_raise(rb_ePGerror, "result has been cleared");
412
+ return this->pgresult;
413
+ }
414
+
415
+ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
416
+ {
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;
150
439
  }
151
440
 
441
+ static void pgresult_init_fnames(VALUE self)
442
+ {
443
+ t_pg_result *this = pgresult_get_this_safe(self);
444
+
445
+ if( this->nfields == -1 ){
446
+ int i;
447
+ int nfields = PQnfields(this->pgresult);
448
+
449
+ for( i=0; i<nfields; i++ ){
450
+ char *cfname = PQfname(this->pgresult, i);
451
+ this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
452
+ this->nfields = i + 1;
453
+ }
454
+ this->nfields = nfields;
455
+ }
456
+ }
152
457
 
153
458
  /********************************************************************
154
459
  *
@@ -156,12 +461,15 @@ pgresult_get(VALUE self)
156
461
  *
157
462
  * The class to represent the query result tuples (rows).
158
463
  * An instance of this class is created as the result of every query.
159
- * You may need to invoke the #clear method of the instance when finished with
160
- * the result for better memory performance.
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 .
466
+ *
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.
468
+ * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
161
469
  *
162
470
  * Example:
163
471
  * require 'pg'
164
- * conn = PGconn.open(:dbname => 'test')
472
+ * conn = PG.connect(:dbname => 'test')
165
473
  * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
166
474
  * res.getvalue(0,0) # '1'
167
475
  * res[0]['b'] # '2'
@@ -175,7 +483,7 @@ pgresult_get(VALUE self)
175
483
 
176
484
  /*
177
485
  * call-seq:
178
- * res.result_status() -> Fixnum
486
+ * res.result_status() -> Integer
179
487
  *
180
488
  * Returns the status of the query. The status value is one of:
181
489
  * * +PGRES_EMPTY_QUERY+
@@ -204,8 +512,9 @@ pgresult_result_status(VALUE self)
204
512
  static VALUE
205
513
  pgresult_res_status(VALUE self, VALUE status)
206
514
  {
207
- VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
208
- ASSOCIATE_INDEX(ret, 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);
209
518
  return ret;
210
519
  }
211
520
 
@@ -218,11 +527,40 @@ pgresult_res_status(VALUE self, VALUE status)
218
527
  static VALUE
219
528
  pgresult_error_message(VALUE self)
220
529
  {
221
- VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
222
- ASSOCIATE_INDEX(ret, 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);
223
533
  return ret;
224
534
  }
225
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
+
226
564
  /*
227
565
  * call-seq:
228
566
  * res.error_field(fieldcode) -> String
@@ -272,14 +610,14 @@ pgresult_error_message(VALUE self)
272
610
  static VALUE
273
611
  pgresult_error_field(VALUE self, VALUE field)
274
612
  {
275
- PGresult *result = pgresult_get( self );
613
+ t_pg_result *this = pgresult_get_this_safe(self);
276
614
  int fieldcode = NUM2INT( field );
277
- char * fieldstr = PQresultErrorField( result, fieldcode );
615
+ char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
278
616
  VALUE ret = Qnil;
279
617
 
280
618
  if ( fieldstr ) {
281
- ret = rb_tainted_str_new2( fieldstr );
282
- ASSOCIATE_INDEX( ret, self );
619
+ ret = rb_str_new2( fieldstr );
620
+ PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
283
621
  }
284
622
 
285
623
  return ret;
@@ -287,7 +625,7 @@ pgresult_error_field(VALUE self, VALUE field)
287
625
 
288
626
  /*
289
627
  * call-seq:
290
- * res.ntuples() -> Fixnum
628
+ * res.ntuples() -> Integer
291
629
  *
292
630
  * Returns the number of tuples in the query result.
293
631
  */
@@ -297,9 +635,15 @@ pgresult_ntuples(VALUE self)
297
635
  return INT2FIX(PQntuples(pgresult_get(self)));
298
636
  }
299
637
 
638
+ static VALUE
639
+ pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
640
+ {
641
+ return pgresult_ntuples(self);
642
+ }
643
+
300
644
  /*
301
645
  * call-seq:
302
- * res.nfields() -> Fixnum
646
+ * res.nfields() -> Integer
303
647
  *
304
648
  * Returns the number of columns in the query result.
305
649
  */
@@ -311,29 +655,30 @@ pgresult_nfields(VALUE self)
311
655
 
312
656
  /*
313
657
  * call-seq:
314
- * res.fname( index ) -> String
658
+ * res.fname( index ) -> String or Symbol
315
659
  *
316
660
  * Returns the name of the column corresponding to _index_.
661
+ * Depending on #field_name_type= it's a String or Symbol.
662
+ *
317
663
  */
318
664
  static VALUE
319
665
  pgresult_fname(VALUE self, VALUE index)
320
666
  {
321
- VALUE fname;
322
- PGresult *result;
667
+ t_pg_result *this = pgresult_get_this_safe(self);
323
668
  int i = NUM2INT(index);
669
+ char *cfname;
324
670
 
325
- result = pgresult_get(self);
326
- if (i < 0 || i >= PQnfields(result)) {
671
+ if (i < 0 || i >= PQnfields(this->pgresult)) {
327
672
  rb_raise(rb_eArgError,"invalid field number %d", i);
328
673
  }
329
- fname = rb_tainted_str_new2(PQfname(result, i));
330
- ASSOCIATE_INDEX(fname, self);
331
- return fname;
674
+
675
+ cfname = PQfname(this->pgresult, i);
676
+ return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
332
677
  }
333
678
 
334
679
  /*
335
680
  * call-seq:
336
- * res.fnumber( name ) -> Fixnum
681
+ * res.fnumber( name ) -> Integer
337
682
  *
338
683
  * Returns the index of the field specified by the string +name+.
339
684
  * The given +name+ is treated like an identifier in an SQL command, that is,
@@ -361,16 +706,16 @@ pgresult_fnumber(VALUE self, VALUE name)
361
706
 
362
707
  Check_Type(name, T_STRING);
363
708
 
364
- n = PQfnumber(pgresult_get(self), StringValuePtr(name));
709
+ n = PQfnumber(pgresult_get(self), StringValueCStr(name));
365
710
  if (n == -1) {
366
- rb_raise(rb_eArgError,"Unknown field: %s", StringValuePtr(name));
711
+ rb_raise(rb_eArgError,"Unknown field: %s", StringValueCStr(name));
367
712
  }
368
713
  return INT2FIX(n);
369
714
  }
370
715
 
371
716
  /*
372
717
  * call-seq:
373
- * res.ftable( column_number ) -> Fixnum
718
+ * res.ftable( column_number ) -> Integer
374
719
  *
375
720
  * Returns the Oid of the table from which the column _column_number_
376
721
  * was fetched.
@@ -389,12 +734,12 @@ pgresult_ftable(VALUE self, VALUE column_number)
389
734
  rb_raise(rb_eArgError,"Invalid column index: %d", col_number);
390
735
 
391
736
  n = PQftable(pgresult, col_number);
392
- return INT2FIX(n);
737
+ return UINT2NUM(n);
393
738
  }
394
739
 
395
740
  /*
396
741
  * call-seq:
397
- * res.ftablecol( column_number ) -> Fixnum
742
+ * res.ftablecol( column_number ) -> Integer
398
743
  *
399
744
  * Returns the column number (within its table) of the table from
400
745
  * which the column _column_number_ is made up.
@@ -419,7 +764,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
419
764
 
420
765
  /*
421
766
  * call-seq:
422
- * res.fformat( column_number ) -> Fixnum
767
+ * res.fformat( column_number ) -> Integer
423
768
  *
424
769
  * Returns the format (0 for text, 1 for binary) of column
425
770
  * _column_number_.
@@ -440,7 +785,7 @@ pgresult_fformat(VALUE self, VALUE column_number)
440
785
 
441
786
  /*
442
787
  * call-seq:
443
- * res.ftype( column_number )
788
+ * res.ftype( column_number ) -> Integer
444
789
  *
445
790
  * Returns the data type associated with _column_number_.
446
791
  *
@@ -464,7 +809,7 @@ pgresult_ftype(VALUE self, VALUE index)
464
809
  if (i < 0 || i >= PQnfields(result)) {
465
810
  rb_raise(rb_eArgError, "invalid field number %d", i);
466
811
  }
467
- return INT2NUM(PQftype(result, i));
812
+ return UINT2NUM(PQftype(result, i));
468
813
  }
469
814
 
470
815
  /*
@@ -515,30 +860,6 @@ pgresult_fsize(VALUE self, VALUE index)
515
860
  }
516
861
 
517
862
 
518
- static VALUE
519
- pgresult_value(VALUE self, PGresult *result, int tuple_num, int field_num)
520
- {
521
- VALUE val;
522
- if ( PQgetisnull(result, tuple_num, field_num) ) {
523
- return Qnil;
524
- }
525
- else {
526
- val = rb_tainted_str_new( PQgetvalue(result, tuple_num, field_num ),
527
- PQgetlength(result, tuple_num, field_num) );
528
-
529
- #ifdef M17N_SUPPORTED
530
- /* associate client encoding for text format only */
531
- if ( 0 == PQfformat(result, field_num) ) {
532
- ASSOCIATE_INDEX( val, self );
533
- } else {
534
- rb_enc_associate( val, rb_ascii8bit_encoding() );
535
- }
536
- #endif
537
-
538
- return val;
539
- }
540
- }
541
-
542
863
  /*
543
864
  * call-seq:
544
865
  * res.getvalue( tup_num, field_num )
@@ -549,18 +870,17 @@ pgresult_value(VALUE self, PGresult *result, int tuple_num, int field_num)
549
870
  static VALUE
550
871
  pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
551
872
  {
552
- PGresult *result;
873
+ t_pg_result *this = pgresult_get_this_safe(self);
553
874
  int i = NUM2INT(tup_num);
554
875
  int j = NUM2INT(field_num);
555
876
 
556
- result = pgresult_get(self);
557
- if(i < 0 || i >= PQntuples(result)) {
877
+ if(i < 0 || i >= PQntuples(this->pgresult)) {
558
878
  rb_raise(rb_eArgError,"invalid tuple number %d", i);
559
879
  }
560
- if(j < 0 || j >= PQnfields(result)) {
880
+ if(j < 0 || j >= PQnfields(this->pgresult)) {
561
881
  rb_raise(rb_eArgError,"invalid field number %d", j);
562
882
  }
563
- return pgresult_value(self, result, i, j);
883
+ return this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, j);
564
884
  }
565
885
 
566
886
  /*
@@ -588,7 +908,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
588
908
 
589
909
  /*
590
910
  * call-seq:
591
- * res.getlength( tup_num, field_num ) -> Fixnum
911
+ * res.getlength( tup_num, field_num ) -> Integer
592
912
  *
593
913
  * Returns the (String) length of the field in bytes.
594
914
  *
@@ -613,7 +933,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
613
933
 
614
934
  /*
615
935
  * call-seq:
616
- * res.nparams() -> Fixnum
936
+ * res.nparams() -> Integer
617
937
  *
618
938
  * Returns the number of parameters of a prepared statement.
619
939
  * Only useful for the result returned by conn.describePrepared
@@ -640,7 +960,7 @@ pgresult_paramtype(VALUE self, VALUE param_number)
640
960
  PGresult *result;
641
961
 
642
962
  result = pgresult_get(self);
643
- return INT2FIX(PQparamtype(result,NUM2INT(param_number)));
963
+ return UINT2NUM(PQparamtype(result,NUM2INT(param_number)));
644
964
  }
645
965
 
646
966
  /*
@@ -652,23 +972,30 @@ pgresult_paramtype(VALUE self, VALUE param_number)
652
972
  static VALUE
653
973
  pgresult_cmd_status(VALUE self)
654
974
  {
655
- VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
656
- ASSOCIATE_INDEX(ret, 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);
657
978
  return ret;
658
979
  }
659
980
 
660
981
  /*
661
982
  * call-seq:
662
- * res.cmd_tuples() -> Fixnum
983
+ * res.cmd_tuples() -> Integer
663
984
  *
664
985
  * Returns the number of tuples (rows) affected by the SQL command.
665
986
  *
666
987
  * If the SQL command that generated the PG::Result was not one of:
667
- * * +INSERT+
668
- * * +UPDATE+
669
- * * +DELETE+
670
- * * +MOVE+
671
- * * +FETCH+
988
+ *
989
+ * * <tt>SELECT</tt>
990
+ * * <tt>CREATE TABLE AS</tt>
991
+ * * <tt>INSERT</tt>
992
+ * * <tt>UPDATE</tt>
993
+ * * <tt>DELETE</tt>
994
+ * * <tt>MOVE</tt>
995
+ * * <tt>FETCH</tt>
996
+ * * <tt>COPY</tt>
997
+ * * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
998
+ *
672
999
  * or if no tuples were affected, <tt>0</tt> is returned.
673
1000
  */
674
1001
  static VALUE
@@ -681,7 +1008,7 @@ pgresult_cmd_tuples(VALUE self)
681
1008
 
682
1009
  /*
683
1010
  * call-seq:
684
- * res.oid_value() -> Fixnum
1011
+ * res.oid_value() -> Integer
685
1012
  *
686
1013
  * Returns the +oid+ of the inserted row if applicable,
687
1014
  * otherwise +nil+.
@@ -693,7 +1020,7 @@ pgresult_oid_value(VALUE self)
693
1020
  if (n == InvalidOid)
694
1021
  return Qnil;
695
1022
  else
696
- return INT2FIX(n);
1023
+ return UINT2NUM(n);
697
1024
  }
698
1025
 
699
1026
  /* Utility methods not in libpq */
@@ -707,21 +1034,29 @@ pgresult_oid_value(VALUE self)
707
1034
  static VALUE
708
1035
  pgresult_aref(VALUE self, VALUE index)
709
1036
  {
710
- PGresult *result = pgresult_get(self);
1037
+ t_pg_result *this = pgresult_get_this_safe(self);
711
1038
  int tuple_num = NUM2INT(index);
712
1039
  int field_num;
713
- VALUE fname;
1040
+ int num_tuples = PQntuples(this->pgresult);
714
1041
  VALUE tuple;
715
1042
 
716
- if ( tuple_num < 0 || tuple_num >= PQntuples(result) )
1043
+ if( this->nfields == -1 )
1044
+ pgresult_init_fnames( self );
1045
+
1046
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
717
1047
  rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
718
1048
 
719
- tuple = rb_hash_new();
720
- for ( field_num = 0; field_num < PQnfields(result); field_num++ ) {
721
- fname = rb_tainted_str_new2( PQfname(result,field_num) );
722
- ASSOCIATE_INDEX(fname, self);
723
- rb_hash_aset( tuple, fname, pgresult_value(self, result, tuple_num, field_num) );
1049
+ /* We reuse the Hash of the previous output for larger row counts.
1050
+ * This is somewhat faster than populating an empty Hash object. */
1051
+ tuple = NIL_P(this->tuple_hash) ? rb_hash_new() : this->tuple_hash;
1052
+ for ( field_num = 0; field_num < this->nfields; field_num++ ) {
1053
+ VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field_num);
1054
+ rb_hash_aset( tuple, this->fnames[field_num], val );
724
1055
  }
1056
+ /* Store a copy of the filled hash for use at the next row. */
1057
+ if( num_tuples > 10 )
1058
+ this->tuple_hash = rb_hash_dup(tuple);
1059
+
725
1060
  return tuple;
726
1061
  }
727
1062
 
@@ -734,25 +1069,59 @@ pgresult_aref(VALUE self, VALUE index)
734
1069
  static VALUE
735
1070
  pgresult_each_row(VALUE self)
736
1071
  {
737
- PGresult* result = (PGresult*) pgresult_get(self);
1072
+ t_pg_result *this;
738
1073
  int row;
739
1074
  int field;
740
- int num_rows = PQntuples(result);
741
- int num_fields = PQnfields(result);
1075
+ int num_rows;
1076
+ int num_fields;
1077
+
1078
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
1079
+
1080
+ this = pgresult_get_this_safe(self);
1081
+ num_rows = PQntuples(this->pgresult);
1082
+ num_fields = PQnfields(this->pgresult);
742
1083
 
743
1084
  for ( row = 0; row < num_rows; row++ ) {
744
- VALUE new_row = rb_ary_new2(num_fields);
1085
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
745
1086
 
746
1087
  /* populate the row */
747
1088
  for ( field = 0; field < num_fields; field++ ) {
748
- rb_ary_store( new_row, field, pgresult_value(self, result, row, field) );
1089
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
749
1090
  }
750
- rb_yield( new_row );
1091
+ rb_yield( rb_ary_new4( num_fields, row_values ));
751
1092
  }
752
1093
 
753
1094
  return Qnil;
754
1095
  }
755
1096
 
1097
+ /*
1098
+ * call-seq:
1099
+ * res.values -> Array
1100
+ *
1101
+ * Returns all tuples as an array of arrays.
1102
+ */
1103
+ static VALUE
1104
+ pgresult_values(VALUE self)
1105
+ {
1106
+ t_pg_result *this = pgresult_get_this_safe(self);
1107
+ int row;
1108
+ int field;
1109
+ int num_rows = PQntuples(this->pgresult);
1110
+ int num_fields = PQnfields(this->pgresult);
1111
+ VALUE results = rb_ary_new2( num_rows );
1112
+
1113
+ for ( row = 0; row < num_rows; row++ ) {
1114
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
1115
+
1116
+ /* populate the row */
1117
+ for ( field = 0; field < num_fields; field++ ) {
1118
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1119
+ }
1120
+ rb_ary_store( results, row, rb_ary_new4( num_fields, row_values ) );
1121
+ }
1122
+
1123
+ return results;
1124
+ }
756
1125
 
757
1126
  /*
758
1127
  * Make a Ruby array out of the encoded values from the specified
@@ -761,28 +1130,16 @@ pgresult_each_row(VALUE self)
761
1130
  static VALUE
762
1131
  make_column_result_array( VALUE self, int col )
763
1132
  {
764
- PGresult *result = pgresult_get( self );
765
- int rows = PQntuples( result );
1133
+ t_pg_result *this = pgresult_get_this_safe(self);
1134
+ int rows = PQntuples( this->pgresult );
766
1135
  int i;
767
- VALUE val = Qnil;
768
1136
  VALUE results = rb_ary_new2( rows );
769
1137
 
770
- if ( col >= PQnfields(result) )
1138
+ if ( col >= PQnfields(this->pgresult) )
771
1139
  rb_raise( rb_eIndexError, "no column %d in result", col );
772
1140
 
773
1141
  for ( i=0; i < rows; i++ ) {
774
- val = rb_tainted_str_new( PQgetvalue(result, i, col),
775
- PQgetlength(result, i, col) );
776
-
777
- #ifdef M17N_SUPPORTED
778
- /* associate client encoding for text format only */
779
- if ( 0 == PQfformat(result, col) ) {
780
- ASSOCIATE_INDEX( val, self );
781
- } else {
782
- rb_enc_associate( val, rb_ascii8bit_encoding() );
783
- }
784
- #endif
785
-
1142
+ VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, col);
786
1143
  rb_ary_store( results, i, val );
787
1144
  }
788
1145
 
@@ -817,8 +1174,12 @@ static VALUE
817
1174
  pgresult_field_values( VALUE self, VALUE field )
818
1175
  {
819
1176
  PGresult *result = pgresult_get( self );
820
- const char *fieldname = StringValuePtr( field );
821
- 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 );
822
1183
 
823
1184
  if ( fnum < 0 )
824
1185
  rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
@@ -827,6 +1188,85 @@ pgresult_field_values( VALUE self, VALUE field )
827
1188
  }
828
1189
 
829
1190
 
1191
+ /*
1192
+ * call-seq:
1193
+ * res.tuple_values( n ) -> array
1194
+ *
1195
+ * Returns an Array of the field values from the nth row of the result.
1196
+ *
1197
+ */
1198
+ static VALUE
1199
+ pgresult_tuple_values(VALUE self, VALUE index)
1200
+ {
1201
+ int tuple_num = NUM2INT( index );
1202
+ t_pg_result *this;
1203
+ int field;
1204
+ int num_tuples;
1205
+ int num_fields;
1206
+
1207
+ this = pgresult_get_this_safe(self);
1208
+ num_tuples = PQntuples(this->pgresult);
1209
+ num_fields = PQnfields(this->pgresult);
1210
+
1211
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1212
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1213
+
1214
+ {
1215
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
1216
+
1217
+ /* populate the row */
1218
+ for ( field = 0; field < num_fields; field++ ) {
1219
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
1220
+ }
1221
+ return rb_ary_new4( num_fields, row_values );
1222
+ }
1223
+ }
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
+
1244
+ /*
1245
+ * call-seq:
1246
+ * res.tuple( n ) -> PG::Tuple
1247
+ *
1248
+ * Returns a PG::Tuple from the nth row of the result.
1249
+ *
1250
+ */
1251
+ static VALUE
1252
+ pgresult_tuple(VALUE self, VALUE index)
1253
+ {
1254
+ int tuple_num = NUM2INT( index );
1255
+ t_pg_result *this;
1256
+ int num_tuples;
1257
+
1258
+ this = pgresult_get_this_safe(self);
1259
+ num_tuples = PQntuples(this->pgresult);
1260
+
1261
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1262
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1263
+
1264
+ ensure_init_for_tuple(self);
1265
+
1266
+ return pg_tuple_new(self, tuple_num);
1267
+ }
1268
+
1269
+
830
1270
  /*
831
1271
  * call-seq:
832
1272
  * res.each{ |tuple| ... }
@@ -836,9 +1276,13 @@ pgresult_field_values( VALUE self, VALUE field )
836
1276
  static VALUE
837
1277
  pgresult_each(VALUE self)
838
1278
  {
839
- PGresult *result = pgresult_get(self);
1279
+ PGresult *result;
840
1280
  int tuple_num;
841
1281
 
1282
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
1283
+
1284
+ result = pgresult_get(self);
1285
+
842
1286
  for(tuple_num = 0; tuple_num < PQntuples(result); tuple_num++) {
843
1287
  rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
844
1288
  }
@@ -849,30 +1293,301 @@ pgresult_each(VALUE self)
849
1293
  * call-seq:
850
1294
  * res.fields() -> Array
851
1295
  *
852
- * 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.
853
1297
  */
854
1298
  static VALUE
855
1299
  pgresult_fields(VALUE self)
856
1300
  {
857
- PGresult *result = pgresult_get( self );
858
- int n = PQnfields( result );
859
- VALUE fields = rb_ary_new2( n );
860
- int i;
1301
+ t_pg_result *this = pgresult_get_this_safe(self);
1302
+
1303
+ if( this->nfields == -1 )
1304
+ pgresult_init_fnames( self );
1305
+
1306
+ return rb_ary_new4( this->nfields, this->fnames );
1307
+ }
1308
+
1309
+ /*
1310
+ * call-seq:
1311
+ * res.type_map = typemap
1312
+ *
1313
+ * Set the TypeMap that is used for type casts of result values to ruby objects.
1314
+ *
1315
+ * All value retrieval methods will respect the type map and will do the
1316
+ * type casts from PostgreSQL's wire format to Ruby objects on the fly,
1317
+ * according to the rules and decoders defined in the given typemap.
1318
+ *
1319
+ * +typemap+ must be a kind of PG::TypeMap .
1320
+ *
1321
+ */
1322
+ static VALUE
1323
+ pgresult_type_map_set(VALUE self, VALUE typemap)
1324
+ {
1325
+ t_pg_result *this = pgresult_get_this(self);
1326
+ t_typemap *p_typemap;
1327
+
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);
1333
+
1334
+ this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1335
+ this->p_typemap = DATA_PTR( this->typemap );
1336
+
1337
+ return typemap;
1338
+ }
1339
+
1340
+ /*
1341
+ * call-seq:
1342
+ * res.type_map -> value
1343
+ *
1344
+ * Returns the TypeMap that is currently set for type casts of result values to ruby objects.
1345
+ *
1346
+ */
1347
+ static VALUE
1348
+ pgresult_type_map_get(VALUE self)
1349
+ {
1350
+ t_pg_result *this = pgresult_get_this(self);
1351
+
1352
+ return this->typemap;
1353
+ }
1354
+
1355
+
1356
+ static void
1357
+ yield_hash(VALUE self, int ntuples, int nfields)
1358
+ {
1359
+ int tuple_num;
1360
+ t_pg_result *this = pgresult_get_this(self);
1361
+ UNUSED(nfields);
1362
+
1363
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1364
+ rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1365
+ }
1366
+
1367
+ pgresult_clear( this );
1368
+ }
1369
+
1370
+ static void
1371
+ yield_array(VALUE self, int ntuples, int nfields)
1372
+ {
1373
+ int row;
1374
+ t_pg_result *this = pgresult_get_this(self);
1375
+
1376
+ for ( row = 0; row < ntuples; row++ ) {
1377
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1378
+ int field;
1379
+
1380
+ /* populate the row */
1381
+ for ( field = 0; field < nfields; field++ ) {
1382
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1383
+ }
1384
+ rb_yield( rb_ary_new4( nfields, row_values ));
1385
+ }
1386
+
1387
+ pgresult_clear( this );
1388
+ }
1389
+
1390
+ static void
1391
+ yield_tuple(VALUE self, int ntuples, int nfields)
1392
+ {
1393
+ int tuple_num;
1394
+ t_pg_result *this = pgresult_get_this(self);
1395
+ VALUE copy;
1396
+ UNUSED(nfields);
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
+
1404
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1405
+ VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1406
+ rb_yield( tuple );
1407
+ }
1408
+ }
1409
+
1410
+ static VALUE
1411
+ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1412
+ {
1413
+ t_pg_result *this;
1414
+ int nfields;
1415
+ PGconn *pgconn;
1416
+ PGresult *pgresult;
1417
+
1418
+ RETURN_ENUMERATOR(self, 0, NULL);
1419
+
1420
+ this = pgresult_get_this_safe(self);
1421
+ pgconn = pg_get_pgconn(this->connection);
1422
+ pgresult = this->pgresult;
1423
+ nfields = PQnfields(pgresult);
1424
+
1425
+ for(;;){
1426
+ int ntuples = PQntuples(pgresult);
1427
+
1428
+ switch( PQresultStatus(pgresult) ){
1429
+ case PGRES_TUPLES_OK:
1430
+ if( ntuples == 0 )
1431
+ return self;
1432
+ rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1433
+ case PGRES_SINGLE_TUPLE:
1434
+ break;
1435
+ default:
1436
+ pg_result_check( self );
1437
+ }
1438
+
1439
+ yielder( self, ntuples, nfields );
861
1440
 
862
- for ( i = 0; i < n; i++ ) {
863
- VALUE val = rb_tainted_str_new2(PQfname(result, i));
864
- ASSOCIATE_INDEX(val, self);
865
- rb_ary_store( fields, i, val );
1441
+ pgresult = gvl_PQgetResult(pgconn);
1442
+ 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");
1447
+
1448
+ this->pgresult = pgresult;
866
1449
  }
867
1450
 
868
- return fields;
1451
+ /* never reached */
1452
+ return self;
1453
+ }
1454
+
1455
+
1456
+ /*
1457
+ * call-seq:
1458
+ * res.stream_each{ |tuple| ... }
1459
+ *
1460
+ * Invokes block for each tuple in the result set in single row mode.
1461
+ *
1462
+ * This is a convenience method for retrieving all result tuples
1463
+ * as they are transferred. It is an alternative to repeated calls of
1464
+ * PG::Connection#get_result , but given that it avoids the overhead of
1465
+ * wrapping each row into a dedicated result object, it delivers data in nearly
1466
+ * the same speed as with ordinary results.
1467
+ *
1468
+ * The base result must be in status PGRES_SINGLE_TUPLE.
1469
+ * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1470
+ * A PG::Error is raised for any errors from the server.
1471
+ *
1472
+ * Row description data does not change while the iteration. All value retrieval
1473
+ * methods refer to only the current row. Result#ntuples returns +1+ while
1474
+ * the iteration and +0+ after all tuples were yielded.
1475
+ *
1476
+ * Example:
1477
+ * conn.send_query( "first SQL query; second SQL query" )
1478
+ * conn.set_single_row_mode
1479
+ * conn.get_result.stream_each do |row|
1480
+ * # do something with each received row of the first query
1481
+ * end
1482
+ * conn.get_result.stream_each do |row|
1483
+ * # do something with each received row of the second query
1484
+ * end
1485
+ * conn.get_result # => nil (no more results)
1486
+ */
1487
+ static VALUE
1488
+ pgresult_stream_each(VALUE self)
1489
+ {
1490
+ return pgresult_stream_any(self, yield_hash);
1491
+ }
1492
+
1493
+ /*
1494
+ * call-seq:
1495
+ * res.stream_each_row { |row| ... }
1496
+ *
1497
+ * Yields each row of the result set in single row mode.
1498
+ * The row is a list of column values.
1499
+ *
1500
+ * This method works equally to #stream_each , but yields an Array of
1501
+ * values.
1502
+ */
1503
+ static VALUE
1504
+ pgresult_stream_each_row(VALUE self)
1505
+ {
1506
+ return pgresult_stream_any(self, yield_array);
1507
+ }
1508
+
1509
+ /*
1510
+ * call-seq:
1511
+ * res.stream_each_tuple { |tuple| ... }
1512
+ *
1513
+ * Yields each row of the result set in single row mode.
1514
+ *
1515
+ * This method works equally to #stream_each , but yields a PG::Tuple object.
1516
+ */
1517
+ static VALUE
1518
+ pgresult_stream_each_tuple(VALUE self)
1519
+ {
1520
+ /* allocate VALUEs that are shared between all streamed tuples */
1521
+ ensure_init_for_tuple(self);
1522
+
1523
+ return pgresult_stream_any(self, yield_tuple);
1524
+ }
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;
869
1560
  }
870
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
+ }
871
1582
 
872
1583
  void
873
1584
  init_pg_result()
874
1585
  {
875
- rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
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 );
876
1591
  rb_include_module(rb_cPGresult, rb_mEnumerable);
877
1592
  rb_include_module(rb_cPGresult, rb_mPGconstants);
878
1593
 
@@ -881,6 +1596,10 @@ init_pg_result()
881
1596
  rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
882
1597
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
883
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
884
1603
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
885
1604
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
886
1605
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
@@ -913,8 +1632,22 @@ init_pg_result()
913
1632
  rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
914
1633
  rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
915
1634
  rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
1635
+ rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
916
1636
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
917
1637
  rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
918
- }
1638
+ rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
1639
+ rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
1640
+ rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
1641
+ rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
919
1642
 
1643
+ rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1644
+ rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
920
1645
 
1646
+ /****** PG::Result INSTANCE METHODS: streaming ******/
1647
+ rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1648
+ rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1649
+ rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
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
+ }