pg 0.18.2 → 1.5.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 (139) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +137 -0
  7. data/.gitignore +22 -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.md +876 -0
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.md +276 -0
  20. data/README.md +286 -0
  21. data/Rakefile +40 -131
  22. data/Rakefile.cross +88 -70
  23. data/certs/ged.pem +24 -0
  24. data/certs/larskanis-2022.pem +26 -0
  25. data/certs/larskanis-2023.pem +24 -0
  26. data/ext/errorcodes.def +113 -0
  27. data/ext/errorcodes.rb +1 -1
  28. data/ext/errorcodes.txt +36 -2
  29. data/ext/extconf.rb +120 -54
  30. data/ext/gvl_wrappers.c +8 -0
  31. data/ext/gvl_wrappers.h +44 -33
  32. data/ext/pg.c +226 -200
  33. data/ext/pg.h +99 -99
  34. data/ext/pg_binary_decoder.c +164 -16
  35. data/ext/pg_binary_encoder.c +249 -22
  36. data/ext/pg_coder.c +189 -44
  37. data/ext/pg_connection.c +1866 -1173
  38. data/ext/pg_copy_coder.c +398 -42
  39. data/ext/pg_errors.c +1 -1
  40. data/ext/pg_record_coder.c +522 -0
  41. data/ext/pg_result.c +727 -232
  42. data/ext/pg_text_decoder.c +629 -43
  43. data/ext/pg_text_encoder.c +269 -102
  44. data/ext/pg_tuple.c +572 -0
  45. data/ext/pg_type_map.c +64 -23
  46. data/ext/pg_type_map_all_strings.c +21 -7
  47. data/ext/pg_type_map_by_class.c +59 -27
  48. data/ext/pg_type_map_by_column.c +86 -43
  49. data/ext/pg_type_map_by_mri_type.c +49 -20
  50. data/ext/pg_type_map_by_oid.c +62 -29
  51. data/ext/pg_type_map_in_ruby.c +56 -22
  52. data/ext/{util.c → pg_util.c} +12 -12
  53. data/ext/{util.h → pg_util.h} +2 -2
  54. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  55. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  56. data/lib/pg/basic_type_map_for_results.rb +104 -0
  57. data/lib/pg/basic_type_registry.rb +299 -0
  58. data/lib/pg/binary_decoder/date.rb +9 -0
  59. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  60. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  61. data/lib/pg/coder.rb +36 -13
  62. data/lib/pg/connection.rb +797 -77
  63. data/lib/pg/exceptions.rb +16 -2
  64. data/lib/pg/result.rb +24 -7
  65. data/lib/pg/text_decoder/date.rb +18 -0
  66. data/lib/pg/text_decoder/inet.rb +9 -0
  67. data/lib/pg/text_decoder/json.rb +14 -0
  68. data/lib/pg/text_decoder/numeric.rb +9 -0
  69. data/lib/pg/text_decoder/timestamp.rb +30 -0
  70. data/lib/pg/text_encoder/date.rb +12 -0
  71. data/lib/pg/text_encoder/inet.rb +28 -0
  72. data/lib/pg/text_encoder/json.rb +14 -0
  73. data/lib/pg/text_encoder/numeric.rb +9 -0
  74. data/lib/pg/text_encoder/timestamp.rb +24 -0
  75. data/lib/pg/tuple.rb +30 -0
  76. data/lib/pg/type_map_by_column.rb +3 -2
  77. data/lib/pg/version.rb +4 -0
  78. data/lib/pg.rb +106 -41
  79. data/misc/openssl-pg-segfault.rb +31 -0
  80. data/misc/postgres/History.txt +9 -0
  81. data/misc/postgres/Manifest.txt +5 -0
  82. data/misc/postgres/README.txt +21 -0
  83. data/misc/postgres/Rakefile +21 -0
  84. data/misc/postgres/lib/postgres.rb +16 -0
  85. data/misc/ruby-pg/History.txt +9 -0
  86. data/misc/ruby-pg/Manifest.txt +5 -0
  87. data/misc/ruby-pg/README.txt +21 -0
  88. data/misc/ruby-pg/Rakefile +21 -0
  89. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  90. data/pg.gemspec +34 -0
  91. data/rakelib/task_extension.rb +46 -0
  92. data/sample/array_insert.rb +1 -1
  93. data/sample/async_api.rb +4 -8
  94. data/sample/async_copyto.rb +1 -1
  95. data/sample/async_mixed.rb +1 -1
  96. data/sample/check_conn.rb +1 -1
  97. data/sample/copydata.rb +71 -0
  98. data/sample/copyfrom.rb +1 -1
  99. data/sample/copyto.rb +1 -1
  100. data/sample/cursor.rb +1 -1
  101. data/sample/disk_usage_report.rb +6 -15
  102. data/sample/issue-119.rb +2 -2
  103. data/sample/losample.rb +1 -1
  104. data/sample/minimal-testcase.rb +2 -2
  105. data/sample/notify_wait.rb +1 -1
  106. data/sample/pg_statistics.rb +6 -15
  107. data/sample/replication_monitor.rb +9 -18
  108. data/sample/test_binary_values.rb +1 -1
  109. data/sample/wal_shipper.rb +2 -2
  110. data/sample/warehouse_partitions.rb +8 -17
  111. data/translation/.po4a-version +7 -0
  112. data/translation/po/all.pot +910 -0
  113. data/translation/po/ja.po +1047 -0
  114. data/translation/po4a.cfg +12 -0
  115. data.tar.gz.sig +0 -0
  116. metadata +137 -204
  117. metadata.gz.sig +0 -0
  118. data/ChangeLog +0 -5545
  119. data/History.rdoc +0 -313
  120. data/README.ja.rdoc +0 -14
  121. data/README.rdoc +0 -161
  122. data/lib/pg/basic_type_mapping.rb +0 -399
  123. data/lib/pg/constants.rb +0 -11
  124. data/lib/pg/text_decoder.rb +0 -42
  125. data/lib/pg/text_encoder.rb +0 -27
  126. data/spec/data/expected_trace.out +0 -26
  127. data/spec/data/random_binary_data +0 -0
  128. data/spec/helpers.rb +0 -355
  129. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  130. data/spec/pg/connection_spec.rb +0 -1535
  131. data/spec/pg/result_spec.rb +0 -449
  132. data/spec/pg/type_map_by_class_spec.rb +0 -138
  133. data/spec/pg/type_map_by_column_spec.rb +0 -222
  134. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  135. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  136. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  137. data/spec/pg/type_map_spec.rb +0 -22
  138. data/spec/pg/type_spec.rb +0 -688
  139. 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 */
18
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 );
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 | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
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,91 @@ 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;
211
+ /* Initialize connection and typemap prior to any object allocations,
212
+ * to make sure valid objects are marked. */
38
213
  this->connection = rb_pgconn;
39
214
  this->typemap = pg_typemap_all_strings;
40
- this->p_typemap = DATA_PTR( this->typemap );
41
- this->autoclear = 0;
215
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
42
216
  this->nfields = -1;
43
217
  this->tuple_hash = Qnil;
44
-
45
- PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
218
+ this->field_map = Qnil;
219
+ this->flags = 0;
220
+ self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
46
221
 
47
222
  if( result ){
48
223
  t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
49
224
  VALUE typemap = p_conn->type_map_for_results;
50
-
51
225
  /* Type check is done when assigned to PG::Connection. */
52
- t_typemap *p_typemap = DATA_PTR(typemap);
53
-
54
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
55
- this->p_typemap = DATA_PTR( this->typemap );
226
+ t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
227
+
228
+ this->enc_idx = p_conn->enc_idx;
229
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
230
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
231
+ this->p_typemap = RTYPEDDATA_DATA( this->typemap );
232
+ this->flags = p_conn->flags;
233
+ } else {
234
+ this->enc_idx = rb_locale_encindex();
56
235
  }
57
236
 
58
237
  return self;
59
238
  }
60
239
 
240
+ VALUE
241
+ pg_new_result(PGresult *result, VALUE rb_pgconn)
242
+ {
243
+ VALUE self = pg_new_result2(result, rb_pgconn);
244
+ t_pg_result *this = pgresult_get_this(self);
245
+
246
+ this->autoclear = 0;
247
+
248
+ /* Estimate size of underlying pgresult memory storage and account to ruby GC.
249
+ * There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
250
+ */
251
+ /* 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.
252
+ * For now the memory savings don't justify the ifdefs necessary to support both cases.
253
+ */
254
+ this->result_size = pgresult_approx_size(result);
255
+
256
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
257
+ rb_gc_adjust_memory_usage(this->result_size);
258
+ #endif
259
+
260
+ return self;
261
+ }
262
+
263
+ static VALUE
264
+ pg_copy_result(t_pg_result *this)
265
+ {
266
+ int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
267
+ size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
268
+ t_pg_result *copy;
269
+
270
+ copy = (t_pg_result *)xmalloc(len);
271
+ memcpy(copy, this, len);
272
+ this->result_size = 0;
273
+
274
+ return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
275
+ }
276
+
61
277
  VALUE
62
278
  pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
63
279
  {
64
- VALUE self = pg_new_result(result, rb_pgconn);
280
+ VALUE self = pg_new_result2(result, rb_pgconn);
65
281
  t_pg_result *this = pgresult_get_this(self);
282
+
283
+ /* Autocleared results are freed implicit instead of by PQclear().
284
+ * So it's not very useful to be accounted by ruby GC.
285
+ */
286
+ this->result_size = 0;
66
287
  this->autoclear = 1;
67
288
  return self;
68
289
  }
@@ -71,7 +292,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
71
292
  * call-seq:
72
293
  * res.check -> nil
73
294
  *
74
- * Raises appropriate exception if PG::Result is in a bad state.
295
+ * Raises appropriate exception if PG::Result is in a bad state, which is:
296
+ * * +PGRES_BAD_RESPONSE+
297
+ * * +PGRES_FATAL_ERROR+
298
+ * * +PGRES_NONFATAL_ERROR+
299
+ * * +PGRES_PIPELINE_ABORTED+
75
300
  */
76
301
  VALUE
77
302
  pg_result_check( VALUE self )
@@ -92,18 +317,20 @@ pg_result_check( VALUE self )
92
317
  case PGRES_TUPLES_OK:
93
318
  case PGRES_COPY_OUT:
94
319
  case PGRES_COPY_IN:
95
- #ifdef HAVE_CONST_PGRES_COPY_BOTH
96
320
  case PGRES_COPY_BOTH:
97
- #endif
98
- #ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
99
321
  case PGRES_SINGLE_TUPLE:
100
- #endif
101
322
  case PGRES_EMPTY_QUERY:
102
323
  case PGRES_COMMAND_OK:
324
+ #ifdef HAVE_PQENTERPIPELINEMODE
325
+ case PGRES_PIPELINE_SYNC:
326
+ #endif
103
327
  return self;
104
328
  case PGRES_BAD_RESPONSE:
105
329
  case PGRES_FATAL_ERROR:
106
330
  case PGRES_NONFATAL_ERROR:
331
+ #ifdef HAVE_PQENTERPIPELINEMODE
332
+ case PGRES_PIPELINE_ABORTED:
333
+ #endif
107
334
  error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
108
335
  break;
109
336
  default:
@@ -111,7 +338,7 @@ pg_result_check( VALUE self )
111
338
  }
112
339
  }
113
340
 
114
- PG_ENCODING_SET_NOCHECK( error, ENCODING_GET(self) );
341
+ PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
115
342
 
116
343
  sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
117
344
  klass = lookup_error_class( sqlstate );
@@ -135,29 +362,52 @@ pg_result_check( VALUE self )
135
362
  * call-seq:
136
363
  * res.clear() -> nil
137
364
  *
138
- * Clears the PG::Result object as the result of the query.
365
+ * Clears the PG::Result object as the result of a query.
366
+ * This frees all underlying memory consumed by the result object.
367
+ * Afterwards access to result methods raises PG::Error "result has been cleared".
368
+ *
369
+ * Explicit calling #clear can lead to better memory performance, but is not generally necessary.
370
+ * Special care must be taken when PG::Tuple objects are used.
371
+ * In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
139
372
  *
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.
373
+ * 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
374
  *
143
375
  */
144
376
  VALUE
145
377
  pg_result_clear(VALUE self)
146
378
  {
147
379
  t_pg_result *this = pgresult_get_this(self);
148
- if( !this->autoclear )
149
- PQclear(pgresult_get(self));
150
- this->pgresult = NULL;
380
+ rb_check_frozen(self);
381
+ pgresult_clear( this );
151
382
  return Qnil;
152
383
  }
153
384
 
154
385
  /*
155
386
  * call-seq:
156
- * res.cleared? -> boolean
387
+ * res.freeze
388
+ *
389
+ * Freeze the PG::Result object and unlink the result from the related PG::Connection.
390
+ *
391
+ * A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
392
+ * It also denies setting a type_map or field_name_type.
157
393
  *
158
- * Returns +true+ if the backend result memory has been free'd.
159
394
  */
160
395
  VALUE
396
+ static pg_result_freeze(VALUE self)
397
+ {
398
+ t_pg_result *this = pgresult_get_this(self);
399
+
400
+ RB_OBJ_WRITE(self, &this->connection, Qnil);
401
+ return rb_call_super(0, NULL);
402
+ }
403
+
404
+ /*
405
+ * call-seq:
406
+ * res.cleared? -> boolean
407
+ *
408
+ * Returns +true+ if the backend result memory has been freed.
409
+ */
410
+ static VALUE
161
411
  pgresult_cleared_p( VALUE self )
162
412
  {
163
413
  t_pg_result *this = pgresult_get_this(self);
@@ -168,11 +418,13 @@ pgresult_cleared_p( VALUE self )
168
418
  * call-seq:
169
419
  * res.autoclear? -> boolean
170
420
  *
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.
421
+ * Returns +true+ if the underlying C struct will be cleared at the end of a callback.
422
+ * This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
423
+ *
424
+ * 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
425
  *
174
426
  */
175
- VALUE
427
+ static VALUE
176
428
  pgresult_autoclear_p( VALUE self )
177
429
  {
178
430
  t_pg_result *this = pgresult_get_this(self);
@@ -183,37 +435,6 @@ pgresult_autoclear_p( VALUE self )
183
435
  * DATA pointer functions
184
436
  */
185
437
 
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
438
  /*
218
439
  * Fetch the PG::Result object data pointer and check it's
219
440
  * PGresult data pointer for sanity.
@@ -243,18 +464,30 @@ pgresult_get(VALUE self)
243
464
  return this->pgresult;
244
465
  }
245
466
 
246
- /*
247
- * Document-method: allocate
248
- *
249
- * call-seq:
250
- * PG::Result.allocate -> result
251
- */
252
- static VALUE
253
- pgresult_s_allocate( VALUE klass )
467
+ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
254
468
  {
255
- VALUE self = Data_Wrap_Struct( klass, pgresult_gc_mark, pgresult_gc_free, NULL );
256
-
257
- return self;
469
+ VALUE fname;
470
+ #ifdef TRUFFLERUBY
471
+ if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
472
+ #else
473
+ if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
474
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
475
+ fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
476
+ if( fname == Qnil ){
477
+ fname = rb_str_new2(cstr);
478
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
479
+ fname = rb_str_intern(fname);
480
+ }
481
+ } else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
482
+ #endif
483
+ rb_encoding *enc = rb_enc_from_index(enc_idx);
484
+ fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
485
+ } else {
486
+ fname = rb_str_new2(cstr);
487
+ PG_ENCODING_SET_NOCHECK(fname, enc_idx);
488
+ fname = rb_obj_freeze(fname);
489
+ }
490
+ return fname;
258
491
  }
259
492
 
260
493
  static void pgresult_init_fnames(VALUE self)
@@ -266,12 +499,10 @@ static void pgresult_init_fnames(VALUE self)
266
499
  int nfields = PQnfields(this->pgresult);
267
500
 
268
501
  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);
502
+ char *cfname = PQfname(this->pgresult, i);
503
+ VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
504
+ RB_OBJ_WRITE(self, &this->fnames[i], fname);
272
505
  this->nfields = i + 1;
273
-
274
- RB_GC_GUARD(fname);
275
506
  }
276
507
  this->nfields = nfields;
277
508
  }
@@ -283,12 +514,15 @@ static void pgresult_init_fnames(VALUE self)
283
514
  *
284
515
  * The class to represent the query result tuples (rows).
285
516
  * 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.
517
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
518
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
519
+ *
520
+ * Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
521
+ * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
288
522
  *
289
523
  * Example:
290
524
  * require 'pg'
291
- * conn = PGconn.open(:dbname => 'test')
525
+ * conn = PG.connect(:dbname => 'test')
292
526
  * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
293
527
  * res.getvalue(0,0) # '1'
294
528
  * res[0]['b'] # '2'
@@ -302,7 +536,7 @@ static void pgresult_init_fnames(VALUE self)
302
536
 
303
537
  /*
304
538
  * call-seq:
305
- * res.result_status() -> Fixnum
539
+ * res.result_status() -> Integer
306
540
  *
307
541
  * Returns the status of the query. The status value is one of:
308
542
  * * +PGRES_EMPTY_QUERY+
@@ -314,6 +548,11 @@ static void pgresult_init_fnames(VALUE self)
314
548
  * * +PGRES_NONFATAL_ERROR+
315
549
  * * +PGRES_FATAL_ERROR+
316
550
  * * +PGRES_COPY_BOTH+
551
+ * * +PGRES_SINGLE_TUPLE+
552
+ * * +PGRES_PIPELINE_SYNC+
553
+ * * +PGRES_PIPELINE_ABORTED+
554
+ *
555
+ * Use <tt>res.res_status</tt> to retrieve the string representation.
317
556
  */
318
557
  static VALUE
319
558
  pgresult_result_status(VALUE self)
@@ -323,16 +562,39 @@ pgresult_result_status(VALUE self)
323
562
 
324
563
  /*
325
564
  * call-seq:
326
- * res.res_status( status ) -> String
565
+ * PG::Result.res_status( status ) -> String
327
566
  *
328
- * Returns the string representation of status +status+.
567
+ * Returns the string representation of +status+.
329
568
  *
330
569
  */
331
570
  static VALUE
332
- pgresult_res_status(VALUE self, VALUE status)
571
+ pgresult_s_res_status(VALUE self, VALUE status)
572
+ {
573
+ return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
574
+ }
575
+
576
+ /*
577
+ * call-seq:
578
+ * res.res_status -> String
579
+ * res.res_status( status ) -> String
580
+ *
581
+ * Returns the string representation of the status of the result or of the provided +status+.
582
+ *
583
+ */
584
+ static VALUE
585
+ pgresult_res_status(int argc, VALUE *argv, VALUE self)
333
586
  {
334
- VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
335
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
587
+ t_pg_result *this = pgresult_get_this_safe(self);
588
+ VALUE ret;
589
+
590
+ if( argc == 0 ){
591
+ ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
592
+ }else if( argc == 1 ){
593
+ ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
594
+ }else{
595
+ rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
596
+ }
597
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
336
598
  return ret;
337
599
  }
338
600
 
@@ -345,11 +607,40 @@ pgresult_res_status(VALUE self, VALUE status)
345
607
  static VALUE
346
608
  pgresult_error_message(VALUE self)
347
609
  {
348
- VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
349
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
610
+ t_pg_result *this = pgresult_get_this_safe(self);
611
+ VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
612
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
350
613
  return ret;
351
614
  }
352
615
 
616
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
617
+ /*
618
+ * call-seq:
619
+ * res.verbose_error_message( verbosity, show_context ) -> String
620
+ *
621
+ * Returns a reformatted version of the error message associated with a PGresult object.
622
+ *
623
+ * Available since PostgreSQL-9.6
624
+ */
625
+ static VALUE
626
+ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
627
+ {
628
+ t_pg_result *this = pgresult_get_this_safe(self);
629
+ VALUE ret;
630
+ char *c_str;
631
+
632
+ c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
633
+ if(!c_str)
634
+ rb_raise(rb_eNoMemError, "insufficient memory to format error message");
635
+
636
+ ret = rb_str_new2(c_str);
637
+ PQfreemem(c_str);
638
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
639
+
640
+ return ret;
641
+ }
642
+ #endif
643
+
353
644
  /*
354
645
  * call-seq:
355
646
  * res.error_field(fieldcode) -> String
@@ -399,14 +690,14 @@ pgresult_error_message(VALUE self)
399
690
  static VALUE
400
691
  pgresult_error_field(VALUE self, VALUE field)
401
692
  {
402
- PGresult *result = pgresult_get( self );
693
+ t_pg_result *this = pgresult_get_this_safe(self);
403
694
  int fieldcode = NUM2INT( field );
404
- char * fieldstr = PQresultErrorField( result, fieldcode );
695
+ char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
405
696
  VALUE ret = Qnil;
406
697
 
407
698
  if ( fieldstr ) {
408
- ret = rb_tainted_str_new2( fieldstr );
409
- PG_ENCODING_SET_NOCHECK( ret, ENCODING_GET(self ));
699
+ ret = rb_str_new2( fieldstr );
700
+ PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
410
701
  }
411
702
 
412
703
  return ret;
@@ -414,7 +705,7 @@ pgresult_error_field(VALUE self, VALUE field)
414
705
 
415
706
  /*
416
707
  * call-seq:
417
- * res.ntuples() -> Fixnum
708
+ * res.ntuples() -> Integer
418
709
  *
419
710
  * Returns the number of tuples in the query result.
420
711
  */
@@ -427,7 +718,7 @@ pgresult_ntuples(VALUE self)
427
718
  static VALUE
428
719
  pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
429
720
  {
430
- return pgresult_ntuples(self);
721
+ return pgresult_ntuples(self);
431
722
  }
432
723
 
433
724
  /*
@@ -444,29 +735,45 @@ pgresult_nfields(VALUE self)
444
735
 
445
736
  /*
446
737
  * call-seq:
447
- * res.fname( index ) -> String
738
+ * res.binary_tuples() -> Integer
739
+ *
740
+ * Returns 1 if the PGresult contains binary data and 0 if it contains text data.
741
+ *
742
+ * This function is deprecated (except for its use in connection with COPY), because it is possible for a single PGresult to contain text data in some columns and binary data in others.
743
+ * Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
744
+ */
745
+ static VALUE
746
+ pgresult_binary_tuples(VALUE self)
747
+ {
748
+ return INT2NUM(PQbinaryTuples(pgresult_get(self)));
749
+ }
750
+
751
+ /*
752
+ * call-seq:
753
+ * res.fname( index ) -> String or Symbol
448
754
  *
449
755
  * Returns the name of the column corresponding to _index_.
756
+ * Depending on #field_name_type= it's a String or Symbol.
757
+ *
450
758
  */
451
759
  static VALUE
452
760
  pgresult_fname(VALUE self, VALUE index)
453
761
  {
454
- VALUE fname;
455
- PGresult *result = pgresult_get(self);
762
+ t_pg_result *this = pgresult_get_this_safe(self);
456
763
  int i = NUM2INT(index);
764
+ char *cfname;
457
765
 
458
- if (i < 0 || i >= PQnfields(result)) {
766
+ if (i < 0 || i >= PQnfields(this->pgresult)) {
459
767
  rb_raise(rb_eArgError,"invalid field number %d", i);
460
768
  }
461
769
 
462
- fname = rb_tainted_str_new2(PQfname(result, i));
463
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
464
- return rb_obj_freeze(fname);
770
+ cfname = PQfname(this->pgresult, i);
771
+ return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
465
772
  }
466
773
 
467
774
  /*
468
775
  * call-seq:
469
- * res.fnumber( name ) -> Fixnum
776
+ * res.fnumber( name ) -> Integer
470
777
  *
471
778
  * Returns the index of the field specified by the string +name+.
472
779
  * The given +name+ is treated like an identifier in an SQL command, that is,
@@ -527,7 +834,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
527
834
 
528
835
  /*
529
836
  * call-seq:
530
- * res.ftablecol( column_number ) -> Fixnum
837
+ * res.ftablecol( column_number ) -> Integer
531
838
  *
532
839
  * Returns the column number (within its table) of the table from
533
840
  * which the column _column_number_ is made up.
@@ -552,7 +859,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
552
859
 
553
860
  /*
554
861
  * call-seq:
555
- * res.fformat( column_number ) -> Fixnum
862
+ * res.fformat( column_number ) -> Integer
556
863
  *
557
864
  * Returns the format (0 for text, 1 for binary) of column
558
865
  * _column_number_.
@@ -696,7 +1003,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
696
1003
 
697
1004
  /*
698
1005
  * call-seq:
699
- * res.getlength( tup_num, field_num ) -> Fixnum
1006
+ * res.getlength( tup_num, field_num ) -> Integer
700
1007
  *
701
1008
  * Returns the (String) length of the field in bytes.
702
1009
  *
@@ -721,7 +1028,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
721
1028
 
722
1029
  /*
723
1030
  * call-seq:
724
- * res.nparams() -> Fixnum
1031
+ * res.nparams() -> Integer
725
1032
  *
726
1033
  * Returns the number of parameters of a prepared statement.
727
1034
  * Only useful for the result returned by conn.describePrepared
@@ -760,8 +1067,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
760
1067
  static VALUE
761
1068
  pgresult_cmd_status(VALUE self)
762
1069
  {
763
- VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
764
- PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
1070
+ t_pg_result *this = pgresult_get_this_safe(self);
1071
+ VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
1072
+ PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
765
1073
  return ret;
766
1074
  }
767
1075
 
@@ -772,11 +1080,17 @@ pgresult_cmd_status(VALUE self)
772
1080
  * Returns the number of tuples (rows) affected by the SQL command.
773
1081
  *
774
1082
  * If the SQL command that generated the PG::Result was not one of:
775
- * * +INSERT+
776
- * * +UPDATE+
777
- * * +DELETE+
778
- * * +MOVE+
779
- * * +FETCH+
1083
+ *
1084
+ * * <tt>SELECT</tt>
1085
+ * * <tt>CREATE TABLE AS</tt>
1086
+ * * <tt>INSERT</tt>
1087
+ * * <tt>UPDATE</tt>
1088
+ * * <tt>DELETE</tt>
1089
+ * * <tt>MOVE</tt>
1090
+ * * <tt>FETCH</tt>
1091
+ * * <tt>COPY</tt>
1092
+ * * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
1093
+ *
780
1094
  * or if no tuples were affected, <tt>0</tt> is returned.
781
1095
  */
782
1096
  static VALUE
@@ -784,7 +1098,7 @@ pgresult_cmd_tuples(VALUE self)
784
1098
  {
785
1099
  long n;
786
1100
  n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
787
- return INT2NUM(n);
1101
+ return LONG2NUM(n);
788
1102
  }
789
1103
 
790
1104
  /*
@@ -836,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
836
1150
  }
837
1151
  /* Store a copy of the filled hash for use at the next row. */
838
1152
  if( num_tuples > 10 )
839
- this->tuple_hash = rb_hash_dup(tuple);
1153
+ RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
840
1154
 
841
1155
  return tuple;
842
1156
  }
@@ -955,8 +1269,12 @@ static VALUE
955
1269
  pgresult_field_values( VALUE self, VALUE field )
956
1270
  {
957
1271
  PGresult *result = pgresult_get( self );
958
- const char *fieldname = StringValueCStr( field );
959
- int fnum = PQfnumber( result, fieldname );
1272
+ const char *fieldname;
1273
+ int fnum;
1274
+
1275
+ if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
1276
+ fieldname = StringValueCStr( field );
1277
+ fnum = PQfnumber( result, fieldname );
960
1278
 
961
1279
  if ( fnum < 0 )
962
1280
  rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
@@ -965,6 +1283,85 @@ pgresult_field_values( VALUE self, VALUE field )
965
1283
  }
966
1284
 
967
1285
 
1286
+ /*
1287
+ * call-seq:
1288
+ * res.tuple_values( n ) -> array
1289
+ *
1290
+ * Returns an Array of the field values from the nth row of the result.
1291
+ *
1292
+ */
1293
+ static VALUE
1294
+ pgresult_tuple_values(VALUE self, VALUE index)
1295
+ {
1296
+ int tuple_num = NUM2INT( index );
1297
+ t_pg_result *this;
1298
+ int field;
1299
+ int num_tuples;
1300
+ int num_fields;
1301
+
1302
+ this = pgresult_get_this_safe(self);
1303
+ num_tuples = PQntuples(this->pgresult);
1304
+ num_fields = PQnfields(this->pgresult);
1305
+
1306
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1307
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1308
+
1309
+ {
1310
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
1311
+
1312
+ /* populate the row */
1313
+ for ( field = 0; field < num_fields; field++ ) {
1314
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
1315
+ }
1316
+ return rb_ary_new4( num_fields, row_values );
1317
+ }
1318
+ }
1319
+
1320
+ static void ensure_init_for_tuple(VALUE self)
1321
+ {
1322
+ t_pg_result *this = pgresult_get_this_safe(self);
1323
+
1324
+ if( this->field_map == Qnil ){
1325
+ int i;
1326
+ VALUE field_map = rb_hash_new();
1327
+
1328
+ if( this->nfields == -1 )
1329
+ pgresult_init_fnames( self );
1330
+
1331
+ for( i = 0; i < this->nfields; i++ ){
1332
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1333
+ }
1334
+ rb_obj_freeze(field_map);
1335
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
1336
+ }
1337
+ }
1338
+
1339
+ /*
1340
+ * call-seq:
1341
+ * res.tuple( n ) -> PG::Tuple
1342
+ *
1343
+ * Returns a PG::Tuple from the nth row of the result.
1344
+ *
1345
+ */
1346
+ static VALUE
1347
+ pgresult_tuple(VALUE self, VALUE index)
1348
+ {
1349
+ int tuple_num = NUM2INT( index );
1350
+ t_pg_result *this;
1351
+ int num_tuples;
1352
+
1353
+ this = pgresult_get_this_safe(self);
1354
+ num_tuples = PQntuples(this->pgresult);
1355
+
1356
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1357
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1358
+
1359
+ ensure_init_for_tuple(self);
1360
+
1361
+ return pg_tuple_new(self, tuple_num);
1362
+ }
1363
+
1364
+
968
1365
  /*
969
1366
  * call-seq:
970
1367
  * res.each{ |tuple| ... }
@@ -991,7 +1388,7 @@ pgresult_each(VALUE self)
991
1388
  * call-seq:
992
1389
  * res.fields() -> Array
993
1390
  *
994
- * Returns an array of Strings representing the names of the fields in the result.
1391
+ * Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
995
1392
  */
996
1393
  static VALUE
997
1394
  pgresult_fields(VALUE self)
@@ -1023,14 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1023
1420
  t_pg_result *this = pgresult_get_this(self);
1024
1421
  t_typemap *p_typemap;
1025
1422
 
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);
1423
+ rb_check_frozen(self);
1424
+ /* Check type of method param */
1425
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
1031
1426
 
1032
- this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1033
- this->p_typemap = DATA_PTR( this->typemap );
1427
+ typemap = p_typemap->funcs.fit_to_result( typemap, self );
1428
+ RB_OBJ_WRITE(self, &this->typemap, typemap);
1429
+ this->p_typemap = RTYPEDDATA_DATA( typemap );
1034
1430
 
1035
1431
  return typemap;
1036
1432
  }
@@ -1050,46 +1446,71 @@ pgresult_type_map_get(VALUE self)
1050
1446
  return this->typemap;
1051
1447
  }
1052
1448
 
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)
1449
+
1450
+ static int
1451
+ yield_hash(VALUE self, int ntuples, int nfields, void *data)
1452
+ {
1453
+ int tuple_num;
1454
+ UNUSED(nfields);
1455
+
1456
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1457
+ rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1458
+ }
1459
+
1460
+ return 1; /* clear the result */
1461
+ }
1462
+
1463
+ static int
1464
+ yield_array(VALUE self, int ntuples, int nfields, void *data)
1465
+ {
1466
+ int row;
1467
+ t_pg_result *this = pgresult_get_this(self);
1468
+
1469
+ for ( row = 0; row < ntuples; row++ ) {
1470
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1471
+ int field;
1472
+
1473
+ /* populate the row */
1474
+ for ( field = 0; field < nfields; field++ ) {
1475
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1476
+ }
1477
+ rb_yield( rb_ary_new4( nfields, row_values ));
1478
+ }
1479
+
1480
+ return 1; /* clear the result */
1481
+ }
1482
+
1483
+ static int
1484
+ yield_tuple(VALUE self, int ntuples, int nfields, void *data)
1485
+ {
1486
+ int tuple_num;
1487
+ t_pg_result *this = pgresult_get_this(self);
1488
+ VALUE copy;
1489
+ UNUSED(nfields);
1490
+
1491
+ /* make a copy of the base result, that is bound to the PG::Tuple */
1492
+ copy = pg_copy_result(this);
1493
+ /* The copy is now owner of the PGresult and is responsible to PQclear it.
1494
+ * We clear the pgresult here, so that it's not double freed on error within yield. */
1495
+ this->pgresult = NULL;
1496
+
1497
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1498
+ VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
1499
+ rb_yield( tuple );
1500
+ }
1501
+ return 0; /* don't clear the result */
1502
+ }
1503
+
1504
+ /* Non-static, and data pointer for use by sequel_pg */
1505
+ VALUE
1506
+ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
1087
1507
  {
1088
1508
  t_pg_result *this;
1089
- int nfields;
1509
+ int nfields, nfields2;
1090
1510
  PGconn *pgconn;
1091
1511
  PGresult *pgresult;
1092
1512
 
1513
+ rb_check_frozen(self);
1093
1514
  RETURN_ENUMERATOR(self, 0, NULL);
1094
1515
 
1095
1516
  this = pgresult_get_this_safe(self);
@@ -1098,11 +1519,11 @@ pgresult_stream_each(VALUE self)
1098
1519
  nfields = PQnfields(pgresult);
1099
1520
 
1100
1521
  for(;;){
1101
- int tuple_num;
1102
1522
  int ntuples = PQntuples(pgresult);
1103
1523
 
1104
1524
  switch( PQresultStatus(pgresult) ){
1105
1525
  case PGRES_TUPLES_OK:
1526
+ case PGRES_COMMAND_OK:
1106
1527
  if( ntuples == 0 )
1107
1528
  return self;
1108
1529
  rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
@@ -1112,21 +1533,24 @@ pgresult_stream_each(VALUE self)
1112
1533
  pg_result_check( self );
1113
1534
  }
1114
1535
 
1115
- for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1116
- rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1536
+ nfields2 = PQnfields(pgresult);
1537
+ if( nfields != nfields2 ){
1538
+ pgresult_clear( this );
1539
+ 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, nfields2);
1117
1540
  }
1118
1541
 
1119
- if( !this->autoclear ){
1120
- PQclear( pgresult );
1121
- this->pgresult = NULL;
1542
+ if( yielder( self, ntuples, nfields, data ) ){
1543
+ pgresult_clear( this );
1544
+ }
1545
+
1546
+ if( gvl_PQisBusy(pgconn) ){
1547
+ /* wait for input (without blocking) before reading each result */
1548
+ pgconn_block( 0, NULL, this->connection );
1122
1549
  }
1123
1550
 
1124
1551
  pgresult = gvl_PQgetResult(pgconn);
1125
1552
  if( pgresult == NULL )
1126
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1127
-
1128
- if( nfields != PQnfields(pgresult) )
1129
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1553
+ rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
1130
1554
 
1131
1555
  this->pgresult = pgresult;
1132
1556
  }
@@ -1135,6 +1559,44 @@ pgresult_stream_each(VALUE self)
1135
1559
  return self;
1136
1560
  }
1137
1561
 
1562
+
1563
+ /*
1564
+ * call-seq:
1565
+ * res.stream_each{ |tuple| ... }
1566
+ *
1567
+ * Invokes block for each tuple in the result set in single row mode.
1568
+ *
1569
+ * This is a convenience method for retrieving all result tuples
1570
+ * as they are transferred. It is an alternative to repeated calls of
1571
+ * PG::Connection#get_result , but given that it avoids the overhead of
1572
+ * wrapping each row into a dedicated result object, it delivers data in nearly
1573
+ * the same speed as with ordinary results.
1574
+ *
1575
+ * The base result must be in status PGRES_SINGLE_TUPLE.
1576
+ * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1577
+ * A PG::Error is raised for any errors from the server.
1578
+ *
1579
+ * Row description data does not change while the iteration. All value retrieval
1580
+ * methods refer to only the current row. Result#ntuples returns +1+ while
1581
+ * the iteration and +0+ after all tuples were yielded.
1582
+ *
1583
+ * Example:
1584
+ * conn.send_query( "first SQL query; second SQL query" )
1585
+ * conn.set_single_row_mode
1586
+ * conn.get_result.stream_each do |row|
1587
+ * # do something with each received row of the first query
1588
+ * end
1589
+ * conn.get_result.stream_each do |row|
1590
+ * # do something with each received row of the second query
1591
+ * end
1592
+ * conn.get_result # => nil (no more results)
1593
+ */
1594
+ static VALUE
1595
+ pgresult_stream_each(VALUE self)
1596
+ {
1597
+ return pgresult_stream_any(self, yield_hash, NULL);
1598
+ }
1599
+
1138
1600
  /*
1139
1601
  * call-seq:
1140
1602
  * res.stream_each_row { |row| ... }
@@ -1148,87 +1610,118 @@ pgresult_stream_each(VALUE self)
1148
1610
  static VALUE
1149
1611
  pgresult_stream_each_row(VALUE self)
1150
1612
  {
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);
1613
+ return pgresult_stream_any(self, yield_array, NULL);
1614
+ }
1166
1615
 
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
- }
1616
+ /*
1617
+ * call-seq:
1618
+ * res.stream_each_tuple { |tuple| ... }
1619
+ *
1620
+ * Yields each row of the result set in single row mode.
1621
+ *
1622
+ * This method works equally to #stream_each , but yields a PG::Tuple object.
1623
+ */
1624
+ static VALUE
1625
+ pgresult_stream_each_tuple(VALUE self)
1626
+ {
1627
+ /* allocate VALUEs that are shared between all streamed tuples */
1628
+ ensure_init_for_tuple(self);
1177
1629
 
1178
- for ( row = 0; row < ntuples; row++ ) {
1179
- PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1180
- int field;
1630
+ return pgresult_stream_any(self, yield_tuple, NULL);
1631
+ }
1181
1632
 
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
- }
1633
+ /*
1634
+ * call-seq:
1635
+ * res.field_name_type = Symbol
1636
+ *
1637
+ * Set type of field names specific to this result.
1638
+ * It can be set to one of:
1639
+ * * +:string+ to use String based field names
1640
+ * * +:symbol+ to use Symbol based field names
1641
+ * * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
1642
+ *
1643
+ * The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
1644
+ *
1645
+ * This setting affects several result methods:
1646
+ * * keys of Hash returned by #[] , #each and #stream_each
1647
+ * * #fields
1648
+ * * #fname
1649
+ * * field names used by #tuple and #stream_each_tuple
1650
+ *
1651
+ * The type of field names can only be changed before any of the affected methods have been called.
1652
+ *
1653
+ */
1654
+ static VALUE
1655
+ pgresult_field_name_type_set(VALUE self, VALUE sym)
1656
+ {
1657
+ t_pg_result *this = pgresult_get_this(self);
1188
1658
 
1189
- if( !this->autoclear ){
1190
- PQclear( pgresult );
1191
- this->pgresult = NULL;
1192
- }
1659
+ rb_check_frozen(self);
1660
+ if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
1193
1661
 
1194
- pgresult = gvl_PQgetResult(pgconn);
1195
- if( pgresult == NULL )
1196
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1662
+ this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
1663
+ if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
1664
+ else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
1665
+ else if ( sym == sym_string );
1666
+ else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
1197
1667
 
1198
- if( nfields != PQnfields(pgresult) )
1199
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1668
+ return sym;
1669
+ }
1200
1670
 
1201
- this->pgresult = pgresult;
1671
+ /*
1672
+ * call-seq:
1673
+ * res.field_name_type -> Symbol
1674
+ *
1675
+ * Get type of field names.
1676
+ *
1677
+ * See description at #field_name_type=
1678
+ */
1679
+ static VALUE
1680
+ pgresult_field_name_type_get(VALUE self)
1681
+ {
1682
+ t_pg_result *this = pgresult_get_this(self);
1683
+ if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
1684
+ return sym_symbol;
1685
+ } else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
1686
+ return sym_static_symbol;
1687
+ } else {
1688
+ return sym_string;
1202
1689
  }
1203
-
1204
- /* never reached */
1205
- return self;
1206
1690
  }
1207
- #endif
1208
-
1209
1691
 
1210
1692
  void
1211
- init_pg_result()
1693
+ init_pg_result(void)
1212
1694
  {
1695
+ sym_string = ID2SYM(rb_intern("string"));
1696
+ sym_symbol = ID2SYM(rb_intern("symbol"));
1697
+ sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
1698
+
1213
1699
  rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
1214
- rb_define_alloc_func( rb_cPGresult, pgresult_s_allocate );
1700
+ rb_undef_alloc_func(rb_cPGresult);
1215
1701
  rb_include_module(rb_cPGresult, rb_mEnumerable);
1216
1702
  rb_include_module(rb_cPGresult, rb_mPGconstants);
1217
1703
 
1218
1704
  /****** PG::Result INSTANCE METHODS: libpq ******/
1219
1705
  rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
1220
- rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
1706
+ rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, -1);
1707
+ rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
1221
1708
  rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
1222
1709
  rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
1710
+ #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
1711
+ rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
1712
+ rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
1713
+ #endif
1223
1714
  rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
1224
1715
  rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
1225
1716
  rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
1717
+ rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
1226
1718
  rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
1227
1719
  rb_define_alias (rb_cPGresult, "check_result", "check");
1228
1720
  rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
1229
1721
  rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
1230
1722
  rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
1231
1723
  rb_define_alias(rb_cPGresult, "num_fields", "nfields");
1724
+ rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
1232
1725
  rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
1233
1726
  rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
1234
1727
  rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
@@ -1255,17 +1748,19 @@ init_pg_result()
1255
1748
  rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
1256
1749
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
1257
1750
  rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
1751
+ rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
1752
+ rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
1258
1753
  rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
1259
1754
  rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
1260
1755
 
1261
1756
  rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1262
1757
  rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
1263
1758
 
1264
- #ifdef HAVE_PQSETSINGLEROWMODE
1265
1759
  /****** PG::Result INSTANCE METHODS: streaming ******/
1266
1760
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1267
1761
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1268
- #endif
1269
- }
1270
-
1762
+ rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1271
1763
 
1764
+ rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
1765
+ rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
1766
+ }