pg 0.18.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +1221 -4
  5. data/History.rdoc +200 -0
  6. data/Manifest.txt +5 -18
  7. data/README-Windows.rdoc +15 -26
  8. data/README.rdoc +27 -10
  9. data/Rakefile +33 -24
  10. data/Rakefile.cross +57 -39
  11. data/ext/errorcodes.def +37 -0
  12. data/ext/errorcodes.rb +1 -1
  13. data/ext/errorcodes.txt +16 -1
  14. data/ext/extconf.rb +29 -35
  15. data/ext/gvl_wrappers.c +4 -0
  16. data/ext/gvl_wrappers.h +27 -39
  17. data/ext/pg.c +27 -53
  18. data/ext/pg.h +66 -83
  19. data/ext/pg_binary_decoder.c +75 -6
  20. data/ext/pg_binary_encoder.c +14 -12
  21. data/ext/pg_coder.c +83 -13
  22. data/ext/pg_connection.c +627 -351
  23. data/ext/pg_copy_coder.c +44 -9
  24. data/ext/pg_result.c +364 -134
  25. data/ext/pg_text_decoder.c +605 -46
  26. data/ext/pg_text_encoder.c +95 -76
  27. data/ext/pg_tuple.c +541 -0
  28. data/ext/pg_type_map.c +20 -13
  29. data/ext/pg_type_map_by_column.c +7 -7
  30. data/ext/pg_type_map_by_mri_type.c +2 -2
  31. data/ext/pg_type_map_in_ruby.c +4 -7
  32. data/ext/util.c +7 -7
  33. data/ext/util.h +3 -3
  34. data/lib/pg/basic_type_mapping.rb +105 -45
  35. data/lib/pg/binary_decoder.rb +22 -0
  36. data/lib/pg/coder.rb +1 -1
  37. data/lib/pg/connection.rb +109 -39
  38. data/lib/pg/constants.rb +1 -1
  39. data/lib/pg/exceptions.rb +1 -1
  40. data/lib/pg/result.rb +11 -6
  41. data/lib/pg/text_decoder.rb +25 -20
  42. data/lib/pg/text_encoder.rb +43 -1
  43. data/lib/pg/tuple.rb +30 -0
  44. data/lib/pg/type_map_by_column.rb +1 -1
  45. data/lib/pg.rb +21 -11
  46. data/spec/helpers.rb +50 -25
  47. data/spec/pg/basic_type_mapping_spec.rb +287 -30
  48. data/spec/pg/connection_spec.rb +695 -282
  49. data/spec/pg/connection_sync_spec.rb +41 -0
  50. data/spec/pg/result_spec.rb +59 -17
  51. data/spec/pg/tuple_spec.rb +280 -0
  52. data/spec/pg/type_map_by_class_spec.rb +3 -3
  53. data/spec/pg/type_map_by_column_spec.rb +1 -1
  54. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  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 +319 -35
  59. data/spec/pg_spec.rb +2 -2
  60. data.tar.gz.sig +0 -0
  61. metadata +68 -68
  62. metadata.gz.sig +0 -0
  63. data/sample/array_insert.rb +0 -20
  64. data/sample/async_api.rb +0 -106
  65. data/sample/async_copyto.rb +0 -39
  66. data/sample/async_mixed.rb +0 -56
  67. data/sample/check_conn.rb +0 -21
  68. data/sample/copyfrom.rb +0 -81
  69. data/sample/copyto.rb +0 -19
  70. data/sample/cursor.rb +0 -21
  71. data/sample/disk_usage_report.rb +0 -186
  72. data/sample/issue-119.rb +0 -94
  73. data/sample/losample.rb +0 -69
  74. data/sample/minimal-testcase.rb +0 -17
  75. data/sample/notify_wait.rb +0 -72
  76. data/sample/pg_statistics.rb +0 -294
  77. data/sample/replication_monitor.rb +0 -231
  78. data/sample/test_binary_values.rb +0 -33
  79. data/sample/wal_shipper.rb +0 -434
  80. data/sample/warehouse_partitions.rb +0 -320
data/ext/pg_result.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v f23dd01bcb52 2014/11/17 10:47:53 kanis $
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,12 +419,15 @@ 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'
291
- * conn = PGconn.open(:dbname => 'test')
430
+ * conn = PG.connect(:dbname => 'test')
292
431
  * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
293
432
  * res.getvalue(0,0) # '1'
294
433
  * res[0]['b'] # '2'
@@ -302,7 +441,7 @@ static void pgresult_init_fnames(VALUE self)
302
441
 
303
442
  /*
304
443
  * call-seq:
305
- * res.result_status() -> Fixnum
444
+ * res.result_status() -> Integer
306
445
  *
307
446
  * Returns the status of the query. The status value is one of:
308
447
  * * +PGRES_EMPTY_QUERY+
@@ -414,7 +553,7 @@ pgresult_error_field(VALUE self, VALUE field)
414
553
 
415
554
  /*
416
555
  * call-seq:
417
- * res.ntuples() -> Fixnum
556
+ * res.ntuples() -> Integer
418
557
  *
419
558
  * Returns the number of tuples in the query result.
420
559
  */
@@ -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
  /*
@@ -466,7 +605,7 @@ pgresult_fname(VALUE self, VALUE index)
466
605
 
467
606
  /*
468
607
  * call-seq:
469
- * res.fnumber( name ) -> Fixnum
608
+ * res.fnumber( name ) -> Integer
470
609
  *
471
610
  * Returns the index of the field specified by the string +name+.
472
611
  * The given +name+ is treated like an identifier in an SQL command, that is,
@@ -527,7 +666,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
527
666
 
528
667
  /*
529
668
  * call-seq:
530
- * res.ftablecol( column_number ) -> Fixnum
669
+ * res.ftablecol( column_number ) -> Integer
531
670
  *
532
671
  * Returns the column number (within its table) of the table from
533
672
  * which the column _column_number_ is made up.
@@ -552,7 +691,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
552
691
 
553
692
  /*
554
693
  * call-seq:
555
- * res.fformat( column_number ) -> Fixnum
694
+ * res.fformat( column_number ) -> Integer
556
695
  *
557
696
  * Returns the format (0 for text, 1 for binary) of column
558
697
  * _column_number_.
@@ -696,7 +835,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
696
835
 
697
836
  /*
698
837
  * call-seq:
699
- * res.getlength( tup_num, field_num ) -> Fixnum
838
+ * res.getlength( tup_num, field_num ) -> Integer
700
839
  *
701
840
  * Returns the (String) length of the field in bytes.
702
841
  *
@@ -721,7 +860,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
721
860
 
722
861
  /*
723
862
  * call-seq:
724
- * res.nparams() -> Fixnum
863
+ * res.nparams() -> Integer
725
864
  *
726
865
  * Returns the number of parameters of a prepared statement.
727
866
  * Only useful for the result returned by conn.describePrepared
@@ -772,11 +911,17 @@ pgresult_cmd_status(VALUE self)
772
911
  * Returns the number of tuples (rows) affected by the SQL command.
773
912
  *
774
913
  * If the SQL command that generated the PG::Result was not one of:
775
- * * +INSERT+
776
- * * +UPDATE+
777
- * * +DELETE+
778
- * * +MOVE+
779
- * * +FETCH+
914
+ *
915
+ * * <tt>SELECT</tt>
916
+ * * <tt>CREATE TABLE AS</tt>
917
+ * * <tt>INSERT</tt>
918
+ * * <tt>UPDATE</tt>
919
+ * * <tt>DELETE</tt>
920
+ * * <tt>MOVE</tt>
921
+ * * <tt>FETCH</tt>
922
+ * * <tt>COPY</tt>
923
+ * * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
924
+ *
780
925
  * or if no tuples were affected, <tt>0</tt> is returned.
781
926
  */
782
927
  static VALUE
@@ -863,7 +1008,7 @@ pgresult_each_row(VALUE self)
863
1008
  num_fields = PQnfields(this->pgresult);
864
1009
 
865
1010
  for ( row = 0; row < num_rows; row++ ) {
866
- VALUE row_values[num_fields];
1011
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
867
1012
 
868
1013
  /* populate the row */
869
1014
  for ( field = 0; field < num_fields; field++ ) {
@@ -892,7 +1037,7 @@ pgresult_values(VALUE self)
892
1037
  VALUE results = rb_ary_new2( num_rows );
893
1038
 
894
1039
  for ( row = 0; row < num_rows; row++ ) {
895
- VALUE row_values[num_fields];
1040
+ PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
896
1041
 
897
1042
  /* populate the row */
898
1043
  for ( field = 0; field < num_fields; field++ ) {
@@ -965,6 +1110,78 @@ pgresult_field_values( VALUE self, VALUE field )
965
1110
  }
966
1111
 
967
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
+
968
1185
  /*
969
1186
  * call-seq:
970
1187
  * res.each{ |tuple| ... }
@@ -1050,40 +1267,57 @@ pgresult_type_map_get(VALUE self)
1050
1267
  return this->typemap;
1051
1268
  }
1052
1269
 
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
- */
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
+
1085
1319
  static VALUE
1086
- pgresult_stream_each(VALUE self)
1320
+ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
1087
1321
  {
1088
1322
  t_pg_result *this;
1089
1323
  int nfields;
@@ -1098,7 +1332,6 @@ pgresult_stream_each(VALUE self)
1098
1332
  nfields = PQnfields(pgresult);
1099
1333
 
1100
1334
  for(;;){
1101
- int tuple_num;
1102
1335
  int ntuples = PQntuples(pgresult);
1103
1336
 
1104
1337
  switch( PQresultStatus(pgresult) ){
@@ -1112,14 +1345,7 @@ pgresult_stream_each(VALUE self)
1112
1345
  pg_result_check( self );
1113
1346
  }
1114
1347
 
1115
- for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
1116
- rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
1117
- }
1118
-
1119
- if( !this->autoclear ){
1120
- PQclear( pgresult );
1121
- this->pgresult = NULL;
1122
- }
1348
+ yielder( self, ntuples, nfields );
1123
1349
 
1124
1350
  pgresult = gvl_PQgetResult(pgconn);
1125
1351
  if( pgresult == NULL )
@@ -1135,6 +1361,46 @@ pgresult_stream_each(VALUE self)
1135
1361
  return self;
1136
1362
  }
1137
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
+
1138
1404
  /*
1139
1405
  * call-seq:
1140
1406
  * res.stream_each_row { |row| ... }
@@ -1144,67 +1410,30 @@ pgresult_stream_each(VALUE self)
1144
1410
  *
1145
1411
  * This method works equally to #stream_each , but yields an Array of
1146
1412
  * values.
1413
+ *
1414
+ * Available since PostgreSQL-9.2
1147
1415
  */
1148
1416
  static VALUE
1149
1417
  pgresult_stream_each_row(VALUE self)
1150
1418
  {
1151
- t_pg_result *this;
1152
- int row;
1153
- int nfields;
1154
- PGconn *pgconn;
1155
- PGresult *pgresult;
1156
-
1157
- RETURN_ENUMERATOR(self, 0, NULL);
1158
-
1159
- this = pgresult_get_this_safe(self);
1160
- pgconn = pg_get_pgconn(this->connection);
1161
- pgresult = this->pgresult;
1162
- nfields = PQnfields(pgresult);
1163
-
1164
- for(;;){
1165
- int ntuples = PQntuples(pgresult);
1166
-
1167
- switch( PQresultStatus(pgresult) ){
1168
- case PGRES_TUPLES_OK:
1169
- if( ntuples == 0 )
1170
- return self;
1171
- rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1172
- case PGRES_SINGLE_TUPLE:
1173
- break;
1174
- default:
1175
- pg_result_check( self );
1176
- }
1177
-
1178
- for ( row = 0; row < ntuples; row++ ) {
1179
- VALUE row_values[nfields];
1180
- int field;
1181
-
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
- }
1188
-
1189
- if( !this->autoclear ){
1190
- PQclear( pgresult );
1191
- this->pgresult = NULL;
1192
- }
1193
-
1194
- pgresult = gvl_PQgetResult(pgconn);
1195
- if( pgresult == NULL )
1196
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1197
-
1198
- if( nfields != PQnfields(pgresult) )
1199
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1200
-
1201
- this->pgresult = pgresult;
1202
- }
1419
+ return pgresult_stream_any(self, yield_array);
1420
+ }
1203
1421
 
1204
- /* never reached */
1205
- 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);
1206
1436
  }
1207
- #endif
1208
1437
 
1209
1438
 
1210
1439
  void
@@ -1255,17 +1484,18 @@ init_pg_result()
1255
1484
  rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
1256
1485
  rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
1257
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);
1258
1489
  rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
1259
1490
  rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
1260
1491
 
1261
1492
  rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1262
1493
  rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
1263
1494
 
1264
- #ifdef HAVE_PQSETSINGLEROWMODE
1265
1495
  /****** PG::Result INSTANCE METHODS: streaming ******/
1266
1496
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1267
1497
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1268
- #endif
1498
+ rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
1269
1499
  }
1270
1500
 
1271
1501