pg 0.18.2 → 1.4.5

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 (119) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +86 -0
  6. data/.github/workflows/source-gem.yml +131 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/BSDL +2 -2
  15. data/Gemfile +14 -0
  16. data/History.rdoc +480 -4
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.rdoc +1 -2
  20. data/README.rdoc +92 -20
  21. data/Rakefile +33 -133
  22. data/Rakefile.cross +89 -67
  23. data/certs/ged.pem +24 -0
  24. data/certs/larskanis-2022.pem +26 -0
  25. data/ext/errorcodes.def +113 -0
  26. data/ext/errorcodes.rb +1 -1
  27. data/ext/errorcodes.txt +36 -2
  28. data/ext/extconf.rb +120 -54
  29. data/ext/gvl_wrappers.c +8 -0
  30. data/ext/gvl_wrappers.h +44 -33
  31. data/ext/pg.c +216 -172
  32. data/ext/pg.h +93 -98
  33. data/ext/pg_binary_decoder.c +85 -16
  34. data/ext/pg_binary_encoder.c +25 -22
  35. data/ext/pg_coder.c +176 -40
  36. data/ext/pg_connection.c +1735 -1138
  37. data/ext/pg_copy_coder.c +95 -28
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +521 -0
  40. data/ext/pg_result.c +642 -221
  41. data/ext/pg_text_decoder.c +609 -41
  42. data/ext/pg_text_encoder.c +254 -100
  43. data/ext/pg_tuple.c +569 -0
  44. data/ext/pg_type_map.c +62 -22
  45. data/ext/pg_type_map_all_strings.c +20 -6
  46. data/ext/pg_type_map_by_class.c +55 -25
  47. data/ext/pg_type_map_by_column.c +81 -42
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +56 -26
  50. data/ext/pg_type_map_in_ruby.c +52 -21
  51. data/ext/{util.c → pg_util.c} +12 -12
  52. data/ext/{util.h → pg_util.h} +2 -2
  53. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  54. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  55. data/lib/pg/basic_type_map_for_results.rb +81 -0
  56. data/lib/pg/basic_type_registry.rb +301 -0
  57. data/lib/pg/binary_decoder.rb +23 -0
  58. data/lib/pg/coder.rb +24 -3
  59. data/lib/pg/connection.rb +711 -64
  60. data/lib/pg/constants.rb +2 -1
  61. data/lib/pg/exceptions.rb +9 -2
  62. data/lib/pg/result.rb +24 -7
  63. data/lib/pg/text_decoder.rb +27 -23
  64. data/lib/pg/text_encoder.rb +40 -8
  65. data/lib/pg/tuple.rb +30 -0
  66. data/lib/pg/type_map_by_column.rb +3 -2
  67. data/lib/pg/version.rb +4 -0
  68. data/lib/pg.rb +61 -36
  69. data/misc/openssl-pg-segfault.rb +31 -0
  70. data/misc/postgres/History.txt +9 -0
  71. data/misc/postgres/Manifest.txt +5 -0
  72. data/misc/postgres/README.txt +21 -0
  73. data/misc/postgres/Rakefile +21 -0
  74. data/misc/postgres/lib/postgres.rb +16 -0
  75. data/misc/ruby-pg/History.txt +9 -0
  76. data/misc/ruby-pg/Manifest.txt +5 -0
  77. data/misc/ruby-pg/README.txt +21 -0
  78. data/misc/ruby-pg/Rakefile +21 -0
  79. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  80. data/pg.gemspec +32 -0
  81. data/rakelib/task_extension.rb +46 -0
  82. data/sample/array_insert.rb +1 -1
  83. data/sample/async_api.rb +4 -8
  84. data/sample/async_copyto.rb +1 -1
  85. data/sample/async_mixed.rb +1 -1
  86. data/sample/check_conn.rb +1 -1
  87. data/sample/copydata.rb +71 -0
  88. data/sample/copyfrom.rb +1 -1
  89. data/sample/copyto.rb +1 -1
  90. data/sample/cursor.rb +1 -1
  91. data/sample/disk_usage_report.rb +6 -15
  92. data/sample/issue-119.rb +2 -2
  93. data/sample/losample.rb +1 -1
  94. data/sample/minimal-testcase.rb +2 -2
  95. data/sample/notify_wait.rb +1 -1
  96. data/sample/pg_statistics.rb +6 -15
  97. data/sample/replication_monitor.rb +9 -18
  98. data/sample/test_binary_values.rb +1 -1
  99. data/sample/wal_shipper.rb +2 -2
  100. data/sample/warehouse_partitions.rb +8 -17
  101. data.tar.gz.sig +0 -0
  102. metadata +74 -216
  103. metadata.gz.sig +0 -0
  104. data/ChangeLog +0 -5545
  105. data/lib/pg/basic_type_mapping.rb +0 -399
  106. data/spec/data/expected_trace.out +0 -26
  107. data/spec/data/random_binary_data +0 -0
  108. data/spec/helpers.rb +0 -355
  109. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  110. data/spec/pg/connection_spec.rb +0 -1535
  111. data/spec/pg/result_spec.rb +0 -449
  112. data/spec/pg/type_map_by_class_spec.rb +0 -138
  113. data/spec/pg/type_map_by_column_spec.rb +0 -222
  114. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  115. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  116. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  117. data/spec/pg/type_map_spec.rb +0 -22
  118. data/spec/pg/type_spec.rb +0 -688
  119. data/spec/pg_spec.rb +0 -50
data/ext/pg_result.c CHANGED
@@ -1,21 +1,196 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v 1269b8ad77b8 2015/02/06 16:38:23 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
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
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;
95
+
96
+ /* count tuple pointers */
97
+ size += sizeof(void*) * ((num_tuples + 128 - 1) / 128 * 128);
98
+ }
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( void *_this )
111
+ {
112
+ t_pg_result *this = (t_pg_result *)_this;
113
+ int i;
114
+
115
+ rb_gc_mark_movable( this->connection );
116
+ rb_gc_mark_movable( this->typemap );
117
+ rb_gc_mark_movable( this->tuple_hash );
118
+ rb_gc_mark_movable( this->field_map );
18
119
 
120
+ for( i=0; i < this->nfields; i++ ){
121
+ rb_gc_mark_movable( this->fnames[i] );
122
+ }
123
+ }
124
+
125
+ static void
126
+ pgresult_gc_compact( void *_this )
127
+ {
128
+ t_pg_result *this = (t_pg_result *)_this;
129
+ int i;
130
+
131
+ pg_gc_location( this->connection );
132
+ pg_gc_location( this->typemap );
133
+ pg_gc_location( this->tuple_hash );
134
+ pg_gc_location( this->field_map );
135
+
136
+ for( i=0; i < this->nfields; i++ ){
137
+ pg_gc_location( this->fnames[i] );
138
+ }
139
+ }
140
+
141
+ /*
142
+ * GC Free function
143
+ */
144
+ static void
145
+ pgresult_clear( void *_this )
146
+ {
147
+ t_pg_result *this = (t_pg_result *)_this;
148
+ if( this->pgresult && !this->autoclear ){
149
+ PQclear(this->pgresult);
150
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
151
+ rb_gc_adjust_memory_usage(-this->result_size);
152
+ #endif
153
+ }
154
+ this->result_size = 0;
155
+ this->nfields = -1;
156
+ this->pgresult = NULL;
157
+ }
158
+
159
+ static void
160
+ pgresult_gc_free( void *_this )
161
+ {
162
+ t_pg_result *this = (t_pg_result *)_this;
163
+ pgresult_clear( this );
164
+ xfree(this);
165
+ }
166
+
167
+ static size_t
168
+ pgresult_memsize( const void *_this )
169
+ {
170
+ const t_pg_result *this = (const t_pg_result *)_this;
171
+ /* Ideally the memory 'this' is pointing to should be taken into account as well.
172
+ * However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
173
+ */
174
+ return this->result_size;
175
+ }
176
+
177
+ static const rb_data_type_t pgresult_type = {
178
+ "PG::Result",
179
+ {
180
+ pgresult_gc_mark,
181
+ pgresult_gc_free,
182
+ pgresult_memsize,
183
+ pg_compact_callback(pgresult_gc_compact),
184
+ },
185
+ 0, 0,
186
+ RUBY_TYPED_FREE_IMMEDIATELY,
187
+ };
188
+
189
+ /* Needed by sequel_pg gem, do not delete */
190
+ int pg_get_result_enc_idx(VALUE self)
191
+ {
192
+ return pgresult_get_this(self)->enc_idx;
193
+ }
19
194
 
20
195
  /*
21
196
  * Global functions
@@ -24,45 +199,88 @@ static t_pg_result *pgresult_get_this_safe( VALUE );
24
199
  /*
25
200
  * Result constructor
26
201
  */
27
- VALUE
28
- pg_new_result(PGresult *result, VALUE rb_pgconn)
202
+ static VALUE
203
+ pg_new_result2(PGresult *result, VALUE rb_pgconn)
29
204
  {
30
205
  int nfields = result ? PQnfields(result) : 0;
31
- VALUE self = pgresult_s_allocate( rb_cPGresult );
206
+ VALUE self;
32
207
  t_pg_result *this;
33
208
 
34
209
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
35
- DATA_PTR(self) = this;
36
-
37
210
  this->pgresult = result;
38
211
  this->connection = rb_pgconn;
39
212
  this->typemap = pg_typemap_all_strings;
40
- this->p_typemap = DATA_PTR( this->typemap );
41
- this->autoclear = 0;
213
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
42
214
  this->nfields = -1;
43
215
  this->tuple_hash = Qnil;
44
-
45
- PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
216
+ this->field_map = Qnil;
217
+ this->flags = 0;
218
+ self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
46
219
 
47
220
  if( result ){
48
221
  t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
49
222
  VALUE typemap = p_conn->type_map_for_results;
50
-
51
223
  /* Type check is done when assigned to PG::Connection. */
52
- t_typemap *p_typemap = DATA_PTR(typemap);
224
+ t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
53
225
 
226
+ this->enc_idx = p_conn->enc_idx;
54
227
  this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
55
- this->p_typemap = DATA_PTR( this->typemap );
228
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
229
+ this->flags = p_conn->flags;
230
+ } else {
231
+ this->enc_idx = rb_locale_encindex();
56
232
  }
57
233
 
58
234
  return self;
59
235
  }
60
236
 
237
+ VALUE
238
+ pg_new_result(PGresult *result, VALUE rb_pgconn)
239
+ {
240
+ VALUE self = pg_new_result2(result, rb_pgconn);
241
+ t_pg_result *this = pgresult_get_this(self);
242
+
243
+ this->autoclear = 0;
244
+
245
+ /* Estimate size of underlying pgresult memory storage and account to ruby GC.
246
+ * There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
247
+ */
248
+ /* TODO: If someday most systems provide PQresultMemorySize(), it's questionable to store result_size in t_pg_result in addition to the value already stored in PGresult.
249
+ * For now the memory savings don't justify the ifdefs necessary to support both cases.
250
+ */
251
+ this->result_size = pgresult_approx_size(result);
252
+
253
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
254
+ rb_gc_adjust_memory_usage(this->result_size);
255
+ #endif
256
+
257
+ return self;
258
+ }
259
+
260
+ static VALUE
261
+ pg_copy_result(t_pg_result *this)
262
+ {
263
+ int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
264
+ size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
265
+ t_pg_result *copy;
266
+
267
+ copy = (t_pg_result *)xmalloc(len);
268
+ memcpy(copy, this, len);
269
+ this->result_size = 0;
270
+
271
+ return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
272
+ }
273
+
61
274
  VALUE
62
275
  pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
63
276
  {
64
- VALUE self = pg_new_result(result, rb_pgconn);
277
+ VALUE self = pg_new_result2(result, rb_pgconn);
65
278
  t_pg_result *this = pgresult_get_this(self);
279
+
280
+ /* Autocleared results are freed implicit instead of by PQclear().
281
+ * So it's not very useful to be accounted by ruby GC.
282
+ */
283
+ this->result_size = 0;
66
284
  this->autoclear = 1;
67
285
  return self;
68
286
  }
@@ -71,7 +289,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
71
289
  * call-seq:
72
290
  * res.check -> nil
73
291
  *
74
- * Raises appropriate exception if PG::Result is in a bad state.
292
+ * Raises appropriate exception if PG::Result is in a bad state, which is:
293
+ * * +PGRES_BAD_RESPONSE+
294
+ * * +PGRES_FATAL_ERROR+
295
+ * * +PGRES_NONFATAL_ERROR+
296
+ * * +PGRES_PIPELINE_ABORTED+
75
297
  */
76
298
  VALUE
77
299
  pg_result_check( VALUE self )
@@ -92,18 +314,20 @@ pg_result_check( VALUE self )
92
314
  case PGRES_TUPLES_OK:
93
315
  case PGRES_COPY_OUT:
94
316
  case PGRES_COPY_IN:
95
- #ifdef HAVE_CONST_PGRES_COPY_BOTH
96
317
  case PGRES_COPY_BOTH:
97
- #endif
98
- #ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
99
318
  case PGRES_SINGLE_TUPLE:
100
- #endif
101
319
  case PGRES_EMPTY_QUERY:
102
320
  case PGRES_COMMAND_OK:
321
+ #ifdef HAVE_PQENTERPIPELINEMODE
322
+ case PGRES_PIPELINE_SYNC:
323
+ #endif
103
324
  return self;
104
325
  case PGRES_BAD_RESPONSE:
105
326
  case PGRES_FATAL_ERROR:
106
327
  case PGRES_NONFATAL_ERROR:
328
+ #ifdef HAVE_PQENTERPIPELINEMODE
329
+ case PGRES_PIPELINE_ABORTED:
330
+ #endif
107
331
  error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
108
332
  break;
109
333
  default:
@@ -111,7 +335,7 @@ pg_result_check( VALUE self )
111
335
  }
112
336
  }
113
337
 
114
- PG_ENCODING_SET_NOCHECK( error, ENCODING_GET(self) );
338
+ PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
115
339
 
116
340
  sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
117
341
  klass = lookup_error_class( sqlstate );
@@ -135,19 +359,22 @@ pg_result_check( VALUE self )
135
359
  * call-seq:
136
360
  * res.clear() -> nil
137
361
  *
138
- * Clears the PG::Result object as the result of the query.
362
+ * Clears the PG::Result object as the result of a query.
363
+ * This frees all underlying memory consumed by the result object.
364
+ * Afterwards access to result methods raises PG::Error "result has been cleared".
365
+ *
366
+ * Explicit calling #clear can lead to better memory performance, but is not generally necessary.
367
+ * Special care must be taken when PG::Tuple objects are used.
368
+ * In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
139
369
  *
140
- * If PG::Result#autoclear? is true then the result is marked as cleared
141
- * and the underlying C struct will be cleared automatically by libpq.
370
+ * If PG::Result#autoclear? is +true+ then the result is only marked as cleared but clearing the underlying C struct will happen when the callback returns.
142
371
  *
143
372
  */
144
373
  VALUE
145
374
  pg_result_clear(VALUE self)
146
375
  {
147
376
  t_pg_result *this = pgresult_get_this(self);
148
- if( !this->autoclear )
149
- PQclear(pgresult_get(self));
150
- this->pgresult = NULL;
377
+ pgresult_clear( this );
151
378
  return Qnil;
152
379
  }
153
380
 
@@ -155,7 +382,7 @@ pg_result_clear(VALUE self)
155
382
  * call-seq:
156
383
  * res.cleared? -> boolean
157
384
  *
158
- * Returns +true+ if the backend result memory has been free'd.
385
+ * Returns +true+ if the backend result memory has been freed.
159
386
  */
160
387
  VALUE
161
388
  pgresult_cleared_p( VALUE self )
@@ -168,8 +395,10 @@ pgresult_cleared_p( VALUE self )
168
395
  * call-seq:
169
396
  * res.autoclear? -> boolean
170
397
  *
171
- * Returns +true+ if the underlying C struct will be cleared automatically by libpq.
172
- * Elsewise the result is cleared by PG::Result#clear or by the GC when it's no longer in use.
398
+ * Returns +true+ if the underlying C struct will be cleared at the end of a callback.
399
+ * This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
400
+ *
401
+ * All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
173
402
  *
174
403
  */
175
404
  VALUE
@@ -183,37 +412,6 @@ pgresult_autoclear_p( VALUE self )
183
412
  * DATA pointer functions
184
413
  */
185
414
 
186
- /*
187
- * GC Mark function
188
- */
189
- static void
190
- pgresult_gc_mark( t_pg_result *this )
191
- {
192
- int i;
193
-
194
- if( !this ) return;
195
- rb_gc_mark( this->connection );
196
- rb_gc_mark( this->typemap );
197
- rb_gc_mark( this->tuple_hash );
198
-
199
- for( i=0; i < this->nfields; i++ ){
200
- rb_gc_mark( this->fnames[i] );
201
- }
202
- }
203
-
204
- /*
205
- * GC Free function
206
- */
207
- static void
208
- pgresult_gc_free( t_pg_result *this )
209
- {
210
- if( !this ) return;
211
- if(this->pgresult != NULL && !this->autoclear)
212
- PQclear(this->pgresult);
213
-
214
- xfree(this);
215
- }
216
-
217
415
  /*
218
416
  * Fetch the PG::Result object data pointer and check it's
219
417
  * PGresult data pointer for sanity.
@@ -243,18 +441,30 @@ pgresult_get(VALUE self)
243
441
  return this->pgresult;
244
442
  }
245
443
 
246
- /*
247
- * Document-method: allocate
248
- *
249
- * call-seq:
250
- * PG::Result.allocate -> result
251
- */
252
- static VALUE
253
- pgresult_s_allocate( VALUE klass )
444
+ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
254
445
  {
255
- VALUE self = Data_Wrap_Struct( klass, pgresult_gc_mark, pgresult_gc_free, NULL );
256
-
257
- return self;
446
+ VALUE fname;
447
+ #ifdef TRUFFLERUBY
448
+ if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
449
+ #else
450
+ if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
451
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
452
+ fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
453
+ if( fname == Qnil ){
454
+ fname = rb_str_new2(cstr);
455
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
456
+ fname = rb_str_intern(fname);
457
+ }
458
+ } else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
459
+ #endif
460
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
461
+ fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
462
+ } else {
463
+ fname = rb_str_new2(cstr);
464
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
465
+ fname = rb_obj_freeze(fname);
466
+ }
467
+ return fname;
258
468
  }
259
469
 
260
470
  static void pgresult_init_fnames(VALUE self)
@@ -266,12 +476,9 @@ static void pgresult_init_fnames(VALUE self)
266
476
  int nfields = PQnfields(this->pgresult);
267
477
 
268
478
  for( i=0; i<nfields; i++ ){
269
- VALUE fname = rb_tainted_str_new2(PQfname(this->pgresult, i));
270
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
271
- this->fnames[i] = rb_obj_freeze(fname);
479
+ char *cfname = PQfname(this->pgresult, i);
480
+ this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
272
481
  this->nfields = i + 1;
273
-
274
- RB_GC_GUARD(fname);
275
482
  }
276
483
  this->nfields = nfields;
277
484
  }
@@ -283,12 +490,15 @@ static void pgresult_init_fnames(VALUE self)
283
490
  *
284
491
  * The class to represent the query result tuples (rows).
285
492
  * An instance of this class is created as the result of every query.
286
- * You may need to invoke the #clear method of the instance when finished with
287
- * the result for better memory performance.
493
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
494
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
495
+ *
496
+ * Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
497
+ * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
288
498
  *
289
499
  * Example:
290
500
  * require 'pg'
291
- * conn = PGconn.open(:dbname => 'test')
501
+ * conn = PG.connect(:dbname => 'test')
292
502
  * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
293
503
  * res.getvalue(0,0) # '1'
294
504
  * res[0]['b'] # '2'
@@ -302,7 +512,7 @@ static void pgresult_init_fnames(VALUE self)
302
512
 
303
513
  /*
304
514
  * call-seq:
305
- * res.result_status() -> Fixnum
515
+ * res.result_status() -> Integer
306
516
  *
307
517
  * Returns the status of the query. The status value is one of:
308
518
  * * +PGRES_EMPTY_QUERY+
@@ -314,6 +524,9 @@ static void pgresult_init_fnames(VALUE self)
314
524
  * * +PGRES_NONFATAL_ERROR+
315
525
  * * +PGRES_FATAL_ERROR+
316
526
  * * +PGRES_COPY_BOTH+
527
+ * * +PGRES_SINGLE_TUPLE+
528
+ * * +PGRES_PIPELINE_SYNC+
529
+ * * +PGRES_PIPELINE_ABORTED+
317
530
  */
318
531
  static VALUE
319
532
  pgresult_result_status(VALUE self)
@@ -325,14 +538,15 @@ pgresult_result_status(VALUE self)
325
538
  * call-seq:
326
539
  * res.res_status( status ) -> String
327
540
  *
328
- * Returns the string representation of status +status+.
541
+ * Returns the string representation of +status+.
329
542
  *
330
543
  */
331
544
  static VALUE
332
545
  pgresult_res_status(VALUE self, VALUE status)
333
546
  {
334
- VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
335
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
547
+ t_pg_result *this = pgresult_get_this_safe(self);
548
+ VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
549
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
336
550
  return ret;
337
551
  }
338
552
 
@@ -345,10 +559,39 @@ pgresult_res_status(VALUE self, VALUE status)
345
559
  static VALUE
346
560
  pgresult_error_message(VALUE self)
347
561
  {
348
- VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
349
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
562
+ t_pg_result *this = pgresult_get_this_safe(self);
563
+ VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
564
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
565
+ return ret;
566
+ }
567
+
568
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
569
+ /*
570
+ * call-seq:
571
+ * res.verbose_error_message( verbosity, show_context ) -> String
572
+ *
573
+ * Returns a reformatted version of the error message associated with a PGresult object.
574
+ *
575
+ * Available since PostgreSQL-9.6
576
+ */
577
+ static VALUE
578
+ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
579
+ {
580
+ t_pg_result *this = pgresult_get_this_safe(self);
581
+ VALUE ret;
582
+ char *c_str;
583
+
584
+ c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
585
+ if(!c_str)
586
+ rb_raise(rb_eNoMemError, "insufficient memory to format error message");
587
+
588
+ ret = rb_str_new2(c_str);
589
+ PQfreemem(c_str);
590
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
591
+
350
592
  return ret;
351
593
  }
594
+ #endif
352
595
 
353
596
  /*
354
597
  * call-seq:
@@ -399,14 +642,14 @@ pgresult_error_message(VALUE self)
399
642
  static VALUE
400
643
  pgresult_error_field(VALUE self, VALUE field)
401
644
  {
402
- PGresult *result = pgresult_get( self );
645
+ t_pg_result *this = pgresult_get_this_safe(self);
403
646
  int fieldcode = NUM2INT( field );
404
- char * fieldstr = PQresultErrorField( result, fieldcode );
647
+ char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
405
648
  VALUE ret = Qnil;
406
649
 
407
650
  if ( fieldstr ) {
408
- ret = rb_tainted_str_new2( fieldstr );
409
- PG_ENCODING_SET_NOCHECK( ret, ENCODING_GET(self ));
651
+ ret = rb_str_new2( fieldstr );
652
+ PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
410
653
  }
411
654
 
412
655
  return ret;
@@ -414,7 +657,7 @@ pgresult_error_field(VALUE self, VALUE field)
414
657
 
415
658
  /*
416
659
  * call-seq:
417
- * res.ntuples() -> Fixnum
660
+ * res.ntuples() -> Integer
418
661
  *
419
662
  * Returns the number of tuples in the query result.
420
663
  */
@@ -427,7 +670,7 @@ pgresult_ntuples(VALUE self)
427
670
  static VALUE
428
671
  pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
429
672
  {
430
- return pgresult_ntuples(self);
673
+ return pgresult_ntuples(self);
431
674
  }
432
675
 
433
676
  /*
@@ -444,29 +687,30 @@ pgresult_nfields(VALUE self)
444
687
 
445
688
  /*
446
689
  * call-seq:
447
- * res.fname( index ) -> String
690
+ * res.fname( index ) -> String or Symbol
448
691
  *
449
692
  * Returns the name of the column corresponding to _index_.
693
+ * Depending on #field_name_type= it's a String or Symbol.
694
+ *
450
695
  */
451
696
  static VALUE
452
697
  pgresult_fname(VALUE self, VALUE index)
453
698
  {
454
- VALUE fname;
455
- PGresult *result = pgresult_get(self);
699
+ t_pg_result *this = pgresult_get_this_safe(self);
456
700
  int i = NUM2INT(index);
701
+ char *cfname;
457
702
 
458
- if (i < 0 || i >= PQnfields(result)) {
703
+ if (i < 0 || i >= PQnfields(this->pgresult)) {
459
704
  rb_raise(rb_eArgError,"invalid field number %d", i);
460
705
  }
461
706
 
462
- fname = rb_tainted_str_new2(PQfname(result, i));
463
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
464
- return rb_obj_freeze(fname);
707
+ cfname = PQfname(this->pgresult, i);
708
+ return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
465
709
  }
466
710
 
467
711
  /*
468
712
  * call-seq:
469
- * res.fnumber( name ) -> Fixnum
713
+ * res.fnumber( name ) -> Integer
470
714
  *
471
715
  * Returns the index of the field specified by the string +name+.
472
716
  * The given +name+ is treated like an identifier in an SQL command, that is,
@@ -527,7 +771,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
527
771
 
528
772
  /*
529
773
  * call-seq:
530
- * res.ftablecol( column_number ) -> Fixnum
774
+ * res.ftablecol( column_number ) -> Integer
531
775
  *
532
776
  * Returns the column number (within its table) of the table from
533
777
  * which the column _column_number_ is made up.
@@ -552,7 +796,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
552
796
 
553
797
  /*
554
798
  * call-seq:
555
- * res.fformat( column_number ) -> Fixnum
799
+ * res.fformat( column_number ) -> Integer
556
800
  *
557
801
  * Returns the format (0 for text, 1 for binary) of column
558
802
  * _column_number_.
@@ -696,7 +940,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
696
940
 
697
941
  /*
698
942
  * call-seq:
699
- * res.getlength( tup_num, field_num ) -> Fixnum
943
+ * res.getlength( tup_num, field_num ) -> Integer
700
944
  *
701
945
  * Returns the (String) length of the field in bytes.
702
946
  *
@@ -721,7 +965,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
721
965
 
722
966
  /*
723
967
  * call-seq:
724
- * res.nparams() -> Fixnum
968
+ * res.nparams() -> Integer
725
969
  *
726
970
  * Returns the number of parameters of a prepared statement.
727
971
  * Only useful for the result returned by conn.describePrepared
@@ -760,8 +1004,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
760
1004
  static VALUE
761
1005
  pgresult_cmd_status(VALUE self)
762
1006
  {
763
- VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
764
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
1007
+ t_pg_result *this = pgresult_get_this_safe(self);
1008
+ VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
1009
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
765
1010
  return ret;
766
1011
  }
767
1012
 
@@ -772,11 +1017,17 @@ pgresult_cmd_status(VALUE self)
772
1017
  * Returns the number of tuples (rows) affected by the SQL command.
773
1018
  *
774
1019
  * If the SQL command that generated the PG::Result was not one of:
775
- * * +INSERT+
776
- * * +UPDATE+
777
- * * +DELETE+
778
- * * +MOVE+
779
- * * +FETCH+
1020
+ *
1021
+ * * <tt>SELECT</tt>
1022
+ * * <tt>CREATE TABLE AS</tt>
1023
+ * * <tt>INSERT</tt>
1024
+ * * <tt>UPDATE</tt>
1025
+ * * <tt>DELETE</tt>
1026
+ * * <tt>MOVE</tt>
1027
+ * * <tt>FETCH</tt>
1028
+ * * <tt>COPY</tt>
1029
+ * * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
1030
+ *
780
1031
  * or if no tuples were affected, <tt>0</tt> is returned.
781
1032
  */
782
1033
  static VALUE
@@ -784,7 +1035,7 @@ pgresult_cmd_tuples(VALUE self)
784
1035
  {
785
1036
  long n;
786
1037
  n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
787
- return INT2NUM(n);
1038
+ return LONG2NUM(n);
788
1039
  }
789
1040
 
790
1041
  /*
@@ -955,8 +1206,12 @@ static VALUE
955
1206
  pgresult_field_values( VALUE self, VALUE field )
956
1207
  {
957
1208
  PGresult *result = pgresult_get( self );
958
- const char *fieldname = StringValueCStr( field );
959
- int fnum = PQfnumber( result, fieldname );
1209
+ const char *fieldname;
1210
+ int fnum;
1211
+
1212
+ if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
1213
+ fieldname = StringValueCStr( field );
1214
+ fnum = PQfnumber( result, fieldname );
960
1215
 
961
1216
  if ( fnum < 0 )
962
1217
  rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
@@ -965,6 +1220,85 @@ pgresult_field_values( VALUE self, VALUE field )
965
1220
  }
966
1221
 
967
1222
 
1223
+ /*
1224
+ * call-seq:
1225
+ * res.tuple_values( n ) -> array
1226
+ *
1227
+ * Returns an Array of the field values from the nth row of the result.
1228
+ *
1229
+ */
1230
+ static VALUE
1231
+ pgresult_tuple_values(VALUE self, VALUE index)
1232
+ {
1233
+ int tuple_num = NUM2INT( index );
1234
+ t_pg_result *this;
1235
+ int field;
1236
+ int num_tuples;
1237
+ int num_fields;
1238
+
1239
+ this = pgresult_get_this_safe(self);
1240
+ num_tuples = PQntuples(this->pgresult);
1241
+ num_fields = PQnfields(this->pgresult);
1242
+
1243
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1244
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1245
+
1246
+ {
1247
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
1248
+
1249
+ /* populate the row */
1250
+ for ( field = 0; field < num_fields; field++ ) {
1251
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
1252
+ }
1253
+ return rb_ary_new4( num_fields, row_values );
1254
+ }
1255
+ }
1256
+
1257
+ static void ensure_init_for_tuple(VALUE self)
1258
+ {
1259
+ t_pg_result *this = pgresult_get_this_safe(self);
1260
+
1261
+ if( this->field_map == Qnil ){
1262
+ int i;
1263
+ VALUE field_map = rb_hash_new();
1264
+
1265
+ if( this->nfields == -1 )
1266
+ pgresult_init_fnames( self );
1267
+
1268
+ for( i = 0; i < this->nfields; i++ ){
1269
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1270
+ }
1271
+ rb_obj_freeze(field_map);
1272
+ this->field_map = field_map;
1273
+ }
1274
+ }
1275
+
1276
+ /*
1277
+ * call-seq:
1278
+ * res.tuple( n ) -> PG::Tuple
1279
+ *
1280
+ * Returns a PG::Tuple from the nth row of the result.
1281
+ *
1282
+ */
1283
+ static VALUE
1284
+ pgresult_tuple(VALUE self, VALUE index)
1285
+ {
1286
+ int tuple_num = NUM2INT( index );
1287
+ t_pg_result *this;
1288
+ int num_tuples;
1289
+
1290
+ this = pgresult_get_this_safe(self);
1291
+ num_tuples = PQntuples(this->pgresult);
1292
+
1293
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1294
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1295
+
1296
+ ensure_init_for_tuple(self);
1297
+
1298
+ return pg_tuple_new(self, tuple_num);
1299
+ }
1300
+
1301
+
968
1302
  /*
969
1303
  * call-seq:
970
1304
  * res.each{ |tuple| ... }
@@ -991,7 +1325,7 @@ pgresult_each(VALUE self)
991
1325
  * call-seq:
992
1326
  * res.fields() -> Array
993
1327
  *
994
- * Returns an array of Strings representing the names of the fields in the result.
1328
+ * Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
995
1329
  */
996
1330
  static VALUE
997
1331
  pgresult_fields(VALUE self)
@@ -1023,14 +1357,11 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1023
1357
  t_pg_result *this = pgresult_get_this(self);
1024
1358
  t_typemap *p_typemap;
1025
1359
 
1026
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
1027
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
1028
- rb_obj_classname( typemap ) );
1029
- }
1030
- Data_Get_Struct(typemap, t_typemap, p_typemap);
1360
+ /* Check type of method param */
1361
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
1031
1362
 
1032
1363
  this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1033
- this->p_typemap = DATA_PTR( this->typemap );
1364
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
1034
1365
 
1035
1366
  return typemap;
1036
1367
  }
@@ -1050,40 +1381,64 @@ pgresult_type_map_get(VALUE self)
1050
1381
  return this->typemap;
1051
1382
  }
1052
1383
 
1053
- #ifdef HAVE_PQSETSINGLEROWMODE
1054
- /*
1055
- * call-seq:
1056
- * res.stream_each{ |tuple| ... }
1057
- *
1058
- * Invokes block for each tuple in the result set in single row mode.
1059
- *
1060
- * This is a convenience method for retrieving all result tuples
1061
- * as they are transferred. It is an alternative to repeated calls of
1062
- * PG::Connection#get_result , but given that it avoids the overhead of
1063
- * wrapping each row into a dedicated result object, it delivers data in nearly
1064
- * the same speed as with ordinary results.
1065
- *
1066
- * The result must be in status PGRES_SINGLE_TUPLE.
1067
- * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1068
- * A PG::Error is raised for any errors from the server.
1069
- *
1070
- * Row description data does not change while the iteration. All value retrieval
1071
- * methods refer to only the current row. Result#ntuples returns +1+ while
1072
- * the iteration and +0+ after all tuples were yielded.
1073
- *
1074
- * Example:
1075
- * conn.send_query( "first SQL query; second SQL query" )
1076
- * conn.set_single_row_mode
1077
- * conn.get_result.stream_each do |row|
1078
- * # do something with the received row of the first query
1079
- * end
1080
- * conn.get_result.stream_each do |row|
1081
- * # do something with the received row of the second query
1082
- * end
1083
- * conn.get_result # => nil (no more results)
1084
- */
1085
- static VALUE
1086
- pgresult_stream_each(VALUE self)
1384
+
1385
+ static int
1386
+ yield_hash(VALUE self, int ntuples, int nfields, void *data)
1387
+ {
1388
+ int tuple_num;
1389
+ UNUSED(nfields);
1390
+
1391
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1392
+ rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1393
+ }
1394
+
1395
+ return 1; /* clear the result */
1396
+ }
1397
+
1398
+ static int
1399
+ yield_array(VALUE self, int ntuples, int nfields, void *data)
1400
+ {
1401
+ int row;
1402
+ t_pg_result *this = pgresult_get_this(self);
1403
+
1404
+ for ( row = 0; row < ntuples; row++ ) {
1405
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1406
+ int field;
1407
+
1408
+ /* populate the row */
1409
+ for ( field = 0; field < nfields; field++ ) {
1410
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1411
+ }
1412
+ rb_yield( rb_ary_new4( nfields, row_values ));
1413
+ }
1414
+
1415
+ return 1; /* clear the result */
1416
+ }
1417
+
1418
+ static int
1419
+ yield_tuple(VALUE self, int ntuples, int nfields, void *data)
1420
+ {
1421
+ int tuple_num;
1422
+ t_pg_result *this = pgresult_get_this(self);
1423
+ VALUE copy;
1424
+ UNUSED(nfields);
1425
+
1426
+ /* make a copy of the base result, that is bound to the PG::Tuple */
1427
+ copy = pg_copy_result(this);
1428
+ /* The copy is now owner of the PGresult and is responsible to PQclear it.
1429
+ * We clear the pgresult here, so that it's not double freed on error within yield. */
1430
+ this->pgresult = NULL;
1431
+
1432
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1433
+ VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1434
+ rb_yield( tuple );
1435
+ }
1436
+ return 0; /* don't clear the result */
1437
+ }
1438
+
1439
+ /* Non-static, and data pointer for use by sequel_pg */
1440
+ VALUE
1441
+ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
1087
1442
  {
1088
1443
  t_pg_result *this;
1089
1444
  int nfields;
@@ -1098,11 +1453,11 @@ pgresult_stream_each(VALUE self)
1098
1453
  nfields = PQnfields(pgresult);
1099
1454
 
1100
1455
  for(;;){
1101
- int tuple_num;
1102
1456
  int ntuples = PQntuples(pgresult);
1103
1457
 
1104
1458
  switch( PQresultStatus(pgresult) ){
1105
1459
  case PGRES_TUPLES_OK:
1460
+ case PGRES_COMMAND_OK:
1106
1461
  if( ntuples == 0 )
1107
1462
  return self;
1108
1463
  rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
@@ -1112,21 +1467,21 @@ pgresult_stream_each(VALUE self)
1112
1467
  pg_result_check( self );
1113
1468
  }
1114
1469
 
1115
- for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1116
- rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1470
+ if( yielder( self, ntuples, nfields, data ) ){
1471
+ pgresult_clear( this );
1117
1472
  }
1118
1473
 
1119
- if( !this->autoclear ){
1120
- PQclear( pgresult );
1121
- this->pgresult = NULL;
1474
+ if( gvl_PQisBusy(pgconn) ){
1475
+ /* wait for input (without blocking) before reading each result */
1476
+ pgconn_block( 0, NULL, this->connection );
1122
1477
  }
1123
1478
 
1124
1479
  pgresult = gvl_PQgetResult(pgconn);
1125
1480
  if( pgresult == NULL )
1126
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1481
+ rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
1127
1482
 
1128
1483
  if( nfields != PQnfields(pgresult) )
1129
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1484
+ rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, PQnfields(pgresult));
1130
1485
 
1131
1486
  this->pgresult = pgresult;
1132
1487
  }
@@ -1135,6 +1490,44 @@ pgresult_stream_each(VALUE self)
1135
1490
  return self;
1136
1491
  }
1137
1492
 
1493
+
1494
+ /*
1495
+ * call-seq:
1496
+ * res.stream_each{ |tuple| ... }
1497
+ *
1498
+ * Invokes block for each tuple in the result set in single row mode.
1499
+ *
1500
+ * This is a convenience method for retrieving all result tuples
1501
+ * as they are transferred. It is an alternative to repeated calls of
1502
+ * PG::Connection#get_result , but given that it avoids the overhead of
1503
+ * wrapping each row into a dedicated result object, it delivers data in nearly
1504
+ * the same speed as with ordinary results.
1505
+ *
1506
+ * The base result must be in status PGRES_SINGLE_TUPLE.
1507
+ * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1508
+ * A PG::Error is raised for any errors from the server.
1509
+ *
1510
+ * Row description data does not change while the iteration. All value retrieval
1511
+ * methods refer to only the current row. Result#ntuples returns +1+ while
1512
+ * the iteration and +0+ after all tuples were yielded.
1513
+ *
1514
+ * Example:
1515
+ * conn.send_query( "first SQL query; second SQL query" )
1516
+ * conn.set_single_row_mode
1517
+ * conn.get_result.stream_each do |row|
1518
+ * # do something with each received row of the first query
1519
+ * end
1520
+ * conn.get_result.stream_each do |row|
1521
+ * # do something with each received row of the second query
1522
+ * end
1523
+ * conn.get_result # => nil (no more results)
1524
+ */
1525
+ static VALUE
1526
+ pgresult_stream_each(VALUE self)
1527
+ {
1528
+ return pgresult_stream_any(self, yield_hash, NULL);
1529
+ }
1530
+
1138
1531
  /*
1139
1532
  * call-seq:
1140
1533
  * res.stream_each_row { |row| ... }
@@ -1148,70 +1541,92 @@ pgresult_stream_each(VALUE self)
1148
1541
  static VALUE
1149
1542
  pgresult_stream_each_row(VALUE self)
1150
1543
  {
1151
- t_pg_result *this;
1152
- int row;
1153
- int nfields;
1154
- PGconn *pgconn;
1155
- PGresult *pgresult;
1156
-
1157
- RETURN_ENUMERATOR(self, 0, NULL);
1158
-
1159
- this = pgresult_get_this_safe(self);
1160
- pgconn = pg_get_pgconn(this->connection);
1161
- pgresult = this->pgresult;
1162
- nfields = PQnfields(pgresult);
1163
-
1164
- for(;;){
1165
- int ntuples = PQntuples(pgresult);
1166
-
1167
- switch( PQresultStatus(pgresult) ){
1168
- case PGRES_TUPLES_OK:
1169
- if( ntuples == 0 )
1170
- return self;
1171
- rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1172
- case PGRES_SINGLE_TUPLE:
1173
- break;
1174
- default:
1175
- pg_result_check( self );
1176
- }
1544
+ return pgresult_stream_any(self, yield_array, NULL);
1545
+ }
1177
1546
 
1178
- for ( row = 0; row < ntuples; row++ ) {
1179
- PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1180
- int field;
1547
+ /*
1548
+ * call-seq:
1549
+ * res.stream_each_tuple { |tuple| ... }
1550
+ *
1551
+ * Yields each row of the result set in single row mode.
1552
+ *
1553
+ * This method works equally to #stream_each , but yields a PG::Tuple object.
1554
+ */
1555
+ static VALUE
1556
+ pgresult_stream_each_tuple(VALUE self)
1557
+ {
1558
+ /* allocate VALUEs that are shared between all streamed tuples */
1559
+ ensure_init_for_tuple(self);
1181
1560
 
1182
- /* populate the row */
1183
- for ( field = 0; field < nfields; field++ ) {
1184
- row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1185
- }
1186
- rb_yield( rb_ary_new4( nfields, row_values ));
1187
- }
1561
+ return pgresult_stream_any(self, yield_tuple, NULL);
1562
+ }
1188
1563
 
1189
- if( !this->autoclear ){
1190
- PQclear( pgresult );
1191
- this->pgresult = NULL;
1192
- }
1564
+ /*
1565
+ * call-seq:
1566
+ * res.field_name_type = Symbol
1567
+ *
1568
+ * Set type of field names specific to this result.
1569
+ * It can be set to one of:
1570
+ * * +:string+ to use String based field names
1571
+ * * +:symbol+ to use Symbol based field names
1572
+ * * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
1573
+ *
1574
+ * The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
1575
+ *
1576
+ * This setting affects several result methods:
1577
+ * * keys of Hash returned by #[] , #each and #stream_each
1578
+ * * #fields
1579
+ * * #fname
1580
+ * * field names used by #tuple and #stream_each_tuple
1581
+ *
1582
+ * The type of field names can only be changed before any of the affected methods have been called.
1583
+ *
1584
+ */
1585
+ static VALUE
1586
+ pgresult_field_name_type_set(VALUE self, VALUE sym)
1587
+ {
1588
+ t_pg_result *this = pgresult_get_this(self);
1589
+ if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1193
1590
 
1194
- pgresult = gvl_PQgetResult(pgconn);
1195
- if( pgresult == NULL )
1196
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1591
+ this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
1592
+ if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
1593
+ else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
1594
+ else if ( sym == sym_string );
1595
+ else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
1197
1596
 
1198
- if( nfields != PQnfields(pgresult) )
1199
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1597
+ return sym;
1598
+ }
1200
1599
 
1201
- this->pgresult = pgresult;
1600
+ /*
1601
+ * call-seq:
1602
+ * res.field_name_type -> Symbol
1603
+ *
1604
+ * Get type of field names.
1605
+ *
1606
+ * See description at #field_name_type=
1607
+ */
1608
+ static VALUE
1609
+ pgresult_field_name_type_get(VALUE self)
1610
+ {
1611
+ t_pg_result *this = pgresult_get_this(self);
1612
+ if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
1613
+ return sym_symbol;
1614
+ } else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
1615
+ return sym_static_symbol;
1616
+ } else {
1617
+ return sym_string;
1202
1618
  }
1203
-
1204
- /* never reached */
1205
- return self;
1206
1619
  }
1207
- #endif
1208
-
1209
1620
 
1210
1621
  void
1211
- init_pg_result()
1622
+ init_pg_result(void)
1212
1623
  {
1624
+ sym_string = ID2SYM(rb_intern("string"));
1625
+ sym_symbol = ID2SYM(rb_intern("symbol"));
1626
+ sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
1627
+
1213
1628
  rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
1214
- rb_define_alloc_func( rb_cPGresult, pgresult_s_allocate );
1629
+ rb_undef_alloc_func(rb_cPGresult);
1215
1630
  rb_include_module(rb_cPGresult, rb_mEnumerable);
1216
1631
  rb_include_module(rb_cPGresult, rb_mPGconstants);
1217
1632
 
@@ -1220,6 +1635,10 @@ init_pg_result()
1220
1635
  rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1221
1636
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1222
1637
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1638
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1639
+ rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1640
+ rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1641
+ #endif
1223
1642
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1224
1643
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1225
1644
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
@@ -1255,17 +1674,19 @@ init_pg_result()
1255
1674
  rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
1256
1675
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
1257
1676
  rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
1677
+ rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
1678
+ rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
1258
1679
  rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
1259
1680
  rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
1260
1681
 
1261
1682
  rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1262
1683
  rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
1263
1684
 
1264
- #ifdef HAVE_PQSETSINGLEROWMODE
1265
1685
  /****** PG::Result INSTANCE METHODS: streaming ******/
1266
1686
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1267
1687
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1268
- #endif
1269
- }
1270
-
1688
+ rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1271
1689
 
1690
+ rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
1691
+ rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
1692
+ }