pg 0.18.0.pre20141017160319 → 0.18.0.pre20141117110243

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,6 +10,9 @@ VALUE rb_ePGerror;
10
10
  VALUE rb_eServerError;
11
11
  VALUE rb_eUnableToSend;
12
12
  VALUE rb_eConnectionBad;
13
+ VALUE rb_eInvalidResultStatus;
14
+ VALUE rb_eNoResultError;
15
+ VALUE rb_eInvalidChangeOfResultFields;
13
16
 
14
17
  static VALUE
15
18
  define_error_class(const char *name, const char *baseclass_code)
@@ -84,6 +87,9 @@ init_pg_errors()
84
87
  rb_eServerError = rb_define_class_under( rb_mPG, "ServerError", rb_ePGerror );
85
88
  rb_eUnableToSend = rb_define_class_under( rb_mPG, "UnableToSend", rb_ePGerror );
86
89
  rb_eConnectionBad = rb_define_class_under( rb_mPG, "ConnectionBad", rb_ePGerror );
90
+ rb_eInvalidResultStatus = rb_define_class_under( rb_mPG, "InvalidResultStatus", rb_ePGerror );
91
+ rb_eNoResultError = rb_define_class_under( rb_mPG, "NoResultError", rb_ePGerror );
92
+ rb_eInvalidChangeOfResultFields = rb_define_class_under( rb_mPG, "InvalidChangeOfResultFields", rb_ePGerror );
87
93
 
88
94
  #include "errorcodes.def"
89
95
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v f0b7f99b1dd5 2014/10/06 08:31:02 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -27,28 +27,32 @@ static t_pg_result *pgresult_get_this_safe( VALUE );
27
27
  VALUE
28
28
  pg_new_result(PGresult *result, VALUE rb_pgconn)
29
29
  {
30
+ int nfields = result ? PQnfields(result) : 0;
30
31
  VALUE self = pgresult_s_allocate( rb_cPGresult );
31
- t_pg_result *this = pgresult_get_this(self);
32
- t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
32
+ t_pg_result *this;
33
33
 
34
- PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
34
+ this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
35
+ DATA_PTR(self) = this;
35
36
 
36
37
  this->pgresult = result;
37
38
  this->connection = rb_pgconn;
38
- if( result ){
39
- VALUE typemap = p_conn->type_map_for_results;
39
+ this->typemap = pg_typemap_all_strings;
40
+ this->p_typemap = DATA_PTR( this->typemap );
41
+ this->autoclear = 0;
42
+ this->nfields = -1;
43
+ this->tuple_hash = Qnil;
40
44
 
41
- if( !NIL_P(typemap) ){
42
- t_typemap *p_typemap;
45
+ PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
43
46
 
44
- /* Type check is done when assigned to PG::Connection. */
45
- p_typemap = DATA_PTR(typemap);
47
+ if( result ){
48
+ t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
49
+ VALUE typemap = p_conn->type_map_for_results;
46
50
 
47
- typemap = p_typemap->fit_to_result( typemap, self );
48
- this->p_typemap = DATA_PTR( typemap );
49
- }
51
+ /* Type check is done when assigned to PG::Connection. */
52
+ t_typemap *p_typemap = DATA_PTR(typemap);
50
53
 
51
- this->typemap = typemap;
54
+ this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
55
+ this->p_typemap = DATA_PTR( this->typemap );
52
56
  }
53
57
 
54
58
  return self;
@@ -74,11 +78,11 @@ pg_result_check( VALUE self )
74
78
  {
75
79
  t_pg_result *this = pgresult_get_this(self);
76
80
  VALUE error, exception, klass;
77
- PGconn *conn = pg_get_pgconn(this->connection);
78
81
  char * sqlstate;
79
82
 
80
83
  if(this->pgresult == NULL)
81
84
  {
85
+ PGconn *conn = pg_get_pgconn(this->connection);
82
86
  error = rb_str_new2( PQerrorMessage(conn) );
83
87
  }
84
88
  else
@@ -162,7 +166,7 @@ pgresult_cleared_p( VALUE self )
162
166
 
163
167
  /*
164
168
  * call-seq:
165
- * res.autoclose? -> boolean
169
+ * res.autoclear? -> boolean
166
170
  *
167
171
  * Returns +true+ if the underlying C struct will be cleared automatically by libpq.
168
172
  * Elsewise the result is cleared by PG::Result#clear or by the GC when it's no longer in use.
@@ -185,8 +189,16 @@ pgresult_autoclear_p( VALUE self )
185
189
  static void
186
190
  pgresult_gc_mark( t_pg_result *this )
187
191
  {
192
+ int i;
193
+
194
+ if( !this ) return;
188
195
  rb_gc_mark( this->connection );
189
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
+ }
190
202
  }
191
203
 
192
204
  /*
@@ -195,6 +207,7 @@ pgresult_gc_mark( t_pg_result *this )
195
207
  static void
196
208
  pgresult_gc_free( t_pg_result *this )
197
209
  {
210
+ if( !this ) return;
198
211
  if(this->pgresult != NULL && !this->autoclear)
199
212
  PQclear(this->pgresult);
200
213
 
@@ -216,6 +229,10 @@ pgresult_get_this_safe( VALUE self )
216
229
 
217
230
  /*
218
231
  * Fetch the PGresult pointer for the result object and check validity
232
+ *
233
+ * Note: This function is used externally by the sequel_pg gem,
234
+ * so do changes carefully.
235
+ *
219
236
  */
220
237
  PGresult*
221
238
  pgresult_get(VALUE self)
@@ -235,18 +252,30 @@ pgresult_get(VALUE self)
235
252
  static VALUE
236
253
  pgresult_s_allocate( VALUE klass )
237
254
  {
238
- t_pg_result *this;
239
- VALUE self = Data_Make_Struct( klass, t_pg_result, pgresult_gc_mark, pgresult_gc_free, this );
240
-
241
- this->pgresult = NULL;
242
- this->connection = Qnil;
243
- this->typemap = Qnil;
244
- this->p_typemap = DATA_PTR( pg_default_typemap );
245
- this->autoclear = 0;
255
+ VALUE self = Data_Wrap_Struct( klass, pgresult_gc_mark, pgresult_gc_free, NULL );
246
256
 
247
257
  return self;
248
258
  }
249
259
 
260
+ static void pgresult_init_fnames(VALUE self)
261
+ {
262
+ t_pg_result *this = pgresult_get_this_safe(self);
263
+
264
+ if( this->nfields == -1 ){
265
+ int i;
266
+ int nfields = PQnfields(this->pgresult);
267
+
268
+ 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);
272
+ this->nfields = i + 1;
273
+
274
+ RB_GC_GUARD(fname);
275
+ }
276
+ this->nfields = nfields;
277
+ }
278
+ }
250
279
 
251
280
  /********************************************************************
252
281
  *
@@ -395,6 +424,12 @@ pgresult_ntuples(VALUE self)
395
424
  return INT2FIX(PQntuples(pgresult_get(self)));
396
425
  }
397
426
 
427
+ static VALUE
428
+ pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
429
+ {
430
+ return pgresult_ntuples(self);
431
+ }
432
+
398
433
  /*
399
434
  * call-seq:
400
435
  * res.nfields() -> Integer
@@ -417,16 +452,16 @@ static VALUE
417
452
  pgresult_fname(VALUE self, VALUE index)
418
453
  {
419
454
  VALUE fname;
420
- PGresult *result;
455
+ PGresult *result = pgresult_get(self);
421
456
  int i = NUM2INT(index);
422
457
 
423
- result = pgresult_get(self);
424
458
  if (i < 0 || i >= PQnfields(result)) {
425
459
  rb_raise(rb_eArgError,"invalid field number %d", i);
426
460
  }
461
+
427
462
  fname = rb_tainted_str_new2(PQfname(result, i));
428
463
  PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
429
- return fname;
464
+ return rb_obj_freeze(fname);
430
465
  }
431
466
 
432
467
  /*
@@ -633,7 +668,7 @@ pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
633
668
  if(j < 0 || j >= PQnfields(this->pgresult)) {
634
669
  rb_raise(rb_eArgError,"invalid field number %d", j);
635
670
  }
636
- return this->p_typemap->typecast_result_value(self, i, j);
671
+ return this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, j);
637
672
  }
638
673
 
639
674
  /*
@@ -783,18 +818,26 @@ pgresult_aref(VALUE self, VALUE index)
783
818
  t_pg_result *this = pgresult_get_this_safe(self);
784
819
  int tuple_num = NUM2INT(index);
785
820
  int field_num;
786
- VALUE fname;
821
+ int num_tuples = PQntuples(this->pgresult);
787
822
  VALUE tuple;
788
823
 
789
- if ( tuple_num < 0 || tuple_num >= PQntuples(this->pgresult) )
824
+ if( this->nfields == -1 )
825
+ pgresult_init_fnames( self );
826
+
827
+ if ( tuple_num < 0 || tuple_num >= num_tuples )
790
828
  rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
791
829
 
792
- tuple = rb_hash_new();
793
- for ( field_num = 0; field_num < PQnfields(this->pgresult); field_num++ ) {
794
- fname = rb_tainted_str_new2( PQfname(this->pgresult,field_num) );
795
- PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
796
- rb_hash_aset( tuple, fname, this->p_typemap->typecast_result_value(self, tuple_num, field_num) );
830
+ /* We reuse the Hash of the previous output for larger row counts.
831
+ * This is somewhat faster than populating an empty Hash object. */
832
+ tuple = NIL_P(this->tuple_hash) ? rb_hash_new() : this->tuple_hash;
833
+ for ( field_num = 0; field_num < this->nfields; field_num++ ) {
834
+ VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field_num);
835
+ rb_hash_aset( tuple, this->fnames[field_num], val );
797
836
  }
837
+ /* Store a copy of the filled hash for use at the next row. */
838
+ if( num_tuples > 10 )
839
+ this->tuple_hash = rb_hash_dup(tuple);
840
+
798
841
  return tuple;
799
842
  }
800
843
 
@@ -807,20 +850,26 @@ pgresult_aref(VALUE self, VALUE index)
807
850
  static VALUE
808
851
  pgresult_each_row(VALUE self)
809
852
  {
810
- t_pg_result *this = pgresult_get_this_safe(self);
853
+ t_pg_result *this;
811
854
  int row;
812
855
  int field;
813
- int num_rows = PQntuples(this->pgresult);
814
- int num_fields = PQnfields(this->pgresult);
856
+ int num_rows;
857
+ int num_fields;
858
+
859
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
860
+
861
+ this = pgresult_get_this_safe(self);
862
+ num_rows = PQntuples(this->pgresult);
863
+ num_fields = PQnfields(this->pgresult);
815
864
 
816
865
  for ( row = 0; row < num_rows; row++ ) {
817
- VALUE new_row = rb_ary_new2(num_fields);
866
+ VALUE row_values[num_fields];
818
867
 
819
868
  /* populate the row */
820
869
  for ( field = 0; field < num_fields; field++ ) {
821
- rb_ary_store( new_row, field, this->p_typemap->typecast_result_value(self, row, field) );
870
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
822
871
  }
823
- rb_yield( new_row );
872
+ rb_yield( rb_ary_new4( num_fields, row_values ));
824
873
  }
825
874
 
826
875
  return Qnil;
@@ -843,13 +892,13 @@ pgresult_values(VALUE self)
843
892
  VALUE results = rb_ary_new2( num_rows );
844
893
 
845
894
  for ( row = 0; row < num_rows; row++ ) {
846
- VALUE new_row = rb_ary_new2(num_fields);
895
+ VALUE row_values[num_fields];
847
896
 
848
897
  /* populate the row */
849
898
  for ( field = 0; field < num_fields; field++ ) {
850
- rb_ary_store( new_row, field, this->p_typemap->typecast_result_value(self, row, field) );
899
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
851
900
  }
852
- rb_ary_store( results, row, new_row );
901
+ rb_ary_store( results, row, rb_ary_new4( num_fields, row_values ) );
853
902
  }
854
903
 
855
904
  return results;
@@ -871,7 +920,7 @@ make_column_result_array( VALUE self, int col )
871
920
  rb_raise( rb_eIndexError, "no column %d in result", col );
872
921
 
873
922
  for ( i=0; i < rows; i++ ) {
874
- VALUE val = this->p_typemap->typecast_result_value(self, i, col);
923
+ VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, col);
875
924
  rb_ary_store( results, i, val );
876
925
  }
877
926
 
@@ -925,9 +974,13 @@ pgresult_field_values( VALUE self, VALUE field )
925
974
  static VALUE
926
975
  pgresult_each(VALUE self)
927
976
  {
928
- PGresult *result = pgresult_get(self);
977
+ PGresult *result;
929
978
  int tuple_num;
930
979
 
980
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
981
+
982
+ result = pgresult_get(self);
983
+
931
984
  for(tuple_num = 0; tuple_num < PQntuples(result); tuple_num++) {
932
985
  rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
933
986
  }
@@ -943,18 +996,12 @@ pgresult_each(VALUE self)
943
996
  static VALUE
944
997
  pgresult_fields(VALUE self)
945
998
  {
946
- PGresult *result = pgresult_get( self );
947
- int n = PQnfields( result );
948
- VALUE fields = rb_ary_new2( n );
949
- int i;
999
+ t_pg_result *this = pgresult_get_this_safe(self);
950
1000
 
951
- for ( i = 0; i < n; i++ ) {
952
- VALUE val = rb_tainted_str_new2(PQfname(result, i));
953
- PG_ENCODING_SET_NOCHECK(val, ENCODING_GET(self));
954
- rb_ary_store( fields, i, val );
955
- }
1001
+ if( this->nfields == -1 )
1002
+ pgresult_init_fnames( self );
956
1003
 
957
- return fields;
1004
+ return rb_ary_new4( this->nfields, this->fnames );
958
1005
  }
959
1006
 
960
1007
  /*
@@ -967,32 +1014,23 @@ pgresult_fields(VALUE self)
967
1014
  * type casts from PostgreSQL's wire format to Ruby objects on the fly,
968
1015
  * according to the rules and decoders defined in the given typemap.
969
1016
  *
970
- * +typemap+ can be:
971
- * * a kind of PG::TypeMap
972
- * * +nil+ - to type cast all result values to String.
1017
+ * +typemap+ must be a kind of PG::TypeMap .
973
1018
  *
974
1019
  */
975
1020
  static VALUE
976
1021
  pgresult_type_map_set(VALUE self, VALUE typemap)
977
1022
  {
978
1023
  t_pg_result *this = pgresult_get_this(self);
1024
+ t_typemap *p_typemap;
979
1025
 
980
- if( NIL_P(typemap) ){
981
- this->p_typemap = DATA_PTR( pg_default_typemap );
982
- } else {
983
- t_typemap *p_typemap;
984
-
985
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
986
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
987
- rb_obj_classname( typemap ) );
988
- }
989
- Data_Get_Struct(typemap, t_typemap, p_typemap);
990
-
991
- typemap = p_typemap->fit_to_result( typemap, self );
992
- this->p_typemap = DATA_PTR( typemap );
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 ) );
993
1029
  }
1030
+ Data_Get_Struct(typemap, t_typemap, p_typemap);
994
1031
 
995
- this->typemap = typemap;
1032
+ this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
1033
+ this->p_typemap = DATA_PTR( this->typemap );
996
1034
 
997
1035
  return typemap;
998
1036
  }
@@ -1003,10 +1041,6 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
1003
1041
  *
1004
1042
  * Returns the TypeMap that is currently set for type casts of result values to ruby objects.
1005
1043
  *
1006
- * Returns either:
1007
- * * a kind of PG::TypeMap or
1008
- * * +nil+ - when no type map is set.
1009
- *
1010
1044
  */
1011
1045
  static VALUE
1012
1046
  pgresult_type_map_get(VALUE self)
@@ -1016,6 +1050,162 @@ pgresult_type_map_get(VALUE self)
1016
1050
  return this->typemap;
1017
1051
  }
1018
1052
 
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)
1087
+ {
1088
+ t_pg_result *this;
1089
+ int nfields;
1090
+ PGconn *pgconn;
1091
+ PGresult *pgresult;
1092
+
1093
+ RETURN_ENUMERATOR(self, 0, NULL);
1094
+
1095
+ this = pgresult_get_this_safe(self);
1096
+ pgconn = pg_get_pgconn(this->connection);
1097
+ pgresult = this->pgresult;
1098
+ nfields = PQnfields(pgresult);
1099
+
1100
+ for(;;){
1101
+ int tuple_num;
1102
+ int ntuples = PQntuples(pgresult);
1103
+
1104
+ switch( PQresultStatus(pgresult) ){
1105
+ case PGRES_TUPLES_OK:
1106
+ if( ntuples == 0 )
1107
+ return self;
1108
+ rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
1109
+ case PGRES_SINGLE_TUPLE:
1110
+ break;
1111
+ default:
1112
+ pg_result_check( self );
1113
+ }
1114
+
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
+ }
1123
+
1124
+ pgresult = gvl_PQgetResult(pgconn);
1125
+ 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");
1130
+
1131
+ this->pgresult = pgresult;
1132
+ }
1133
+
1134
+ /* never reached */
1135
+ return self;
1136
+ }
1137
+
1138
+ /*
1139
+ * call-seq:
1140
+ * res.stream_each_row { |row| ... }
1141
+ *
1142
+ * Yields each row of the result set in single row mode.
1143
+ * The row is a list of column values.
1144
+ *
1145
+ * This method works equally to #stream_each , but yields an Array of
1146
+ * values.
1147
+ */
1148
+ static VALUE
1149
+ pgresult_stream_each_row(VALUE self)
1150
+ {
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
+ }
1203
+
1204
+ /* never reached */
1205
+ return self;
1206
+ }
1207
+ #endif
1208
+
1019
1209
 
1020
1210
  void
1021
1211
  init_pg_result()
@@ -1070,6 +1260,12 @@ init_pg_result()
1070
1260
 
1071
1261
  rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1072
1262
  rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
1263
+
1264
+ #ifdef HAVE_PQSETSINGLEROWMODE
1265
+ /****** PG::Result INSTANCE METHODS: streaming ******/
1266
+ rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1267
+ rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1268
+ #endif
1073
1269
  }
1074
1270
 
1075
1271