pg 0.21.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/History.rdoc +98 -0
  5. data/Manifest.txt +5 -1
  6. data/README.rdoc +14 -4
  7. data/Rakefile +4 -5
  8. data/Rakefile.cross +17 -21
  9. data/ext/errorcodes.def +12 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +4 -1
  12. data/ext/extconf.rb +14 -32
  13. data/ext/gvl_wrappers.c +4 -0
  14. data/ext/gvl_wrappers.h +23 -39
  15. data/ext/pg.c +23 -50
  16. data/ext/pg.h +51 -81
  17. data/ext/pg_binary_decoder.c +73 -6
  18. data/ext/pg_coder.c +52 -3
  19. data/ext/pg_connection.c +369 -219
  20. data/ext/pg_copy_coder.c +10 -5
  21. data/ext/pg_result.c +343 -119
  22. data/ext/pg_text_decoder.c +597 -37
  23. data/ext/pg_text_encoder.c +6 -7
  24. data/ext/pg_tuple.c +541 -0
  25. data/ext/util.c +6 -6
  26. data/ext/util.h +2 -2
  27. data/lib/pg.rb +5 -7
  28. data/lib/pg/basic_type_mapping.rb +40 -7
  29. data/lib/pg/binary_decoder.rb +22 -0
  30. data/lib/pg/coder.rb +1 -1
  31. data/lib/pg/connection.rb +27 -3
  32. data/lib/pg/constants.rb +1 -1
  33. data/lib/pg/exceptions.rb +1 -1
  34. data/lib/pg/result.rb +1 -1
  35. data/lib/pg/text_decoder.rb +19 -23
  36. data/lib/pg/text_encoder.rb +35 -1
  37. data/lib/pg/tuple.rb +30 -0
  38. data/lib/pg/type_map_by_column.rb +1 -1
  39. data/spec/helpers.rb +49 -21
  40. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  41. data/spec/pg/connection_spec.rb +473 -277
  42. data/spec/pg/connection_sync_spec.rb +41 -0
  43. data/spec/pg/result_spec.rb +48 -13
  44. data/spec/pg/tuple_spec.rb +280 -0
  45. data/spec/pg/type_map_by_class_spec.rb +1 -1
  46. data/spec/pg/type_map_by_column_spec.rb +1 -1
  47. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  48. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  49. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  50. data/spec/pg/type_map_spec.rb +1 -1
  51. data/spec/pg/type_spec.rb +184 -12
  52. data/spec/pg_spec.rb +2 -2
  53. metadata +37 -33
  54. metadata.gz.sig +0 -0
  55. data/lib/pg/deprecated_constants.rb +0 -21
@@ -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 c4a1abc36c47 2017/06/02 01:00:09 ged $
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
  }
@@ -92,12 +214,8 @@ pg_result_check( VALUE self )
92
214
  case PGRES_TUPLES_OK:
93
215
  case PGRES_COPY_OUT:
94
216
  case PGRES_COPY_IN:
95
- #ifdef HAVE_CONST_PGRES_COPY_BOTH
96
217
  case PGRES_COPY_BOTH:
97
- #endif
98
- #ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
99
218
  case PGRES_SINGLE_TUPLE:
100
- #endif
101
219
  case PGRES_EMPTY_QUERY:
102
220
  case PGRES_COMMAND_OK:
103
221
  return self;
@@ -135,7 +253,13 @@ pg_result_check( VALUE self )
135
253
  * call-seq:
136
254
  * res.clear() -> nil
137
255
  *
138
- * 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.
139
263
  *
140
264
  * If PG::Result#autoclear? is true then the result is marked as cleared
141
265
  * and the underlying C struct will be cleared automatically by libpq.
@@ -145,9 +269,7 @@ VALUE
145
269
  pg_result_clear(VALUE self)
146
270
  {
147
271
  t_pg_result *this = pgresult_get_this(self);
148
- if( !this->autoclear )
149
- PQclear(pgresult_get(self));
150
- this->pgresult = NULL;
272
+ pgresult_clear( this );
151
273
  return Qnil;
152
274
  }
153
275
 
@@ -195,6 +317,7 @@ pgresult_gc_mark( t_pg_result *this )
195
317
  rb_gc_mark( this->connection );
196
318
  rb_gc_mark( this->typemap );
197
319
  rb_gc_mark( this->tuple_hash );
320
+ rb_gc_mark( this->field_map );
198
321
 
199
322
  for( i=0; i < this->nfields; i++ ){
200
323
  rb_gc_mark( this->fnames[i] );
@@ -208,8 +331,7 @@ static void
208
331
  pgresult_gc_free( t_pg_result *this )
209
332
  {
210
333
  if( !this ) return;
211
- if(this->pgresult != NULL && !this->autoclear)
212
- PQclear(this->pgresult);
334
+ pgresult_clear( this );
213
335
 
214
336
  xfree(this);
215
337
  }
@@ -243,6 +365,20 @@ pgresult_get(VALUE self)
243
365
  return this->pgresult;
244
366
  }
245
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
+
246
382
  /*
247
383
  * Document-method: allocate
248
384
  *
@@ -252,7 +388,7 @@ pgresult_get(VALUE self)
252
388
  static VALUE
253
389
  pgresult_s_allocate( VALUE klass )
254
390
  {
255
- VALUE self = Data_Wrap_Struct( klass, pgresult_gc_mark, pgresult_gc_free, NULL );
391
+ VALUE self = TypedData_Wrap_Struct( klass, &pgresult_type, NULL );
256
392
 
257
393
  return self;
258
394
  }
@@ -283,8 +419,11 @@ static void pgresult_init_fnames(VALUE self)
283
419
  *
284
420
  * The class to represent the query result tuples (rows).
285
421
  * 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.
422
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
423
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
424
+ *
425
+ * Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
426
+ * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
288
427
  *
289
428
  * Example:
290
429
  * require 'pg'
@@ -427,7 +566,7 @@ pgresult_ntuples(VALUE self)
427
566
  static VALUE
428
567
  pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
429
568
  {
430
- return pgresult_ntuples(self);
569
+ return pgresult_ntuples(self);
431
570
  }
432
571
 
433
572
  /*
@@ -971,6 +1110,78 @@ pgresult_field_values( VALUE self, VALUE field )
971
1110
  }
972
1111
 
973
1112
 
1113
+ /*
1114
+ * call-seq:
1115
+ * res.tuple_values( n ) -> array
1116
+ *
1117
+ * Returns an Array of the field values from the nth row of the result.
1118
+ *
1119
+ */
1120
+ static VALUE
1121
+ pgresult_tuple_values(VALUE self, VALUE index)
1122
+ {
1123
+ int tuple_num = NUM2INT( index );
1124
+ t_pg_result *this;
1125
+ int field;
1126
+ int num_tuples;
1127
+ int num_fields;
1128
+
1129
+ this = pgresult_get_this_safe(self);
1130
+ num_tuples = PQntuples(this->pgresult);
1131
+ num_fields = PQnfields(this->pgresult);
1132
+
1133
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1134
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1135
+
1136
+ {
1137
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
1138
+
1139
+ /* populate the row */
1140
+ for ( field = 0; field < num_fields; field++ ) {
1141
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
1142
+ }
1143
+ return rb_ary_new4( num_fields, row_values );
1144
+ }
1145
+ }
1146
+
1147
+ /*
1148
+ * call-seq:
1149
+ * res.tuple( n ) -> PG::Tuple
1150
+ *
1151
+ * Returns a PG::Tuple from the nth row of the result.
1152
+ *
1153
+ */
1154
+ static VALUE
1155
+ pgresult_tuple(VALUE self, VALUE index)
1156
+ {
1157
+ int tuple_num = NUM2INT( index );
1158
+ t_pg_result *this;
1159
+ int num_tuples;
1160
+
1161
+ this = pgresult_get_this_safe(self);
1162
+ num_tuples = PQntuples(this->pgresult);
1163
+
1164
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
1165
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
1166
+
1167
+ if( this->field_map == Qnil ){
1168
+ int i;
1169
+ VALUE field_map = rb_hash_new();
1170
+
1171
+ if( this->nfields == -1 )
1172
+ pgresult_init_fnames( self );
1173
+
1174
+ for( i = 0; i < this->nfields; i++ ){
1175
+ rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
1176
+ }
1177
+ rb_obj_freeze(field_map);
1178
+ this->field_map = field_map;
1179
+ }
1180
+
1181
+ return pg_tuple_new(self, tuple_num);
1182
+ }
1183
+
1184
+
974
1185
  /*
975
1186
  * call-seq:
976
1187
  * res.each{ |tuple| ... }
@@ -1056,40 +1267,57 @@ pgresult_type_map_get(VALUE self)
1056
1267
  return this->typemap;
1057
1268
  }
1058
1269
 
1059
- #ifdef HAVE_PQSETSINGLEROWMODE
1060
- /*
1061
- * call-seq:
1062
- * res.stream_each{ |tuple| ... }
1063
- *
1064
- * Invokes block for each tuple in the result set in single row mode.
1065
- *
1066
- * This is a convenience method for retrieving all result tuples
1067
- * as they are transferred. It is an alternative to repeated calls of
1068
- * PG::Connection#get_result , but given that it avoids the overhead of
1069
- * wrapping each row into a dedicated result object, it delivers data in nearly
1070
- * the same speed as with ordinary results.
1071
- *
1072
- * The result must be in status PGRES_SINGLE_TUPLE.
1073
- * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1074
- * A PG::Error is raised for any errors from the server.
1075
- *
1076
- * Row description data does not change while the iteration. All value retrieval
1077
- * methods refer to only the current row. Result#ntuples returns +1+ while
1078
- * the iteration and +0+ after all tuples were yielded.
1079
- *
1080
- * Example:
1081
- * conn.send_query( "first SQL query; second SQL query" )
1082
- * conn.set_single_row_mode
1083
- * conn.get_result.stream_each do |row|
1084
- * # do something with the received row of the first query
1085
- * end
1086
- * conn.get_result.stream_each do |row|
1087
- * # do something with the received row of the second query
1088
- * end
1089
- * conn.get_result # => nil (no more results)
1090
- */
1270
+
1271
+ static void
1272
+ yield_hash(VALUE self, int ntuples, int nfields)
1273
+ {
1274
+ int tuple_num;
1275
+ t_pg_result *this = pgresult_get_this(self);
1276
+ UNUSED(nfields);
1277
+
1278
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1279
+ rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1280
+ }
1281
+
1282
+ pgresult_clear( this );
1283
+ }
1284
+
1285
+ static void
1286
+ yield_array(VALUE self, int ntuples, int nfields)
1287
+ {
1288
+ int row;
1289
+ t_pg_result *this = pgresult_get_this(self);
1290
+
1291
+ for ( row = 0; row < ntuples; row++ ) {
1292
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1293
+ int field;
1294
+
1295
+ /* populate the row */
1296
+ for ( field = 0; field < nfields; field++ ) {
1297
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1298
+ }
1299
+ rb_yield( rb_ary_new4( nfields, row_values ));
1300
+ }
1301
+
1302
+ pgresult_clear( this );
1303
+ }
1304
+
1305
+ static void
1306
+ yield_tuple(VALUE self, int ntuples, int nfields)
1307
+ {
1308
+ int tuple_num;
1309
+ t_pg_result *this = pgresult_get_this(self);
1310
+ VALUE result = pg_new_result(this->pgresult, this->connection);
1311
+ UNUSED(nfields);
1312
+
1313
+ for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1314
+ VALUE tuple = pgresult_tuple(result, INT2FIX(tuple_num));
1315
+ rb_yield( tuple );
1316
+ }
1317
+ }
1318
+
1091
1319
  static VALUE
1092
- pgresult_stream_each(VALUE self)
1320
+ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1093
1321
  {
1094
1322
  t_pg_result *this;
1095
1323
  int nfields;
@@ -1104,7 +1332,6 @@ pgresult_stream_each(VALUE self)
1104
1332
  nfields = PQnfields(pgresult);
1105
1333
 
1106
1334
  for(;;){
1107
- int tuple_num;
1108
1335
  int ntuples = PQntuples(pgresult);
1109
1336
 
1110
1337
  switch( PQresultStatus(pgresult) ){
@@ -1118,14 +1345,7 @@ pgresult_stream_each(VALUE self)
1118
1345
  pg_result_check( self );
1119
1346
  }
1120
1347
 
1121
- for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1122
- rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1123
- }
1124
-
1125
- if( !this->autoclear ){
1126
- PQclear( pgresult );
1127
- this->pgresult = NULL;
1128
- }
1348
+ yielder( self, ntuples, nfields );
1129
1349
 
1130
1350
  pgresult = gvl_PQgetResult(pgconn);
1131
1351
  if( pgresult == NULL )
@@ -1141,6 +1361,46 @@ pgresult_stream_each(VALUE self)
1141
1361
  return self;
1142
1362
  }
1143
1363
 
1364
+
1365
+ /*
1366
+ * call-seq:
1367
+ * res.stream_each{ |tuple| ... }
1368
+ *
1369
+ * Invokes block for each tuple in the result set in single row mode.
1370
+ *
1371
+ * This is a convenience method for retrieving all result tuples
1372
+ * as they are transferred. It is an alternative to repeated calls of
1373
+ * PG::Connection#get_result , but given that it avoids the overhead of
1374
+ * wrapping each row into a dedicated result object, it delivers data in nearly
1375
+ * the same speed as with ordinary results.
1376
+ *
1377
+ * The base result must be in status PGRES_SINGLE_TUPLE.
1378
+ * It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
1379
+ * A PG::Error is raised for any errors from the server.
1380
+ *
1381
+ * Row description data does not change while the iteration. All value retrieval
1382
+ * methods refer to only the current row. Result#ntuples returns +1+ while
1383
+ * the iteration and +0+ after all tuples were yielded.
1384
+ *
1385
+ * Example:
1386
+ * conn.send_query( "first SQL query; second SQL query" )
1387
+ * conn.set_single_row_mode
1388
+ * conn.get_result.stream_each do |row|
1389
+ * # do something with each received row of the first query
1390
+ * end
1391
+ * conn.get_result.stream_each do |row|
1392
+ * # do something with each received row of the second query
1393
+ * end
1394
+ * conn.get_result # => nil (no more results)
1395
+ *
1396
+ * Available since PostgreSQL-9.2
1397
+ */
1398
+ static VALUE
1399
+ pgresult_stream_each(VALUE self)
1400
+ {
1401
+ return pgresult_stream_any(self, yield_hash);
1402
+ }
1403
+
1144
1404
  /*
1145
1405
  * call-seq:
1146
1406
  * res.stream_each_row { |row| ... }
@@ -1150,67 +1410,30 @@ pgresult_stream_each(VALUE self)
1150
1410
  *
1151
1411
  * This method works equally to #stream_each , but yields an Array of
1152
1412
  * values.
1413
+ *
1414
+ * Available since PostgreSQL-9.2
1153
1415
  */
1154
1416
  static VALUE
1155
1417
  pgresult_stream_each_row(VALUE self)
1156
1418
  {
1157
- t_pg_result *this;
1158
- int row;
1159
- int nfields;
1160
- PGconn *pgconn;
1161
- PGresult *pgresult;
1162
-
1163
- RETURN_ENUMERATOR(self, 0, NULL);
1164
-
1165
- this = pgresult_get_this_safe(self);
1166
- pgconn = pg_get_pgconn(this->connection);
1167
- pgresult = this->pgresult;
1168
- nfields = PQnfields(pgresult);
1169
-
1170
- for(;;){
1171
- int ntuples = PQntuples(pgresult);
1172
-
1173
- switch( PQresultStatus(pgresult) ){
1174
- case PGRES_TUPLES_OK:
1175
- if( ntuples == 0 )
1176
- return self;
1177
- rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1178
- case PGRES_SINGLE_TUPLE:
1179
- break;
1180
- default:
1181
- pg_result_check( self );
1182
- }
1183
-
1184
- for ( row = 0; row < ntuples; row++ ) {
1185
- PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
1186
- int field;
1187
-
1188
- /* populate the row */
1189
- for ( field = 0; field < nfields; field++ ) {
1190
- row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
1191
- }
1192
- rb_yield( rb_ary_new4( nfields, row_values ));
1193
- }
1194
-
1195
- if( !this->autoclear ){
1196
- PQclear( pgresult );
1197
- this->pgresult = NULL;
1198
- }
1199
-
1200
- pgresult = gvl_PQgetResult(pgconn);
1201
- if( pgresult == NULL )
1202
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1203
-
1204
- if( nfields != PQnfields(pgresult) )
1205
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1206
-
1207
- this->pgresult = pgresult;
1208
- }
1419
+ return pgresult_stream_any(self, yield_array);
1420
+ }
1209
1421
 
1210
- /* never reached */
1211
- return self;
1422
+ /*
1423
+ * call-seq:
1424
+ * res.stream_each_tuple { |tuple| ... }
1425
+ *
1426
+ * Yields each row of the result set in single row mode.
1427
+ *
1428
+ * This method works equally to #stream_each , but yields a PG::Tuple object.
1429
+ *
1430
+ * Available since PostgreSQL-9.2
1431
+ */
1432
+ static VALUE
1433
+ pgresult_stream_each_tuple(VALUE self)
1434
+ {
1435
+ return pgresult_stream_any(self, yield_tuple);
1212
1436
  }
1213
- #endif
1214
1437
 
1215
1438
 
1216
1439
  void
@@ -1261,17 +1484,18 @@ init_pg_result()
1261
1484
  rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
1262
1485
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
1263
1486
  rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
1487
+ rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
1488
+ rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
1264
1489
  rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
1265
1490
  rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
1266
1491
 
1267
1492
  rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1268
1493
  rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
1269
1494
 
1270
- #ifdef HAVE_PQSETSINGLEROWMODE
1271
1495
  /****** PG::Result INSTANCE METHODS: streaming ******/
1272
1496
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1273
1497
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1274
- #endif
1498
+ rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1275
1499
  }
1276
1500
 
1277
1501