pg 1.0.0-x64-mingw32 → 1.1.0.pre20180730144600-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +0 -6595
  5. data/History.rdoc +52 -0
  6. data/README.rdoc +11 -0
  7. data/Rakefile +2 -2
  8. data/Rakefile.cross +4 -4
  9. data/ext/errorcodes.rb +1 -1
  10. data/ext/extconf.rb +2 -0
  11. data/ext/pg.c +3 -2
  12. data/ext/pg.h +33 -5
  13. data/ext/pg_binary_decoder.c +69 -6
  14. data/ext/pg_binary_encoder.c +1 -1
  15. data/ext/pg_coder.c +52 -3
  16. data/ext/pg_connection.c +290 -103
  17. data/ext/pg_copy_coder.c +10 -5
  18. data/ext/pg_result.c +339 -113
  19. data/ext/pg_text_decoder.c +597 -37
  20. data/ext/pg_text_encoder.c +1 -1
  21. data/ext/pg_tuple.c +540 -0
  22. data/ext/pg_type_map.c +1 -1
  23. data/ext/pg_type_map_all_strings.c +1 -1
  24. data/ext/pg_type_map_by_class.c +1 -1
  25. data/ext/pg_type_map_by_column.c +1 -1
  26. data/ext/pg_type_map_by_mri_type.c +1 -1
  27. data/ext/pg_type_map_by_oid.c +1 -1
  28. data/ext/pg_type_map_in_ruby.c +1 -1
  29. data/ext/util.c +6 -6
  30. data/ext/util.h +2 -2
  31. data/lib/2.0/pg_ext.so +0 -0
  32. data/lib/2.1/pg_ext.so +0 -0
  33. data/lib/2.2/pg_ext.so +0 -0
  34. data/lib/2.3/pg_ext.so +0 -0
  35. data/lib/2.4/pg_ext.so +0 -0
  36. data/lib/2.5/pg_ext.so +0 -0
  37. data/lib/libpq.dll +0 -0
  38. data/lib/pg.rb +5 -3
  39. data/lib/pg/basic_type_mapping.rb +40 -7
  40. data/lib/pg/coder.rb +1 -1
  41. data/lib/pg/connection.rb +20 -1
  42. data/lib/pg/constants.rb +1 -1
  43. data/lib/pg/exceptions.rb +1 -1
  44. data/lib/pg/result.rb +1 -1
  45. data/lib/pg/text_decoder.rb +19 -23
  46. data/lib/pg/text_encoder.rb +35 -1
  47. data/lib/pg/type_map_by_column.rb +1 -1
  48. data/spec/helpers.rb +39 -7
  49. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  50. data/spec/pg/connection_spec.rb +116 -77
  51. data/spec/pg/result_spec.rb +46 -11
  52. data/spec/pg/type_map_by_class_spec.rb +1 -1
  53. data/spec/pg/type_map_by_column_spec.rb +1 -1
  54. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  55. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  56. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  57. data/spec/pg/type_map_spec.rb +1 -1
  58. data/spec/pg/type_spec.rb +177 -11
  59. data/spec/pg_spec.rb +1 -1
  60. metadata +25 -29
  61. metadata.gz.sig +0 -0
@@ -354,7 +354,7 @@ GetDecimalFromHex(char hex)
354
354
  * src/backend/commands/copy.c
355
355
  */
356
356
  static VALUE
357
- pg_text_dec_copy_row(t_pg_coder *conv, char *input_line, int len, int _tuple, int _field, int enc_idx)
357
+ pg_text_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tuple, int _field, int enc_idx)
358
358
  {
359
359
  t_pg_copycoder *this = (t_pg_copycoder *)conv;
360
360
 
@@ -368,8 +368,8 @@ pg_text_dec_copy_row(t_pg_coder *conv, char *input_line, int len, int _tuple, in
368
368
  int fieldno;
369
369
  int expected_fields;
370
370
  char *output_ptr;
371
- char *cur_ptr;
372
- char *line_end_ptr;
371
+ const char *cur_ptr;
372
+ const char *line_end_ptr;
373
373
  char *end_capa_ptr;
374
374
  t_typemap *p_typemap;
375
375
 
@@ -392,8 +392,8 @@ pg_text_dec_copy_row(t_pg_coder *conv, char *input_line, int len, int _tuple, in
392
392
  for (;;)
393
393
  {
394
394
  int found_delim = 0;
395
- char *start_ptr;
396
- char *end_ptr;
395
+ const char *start_ptr;
396
+ const char *end_ptr;
397
397
  int input_len;
398
398
 
399
399
  /* Remember start of field on input side */
@@ -585,7 +585,12 @@ init_pg_copycoder()
585
585
  /* rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" ); */
586
586
  /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "CopyRow", rb_cPG_CopyEncoder ); */
587
587
  pg_define_coder( "CopyRow", pg_text_enc_copy_row, rb_cPG_CopyEncoder, rb_mPG_TextEncoder );
588
+ rb_include_module( rb_cPG_CopyEncoder, rb_mPG_BinaryFormatting );
589
+
588
590
  /* rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" ); */
589
591
  /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "CopyRow", rb_cPG_CopyDecoder ); */
590
592
  pg_define_coder( "CopyRow", pg_text_dec_copy_row, rb_cPG_CopyDecoder, rb_mPG_TextDecoder );
593
+ /* Although CopyRow is a text decoder, data can contain zero bytes and are not zero terminated.
594
+ * They are handled like binaries. So format is set to 1 (binary). */
595
+ rb_include_module( rb_cPG_CopyDecoder, rb_mPG_BinaryFormatting );
591
596
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v 7c5d9608349f 2018/01/03 16:11:52 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -16,6 +16,102 @@ static t_pg_result *pgresult_get_this( VALUE );
16
16
  static t_pg_result *pgresult_get_this_safe( VALUE );
17
17
 
18
18
 
19
+ #define PGRESULT_DATA_BLOCKSIZE 2048
20
+ typedef struct pgresAttValue
21
+ {
22
+ int len; /* length in bytes of the value */
23
+ char *value; /* actual value, plus terminating zero byte */
24
+ } PGresAttValue;
25
+
26
+
27
+ static int
28
+ count_leading_zero_bits(unsigned int x)
29
+ {
30
+ #if defined(__GNUC__) || defined(__clang__)
31
+ return __builtin_clz(x);
32
+ #elif defined(_MSC_VER)
33
+ DWORD r = 0;
34
+ _BitScanForward(&r, x);
35
+ return (int)r;
36
+ #else
37
+ unsigned int a;
38
+ for(a=0; a < sizeof(unsigned int) * 8; a++){
39
+ if( x & (1 << (sizeof(unsigned int) * 8 - 1))) return a;
40
+ x <<= 1;
41
+ }
42
+ return a;
43
+ #endif
44
+ }
45
+
46
+ static ssize_t
47
+ pgresult_approx_size(PGresult *result)
48
+ {
49
+ int num_fields = PQnfields(result);
50
+ ssize_t size = 0;
51
+
52
+ if( num_fields > 0 ){
53
+ int num_tuples = PQntuples(result);
54
+
55
+ if( num_tuples > 0 ){
56
+ int pos;
57
+
58
+ /* 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.
59
+ * 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.
60
+ * The given algorithm has no real scientific background, but is made for speed and typical table layouts.
61
+ */
62
+ int num_samples =
63
+ (num_fields < 9 ? num_fields : 39 - count_leading_zero_bits(num_fields-8)) *
64
+ (num_tuples < 8 ? 1 : 30 - count_leading_zero_bits(num_tuples));
65
+
66
+ /* start with scanning very last fields, since they are most probably in the cache */
67
+ for( pos = 0; pos < (num_samples+1)/2; pos++ ){
68
+ size += PQgetlength(result, num_tuples - 1 - (pos / num_fields), num_fields - 1 - (pos % num_fields));
69
+ }
70
+ /* scan the very first fields */
71
+ for( pos = 0; pos < num_samples/2; pos++ ){
72
+ size += PQgetlength(result, pos / num_fields, pos % num_fields);
73
+ }
74
+ /* extrapolate sample size to whole result set */
75
+ size = size * num_tuples * num_fields / num_samples;
76
+ }
77
+
78
+ /* count metadata */
79
+ size += num_fields * (
80
+ sizeof(PGresAttDesc) + /* column description */
81
+ num_tuples * (
82
+ sizeof(PGresAttValue) + 1 /* ptr, len and zero termination of each value */
83
+ )
84
+ );
85
+
86
+ /* Account free space due to libpq's default block size */
87
+ size = (size + PGRESULT_DATA_BLOCKSIZE - 1) / PGRESULT_DATA_BLOCKSIZE * PGRESULT_DATA_BLOCKSIZE;
88
+
89
+ /* count tuple pointers */
90
+ size += sizeof(void*) * ((num_tuples + 128 - 1) / 128 * 128);
91
+ }
92
+
93
+ size += 216; /* add PGresult size */
94
+
95
+ return size;
96
+ }
97
+
98
+ static void
99
+ pgresult_clear( t_pg_result *this )
100
+ {
101
+ if( this->pgresult && !this->autoclear ){
102
+ PQclear(this->pgresult);
103
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
104
+ rb_gc_adjust_memory_usage(-this->result_size);
105
+ #endif
106
+ }
107
+ this->pgresult = NULL;
108
+ }
109
+
110
+ static size_t
111
+ pgresult_memsize( t_pg_result *this )
112
+ {
113
+ return this->result_size;
114
+ }
19
115
 
20
116
  /*
21
117
  * Global functions
@@ -24,23 +120,23 @@ static t_pg_result *pgresult_get_this_safe( VALUE );
24
120
  /*
25
121
  * Result constructor
26
122
  */
27
- VALUE
28
- pg_new_result(PGresult *result, VALUE rb_pgconn)
123
+ static VALUE
124
+ pg_new_result2(PGresult *result, VALUE rb_pgconn)
29
125
  {
30
126
  int nfields = result ? PQnfields(result) : 0;
31
127
  VALUE self = pgresult_s_allocate( rb_cPGresult );
32
128
  t_pg_result *this;
33
129
 
34
130
  this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
35
- DATA_PTR(self) = this;
131
+ RTYPEDDATA_DATA(self) = this;
36
132
 
37
133
  this->pgresult = result;
38
134
  this->connection = rb_pgconn;
39
135
  this->typemap = pg_typemap_all_strings;
40
136
  this->p_typemap = DATA_PTR( this->typemap );
41
- this->autoclear = 0;
42
137
  this->nfields = -1;
43
138
  this->tuple_hash = Qnil;
139
+ this->field_map = Qnil;
44
140
 
45
141
  PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
46
142
 
@@ -58,11 +154,37 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
58
154
  return self;
59
155
  }
60
156
 
157
+ VALUE
158
+ pg_new_result(PGresult *result, VALUE rb_pgconn)
159
+ {
160
+ VALUE self = pg_new_result2(result, rb_pgconn);
161
+ t_pg_result *this = pgresult_get_this(self);
162
+ t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
163
+
164
+ this->autoclear = 0;
165
+
166
+ if( p_conn->guess_result_memsize ){
167
+ /* Approximate size of underlying pgresult memory storage and account to ruby GC */
168
+ this->result_size = pgresult_approx_size(result);
169
+
170
+ #ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
171
+ rb_gc_adjust_memory_usage(this->result_size);
172
+ #endif
173
+ }
174
+
175
+ return self;
176
+ }
177
+
61
178
  VALUE
62
179
  pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
63
180
  {
64
- VALUE self = pg_new_result(result, rb_pgconn);
181
+ VALUE self = pg_new_result2(result, rb_pgconn);
65
182
  t_pg_result *this = pgresult_get_this(self);
183
+
184
+ /* Autocleared results are freed implicit instead of by PQclear().
185
+ * So it's not very useful to be accounted by ruby GC.
186
+ */
187
+ this->result_size = 0;
66
188
  this->autoclear = 1;
67
189
  return self;
68
190
  }
@@ -131,7 +253,13 @@ pg_result_check( VALUE self )
131
253
  * call-seq:
132
254
  * res.clear() -> nil
133
255
  *
134
- * Clears the PG::Result object as the result of the query.
256
+ * Clears the PG::Result object as the result of a query.
257
+ * This frees all underlying memory consumed by the result object.
258
+ * Afterwards access to result methods raises PG::Error "result has been cleared".
259
+ *
260
+ * Explicit calling #clear can lead to better memory performance, but is not generally necessary.
261
+ * Special care must be taken when PG::Tuple objects are used.
262
+ * In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
135
263
  *
136
264
  * If PG::Result#autoclear? is true then the result is marked as cleared
137
265
  * and the underlying C struct will be cleared automatically by libpq.
@@ -141,9 +269,7 @@ VALUE
141
269
  pg_result_clear(VALUE self)
142
270
  {
143
271
  t_pg_result *this = pgresult_get_this(self);
144
- if( !this->autoclear )
145
- PQclear(pgresult_get(self));
146
- this->pgresult = NULL;
272
+ pgresult_clear( this );
147
273
  return Qnil;
148
274
  }
149
275
 
@@ -191,6 +317,7 @@ pgresult_gc_mark( t_pg_result *this )
191
317
  rb_gc_mark( this->connection );
192
318
  rb_gc_mark( this->typemap );
193
319
  rb_gc_mark( this->tuple_hash );
320
+ rb_gc_mark( this->field_map );
194
321
 
195
322
  for( i=0; i < this->nfields; i++ ){
196
323
  rb_gc_mark( this->fnames[i] );
@@ -204,8 +331,7 @@ static void
204
331
  pgresult_gc_free( t_pg_result *this )
205
332
  {
206
333
  if( !this ) return;
207
- if(this->pgresult != NULL && !this->autoclear)
208
- PQclear(this->pgresult);
334
+ pgresult_clear( this );
209
335
 
210
336
  xfree(this);
211
337
  }
@@ -239,6 +365,20 @@ pgresult_get(VALUE self)
239
365
  return this->pgresult;
240
366
  }
241
367
 
368
+
369
+ static const rb_data_type_t pgresult_type = {
370
+ "pg",
371
+ {
372
+ (void (*)(void*))pgresult_gc_mark,
373
+ (void (*)(void*))pgresult_gc_free,
374
+ (size_t (*)(const void *))pgresult_memsize,
375
+ },
376
+ 0, 0,
377
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
378
+ RUBY_TYPED_FREE_IMMEDIATELY,
379
+ #endif
380
+ };
381
+
242
382
  /*
243
383
  * Document-method: allocate
244
384
  *
@@ -248,7 +388,7 @@ pgresult_get(VALUE self)
248
388
  static VALUE
249
389
  pgresult_s_allocate( VALUE klass )
250
390
  {
251
- VALUE self = Data_Wrap_Struct( klass, pgresult_gc_mark, pgresult_gc_free, NULL );
391
+ VALUE self = TypedData_Wrap_Struct( klass, &pgresult_type, NULL );
252
392
 
253
393
  return self;
254
394
  }
@@ -279,8 +419,9 @@ static void pgresult_init_fnames(VALUE self)
279
419
  *
280
420
  * The class to represent the query result tuples (rows).
281
421
  * An instance of this class is created as the result of every query.
282
- * You may need to invoke the #clear method of the instance when finished with
283
- * the result for better memory performance.
422
+ *
423
+ * Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
424
+ * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
284
425
  *
285
426
  * Example:
286
427
  * require 'pg'
@@ -423,7 +564,7 @@ pgresult_ntuples(VALUE self)
423
564
  static VALUE
424
565
  pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
425
566
  {
426
- return pgresult_ntuples(self);
567
+ return pgresult_ntuples(self);
427
568
  }
428
569
 
429
570
  /*
@@ -967,6 +1108,78 @@ pgresult_field_values( VALUE self, VALUE field )
967
1108
  }
968
1109
 
969
1110
 
1111
+ /*
1112
+ * call-seq:
1113
+ * res.tuple_values( n ) -> array
1114
+ *
1115
+ * Returns an Array of the field values from the nth row of the result.
1116
+ *
1117
+ */
1118
+ static VALUE
1119
+ pgresult_tuple_values(VALUE self, VALUE index)
1120
+ {
1121
+ int tuple_num = NUM2INT( index );
1122
+ t_pg_result *this;
1123
+ int field;
1124
+ int num_tuples;
1125
+ int num_fields;
1126
+
1127
+ this = pgresult_get_this_safe(self);
1128
+ num_tuples = PQntuples(this->pgresult);
1129
+ num_fields = PQnfields(this->pgresult);
1130
+
1131
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1132
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1133
+
1134
+ {
1135
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
1136
+
1137
+ /* populate the row */
1138
+ for ( field = 0; field < num_fields; field++ ) {
1139
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
1140
+ }
1141
+ return rb_ary_new4( num_fields, row_values );
1142
+ }
1143
+ }
1144
+
1145
+ /*
1146
+ * call-seq:
1147
+ * res.tuple( n ) -> PG::Tuple
1148
+ *
1149
+ * Returns a PG::Tuple from the nth row of the result.
1150
+ *
1151
+ */
1152
+ static VALUE
1153
+ pgresult_tuple(VALUE self, VALUE index)
1154
+ {
1155
+ int tuple_num = NUM2INT( index );
1156
+ t_pg_result *this;
1157
+ int num_tuples;
1158
+
1159
+ this = pgresult_get_this_safe(self);
1160
+ num_tuples = PQntuples(this->pgresult);
1161
+
1162
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1163
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1164
+
1165
+ if( this->field_map == Qnil ){
1166
+ int i;
1167
+ VALUE field_map = rb_hash_new();
1168
+
1169
+ if( this->nfields == -1 )
1170
+ pgresult_init_fnames( self );
1171
+
1172
+ for( i = 0; i < this->nfields; i++ ){
1173
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1174
+ }
1175
+ rb_obj_freeze(field_map);
1176
+ this->field_map = field_map;
1177
+ }
1178
+
1179
+ return pg_tuple_new(self, tuple_num);
1180
+ }
1181
+
1182
+
970
1183
  /*
971
1184
  * call-seq:
972
1185
  * res.each{ |tuple| ... }
@@ -1052,41 +1265,57 @@ pgresult_type_map_get(VALUE self)
1052
1265
  return this->typemap;
1053
1266
  }
1054
1267
 
1055
- /*
1056
- * call-seq:
1057
- * res.stream_each{ |tuple| ... }
1058
- *
1059
- * Invokes block for each tuple in the result set in single row mode.
1060
- *
1061
- * This is a convenience method for retrieving all result tuples
1062
- * as they are transferred. It is an alternative to repeated calls of
1063
- * PG::Connection#get_result , but given that it avoids the overhead of
1064
- * wrapping each row into a dedicated result object, it delivers data in nearly
1065
- * the same speed as with ordinary results.
1066
- *
1067
- * The result must be in status PGRES_SINGLE_TUPLE.
1068
- * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1069
- * A PG::Error is raised for any errors from the server.
1070
- *
1071
- * Row description data does not change while the iteration. All value retrieval
1072
- * methods refer to only the current row. Result#ntuples returns +1+ while
1073
- * the iteration and +0+ after all tuples were yielded.
1074
- *
1075
- * Example:
1076
- * conn.send_query( "first SQL query; second SQL query" )
1077
- * conn.set_single_row_mode
1078
- * conn.get_result.stream_each do |row|
1079
- * # do something with the received row of the first query
1080
- * end
1081
- * conn.get_result.stream_each do |row|
1082
- * # do something with the received row of the second query
1083
- * end
1084
- * conn.get_result # => nil (no more results)
1085
- *
1086
- * Available since PostgreSQL-9.2
1087
- */
1268
+
1269
+ static void
1270
+ yield_hash(VALUE self, int ntuples, int nfields)
1271
+ {
1272
+ int tuple_num;
1273
+ t_pg_result *this = pgresult_get_this(self);
1274
+ UNUSED(nfields);
1275
+
1276
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1277
+ rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1278
+ }
1279
+
1280
+ pgresult_clear( this );
1281
+ }
1282
+
1283
+ static void
1284
+ yield_array(VALUE self, int ntuples, int nfields)
1285
+ {
1286
+ int row;
1287
+ t_pg_result *this = pgresult_get_this(self);
1288
+
1289
+ for ( row = 0; row < ntuples; row++ ) {
1290
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1291
+ int field;
1292
+
1293
+ /* populate the row */
1294
+ for ( field = 0; field < nfields; field++ ) {
1295
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1296
+ }
1297
+ rb_yield( rb_ary_new4( nfields, row_values ));
1298
+ }
1299
+
1300
+ pgresult_clear( this );
1301
+ }
1302
+
1303
+ static void
1304
+ yield_tuple(VALUE self, int ntuples, int nfields)
1305
+ {
1306
+ int tuple_num;
1307
+ t_pg_result *this = pgresult_get_this(self);
1308
+ VALUE result = pg_new_result(this->pgresult, this->connection);
1309
+ UNUSED(nfields);
1310
+
1311
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1312
+ VALUE tuple = pgresult_tuple(result, INT2FIX(tuple_num));
1313
+ rb_yield( tuple );
1314
+ }
1315
+ }
1316
+
1088
1317
  static VALUE
1089
- pgresult_stream_each(VALUE self)
1318
+ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1090
1319
  {
1091
1320
  t_pg_result *this;
1092
1321
  int nfields;
@@ -1101,7 +1330,6 @@ pgresult_stream_each(VALUE self)
1101
1330
  nfields = PQnfields(pgresult);
1102
1331
 
1103
1332
  for(;;){
1104
- int tuple_num;
1105
1333
  int ntuples = PQntuples(pgresult);
1106
1334
 
1107
1335
  switch( PQresultStatus(pgresult) ){
@@ -1115,14 +1343,7 @@ pgresult_stream_each(VALUE self)
1115
1343
  pg_result_check( self );
1116
1344
  }
1117
1345
 
1118
- for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1119
- rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1120
- }
1121
-
1122
- if( !this->autoclear ){
1123
- PQclear( pgresult );
1124
- this->pgresult = NULL;
1125
- }
1346
+ yielder( self, ntuples, nfields );
1126
1347
 
1127
1348
  pgresult = gvl_PQgetResult(pgconn);
1128
1349
  if( pgresult == NULL )
@@ -1138,6 +1359,46 @@ pgresult_stream_each(VALUE self)
1138
1359
  return self;
1139
1360
  }
1140
1361
 
1362
+
1363
+ /*
1364
+ * call-seq:
1365
+ * res.stream_each{ |tuple| ... }
1366
+ *
1367
+ * Invokes block for each tuple in the result set in single row mode.
1368
+ *
1369
+ * This is a convenience method for retrieving all result tuples
1370
+ * as they are transferred. It is an alternative to repeated calls of
1371
+ * PG::Connection#get_result , but given that it avoids the overhead of
1372
+ * wrapping each row into a dedicated result object, it delivers data in nearly
1373
+ * the same speed as with ordinary results.
1374
+ *
1375
+ * The base result must be in status PGRES_SINGLE_TUPLE.
1376
+ * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1377
+ * A PG::Error is raised for any errors from the server.
1378
+ *
1379
+ * Row description data does not change while the iteration. All value retrieval
1380
+ * methods refer to only the current row. Result#ntuples returns +1+ while
1381
+ * the iteration and +0+ after all tuples were yielded.
1382
+ *
1383
+ * Example:
1384
+ * conn.send_query( "first SQL query; second SQL query" )
1385
+ * conn.set_single_row_mode
1386
+ * conn.get_result.stream_each do |row|
1387
+ * # do something with each received row of the first query
1388
+ * end
1389
+ * conn.get_result.stream_each do |row|
1390
+ * # do something with each received row of the second query
1391
+ * end
1392
+ * conn.get_result # => nil (no more results)
1393
+ *
1394
+ * Available since PostgreSQL-9.2
1395
+ */
1396
+ static VALUE
1397
+ pgresult_stream_each(VALUE self)
1398
+ {
1399
+ return pgresult_stream_any(self, yield_hash);
1400
+ }
1401
+
1141
1402
  /*
1142
1403
  * call-seq:
1143
1404
  * res.stream_each_row { |row| ... }
@@ -1153,61 +1414,23 @@ pgresult_stream_each(VALUE self)
1153
1414
  static VALUE
1154
1415
  pgresult_stream_each_row(VALUE self)
1155
1416
  {
1156
- t_pg_result *this;
1157
- int row;
1158
- int nfields;
1159
- PGconn *pgconn;
1160
- PGresult *pgresult;
1161
-
1162
- RETURN_ENUMERATOR(self, 0, NULL);
1163
-
1164
- this = pgresult_get_this_safe(self);
1165
- pgconn = pg_get_pgconn(this->connection);
1166
- pgresult = this->pgresult;
1167
- nfields = PQnfields(pgresult);
1168
-
1169
- for(;;){
1170
- int ntuples = PQntuples(pgresult);
1171
-
1172
- switch( PQresultStatus(pgresult) ){
1173
- case PGRES_TUPLES_OK:
1174
- if( ntuples == 0 )
1175
- return self;
1176
- rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1177
- case PGRES_SINGLE_TUPLE:
1178
- break;
1179
- default:
1180
- pg_result_check( self );
1181
- }
1182
-
1183
- for ( row = 0; row < ntuples; row++ ) {
1184
- PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1185
- int field;
1186
-
1187
- /* populate the row */
1188
- for ( field = 0; field < nfields; field++ ) {
1189
- row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1190
- }
1191
- rb_yield( rb_ary_new4( nfields, row_values ));
1192
- }
1193
-
1194
- if( !this->autoclear ){
1195
- PQclear( pgresult );
1196
- this->pgresult = NULL;
1197
- }
1198
-
1199
- pgresult = gvl_PQgetResult(pgconn);
1200
- if( pgresult == NULL )
1201
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1202
-
1203
- if( nfields != PQnfields(pgresult) )
1204
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1205
-
1206
- this->pgresult = pgresult;
1207
- }
1417
+ return pgresult_stream_any(self, yield_array);
1418
+ }
1208
1419
 
1209
- /* never reached */
1210
- return self;
1420
+ /*
1421
+ * call-seq:
1422
+ * res.stream_each_tuple { |tuple| ... }
1423
+ *
1424
+ * Yields each row of the result set in single row mode.
1425
+ *
1426
+ * This method works equally to #stream_each , but yields a PG::Tuple object.
1427
+ *
1428
+ * Available since PostgreSQL-9.2
1429
+ */
1430
+ static VALUE
1431
+ pgresult_stream_each_tuple(VALUE self)
1432
+ {
1433
+ return pgresult_stream_any(self, yield_tuple);
1211
1434
  }
1212
1435
 
1213
1436
 
@@ -1259,6 +1482,8 @@ init_pg_result()
1259
1482
  rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
1260
1483
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
1261
1484
  rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
1485
+ rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
1486
+ rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
1262
1487
  rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
1263
1488
  rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
1264
1489
 
@@ -1268,6 +1493,7 @@ init_pg_result()
1268
1493
  /****** PG::Result INSTANCE METHODS: streaming ******/
1269
1494
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1270
1495
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1496
+ rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1271
1497
  }
1272
1498
 
1273
1499