pg 0.21.0 → 1.5.3
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/.appveyor.yml +42 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +117 -0
- data/.github/workflows/source-gem.yml +137 -0
- data/.gitignore +22 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/Gemfile +14 -0
- data/History.md +876 -0
- data/Manifest.txt +8 -4
- data/README-Windows.rdoc +4 -4
- data/README.ja.md +276 -0
- data/README.md +286 -0
- data/Rakefile +38 -139
- data/Rakefile.cross +70 -74
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/certs/larskanis-2023.pem +24 -0
- data/ext/errorcodes.def +88 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +24 -2
- data/ext/extconf.rb +111 -54
- data/ext/gvl_wrappers.c +8 -0
- data/ext/gvl_wrappers.h +40 -33
- data/ext/pg.c +224 -199
- data/ext/pg.h +93 -96
- data/ext/pg_binary_decoder.c +162 -16
- data/ext/pg_binary_encoder.c +238 -13
- data/ext/pg_coder.c +159 -35
- data/ext/pg_connection.c +1585 -1046
- data/ext/pg_copy_coder.c +364 -38
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +522 -0
- data/ext/pg_result.c +708 -219
- data/ext/pg_text_decoder.c +627 -43
- data/ext/pg_text_encoder.c +206 -62
- data/ext/pg_tuple.c +572 -0
- data/ext/pg_type_map.c +45 -11
- data/ext/pg_type_map_all_strings.c +21 -7
- data/ext/pg_type_map_by_class.c +59 -27
- data/ext/pg_type_map_by_column.c +80 -37
- data/ext/pg_type_map_by_mri_type.c +49 -20
- data/ext/pg_type_map_by_oid.c +62 -29
- data/ext/pg_type_map_in_ruby.c +56 -22
- data/ext/{util.c → pg_util.c} +12 -12
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg/basic_type_map_based_on_result.rb +67 -0
- data/lib/pg/basic_type_map_for_queries.rb +198 -0
- data/lib/pg/basic_type_map_for_results.rb +104 -0
- data/lib/pg/basic_type_registry.rb +299 -0
- data/lib/pg/binary_decoder/date.rb +9 -0
- data/lib/pg/binary_decoder/timestamp.rb +26 -0
- data/lib/pg/binary_encoder/timestamp.rb +20 -0
- data/lib/pg/coder.rb +36 -13
- data/lib/pg/connection.rb +755 -71
- data/lib/pg/exceptions.rb +16 -2
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder/date.rb +18 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +14 -0
- data/lib/pg/text_decoder/numeric.rb +9 -0
- data/lib/pg/text_decoder/timestamp.rb +30 -0
- data/lib/pg/text_encoder/date.rb +12 -0
- data/lib/pg/text_encoder/inet.rb +28 -0
- data/lib/pg/text_encoder/json.rb +14 -0
- data/lib/pg/text_encoder/numeric.rb +9 -0
- data/lib/pg/text_encoder/timestamp.rb +24 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +96 -43
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +34 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +102 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data/translation/.po4a-version +7 -0
- data/translation/po/all.pot +910 -0
- data/translation/po/ja.po +1047 -0
- data/translation/po4a.cfg +12 -0
- data.tar.gz.sig +0 -0
- metadata +154 -217
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +0 -394
- data/README.ja.rdoc +0 -14
- data/README.rdoc +0 -168
- data/lib/pg/basic_type_mapping.rb +0 -426
- data/lib/pg/constants.rb +0 -11
- data/lib/pg/deprecated_constants.rb +0 -21
- data/lib/pg/text_decoder.rb +0 -51
- data/lib/pg/text_encoder.rb +0 -35
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -352
- data/spec/pg/basic_type_mapping_spec.rb +0 -305
- data/spec/pg/connection_spec.rb +0 -1676
- data/spec/pg/result_spec.rb +0 -456
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -222
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -777
- data/spec/pg_spec.rb +0 -50
data/ext/pg_result.c
CHANGED
|
@@ -1,21 +1,196 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* pg_result.c - PG::Result class extension
|
|
3
|
-
* $Id
|
|
3
|
+
* $Id$
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
#include "pg.h"
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
VALUE rb_cPGresult;
|
|
10
|
+
static VALUE sym_symbol, sym_string, sym_static_symbol;
|
|
11
11
|
|
|
12
|
-
static void pgresult_gc_free( t_pg_result * );
|
|
13
12
|
static VALUE pgresult_type_map_set( VALUE, VALUE );
|
|
14
|
-
static VALUE pgresult_s_allocate( VALUE );
|
|
15
13
|
static t_pg_result *pgresult_get_this( VALUE );
|
|
16
14
|
static t_pg_result *pgresult_get_this_safe( VALUE );
|
|
17
15
|
|
|
16
|
+
#if defined(HAVE_PQRESULTMEMORYSIZE)
|
|
17
|
+
|
|
18
|
+
static ssize_t
|
|
19
|
+
pgresult_approx_size(const PGresult *result)
|
|
20
|
+
{
|
|
21
|
+
return PQresultMemorySize(result);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#else
|
|
25
|
+
|
|
26
|
+
#define PGRESULT_DATA_BLOCKSIZE 2048
|
|
27
|
+
typedef struct pgresAttValue
|
|
28
|
+
{
|
|
29
|
+
int len; /* length in bytes of the value */
|
|
30
|
+
char *value; /* actual value, plus terminating zero byte */
|
|
31
|
+
} PGresAttValue;
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
static int
|
|
35
|
+
count_leading_zero_bits(unsigned int x)
|
|
36
|
+
{
|
|
37
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
38
|
+
return __builtin_clz(x);
|
|
39
|
+
#elif defined(_MSC_VER)
|
|
40
|
+
DWORD r = 0;
|
|
41
|
+
_BitScanForward(&r, x);
|
|
42
|
+
return (int)r;
|
|
43
|
+
#else
|
|
44
|
+
unsigned int a;
|
|
45
|
+
for(a=0; a < sizeof(unsigned int) * 8; a++){
|
|
46
|
+
if( x & (1 << (sizeof(unsigned int) * 8 - 1))) return a;
|
|
47
|
+
x <<= 1;
|
|
48
|
+
}
|
|
49
|
+
return a;
|
|
50
|
+
#endif
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static ssize_t
|
|
54
|
+
pgresult_approx_size(const PGresult *result)
|
|
55
|
+
{
|
|
56
|
+
int num_fields = PQnfields(result);
|
|
57
|
+
ssize_t size = 0;
|
|
58
|
+
|
|
59
|
+
if( num_fields > 0 ){
|
|
60
|
+
int num_tuples = PQntuples(result);
|
|
61
|
+
|
|
62
|
+
if( num_tuples > 0 ){
|
|
63
|
+
int pos;
|
|
64
|
+
|
|
65
|
+
/* 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.
|
|
66
|
+
* 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.
|
|
67
|
+
* The given algorithm has no real scientific background, but is made for speed and typical table layouts.
|
|
68
|
+
*/
|
|
69
|
+
int num_samples =
|
|
70
|
+
(num_fields < 9 ? num_fields : 39 - count_leading_zero_bits(num_fields-8)) *
|
|
71
|
+
(num_tuples < 8 ? 1 : 30 - count_leading_zero_bits(num_tuples));
|
|
72
|
+
|
|
73
|
+
/* start with scanning very last fields, since they are most probably in the cache */
|
|
74
|
+
for( pos = 0; pos < (num_samples+1)/2; pos++ ){
|
|
75
|
+
size += PQgetlength(result, num_tuples - 1 - (pos / num_fields), num_fields - 1 - (pos % num_fields));
|
|
76
|
+
}
|
|
77
|
+
/* scan the very first fields */
|
|
78
|
+
for( pos = 0; pos < num_samples/2; pos++ ){
|
|
79
|
+
size += PQgetlength(result, pos / num_fields, pos % num_fields);
|
|
80
|
+
}
|
|
81
|
+
/* extrapolate sample size to whole result set */
|
|
82
|
+
size = size * num_tuples * num_fields / num_samples;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* count metadata */
|
|
86
|
+
size += num_fields * (
|
|
87
|
+
sizeof(PGresAttDesc) + /* column description */
|
|
88
|
+
num_tuples * (
|
|
89
|
+
sizeof(PGresAttValue) + 1 /* ptr, len and zero termination of each value */
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
/* Account free space due to libpq's default block size */
|
|
94
|
+
size = (size + PGRESULT_DATA_BLOCKSIZE - 1) / PGRESULT_DATA_BLOCKSIZE * PGRESULT_DATA_BLOCKSIZE;
|
|
95
|
+
|
|
96
|
+
/* count tuple pointers */
|
|
97
|
+
size += sizeof(void*) * ((num_tuples + 128 - 1) / 128 * 128);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
size += 216; /* add PGresult size */
|
|
101
|
+
|
|
102
|
+
return size;
|
|
103
|
+
}
|
|
104
|
+
#endif
|
|
105
|
+
|
|
106
|
+
/*
|
|
107
|
+
* GC Mark function
|
|
108
|
+
*/
|
|
109
|
+
static void
|
|
110
|
+
pgresult_gc_mark( void *_this )
|
|
111
|
+
{
|
|
112
|
+
t_pg_result *this = (t_pg_result *)_this;
|
|
113
|
+
int i;
|
|
114
|
+
|
|
115
|
+
rb_gc_mark_movable( this->connection );
|
|
116
|
+
rb_gc_mark_movable( this->typemap );
|
|
117
|
+
rb_gc_mark_movable( this->tuple_hash );
|
|
118
|
+
rb_gc_mark_movable( this->field_map );
|
|
119
|
+
|
|
120
|
+
for( i=0; i < this->nfields; i++ ){
|
|
121
|
+
rb_gc_mark_movable( this->fnames[i] );
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static void
|
|
126
|
+
pgresult_gc_compact( void *_this )
|
|
127
|
+
{
|
|
128
|
+
t_pg_result *this = (t_pg_result *)_this;
|
|
129
|
+
int i;
|
|
130
|
+
|
|
131
|
+
pg_gc_location( this->connection );
|
|
132
|
+
pg_gc_location( this->typemap );
|
|
133
|
+
pg_gc_location( this->tuple_hash );
|
|
134
|
+
pg_gc_location( this->field_map );
|
|
135
|
+
|
|
136
|
+
for( i=0; i < this->nfields; i++ ){
|
|
137
|
+
pg_gc_location( this->fnames[i] );
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* GC Free function
|
|
143
|
+
*/
|
|
144
|
+
static void
|
|
145
|
+
pgresult_clear( void *_this )
|
|
146
|
+
{
|
|
147
|
+
t_pg_result *this = (t_pg_result *)_this;
|
|
148
|
+
if( this->pgresult && !this->autoclear ){
|
|
149
|
+
PQclear(this->pgresult);
|
|
150
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
151
|
+
rb_gc_adjust_memory_usage(-this->result_size);
|
|
152
|
+
#endif
|
|
153
|
+
}
|
|
154
|
+
this->result_size = 0;
|
|
155
|
+
this->nfields = -1;
|
|
156
|
+
this->pgresult = NULL;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static void
|
|
160
|
+
pgresult_gc_free( void *_this )
|
|
161
|
+
{
|
|
162
|
+
t_pg_result *this = (t_pg_result *)_this;
|
|
163
|
+
pgresult_clear( this );
|
|
164
|
+
xfree(this);
|
|
165
|
+
}
|
|
18
166
|
|
|
167
|
+
static size_t
|
|
168
|
+
pgresult_memsize( const void *_this )
|
|
169
|
+
{
|
|
170
|
+
const t_pg_result *this = (const t_pg_result *)_this;
|
|
171
|
+
/* Ideally the memory 'this' is pointing to should be taken into account as well.
|
|
172
|
+
* However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
|
|
173
|
+
*/
|
|
174
|
+
return this->result_size;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static const rb_data_type_t pgresult_type = {
|
|
178
|
+
"PG::Result",
|
|
179
|
+
{
|
|
180
|
+
pgresult_gc_mark,
|
|
181
|
+
pgresult_gc_free,
|
|
182
|
+
pgresult_memsize,
|
|
183
|
+
pg_compact_callback(pgresult_gc_compact),
|
|
184
|
+
},
|
|
185
|
+
0, 0,
|
|
186
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/* Needed by sequel_pg gem, do not delete */
|
|
190
|
+
int pg_get_result_enc_idx(VALUE self)
|
|
191
|
+
{
|
|
192
|
+
return pgresult_get_this(self)->enc_idx;
|
|
193
|
+
}
|
|
19
194
|
|
|
20
195
|
/*
|
|
21
196
|
* Global functions
|
|
@@ -24,45 +199,91 @@ static t_pg_result *pgresult_get_this_safe( VALUE );
|
|
|
24
199
|
/*
|
|
25
200
|
* Result constructor
|
|
26
201
|
*/
|
|
27
|
-
VALUE
|
|
28
|
-
|
|
202
|
+
static VALUE
|
|
203
|
+
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
29
204
|
{
|
|
30
205
|
int nfields = result ? PQnfields(result) : 0;
|
|
31
|
-
VALUE self
|
|
206
|
+
VALUE self;
|
|
32
207
|
t_pg_result *this;
|
|
33
208
|
|
|
34
209
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
|
35
|
-
DATA_PTR(self) = this;
|
|
36
|
-
|
|
37
210
|
this->pgresult = result;
|
|
211
|
+
/* Initialize connection and typemap prior to any object allocations,
|
|
212
|
+
* to make sure valid objects are marked. */
|
|
38
213
|
this->connection = rb_pgconn;
|
|
39
214
|
this->typemap = pg_typemap_all_strings;
|
|
40
|
-
this->p_typemap =
|
|
41
|
-
this->autoclear = 0;
|
|
215
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
42
216
|
this->nfields = -1;
|
|
43
217
|
this->tuple_hash = Qnil;
|
|
44
|
-
|
|
45
|
-
|
|
218
|
+
this->field_map = Qnil;
|
|
219
|
+
this->flags = 0;
|
|
220
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
|
46
221
|
|
|
47
222
|
if( result ){
|
|
48
223
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
|
49
224
|
VALUE typemap = p_conn->type_map_for_results;
|
|
50
|
-
|
|
51
225
|
/* Type check is done when assigned to PG::Connection. */
|
|
52
|
-
t_typemap *p_typemap =
|
|
53
|
-
|
|
54
|
-
this->
|
|
55
|
-
|
|
226
|
+
t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
|
|
227
|
+
|
|
228
|
+
this->enc_idx = p_conn->enc_idx;
|
|
229
|
+
typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
|
230
|
+
RB_OBJ_WRITE(self, &this->typemap, typemap);
|
|
231
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
232
|
+
this->flags = p_conn->flags;
|
|
233
|
+
} else {
|
|
234
|
+
this->enc_idx = rb_locale_encindex();
|
|
56
235
|
}
|
|
57
236
|
|
|
58
237
|
return self;
|
|
59
238
|
}
|
|
60
239
|
|
|
240
|
+
VALUE
|
|
241
|
+
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
242
|
+
{
|
|
243
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
244
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
245
|
+
|
|
246
|
+
this->autoclear = 0;
|
|
247
|
+
|
|
248
|
+
/* Estimate size of underlying pgresult memory storage and account to ruby GC.
|
|
249
|
+
* There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
|
|
250
|
+
*/
|
|
251
|
+
/* TODO: If someday most systems provide PQresultMemorySize(), it's questionable to store result_size in t_pg_result in addition to the value already stored in PGresult.
|
|
252
|
+
* For now the memory savings don't justify the ifdefs necessary to support both cases.
|
|
253
|
+
*/
|
|
254
|
+
this->result_size = pgresult_approx_size(result);
|
|
255
|
+
|
|
256
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
257
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
|
258
|
+
#endif
|
|
259
|
+
|
|
260
|
+
return self;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static VALUE
|
|
264
|
+
pg_copy_result(t_pg_result *this)
|
|
265
|
+
{
|
|
266
|
+
int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
|
|
267
|
+
size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
|
|
268
|
+
t_pg_result *copy;
|
|
269
|
+
|
|
270
|
+
copy = (t_pg_result *)xmalloc(len);
|
|
271
|
+
memcpy(copy, this, len);
|
|
272
|
+
this->result_size = 0;
|
|
273
|
+
|
|
274
|
+
return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
|
|
275
|
+
}
|
|
276
|
+
|
|
61
277
|
VALUE
|
|
62
278
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
63
279
|
{
|
|
64
|
-
VALUE self =
|
|
280
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
65
281
|
t_pg_result *this = pgresult_get_this(self);
|
|
282
|
+
|
|
283
|
+
/* Autocleared results are freed implicit instead of by PQclear().
|
|
284
|
+
* So it's not very useful to be accounted by ruby GC.
|
|
285
|
+
*/
|
|
286
|
+
this->result_size = 0;
|
|
66
287
|
this->autoclear = 1;
|
|
67
288
|
return self;
|
|
68
289
|
}
|
|
@@ -71,7 +292,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
|
71
292
|
* call-seq:
|
|
72
293
|
* res.check -> nil
|
|
73
294
|
*
|
|
74
|
-
* Raises appropriate exception if PG::Result is in a bad state
|
|
295
|
+
* Raises appropriate exception if PG::Result is in a bad state, which is:
|
|
296
|
+
* * +PGRES_BAD_RESPONSE+
|
|
297
|
+
* * +PGRES_FATAL_ERROR+
|
|
298
|
+
* * +PGRES_NONFATAL_ERROR+
|
|
299
|
+
* * +PGRES_PIPELINE_ABORTED+
|
|
75
300
|
*/
|
|
76
301
|
VALUE
|
|
77
302
|
pg_result_check( VALUE self )
|
|
@@ -92,18 +317,20 @@ pg_result_check( VALUE self )
|
|
|
92
317
|
case PGRES_TUPLES_OK:
|
|
93
318
|
case PGRES_COPY_OUT:
|
|
94
319
|
case PGRES_COPY_IN:
|
|
95
|
-
#ifdef HAVE_CONST_PGRES_COPY_BOTH
|
|
96
320
|
case PGRES_COPY_BOTH:
|
|
97
|
-
#endif
|
|
98
|
-
#ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
|
|
99
321
|
case PGRES_SINGLE_TUPLE:
|
|
100
|
-
#endif
|
|
101
322
|
case PGRES_EMPTY_QUERY:
|
|
102
323
|
case PGRES_COMMAND_OK:
|
|
324
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
325
|
+
case PGRES_PIPELINE_SYNC:
|
|
326
|
+
#endif
|
|
103
327
|
return self;
|
|
104
328
|
case PGRES_BAD_RESPONSE:
|
|
105
329
|
case PGRES_FATAL_ERROR:
|
|
106
330
|
case PGRES_NONFATAL_ERROR:
|
|
331
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
332
|
+
case PGRES_PIPELINE_ABORTED:
|
|
333
|
+
#endif
|
|
107
334
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
|
108
335
|
break;
|
|
109
336
|
default:
|
|
@@ -111,7 +338,7 @@ pg_result_check( VALUE self )
|
|
|
111
338
|
}
|
|
112
339
|
}
|
|
113
340
|
|
|
114
|
-
PG_ENCODING_SET_NOCHECK( error,
|
|
341
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
|
115
342
|
|
|
116
343
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
|
117
344
|
klass = lookup_error_class( sqlstate );
|
|
@@ -135,29 +362,52 @@ pg_result_check( VALUE self )
|
|
|
135
362
|
* call-seq:
|
|
136
363
|
* res.clear() -> nil
|
|
137
364
|
*
|
|
138
|
-
* Clears the PG::Result object as the result of
|
|
365
|
+
* Clears the PG::Result object as the result of a query.
|
|
366
|
+
* This frees all underlying memory consumed by the result object.
|
|
367
|
+
* Afterwards access to result methods raises PG::Error "result has been cleared".
|
|
139
368
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
369
|
+
* Explicit calling #clear can lead to better memory performance, but is not generally necessary.
|
|
370
|
+
* Special care must be taken when PG::Tuple objects are used.
|
|
371
|
+
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
|
372
|
+
*
|
|
373
|
+
* If PG::Result#autoclear? is +true+ then the result is only marked as cleared but clearing the underlying C struct will happen when the callback returns.
|
|
142
374
|
*
|
|
143
375
|
*/
|
|
144
376
|
VALUE
|
|
145
377
|
pg_result_clear(VALUE self)
|
|
146
378
|
{
|
|
147
379
|
t_pg_result *this = pgresult_get_this(self);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this->pgresult = NULL;
|
|
380
|
+
rb_check_frozen(self);
|
|
381
|
+
pgresult_clear( this );
|
|
151
382
|
return Qnil;
|
|
152
383
|
}
|
|
153
384
|
|
|
154
385
|
/*
|
|
155
386
|
* call-seq:
|
|
156
|
-
* res.
|
|
387
|
+
* res.freeze
|
|
388
|
+
*
|
|
389
|
+
* Freeze the PG::Result object and unlink the result from the related PG::Connection.
|
|
390
|
+
*
|
|
391
|
+
* A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
|
|
392
|
+
* It also denies setting a type_map or field_name_type.
|
|
157
393
|
*
|
|
158
|
-
* Returns +true+ if the backend result memory has been free'd.
|
|
159
394
|
*/
|
|
160
395
|
VALUE
|
|
396
|
+
static pg_result_freeze(VALUE self)
|
|
397
|
+
{
|
|
398
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
399
|
+
|
|
400
|
+
RB_OBJ_WRITE(self, &this->connection, Qnil);
|
|
401
|
+
return rb_call_super(0, NULL);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/*
|
|
405
|
+
* call-seq:
|
|
406
|
+
* res.cleared? -> boolean
|
|
407
|
+
*
|
|
408
|
+
* Returns +true+ if the backend result memory has been freed.
|
|
409
|
+
*/
|
|
410
|
+
static VALUE
|
|
161
411
|
pgresult_cleared_p( VALUE self )
|
|
162
412
|
{
|
|
163
413
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -168,11 +418,13 @@ pgresult_cleared_p( VALUE self )
|
|
|
168
418
|
* call-seq:
|
|
169
419
|
* res.autoclear? -> boolean
|
|
170
420
|
*
|
|
171
|
-
* Returns +true+ if the underlying C struct will be cleared
|
|
172
|
-
*
|
|
421
|
+
* Returns +true+ if the underlying C struct will be cleared at the end of a callback.
|
|
422
|
+
* This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
|
|
423
|
+
*
|
|
424
|
+
* All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
|
|
173
425
|
*
|
|
174
426
|
*/
|
|
175
|
-
VALUE
|
|
427
|
+
static VALUE
|
|
176
428
|
pgresult_autoclear_p( VALUE self )
|
|
177
429
|
{
|
|
178
430
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -183,37 +435,6 @@ pgresult_autoclear_p( VALUE self )
|
|
|
183
435
|
* DATA pointer functions
|
|
184
436
|
*/
|
|
185
437
|
|
|
186
|
-
/*
|
|
187
|
-
* GC Mark function
|
|
188
|
-
*/
|
|
189
|
-
static void
|
|
190
|
-
pgresult_gc_mark( t_pg_result *this )
|
|
191
|
-
{
|
|
192
|
-
int i;
|
|
193
|
-
|
|
194
|
-
if( !this ) return;
|
|
195
|
-
rb_gc_mark( this->connection );
|
|
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
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/*
|
|
205
|
-
* GC Free function
|
|
206
|
-
*/
|
|
207
|
-
static void
|
|
208
|
-
pgresult_gc_free( t_pg_result *this )
|
|
209
|
-
{
|
|
210
|
-
if( !this ) return;
|
|
211
|
-
if(this->pgresult != NULL && !this->autoclear)
|
|
212
|
-
PQclear(this->pgresult);
|
|
213
|
-
|
|
214
|
-
xfree(this);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
438
|
/*
|
|
218
439
|
* Fetch the PG::Result object data pointer and check it's
|
|
219
440
|
* PGresult data pointer for sanity.
|
|
@@ -243,18 +464,30 @@ pgresult_get(VALUE self)
|
|
|
243
464
|
return this->pgresult;
|
|
244
465
|
}
|
|
245
466
|
|
|
246
|
-
|
|
247
|
-
* Document-method: allocate
|
|
248
|
-
*
|
|
249
|
-
* call-seq:
|
|
250
|
-
* PG::Result.allocate -> result
|
|
251
|
-
*/
|
|
252
|
-
static VALUE
|
|
253
|
-
pgresult_s_allocate( VALUE klass )
|
|
467
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
254
468
|
{
|
|
255
|
-
VALUE
|
|
256
|
-
|
|
257
|
-
|
|
469
|
+
VALUE fname;
|
|
470
|
+
#ifdef TRUFFLERUBY
|
|
471
|
+
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
|
472
|
+
#else
|
|
473
|
+
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
474
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
475
|
+
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
|
476
|
+
if( fname == Qnil ){
|
|
477
|
+
fname = rb_str_new2(cstr);
|
|
478
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
479
|
+
fname = rb_str_intern(fname);
|
|
480
|
+
}
|
|
481
|
+
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
482
|
+
#endif
|
|
483
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
484
|
+
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
|
485
|
+
} else {
|
|
486
|
+
fname = rb_str_new2(cstr);
|
|
487
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
488
|
+
fname = rb_obj_freeze(fname);
|
|
489
|
+
}
|
|
490
|
+
return fname;
|
|
258
491
|
}
|
|
259
492
|
|
|
260
493
|
static void pgresult_init_fnames(VALUE self)
|
|
@@ -266,12 +499,10 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
266
499
|
int nfields = PQnfields(this->pgresult);
|
|
267
500
|
|
|
268
501
|
for( i=0; i<nfields; i++ ){
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this->fnames[i]
|
|
502
|
+
char *cfname = PQfname(this->pgresult, i);
|
|
503
|
+
VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
504
|
+
RB_OBJ_WRITE(self, &this->fnames[i], fname);
|
|
272
505
|
this->nfields = i + 1;
|
|
273
|
-
|
|
274
|
-
RB_GC_GUARD(fname);
|
|
275
506
|
}
|
|
276
507
|
this->nfields = nfields;
|
|
277
508
|
}
|
|
@@ -283,8 +514,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
283
514
|
*
|
|
284
515
|
* The class to represent the query result tuples (rows).
|
|
285
516
|
* An instance of this class is created as the result of every query.
|
|
286
|
-
*
|
|
287
|
-
*
|
|
517
|
+
* All result rows and columns are stored in a memory block attached to the PG::Result object.
|
|
518
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
|
519
|
+
*
|
|
520
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
|
521
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
288
522
|
*
|
|
289
523
|
* Example:
|
|
290
524
|
* require 'pg'
|
|
@@ -314,6 +548,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
314
548
|
* * +PGRES_NONFATAL_ERROR+
|
|
315
549
|
* * +PGRES_FATAL_ERROR+
|
|
316
550
|
* * +PGRES_COPY_BOTH+
|
|
551
|
+
* * +PGRES_SINGLE_TUPLE+
|
|
552
|
+
* * +PGRES_PIPELINE_SYNC+
|
|
553
|
+
* * +PGRES_PIPELINE_ABORTED+
|
|
554
|
+
*
|
|
555
|
+
* Use <tt>res.res_status</tt> to retrieve the string representation.
|
|
317
556
|
*/
|
|
318
557
|
static VALUE
|
|
319
558
|
pgresult_result_status(VALUE self)
|
|
@@ -323,16 +562,39 @@ pgresult_result_status(VALUE self)
|
|
|
323
562
|
|
|
324
563
|
/*
|
|
325
564
|
* call-seq:
|
|
326
|
-
*
|
|
565
|
+
* PG::Result.res_status( status ) -> String
|
|
327
566
|
*
|
|
328
|
-
* Returns the string representation of
|
|
567
|
+
* Returns the string representation of +status+.
|
|
329
568
|
*
|
|
330
569
|
*/
|
|
331
570
|
static VALUE
|
|
332
|
-
|
|
571
|
+
pgresult_s_res_status(VALUE self, VALUE status)
|
|
572
|
+
{
|
|
573
|
+
return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* call-seq:
|
|
578
|
+
* res.res_status -> String
|
|
579
|
+
* res.res_status( status ) -> String
|
|
580
|
+
*
|
|
581
|
+
* Returns the string representation of the status of the result or of the provided +status+.
|
|
582
|
+
*
|
|
583
|
+
*/
|
|
584
|
+
static VALUE
|
|
585
|
+
pgresult_res_status(int argc, VALUE *argv, VALUE self)
|
|
333
586
|
{
|
|
334
|
-
|
|
335
|
-
|
|
587
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
588
|
+
VALUE ret;
|
|
589
|
+
|
|
590
|
+
if( argc == 0 ){
|
|
591
|
+
ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
|
|
592
|
+
}else if( argc == 1 ){
|
|
593
|
+
ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
|
|
594
|
+
}else{
|
|
595
|
+
rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
|
|
596
|
+
}
|
|
597
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
336
598
|
return ret;
|
|
337
599
|
}
|
|
338
600
|
|
|
@@ -345,11 +607,40 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
|
345
607
|
static VALUE
|
|
346
608
|
pgresult_error_message(VALUE self)
|
|
347
609
|
{
|
|
348
|
-
|
|
349
|
-
|
|
610
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
611
|
+
VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
|
|
612
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
350
613
|
return ret;
|
|
351
614
|
}
|
|
352
615
|
|
|
616
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
617
|
+
/*
|
|
618
|
+
* call-seq:
|
|
619
|
+
* res.verbose_error_message( verbosity, show_context ) -> String
|
|
620
|
+
*
|
|
621
|
+
* Returns a reformatted version of the error message associated with a PGresult object.
|
|
622
|
+
*
|
|
623
|
+
* Available since PostgreSQL-9.6
|
|
624
|
+
*/
|
|
625
|
+
static VALUE
|
|
626
|
+
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
627
|
+
{
|
|
628
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
629
|
+
VALUE ret;
|
|
630
|
+
char *c_str;
|
|
631
|
+
|
|
632
|
+
c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
|
|
633
|
+
if(!c_str)
|
|
634
|
+
rb_raise(rb_eNoMemError, "insufficient memory to format error message");
|
|
635
|
+
|
|
636
|
+
ret = rb_str_new2(c_str);
|
|
637
|
+
PQfreemem(c_str);
|
|
638
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
639
|
+
|
|
640
|
+
return ret;
|
|
641
|
+
}
|
|
642
|
+
#endif
|
|
643
|
+
|
|
353
644
|
/*
|
|
354
645
|
* call-seq:
|
|
355
646
|
* res.error_field(fieldcode) -> String
|
|
@@ -399,14 +690,14 @@ pgresult_error_message(VALUE self)
|
|
|
399
690
|
static VALUE
|
|
400
691
|
pgresult_error_field(VALUE self, VALUE field)
|
|
401
692
|
{
|
|
402
|
-
|
|
693
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
403
694
|
int fieldcode = NUM2INT( field );
|
|
404
|
-
char * fieldstr = PQresultErrorField(
|
|
695
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
|
405
696
|
VALUE ret = Qnil;
|
|
406
697
|
|
|
407
698
|
if ( fieldstr ) {
|
|
408
|
-
ret =
|
|
409
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
|
699
|
+
ret = rb_str_new2( fieldstr );
|
|
700
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
|
410
701
|
}
|
|
411
702
|
|
|
412
703
|
return ret;
|
|
@@ -427,7 +718,7 @@ pgresult_ntuples(VALUE self)
|
|
|
427
718
|
static VALUE
|
|
428
719
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
|
429
720
|
{
|
|
430
|
-
|
|
721
|
+
return pgresult_ntuples(self);
|
|
431
722
|
}
|
|
432
723
|
|
|
433
724
|
/*
|
|
@@ -444,24 +735,40 @@ pgresult_nfields(VALUE self)
|
|
|
444
735
|
|
|
445
736
|
/*
|
|
446
737
|
* call-seq:
|
|
447
|
-
* res.
|
|
738
|
+
* res.binary_tuples() -> Integer
|
|
739
|
+
*
|
|
740
|
+
* Returns 1 if the PGresult contains binary data and 0 if it contains text data.
|
|
741
|
+
*
|
|
742
|
+
* This function is deprecated (except for its use in connection with COPY), because it is possible for a single PGresult to contain text data in some columns and binary data in others.
|
|
743
|
+
* Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
|
|
744
|
+
*/
|
|
745
|
+
static VALUE
|
|
746
|
+
pgresult_binary_tuples(VALUE self)
|
|
747
|
+
{
|
|
748
|
+
return INT2NUM(PQbinaryTuples(pgresult_get(self)));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/*
|
|
752
|
+
* call-seq:
|
|
753
|
+
* res.fname( index ) -> String or Symbol
|
|
448
754
|
*
|
|
449
755
|
* Returns the name of the column corresponding to _index_.
|
|
756
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
|
757
|
+
*
|
|
450
758
|
*/
|
|
451
759
|
static VALUE
|
|
452
760
|
pgresult_fname(VALUE self, VALUE index)
|
|
453
761
|
{
|
|
454
|
-
|
|
455
|
-
PGresult *result = pgresult_get(self);
|
|
762
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
456
763
|
int i = NUM2INT(index);
|
|
764
|
+
char *cfname;
|
|
457
765
|
|
|
458
|
-
if (i < 0 || i >= PQnfields(
|
|
766
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
|
459
767
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
|
460
768
|
}
|
|
461
769
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return rb_obj_freeze(fname);
|
|
770
|
+
cfname = PQfname(this->pgresult, i);
|
|
771
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
465
772
|
}
|
|
466
773
|
|
|
467
774
|
/*
|
|
@@ -760,8 +1067,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
|
760
1067
|
static VALUE
|
|
761
1068
|
pgresult_cmd_status(VALUE self)
|
|
762
1069
|
{
|
|
763
|
-
|
|
764
|
-
|
|
1070
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
1071
|
+
VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
|
|
1072
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
765
1073
|
return ret;
|
|
766
1074
|
}
|
|
767
1075
|
|
|
@@ -790,7 +1098,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
|
790
1098
|
{
|
|
791
1099
|
long n;
|
|
792
1100
|
n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
|
|
793
|
-
return
|
|
1101
|
+
return LONG2NUM(n);
|
|
794
1102
|
}
|
|
795
1103
|
|
|
796
1104
|
/*
|
|
@@ -842,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
842
1150
|
}
|
|
843
1151
|
/* Store a copy of the filled hash for use at the next row. */
|
|
844
1152
|
if( num_tuples > 10 )
|
|
845
|
-
this->tuple_hash
|
|
1153
|
+
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
|
846
1154
|
|
|
847
1155
|
return tuple;
|
|
848
1156
|
}
|
|
@@ -961,8 +1269,12 @@ static VALUE
|
|
|
961
1269
|
pgresult_field_values( VALUE self, VALUE field )
|
|
962
1270
|
{
|
|
963
1271
|
PGresult *result = pgresult_get( self );
|
|
964
|
-
const char *fieldname
|
|
965
|
-
int fnum
|
|
1272
|
+
const char *fieldname;
|
|
1273
|
+
int fnum;
|
|
1274
|
+
|
|
1275
|
+
if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
|
|
1276
|
+
fieldname = StringValueCStr( field );
|
|
1277
|
+
fnum = PQfnumber( result, fieldname );
|
|
966
1278
|
|
|
967
1279
|
if ( fnum < 0 )
|
|
968
1280
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
|
@@ -971,6 +1283,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
|
971
1283
|
}
|
|
972
1284
|
|
|
973
1285
|
|
|
1286
|
+
/*
|
|
1287
|
+
* call-seq:
|
|
1288
|
+
* res.tuple_values( n ) -> array
|
|
1289
|
+
*
|
|
1290
|
+
* Returns an Array of the field values from the nth row of the result.
|
|
1291
|
+
*
|
|
1292
|
+
*/
|
|
1293
|
+
static VALUE
|
|
1294
|
+
pgresult_tuple_values(VALUE self, VALUE index)
|
|
1295
|
+
{
|
|
1296
|
+
int tuple_num = NUM2INT( index );
|
|
1297
|
+
t_pg_result *this;
|
|
1298
|
+
int field;
|
|
1299
|
+
int num_tuples;
|
|
1300
|
+
int num_fields;
|
|
1301
|
+
|
|
1302
|
+
this = pgresult_get_this_safe(self);
|
|
1303
|
+
num_tuples = PQntuples(this->pgresult);
|
|
1304
|
+
num_fields = PQnfields(this->pgresult);
|
|
1305
|
+
|
|
1306
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1307
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1308
|
+
|
|
1309
|
+
{
|
|
1310
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
|
1311
|
+
|
|
1312
|
+
/* populate the row */
|
|
1313
|
+
for ( field = 0; field < num_fields; field++ ) {
|
|
1314
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
|
|
1315
|
+
}
|
|
1316
|
+
return rb_ary_new4( num_fields, row_values );
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
static void ensure_init_for_tuple(VALUE self)
|
|
1321
|
+
{
|
|
1322
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
1323
|
+
|
|
1324
|
+
if( this->field_map == Qnil ){
|
|
1325
|
+
int i;
|
|
1326
|
+
VALUE field_map = rb_hash_new();
|
|
1327
|
+
|
|
1328
|
+
if( this->nfields == -1 )
|
|
1329
|
+
pgresult_init_fnames( self );
|
|
1330
|
+
|
|
1331
|
+
for( i = 0; i < this->nfields; i++ ){
|
|
1332
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
|
1333
|
+
}
|
|
1334
|
+
rb_obj_freeze(field_map);
|
|
1335
|
+
RB_OBJ_WRITE(self, &this->field_map, field_map);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/*
|
|
1340
|
+
* call-seq:
|
|
1341
|
+
* res.tuple( n ) -> PG::Tuple
|
|
1342
|
+
*
|
|
1343
|
+
* Returns a PG::Tuple from the nth row of the result.
|
|
1344
|
+
*
|
|
1345
|
+
*/
|
|
1346
|
+
static VALUE
|
|
1347
|
+
pgresult_tuple(VALUE self, VALUE index)
|
|
1348
|
+
{
|
|
1349
|
+
int tuple_num = NUM2INT( index );
|
|
1350
|
+
t_pg_result *this;
|
|
1351
|
+
int num_tuples;
|
|
1352
|
+
|
|
1353
|
+
this = pgresult_get_this_safe(self);
|
|
1354
|
+
num_tuples = PQntuples(this->pgresult);
|
|
1355
|
+
|
|
1356
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1357
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1358
|
+
|
|
1359
|
+
ensure_init_for_tuple(self);
|
|
1360
|
+
|
|
1361
|
+
return pg_tuple_new(self, tuple_num);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
|
|
974
1365
|
/*
|
|
975
1366
|
* call-seq:
|
|
976
1367
|
* res.each{ |tuple| ... }
|
|
@@ -997,7 +1388,7 @@ pgresult_each(VALUE self)
|
|
|
997
1388
|
* call-seq:
|
|
998
1389
|
* res.fields() -> Array
|
|
999
1390
|
*
|
|
1000
|
-
*
|
|
1391
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
1001
1392
|
*/
|
|
1002
1393
|
static VALUE
|
|
1003
1394
|
pgresult_fields(VALUE self)
|
|
@@ -1029,14 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1029
1420
|
t_pg_result *this = pgresult_get_this(self);
|
|
1030
1421
|
t_typemap *p_typemap;
|
|
1031
1422
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
}
|
|
1036
|
-
Data_Get_Struct(typemap, t_typemap, p_typemap);
|
|
1423
|
+
rb_check_frozen(self);
|
|
1424
|
+
/* Check type of method param */
|
|
1425
|
+
TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
|
|
1037
1426
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1427
|
+
typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
|
1428
|
+
RB_OBJ_WRITE(self, &this->typemap, typemap);
|
|
1429
|
+
this->p_typemap = RTYPEDDATA_DATA( typemap );
|
|
1040
1430
|
|
|
1041
1431
|
return typemap;
|
|
1042
1432
|
}
|
|
@@ -1056,46 +1446,71 @@ pgresult_type_map_get(VALUE self)
|
|
|
1056
1446
|
return this->typemap;
|
|
1057
1447
|
}
|
|
1058
1448
|
|
|
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
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
*/
|
|
1091
|
-
|
|
1092
|
-
|
|
1449
|
+
|
|
1450
|
+
static int
|
|
1451
|
+
yield_hash(VALUE self, int ntuples, int nfields, void *data)
|
|
1452
|
+
{
|
|
1453
|
+
int tuple_num;
|
|
1454
|
+
UNUSED(nfields);
|
|
1455
|
+
|
|
1456
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1457
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
return 1; /* clear the result */
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
static int
|
|
1464
|
+
yield_array(VALUE self, int ntuples, int nfields, void *data)
|
|
1465
|
+
{
|
|
1466
|
+
int row;
|
|
1467
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1468
|
+
|
|
1469
|
+
for ( row = 0; row < ntuples; row++ ) {
|
|
1470
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
|
1471
|
+
int field;
|
|
1472
|
+
|
|
1473
|
+
/* populate the row */
|
|
1474
|
+
for ( field = 0; field < nfields; field++ ) {
|
|
1475
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
|
1476
|
+
}
|
|
1477
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
return 1; /* clear the result */
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
static int
|
|
1484
|
+
yield_tuple(VALUE self, int ntuples, int nfields, void *data)
|
|
1485
|
+
{
|
|
1486
|
+
int tuple_num;
|
|
1487
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1488
|
+
VALUE copy;
|
|
1489
|
+
UNUSED(nfields);
|
|
1490
|
+
|
|
1491
|
+
/* make a copy of the base result, that is bound to the PG::Tuple */
|
|
1492
|
+
copy = pg_copy_result(this);
|
|
1493
|
+
/* The copy is now owner of the PGresult and is responsible to PQclear it.
|
|
1494
|
+
* We clear the pgresult here, so that it's not double freed on error within yield. */
|
|
1495
|
+
this->pgresult = NULL;
|
|
1496
|
+
|
|
1497
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1498
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
|
1499
|
+
rb_yield( tuple );
|
|
1500
|
+
}
|
|
1501
|
+
return 0; /* don't clear the result */
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/* Non-static, and data pointer for use by sequel_pg */
|
|
1505
|
+
VALUE
|
|
1506
|
+
pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
|
|
1093
1507
|
{
|
|
1094
1508
|
t_pg_result *this;
|
|
1095
|
-
int nfields;
|
|
1509
|
+
int nfields, nfields2;
|
|
1096
1510
|
PGconn *pgconn;
|
|
1097
1511
|
PGresult *pgresult;
|
|
1098
1512
|
|
|
1513
|
+
rb_check_frozen(self);
|
|
1099
1514
|
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1100
1515
|
|
|
1101
1516
|
this = pgresult_get_this_safe(self);
|
|
@@ -1104,11 +1519,11 @@ pgresult_stream_each(VALUE self)
|
|
|
1104
1519
|
nfields = PQnfields(pgresult);
|
|
1105
1520
|
|
|
1106
1521
|
for(;;){
|
|
1107
|
-
int tuple_num;
|
|
1108
1522
|
int ntuples = PQntuples(pgresult);
|
|
1109
1523
|
|
|
1110
1524
|
switch( PQresultStatus(pgresult) ){
|
|
1111
1525
|
case PGRES_TUPLES_OK:
|
|
1526
|
+
case PGRES_COMMAND_OK:
|
|
1112
1527
|
if( ntuples == 0 )
|
|
1113
1528
|
return self;
|
|
1114
1529
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
@@ -1118,21 +1533,24 @@ pgresult_stream_each(VALUE self)
|
|
|
1118
1533
|
pg_result_check( self );
|
|
1119
1534
|
}
|
|
1120
1535
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1536
|
+
nfields2 = PQnfields(pgresult);
|
|
1537
|
+
if( nfields != nfields2 ){
|
|
1538
|
+
pgresult_clear( this );
|
|
1539
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, nfields2);
|
|
1123
1540
|
}
|
|
1124
1541
|
|
|
1125
|
-
if(
|
|
1126
|
-
|
|
1127
|
-
|
|
1542
|
+
if( yielder( self, ntuples, nfields, data ) ){
|
|
1543
|
+
pgresult_clear( this );
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
if( gvl_PQisBusy(pgconn) ){
|
|
1547
|
+
/* wait for input (without blocking) before reading each result */
|
|
1548
|
+
pgconn_block( 0, NULL, this->connection );
|
|
1128
1549
|
}
|
|
1129
1550
|
|
|
1130
1551
|
pgresult = gvl_PQgetResult(pgconn);
|
|
1131
1552
|
if( pgresult == NULL )
|
|
1132
|
-
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another
|
|
1133
|
-
|
|
1134
|
-
if( nfields != PQnfields(pgresult) )
|
|
1135
|
-
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
|
|
1553
|
+
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
|
|
1136
1554
|
|
|
1137
1555
|
this->pgresult = pgresult;
|
|
1138
1556
|
}
|
|
@@ -1141,6 +1559,44 @@ pgresult_stream_each(VALUE self)
|
|
|
1141
1559
|
return self;
|
|
1142
1560
|
}
|
|
1143
1561
|
|
|
1562
|
+
|
|
1563
|
+
/*
|
|
1564
|
+
* call-seq:
|
|
1565
|
+
* res.stream_each{ |tuple| ... }
|
|
1566
|
+
*
|
|
1567
|
+
* Invokes block for each tuple in the result set in single row mode.
|
|
1568
|
+
*
|
|
1569
|
+
* This is a convenience method for retrieving all result tuples
|
|
1570
|
+
* as they are transferred. It is an alternative to repeated calls of
|
|
1571
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
|
1572
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
|
1573
|
+
* the same speed as with ordinary results.
|
|
1574
|
+
*
|
|
1575
|
+
* The base result must be in status PGRES_SINGLE_TUPLE.
|
|
1576
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
|
1577
|
+
* A PG::Error is raised for any errors from the server.
|
|
1578
|
+
*
|
|
1579
|
+
* Row description data does not change while the iteration. All value retrieval
|
|
1580
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
|
1581
|
+
* the iteration and +0+ after all tuples were yielded.
|
|
1582
|
+
*
|
|
1583
|
+
* Example:
|
|
1584
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
|
1585
|
+
* conn.set_single_row_mode
|
|
1586
|
+
* conn.get_result.stream_each do |row|
|
|
1587
|
+
* # do something with each received row of the first query
|
|
1588
|
+
* end
|
|
1589
|
+
* conn.get_result.stream_each do |row|
|
|
1590
|
+
* # do something with each received row of the second query
|
|
1591
|
+
* end
|
|
1592
|
+
* conn.get_result # => nil (no more results)
|
|
1593
|
+
*/
|
|
1594
|
+
static VALUE
|
|
1595
|
+
pgresult_stream_each(VALUE self)
|
|
1596
|
+
{
|
|
1597
|
+
return pgresult_stream_any(self, yield_hash, NULL);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1144
1600
|
/*
|
|
1145
1601
|
* call-seq:
|
|
1146
1602
|
* res.stream_each_row { |row| ... }
|
|
@@ -1154,87 +1610,118 @@ pgresult_stream_each(VALUE self)
|
|
|
1154
1610
|
static VALUE
|
|
1155
1611
|
pgresult_stream_each_row(VALUE self)
|
|
1156
1612
|
{
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
int nfields;
|
|
1160
|
-
PGconn *pgconn;
|
|
1161
|
-
PGresult *pgresult;
|
|
1162
|
-
|
|
1163
|
-
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1164
|
-
|
|
1165
|
-
this = pgresult_get_this_safe(self);
|
|
1166
|
-
pgconn = pg_get_pgconn(this->connection);
|
|
1167
|
-
pgresult = this->pgresult;
|
|
1168
|
-
nfields = PQnfields(pgresult);
|
|
1169
|
-
|
|
1170
|
-
for(;;){
|
|
1171
|
-
int ntuples = PQntuples(pgresult);
|
|
1613
|
+
return pgresult_stream_any(self, yield_array, NULL);
|
|
1614
|
+
}
|
|
1172
1615
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1616
|
+
/*
|
|
1617
|
+
* call-seq:
|
|
1618
|
+
* res.stream_each_tuple { |tuple| ... }
|
|
1619
|
+
*
|
|
1620
|
+
* Yields each row of the result set in single row mode.
|
|
1621
|
+
*
|
|
1622
|
+
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
|
1623
|
+
*/
|
|
1624
|
+
static VALUE
|
|
1625
|
+
pgresult_stream_each_tuple(VALUE self)
|
|
1626
|
+
{
|
|
1627
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
|
1628
|
+
ensure_init_for_tuple(self);
|
|
1183
1629
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
int field;
|
|
1630
|
+
return pgresult_stream_any(self, yield_tuple, NULL);
|
|
1631
|
+
}
|
|
1187
1632
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1633
|
+
/*
|
|
1634
|
+
* call-seq:
|
|
1635
|
+
* res.field_name_type = Symbol
|
|
1636
|
+
*
|
|
1637
|
+
* Set type of field names specific to this result.
|
|
1638
|
+
* It can be set to one of:
|
|
1639
|
+
* * +:string+ to use String based field names
|
|
1640
|
+
* * +:symbol+ to use Symbol based field names
|
|
1641
|
+
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
|
|
1642
|
+
*
|
|
1643
|
+
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
|
1644
|
+
*
|
|
1645
|
+
* This setting affects several result methods:
|
|
1646
|
+
* * keys of Hash returned by #[] , #each and #stream_each
|
|
1647
|
+
* * #fields
|
|
1648
|
+
* * #fname
|
|
1649
|
+
* * field names used by #tuple and #stream_each_tuple
|
|
1650
|
+
*
|
|
1651
|
+
* The type of field names can only be changed before any of the affected methods have been called.
|
|
1652
|
+
*
|
|
1653
|
+
*/
|
|
1654
|
+
static VALUE
|
|
1655
|
+
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
|
1656
|
+
{
|
|
1657
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1194
1658
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
this->pgresult = NULL;
|
|
1198
|
-
}
|
|
1659
|
+
rb_check_frozen(self);
|
|
1660
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
|
1199
1661
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1662
|
+
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
|
1663
|
+
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
|
1664
|
+
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
|
1665
|
+
else if ( sym == sym_string );
|
|
1666
|
+
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
|
1203
1667
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1668
|
+
return sym;
|
|
1669
|
+
}
|
|
1206
1670
|
|
|
1207
|
-
|
|
1671
|
+
/*
|
|
1672
|
+
* call-seq:
|
|
1673
|
+
* res.field_name_type -> Symbol
|
|
1674
|
+
*
|
|
1675
|
+
* Get type of field names.
|
|
1676
|
+
*
|
|
1677
|
+
* See description at #field_name_type=
|
|
1678
|
+
*/
|
|
1679
|
+
static VALUE
|
|
1680
|
+
pgresult_field_name_type_get(VALUE self)
|
|
1681
|
+
{
|
|
1682
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1683
|
+
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
1684
|
+
return sym_symbol;
|
|
1685
|
+
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
1686
|
+
return sym_static_symbol;
|
|
1687
|
+
} else {
|
|
1688
|
+
return sym_string;
|
|
1208
1689
|
}
|
|
1209
|
-
|
|
1210
|
-
/* never reached */
|
|
1211
|
-
return self;
|
|
1212
1690
|
}
|
|
1213
|
-
#endif
|
|
1214
|
-
|
|
1215
1691
|
|
|
1216
1692
|
void
|
|
1217
|
-
init_pg_result()
|
|
1693
|
+
init_pg_result(void)
|
|
1218
1694
|
{
|
|
1695
|
+
sym_string = ID2SYM(rb_intern("string"));
|
|
1696
|
+
sym_symbol = ID2SYM(rb_intern("symbol"));
|
|
1697
|
+
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
|
1698
|
+
|
|
1219
1699
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
|
1220
|
-
|
|
1700
|
+
rb_undef_alloc_func(rb_cPGresult);
|
|
1221
1701
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
|
1222
1702
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
|
1223
1703
|
|
|
1224
1704
|
/****** PG::Result INSTANCE METHODS: libpq ******/
|
|
1225
1705
|
rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
|
|
1226
|
-
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
|
1706
|
+
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, -1);
|
|
1707
|
+
rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
|
|
1227
1708
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
|
1228
1709
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
|
1710
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
1711
|
+
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
|
1712
|
+
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
|
1713
|
+
#endif
|
|
1229
1714
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
|
1230
1715
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
|
1231
1716
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
|
1717
|
+
rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
|
|
1232
1718
|
rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
|
|
1233
1719
|
rb_define_alias (rb_cPGresult, "check_result", "check");
|
|
1234
1720
|
rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
|
|
1235
1721
|
rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
|
|
1236
1722
|
rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
|
|
1237
1723
|
rb_define_alias(rb_cPGresult, "num_fields", "nfields");
|
|
1724
|
+
rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
|
|
1238
1725
|
rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
|
|
1239
1726
|
rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
|
|
1240
1727
|
rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
|
|
@@ -1261,17 +1748,19 @@ init_pg_result()
|
|
|
1261
1748
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
|
1262
1749
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
|
1263
1750
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
|
1751
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
|
1752
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
|
1264
1753
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
|
1265
1754
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
|
1266
1755
|
|
|
1267
1756
|
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
|
1268
1757
|
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
|
1269
1758
|
|
|
1270
|
-
#ifdef HAVE_PQSETSINGLEROWMODE
|
|
1271
1759
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
|
1272
1760
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
|
1273
1761
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
|
1274
|
-
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1762
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
|
1277
1763
|
|
|
1764
|
+
rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
|
|
1765
|
+
rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
|
|
1766
|
+
}
|