pg 1.0.0 → 1.1.0.pre20180730144600
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/ChangeLog +0 -6595
- data/History.rdoc +52 -0
- data/README.rdoc +11 -0
- data/Rakefile +1 -1
- data/Rakefile.cross +1 -1
- data/ext/errorcodes.rb +1 -1
- data/ext/extconf.rb +2 -0
- data/ext/pg.c +3 -2
- data/ext/pg.h +33 -5
- data/ext/pg_binary_decoder.c +69 -6
- data/ext/pg_binary_encoder.c +1 -1
- data/ext/pg_coder.c +52 -3
- data/ext/pg_connection.c +290 -103
- data/ext/pg_copy_coder.c +10 -5
- data/ext/pg_result.c +339 -113
- data/ext/pg_text_decoder.c +597 -37
- data/ext/pg_text_encoder.c +1 -1
- data/ext/pg_tuple.c +540 -0
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +1 -1
- data/ext/pg_type_map_by_class.c +1 -1
- data/ext/pg_type_map_by_column.c +1 -1
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +1 -1
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/util.c +6 -6
- data/ext/util.h +2 -2
- data/lib/pg.rb +5 -3
- data/lib/pg/basic_type_mapping.rb +40 -7
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +20 -1
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +1 -1
- data/lib/pg/text_decoder.rb +19 -23
- data/lib/pg/text_encoder.rb +35 -1
- data/lib/pg/type_map_by_column.rb +1 -1
- data/spec/helpers.rb +39 -7
- data/spec/pg/basic_type_mapping_spec.rb +230 -27
- data/spec/pg/connection_spec.rb +116 -77
- data/spec/pg/result_spec.rb +46 -11
- data/spec/pg/type_map_by_class_spec.rb +1 -1
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +1 -1
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +177 -11
- data/spec/pg_spec.rb +1 -1
- metadata +24 -28
- metadata.gz.sig +0 -0
data/ext/pg_copy_coder.c
CHANGED
@@ -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
|
}
|
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
|
|
@@ -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
|
-
|
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
|
-
|
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 =
|
181
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
65
182
|
t_pg_result *this = pgresult_get_this(self);
|
183
|
+
|
184
|
+
/* Autocleared results are freed implicit instead of by PQclear().
|
185
|
+
* So it's not very useful to be accounted by ruby GC.
|
186
|
+
*/
|
187
|
+
this->result_size = 0;
|
66
188
|
this->autoclear = 1;
|
67
189
|
return self;
|
68
190
|
}
|
@@ -131,7 +253,13 @@ pg_result_check( VALUE self )
|
|
131
253
|
* call-seq:
|
132
254
|
* res.clear() -> nil
|
133
255
|
*
|
134
|
-
* Clears the PG::Result object as the result of
|
256
|
+
* Clears the PG::Result object as the result of a query.
|
257
|
+
* This frees all underlying memory consumed by the result object.
|
258
|
+
* Afterwards access to result methods raises PG::Error "result has been cleared".
|
259
|
+
*
|
260
|
+
* Explicit calling #clear can lead to better memory performance, but is not generally necessary.
|
261
|
+
* Special care must be taken when PG::Tuple objects are used.
|
262
|
+
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
135
263
|
*
|
136
264
|
* If PG::Result#autoclear? is true then the result is marked as cleared
|
137
265
|
* and the underlying C struct will be cleared automatically by libpq.
|
@@ -141,9 +269,7 @@ VALUE
|
|
141
269
|
pg_result_clear(VALUE self)
|
142
270
|
{
|
143
271
|
t_pg_result *this = pgresult_get_this(self);
|
144
|
-
|
145
|
-
PQclear(pgresult_get(self));
|
146
|
-
this->pgresult = NULL;
|
272
|
+
pgresult_clear( this );
|
147
273
|
return Qnil;
|
148
274
|
}
|
149
275
|
|
@@ -191,6 +317,7 @@ pgresult_gc_mark( t_pg_result *this )
|
|
191
317
|
rb_gc_mark( this->connection );
|
192
318
|
rb_gc_mark( this->typemap );
|
193
319
|
rb_gc_mark( this->tuple_hash );
|
320
|
+
rb_gc_mark( this->field_map );
|
194
321
|
|
195
322
|
for( i=0; i < this->nfields; i++ ){
|
196
323
|
rb_gc_mark( this->fnames[i] );
|
@@ -204,8 +331,7 @@ static void
|
|
204
331
|
pgresult_gc_free( t_pg_result *this )
|
205
332
|
{
|
206
333
|
if( !this ) return;
|
207
|
-
|
208
|
-
PQclear(this->pgresult);
|
334
|
+
pgresult_clear( this );
|
209
335
|
|
210
336
|
xfree(this);
|
211
337
|
}
|
@@ -239,6 +365,20 @@ pgresult_get(VALUE self)
|
|
239
365
|
return this->pgresult;
|
240
366
|
}
|
241
367
|
|
368
|
+
|
369
|
+
static const rb_data_type_t pgresult_type = {
|
370
|
+
"pg",
|
371
|
+
{
|
372
|
+
(void (*)(void*))pgresult_gc_mark,
|
373
|
+
(void (*)(void*))pgresult_gc_free,
|
374
|
+
(size_t (*)(const void *))pgresult_memsize,
|
375
|
+
},
|
376
|
+
0, 0,
|
377
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
378
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
379
|
+
#endif
|
380
|
+
};
|
381
|
+
|
242
382
|
/*
|
243
383
|
* Document-method: allocate
|
244
384
|
*
|
@@ -248,7 +388,7 @@ pgresult_get(VALUE self)
|
|
248
388
|
static VALUE
|
249
389
|
pgresult_s_allocate( VALUE klass )
|
250
390
|
{
|
251
|
-
VALUE self =
|
391
|
+
VALUE self = TypedData_Wrap_Struct( klass, &pgresult_type, NULL );
|
252
392
|
|
253
393
|
return self;
|
254
394
|
}
|
@@ -279,8 +419,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
279
419
|
*
|
280
420
|
* The class to represent the query result tuples (rows).
|
281
421
|
* An instance of this class is created as the result of every query.
|
282
|
-
*
|
283
|
-
* the
|
422
|
+
*
|
423
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
424
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
284
425
|
*
|
285
426
|
* Example:
|
286
427
|
* require 'pg'
|
@@ -423,7 +564,7 @@ pgresult_ntuples(VALUE self)
|
|
423
564
|
static VALUE
|
424
565
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
425
566
|
{
|
426
|
-
|
567
|
+
return pgresult_ntuples(self);
|
427
568
|
}
|
428
569
|
|
429
570
|
/*
|
@@ -967,6 +1108,78 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
967
1108
|
}
|
968
1109
|
|
969
1110
|
|
1111
|
+
/*
|
1112
|
+
* call-seq:
|
1113
|
+
* res.tuple_values( n ) -> array
|
1114
|
+
*
|
1115
|
+
* Returns an Array of the field values from the nth row of the result.
|
1116
|
+
*
|
1117
|
+
*/
|
1118
|
+
static VALUE
|
1119
|
+
pgresult_tuple_values(VALUE self, VALUE index)
|
1120
|
+
{
|
1121
|
+
int tuple_num = NUM2INT( index );
|
1122
|
+
t_pg_result *this;
|
1123
|
+
int field;
|
1124
|
+
int num_tuples;
|
1125
|
+
int num_fields;
|
1126
|
+
|
1127
|
+
this = pgresult_get_this_safe(self);
|
1128
|
+
num_tuples = PQntuples(this->pgresult);
|
1129
|
+
num_fields = PQnfields(this->pgresult);
|
1130
|
+
|
1131
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1132
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1133
|
+
|
1134
|
+
{
|
1135
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
1136
|
+
|
1137
|
+
/* populate the row */
|
1138
|
+
for ( field = 0; field < num_fields; field++ ) {
|
1139
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
|
1140
|
+
}
|
1141
|
+
return rb_ary_new4( num_fields, row_values );
|
1142
|
+
}
|
1143
|
+
}
|
1144
|
+
|
1145
|
+
/*
|
1146
|
+
* call-seq:
|
1147
|
+
* res.tuple( n ) -> PG::Tuple
|
1148
|
+
*
|
1149
|
+
* Returns a PG::Tuple from the nth row of the result.
|
1150
|
+
*
|
1151
|
+
*/
|
1152
|
+
static VALUE
|
1153
|
+
pgresult_tuple(VALUE self, VALUE index)
|
1154
|
+
{
|
1155
|
+
int tuple_num = NUM2INT( index );
|
1156
|
+
t_pg_result *this;
|
1157
|
+
int num_tuples;
|
1158
|
+
|
1159
|
+
this = pgresult_get_this_safe(self);
|
1160
|
+
num_tuples = PQntuples(this->pgresult);
|
1161
|
+
|
1162
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1163
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1164
|
+
|
1165
|
+
if( this->field_map == Qnil ){
|
1166
|
+
int i;
|
1167
|
+
VALUE field_map = rb_hash_new();
|
1168
|
+
|
1169
|
+
if( this->nfields == -1 )
|
1170
|
+
pgresult_init_fnames( self );
|
1171
|
+
|
1172
|
+
for( i = 0; i < this->nfields; i++ ){
|
1173
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
1174
|
+
}
|
1175
|
+
rb_obj_freeze(field_map);
|
1176
|
+
this->field_map = field_map;
|
1177
|
+
}
|
1178
|
+
|
1179
|
+
return pg_tuple_new(self, tuple_num);
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
|
970
1183
|
/*
|
971
1184
|
* call-seq:
|
972
1185
|
* res.each{ |tuple| ... }
|
@@ -1052,41 +1265,57 @@ pgresult_type_map_get(VALUE self)
|
|
1052
1265
|
return this->typemap;
|
1053
1266
|
}
|
1054
1267
|
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
*
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
*
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1268
|
+
|
1269
|
+
static void
|
1270
|
+
yield_hash(VALUE self, int ntuples, int nfields)
|
1271
|
+
{
|
1272
|
+
int tuple_num;
|
1273
|
+
t_pg_result *this = pgresult_get_this(self);
|
1274
|
+
UNUSED(nfields);
|
1275
|
+
|
1276
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1277
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
pgresult_clear( this );
|
1281
|
+
}
|
1282
|
+
|
1283
|
+
static void
|
1284
|
+
yield_array(VALUE self, int ntuples, int nfields)
|
1285
|
+
{
|
1286
|
+
int row;
|
1287
|
+
t_pg_result *this = pgresult_get_this(self);
|
1288
|
+
|
1289
|
+
for ( row = 0; row < ntuples; row++ ) {
|
1290
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
1291
|
+
int field;
|
1292
|
+
|
1293
|
+
/* populate the row */
|
1294
|
+
for ( field = 0; field < nfields; field++ ) {
|
1295
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1296
|
+
}
|
1297
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
1298
|
+
}
|
1299
|
+
|
1300
|
+
pgresult_clear( this );
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
static void
|
1304
|
+
yield_tuple(VALUE self, int ntuples, int nfields)
|
1305
|
+
{
|
1306
|
+
int tuple_num;
|
1307
|
+
t_pg_result *this = pgresult_get_this(self);
|
1308
|
+
VALUE result = pg_new_result(this->pgresult, this->connection);
|
1309
|
+
UNUSED(nfields);
|
1310
|
+
|
1311
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1312
|
+
VALUE tuple = pgresult_tuple(result, INT2FIX(tuple_num));
|
1313
|
+
rb_yield( tuple );
|
1314
|
+
}
|
1315
|
+
}
|
1316
|
+
|
1088
1317
|
static VALUE
|
1089
|
-
|
1318
|
+
pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
1090
1319
|
{
|
1091
1320
|
t_pg_result *this;
|
1092
1321
|
int nfields;
|
@@ -1101,7 +1330,6 @@ pgresult_stream_each(VALUE self)
|
|
1101
1330
|
nfields = PQnfields(pgresult);
|
1102
1331
|
|
1103
1332
|
for(;;){
|
1104
|
-
int tuple_num;
|
1105
1333
|
int ntuples = PQntuples(pgresult);
|
1106
1334
|
|
1107
1335
|
switch( PQresultStatus(pgresult) ){
|
@@ -1115,14 +1343,7 @@ pgresult_stream_each(VALUE self)
|
|
1115
1343
|
pg_result_check( self );
|
1116
1344
|
}
|
1117
1345
|
|
1118
|
-
|
1119
|
-
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
1120
|
-
}
|
1121
|
-
|
1122
|
-
if( !this->autoclear ){
|
1123
|
-
PQclear( pgresult );
|
1124
|
-
this->pgresult = NULL;
|
1125
|
-
}
|
1346
|
+
yielder( self, ntuples, nfields );
|
1126
1347
|
|
1127
1348
|
pgresult = gvl_PQgetResult(pgconn);
|
1128
1349
|
if( pgresult == NULL )
|
@@ -1138,6 +1359,46 @@ pgresult_stream_each(VALUE self)
|
|
1138
1359
|
return self;
|
1139
1360
|
}
|
1140
1361
|
|
1362
|
+
|
1363
|
+
/*
|
1364
|
+
* call-seq:
|
1365
|
+
* res.stream_each{ |tuple| ... }
|
1366
|
+
*
|
1367
|
+
* Invokes block for each tuple in the result set in single row mode.
|
1368
|
+
*
|
1369
|
+
* This is a convenience method for retrieving all result tuples
|
1370
|
+
* as they are transferred. It is an alternative to repeated calls of
|
1371
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
1372
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
1373
|
+
* the same speed as with ordinary results.
|
1374
|
+
*
|
1375
|
+
* The base result must be in status PGRES_SINGLE_TUPLE.
|
1376
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
1377
|
+
* A PG::Error is raised for any errors from the server.
|
1378
|
+
*
|
1379
|
+
* Row description data does not change while the iteration. All value retrieval
|
1380
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
1381
|
+
* the iteration and +0+ after all tuples were yielded.
|
1382
|
+
*
|
1383
|
+
* Example:
|
1384
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
1385
|
+
* conn.set_single_row_mode
|
1386
|
+
* conn.get_result.stream_each do |row|
|
1387
|
+
* # do something with each received row of the first query
|
1388
|
+
* end
|
1389
|
+
* conn.get_result.stream_each do |row|
|
1390
|
+
* # do something with each received row of the second query
|
1391
|
+
* end
|
1392
|
+
* conn.get_result # => nil (no more results)
|
1393
|
+
*
|
1394
|
+
* Available since PostgreSQL-9.2
|
1395
|
+
*/
|
1396
|
+
static VALUE
|
1397
|
+
pgresult_stream_each(VALUE self)
|
1398
|
+
{
|
1399
|
+
return pgresult_stream_any(self, yield_hash);
|
1400
|
+
}
|
1401
|
+
|
1141
1402
|
/*
|
1142
1403
|
* call-seq:
|
1143
1404
|
* res.stream_each_row { |row| ... }
|
@@ -1153,61 +1414,23 @@ pgresult_stream_each(VALUE self)
|
|
1153
1414
|
static VALUE
|
1154
1415
|
pgresult_stream_each_row(VALUE self)
|
1155
1416
|
{
|
1156
|
-
|
1157
|
-
|
1158
|
-
int nfields;
|
1159
|
-
PGconn *pgconn;
|
1160
|
-
PGresult *pgresult;
|
1161
|
-
|
1162
|
-
RETURN_ENUMERATOR(self, 0, NULL);
|
1163
|
-
|
1164
|
-
this = pgresult_get_this_safe(self);
|
1165
|
-
pgconn = pg_get_pgconn(this->connection);
|
1166
|
-
pgresult = this->pgresult;
|
1167
|
-
nfields = PQnfields(pgresult);
|
1168
|
-
|
1169
|
-
for(;;){
|
1170
|
-
int ntuples = PQntuples(pgresult);
|
1171
|
-
|
1172
|
-
switch( PQresultStatus(pgresult) ){
|
1173
|
-
case PGRES_TUPLES_OK:
|
1174
|
-
if( ntuples == 0 )
|
1175
|
-
return self;
|
1176
|
-
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
1177
|
-
case PGRES_SINGLE_TUPLE:
|
1178
|
-
break;
|
1179
|
-
default:
|
1180
|
-
pg_result_check( self );
|
1181
|
-
}
|
1182
|
-
|
1183
|
-
for ( row = 0; row < ntuples; row++ ) {
|
1184
|
-
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
1185
|
-
int field;
|
1186
|
-
|
1187
|
-
/* populate the row */
|
1188
|
-
for ( field = 0; field < nfields; field++ ) {
|
1189
|
-
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1190
|
-
}
|
1191
|
-
rb_yield( rb_ary_new4( nfields, row_values ));
|
1192
|
-
}
|
1193
|
-
|
1194
|
-
if( !this->autoclear ){
|
1195
|
-
PQclear( pgresult );
|
1196
|
-
this->pgresult = NULL;
|
1197
|
-
}
|
1198
|
-
|
1199
|
-
pgresult = gvl_PQgetResult(pgconn);
|
1200
|
-
if( pgresult == NULL )
|
1201
|
-
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
|
1202
|
-
|
1203
|
-
if( nfields != PQnfields(pgresult) )
|
1204
|
-
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
|
1205
|
-
|
1206
|
-
this->pgresult = pgresult;
|
1207
|
-
}
|
1417
|
+
return pgresult_stream_any(self, yield_array);
|
1418
|
+
}
|
1208
1419
|
|
1209
|
-
|
1210
|
-
|
1420
|
+
/*
|
1421
|
+
* call-seq:
|
1422
|
+
* res.stream_each_tuple { |tuple| ... }
|
1423
|
+
*
|
1424
|
+
* Yields each row of the result set in single row mode.
|
1425
|
+
*
|
1426
|
+
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
1427
|
+
*
|
1428
|
+
* Available since PostgreSQL-9.2
|
1429
|
+
*/
|
1430
|
+
static VALUE
|
1431
|
+
pgresult_stream_each_tuple(VALUE self)
|
1432
|
+
{
|
1433
|
+
return pgresult_stream_any(self, yield_tuple);
|
1211
1434
|
}
|
1212
1435
|
|
1213
1436
|
|
@@ -1259,6 +1482,8 @@ init_pg_result()
|
|
1259
1482
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
1260
1483
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
1261
1484
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
1485
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
1486
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
1262
1487
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
1263
1488
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
1264
1489
|
|
@@ -1268,6 +1493,7 @@ init_pg_result()
|
|
1268
1493
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
1269
1494
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1270
1495
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1496
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
1271
1497
|
}
|
1272
1498
|
|
1273
1499
|
|