pg 0.18.0.pre20141017160319 → 0.18.0.pre20141117110243
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gemtest +0 -0
- data/ChangeLog +213 -15
- data/History.rdoc +15 -0
- data/Manifest.txt +2 -0
- data/README.rdoc +1 -1
- data/Rakefile +0 -17
- data/ext/extconf.rb +2 -0
- data/ext/pg.c +67 -1
- data/ext/pg.h +50 -13
- data/ext/pg_binary_decoder.c +4 -4
- data/ext/pg_binary_encoder.c +1 -1
- data/ext/pg_coder.c +6 -0
- data/ext/pg_connection.c +50 -55
- data/ext/pg_copy_coder.c +13 -29
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +273 -77
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +44 -1
- data/ext/pg_type_map.c +85 -14
- data/ext/pg_type_map_all_strings.c +16 -13
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +81 -23
- data/ext/pg_type_map_by_mri_type.c +42 -24
- data/ext/pg_type_map_by_oid.c +52 -20
- data/ext/util.c +1 -1
- data/lib/pg.rb +3 -3
- data/lib/pg/basic_type_mapping.rb +13 -13
- data/lib/pg/coder.rb +9 -0
- data/sample/disk_usage_report.rb +1 -1
- data/sample/pg_statistics.rb +1 -1
- data/sample/replication_monitor.rb +1 -1
- data/spec/helpers.rb +5 -3
- data/spec/pg/basic_type_mapping_spec.rb +1 -1
- data/spec/pg/connection_spec.rb +16 -5
- data/spec/pg/result_spec.rb +77 -3
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +87 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +14 -0
- data/spec/pg/type_map_by_oid_spec.rb +21 -0
- data/spec/pg/type_spec.rb +27 -7
- data/spec/pg_spec.rb +14 -0
- metadata +24 -21
- metadata.gz.sig +0 -0
data/ext/pg_errors.c
CHANGED
@@ -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
|
}
|
data/ext/pg_result.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_result.c - PG::Result class extension
|
3
|
-
* $Id
|
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
|
32
|
-
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
32
|
+
t_pg_result *this;
|
33
33
|
|
34
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
42
|
-
t_typemap *p_typemap;
|
45
|
+
PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
|
43
46
|
|
44
|
-
|
45
|
-
|
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
|
-
|
48
|
-
|
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.
|
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
|
-
|
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
|
-
|
821
|
+
int num_tuples = PQntuples(this->pgresult);
|
787
822
|
VALUE tuple;
|
788
823
|
|
789
|
-
if
|
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
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
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
|
853
|
+
t_pg_result *this;
|
811
854
|
int row;
|
812
855
|
int field;
|
813
|
-
int num_rows
|
814
|
-
int num_fields
|
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
|
866
|
+
VALUE row_values[num_fields];
|
818
867
|
|
819
868
|
/* populate the row */
|
820
869
|
for ( field = 0; field < num_fields; field++ ) {
|
821
|
-
|
870
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
822
871
|
}
|
823
|
-
rb_yield(
|
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
|
895
|
+
VALUE row_values[num_fields];
|
847
896
|
|
848
897
|
/* populate the row */
|
849
898
|
for ( field = 0; field < num_fields; field++ ) {
|
850
|
-
|
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,
|
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
|
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
|
-
|
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
|
-
|
952
|
-
|
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
|
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+
|
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(
|
981
|
-
|
982
|
-
|
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
|
|