pg 0.18.0 → 1.1.4
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +1221 -4
- data/History.rdoc +200 -0
- data/Manifest.txt +5 -18
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +27 -10
- data/Rakefile +33 -24
- data/Rakefile.cross +57 -39
- data/ext/errorcodes.def +37 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +16 -1
- data/ext/extconf.rb +29 -35
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +27 -53
- data/ext/pg.h +66 -83
- data/ext/pg_binary_decoder.c +75 -6
- data/ext/pg_binary_encoder.c +14 -12
- data/ext/pg_coder.c +83 -13
- data/ext/pg_connection.c +627 -351
- data/ext/pg_copy_coder.c +44 -9
- data/ext/pg_result.c +364 -134
- data/ext/pg_text_decoder.c +605 -46
- data/ext/pg_text_encoder.c +95 -76
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +20 -13
- data/ext/pg_type_map_by_column.c +7 -7
- data/ext/pg_type_map_by_mri_type.c +2 -2
- data/ext/pg_type_map_in_ruby.c +4 -7
- data/ext/util.c +7 -7
- data/ext/util.h +3 -3
- data/lib/pg/basic_type_mapping.rb +105 -45
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +109 -39
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +11 -6
- data/lib/pg/text_decoder.rb +25 -20
- data/lib/pg/text_encoder.rb +43 -1
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +1 -1
- data/lib/pg.rb +21 -11
- data/spec/helpers.rb +50 -25
- data/spec/pg/basic_type_mapping_spec.rb +287 -30
- data/spec/pg/connection_spec.rb +695 -282
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +59 -17
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +3 -3
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
- 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 +319 -35
- data/spec/pg_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +68 -68
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- 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
|
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
|
}
|
@@ -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
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
*
|
287
|
-
*
|
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 =
|
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() ->
|
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() ->
|
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
|
-
|
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 ) ->
|
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 ) ->
|
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 ) ->
|
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 ) ->
|
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() ->
|
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
|
-
*
|
776
|
-
* *
|
777
|
-
* *
|
778
|
-
* *
|
779
|
-
* *
|
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
|
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
|
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
|
-
|
1054
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1152
|
-
|
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
|
-
|
1205
|
-
|
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
|
-
|
1498
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
1269
1499
|
}
|
1270
1500
|
|
1271
1501
|
|