pg 0.18.3 → 1.4.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 +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +86 -0
- data/.github/workflows/source-gem.yml +129 -0
- data/.gitignore +13 -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/BSDL +2 -2
- data/Gemfile +14 -0
- data/History.rdoc +448 -4
- data/Manifest.txt +8 -21
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +69 -17
- data/Rakefile +33 -135
- data/Rakefile.cross +70 -69
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/ext/errorcodes.def +109 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +35 -2
- data/ext/extconf.rb +120 -54
- data/ext/gvl_wrappers.c +8 -0
- data/ext/gvl_wrappers.h +44 -33
- data/ext/pg.c +213 -171
- data/ext/pg.h +92 -98
- data/ext/pg_binary_decoder.c +84 -15
- data/ext/pg_binary_encoder.c +24 -21
- data/ext/pg_coder.c +175 -39
- data/ext/pg_connection.c +1730 -1135
- data/ext/pg_copy_coder.c +94 -27
- data/ext/pg_record_coder.c +521 -0
- data/ext/pg_result.c +640 -221
- data/ext/pg_text_decoder.c +608 -40
- data/ext/pg_text_encoder.c +253 -99
- data/ext/pg_tuple.c +569 -0
- data/ext/pg_type_map.c +61 -21
- data/ext/pg_type_map_all_strings.c +19 -5
- data/ext/pg_type_map_by_class.c +54 -24
- data/ext/pg_type_map_by_column.c +79 -40
- data/ext/pg_type_map_by_mri_type.c +48 -19
- data/ext/pg_type_map_by_oid.c +55 -25
- data/ext/pg_type_map_in_ruby.c +51 -20
- 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 +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +301 -0
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +24 -3
- data/lib/pg/connection.rb +723 -65
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +9 -2
- data/lib/pg/result.rb +24 -7
- data/lib/pg/text_decoder.rb +24 -22
- data/lib/pg/text_encoder.rb +40 -8
- 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 +61 -36
- 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 +32 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +1 -1
- data/sample/async_api.rb +4 -8
- data/sample/async_copyto.rb +1 -1
- data/sample/async_mixed.rb +1 -1
- data/sample/check_conn.rb +1 -1
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +1 -1
- data/sample/copyto.rb +1 -1
- data/sample/cursor.rb +1 -1
- data/sample/disk_usage_report.rb +6 -15
- data/sample/issue-119.rb +2 -2
- data/sample/losample.rb +1 -1
- data/sample/minimal-testcase.rb +2 -2
- data/sample/notify_wait.rb +1 -1
- data/sample/pg_statistics.rb +6 -15
- data/sample/replication_monitor.rb +9 -18
- data/sample/test_binary_values.rb +1 -1
- data/sample/wal_shipper.rb +2 -2
- data/sample/warehouse_partitions.rb +8 -17
- data.tar.gz.sig +0 -0
- metadata +80 -230
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -5804
- data/lib/pg/basic_type_mapping.rb +0 -399
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -355
- data/spec/pg/basic_type_mapping_spec.rb +0 -251
- data/spec/pg/connection_spec.rb +0 -1538
- data/spec/pg/result_spec.rb +0 -449
- 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 -690
- 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
|
+
}
|
|
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
|
+
}
|
|
18
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,
|
|
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,88 @@ 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;
|
|
38
211
|
this->connection = rb_pgconn;
|
|
39
212
|
this->typemap = pg_typemap_all_strings;
|
|
40
|
-
this->p_typemap =
|
|
41
|
-
this->autoclear = 0;
|
|
213
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
42
214
|
this->nfields = -1;
|
|
43
215
|
this->tuple_hash = Qnil;
|
|
44
|
-
|
|
45
|
-
|
|
216
|
+
this->field_map = Qnil;
|
|
217
|
+
this->flags = 0;
|
|
218
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
|
46
219
|
|
|
47
220
|
if( result ){
|
|
48
221
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
|
49
222
|
VALUE typemap = p_conn->type_map_for_results;
|
|
50
|
-
|
|
51
223
|
/* Type check is done when assigned to PG::Connection. */
|
|
52
|
-
t_typemap *p_typemap =
|
|
224
|
+
t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
|
|
53
225
|
|
|
226
|
+
this->enc_idx = p_conn->enc_idx;
|
|
54
227
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
|
55
|
-
this->p_typemap =
|
|
228
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
229
|
+
this->flags = p_conn->flags;
|
|
230
|
+
} else {
|
|
231
|
+
this->enc_idx = rb_locale_encindex();
|
|
56
232
|
}
|
|
57
233
|
|
|
58
234
|
return self;
|
|
59
235
|
}
|
|
60
236
|
|
|
237
|
+
VALUE
|
|
238
|
+
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
239
|
+
{
|
|
240
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
241
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
242
|
+
|
|
243
|
+
this->autoclear = 0;
|
|
244
|
+
|
|
245
|
+
/* Estimate size of underlying pgresult memory storage and account to ruby GC.
|
|
246
|
+
* There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
|
|
247
|
+
*/
|
|
248
|
+
/* 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.
|
|
249
|
+
* For now the memory savings don't justify the ifdefs necessary to support both cases.
|
|
250
|
+
*/
|
|
251
|
+
this->result_size = pgresult_approx_size(result);
|
|
252
|
+
|
|
253
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
254
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
|
255
|
+
#endif
|
|
256
|
+
|
|
257
|
+
return self;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static VALUE
|
|
261
|
+
pg_copy_result(t_pg_result *this)
|
|
262
|
+
{
|
|
263
|
+
int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
|
|
264
|
+
size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
|
|
265
|
+
t_pg_result *copy;
|
|
266
|
+
|
|
267
|
+
copy = (t_pg_result *)xmalloc(len);
|
|
268
|
+
memcpy(copy, this, len);
|
|
269
|
+
this->result_size = 0;
|
|
270
|
+
|
|
271
|
+
return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
|
|
272
|
+
}
|
|
273
|
+
|
|
61
274
|
VALUE
|
|
62
275
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
63
276
|
{
|
|
64
|
-
VALUE self =
|
|
277
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
65
278
|
t_pg_result *this = pgresult_get_this(self);
|
|
279
|
+
|
|
280
|
+
/* Autocleared results are freed implicit instead of by PQclear().
|
|
281
|
+
* So it's not very useful to be accounted by ruby GC.
|
|
282
|
+
*/
|
|
283
|
+
this->result_size = 0;
|
|
66
284
|
this->autoclear = 1;
|
|
67
285
|
return self;
|
|
68
286
|
}
|
|
@@ -71,7 +289,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
|
71
289
|
* call-seq:
|
|
72
290
|
* res.check -> nil
|
|
73
291
|
*
|
|
74
|
-
* Raises appropriate exception if PG::Result is in a bad state
|
|
292
|
+
* Raises appropriate exception if PG::Result is in a bad state, which is:
|
|
293
|
+
* * +PGRES_BAD_RESPONSE+
|
|
294
|
+
* * +PGRES_FATAL_ERROR+
|
|
295
|
+
* * +PGRES_NONFATAL_ERROR+
|
|
296
|
+
* * +PGRES_PIPELINE_ABORTED+
|
|
75
297
|
*/
|
|
76
298
|
VALUE
|
|
77
299
|
pg_result_check( VALUE self )
|
|
@@ -92,18 +314,20 @@ pg_result_check( VALUE self )
|
|
|
92
314
|
case PGRES_TUPLES_OK:
|
|
93
315
|
case PGRES_COPY_OUT:
|
|
94
316
|
case PGRES_COPY_IN:
|
|
95
|
-
#ifdef HAVE_CONST_PGRES_COPY_BOTH
|
|
96
317
|
case PGRES_COPY_BOTH:
|
|
97
|
-
#endif
|
|
98
|
-
#ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
|
|
99
318
|
case PGRES_SINGLE_TUPLE:
|
|
100
|
-
#endif
|
|
101
319
|
case PGRES_EMPTY_QUERY:
|
|
102
320
|
case PGRES_COMMAND_OK:
|
|
321
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
322
|
+
case PGRES_PIPELINE_SYNC:
|
|
323
|
+
#endif
|
|
103
324
|
return self;
|
|
104
325
|
case PGRES_BAD_RESPONSE:
|
|
105
326
|
case PGRES_FATAL_ERROR:
|
|
106
327
|
case PGRES_NONFATAL_ERROR:
|
|
328
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
329
|
+
case PGRES_PIPELINE_ABORTED:
|
|
330
|
+
#endif
|
|
107
331
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
|
108
332
|
break;
|
|
109
333
|
default:
|
|
@@ -111,7 +335,7 @@ pg_result_check( VALUE self )
|
|
|
111
335
|
}
|
|
112
336
|
}
|
|
113
337
|
|
|
114
|
-
PG_ENCODING_SET_NOCHECK( error,
|
|
338
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
|
115
339
|
|
|
116
340
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
|
117
341
|
klass = lookup_error_class( sqlstate );
|
|
@@ -135,19 +359,22 @@ pg_result_check( VALUE self )
|
|
|
135
359
|
* call-seq:
|
|
136
360
|
* res.clear() -> nil
|
|
137
361
|
*
|
|
138
|
-
* Clears the PG::Result object as the result of
|
|
362
|
+
* Clears the PG::Result object as the result of a query.
|
|
363
|
+
* This frees all underlying memory consumed by the result object.
|
|
364
|
+
* Afterwards access to result methods raises PG::Error "result has been cleared".
|
|
139
365
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
366
|
+
* Explicit calling #clear can lead to better memory performance, but is not generally necessary.
|
|
367
|
+
* Special care must be taken when PG::Tuple objects are used.
|
|
368
|
+
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
|
369
|
+
*
|
|
370
|
+
* 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
371
|
*
|
|
143
372
|
*/
|
|
144
373
|
VALUE
|
|
145
374
|
pg_result_clear(VALUE self)
|
|
146
375
|
{
|
|
147
376
|
t_pg_result *this = pgresult_get_this(self);
|
|
148
|
-
|
|
149
|
-
PQclear(pgresult_get(self));
|
|
150
|
-
this->pgresult = NULL;
|
|
377
|
+
pgresult_clear( this );
|
|
151
378
|
return Qnil;
|
|
152
379
|
}
|
|
153
380
|
|
|
@@ -155,7 +382,7 @@ pg_result_clear(VALUE self)
|
|
|
155
382
|
* call-seq:
|
|
156
383
|
* res.cleared? -> boolean
|
|
157
384
|
*
|
|
158
|
-
* Returns +true+ if the backend result memory has been
|
|
385
|
+
* Returns +true+ if the backend result memory has been freed.
|
|
159
386
|
*/
|
|
160
387
|
VALUE
|
|
161
388
|
pgresult_cleared_p( VALUE self )
|
|
@@ -168,8 +395,10 @@ pgresult_cleared_p( VALUE self )
|
|
|
168
395
|
* call-seq:
|
|
169
396
|
* res.autoclear? -> boolean
|
|
170
397
|
*
|
|
171
|
-
* Returns +true+ if the underlying C struct will be cleared
|
|
172
|
-
*
|
|
398
|
+
* Returns +true+ if the underlying C struct will be cleared at the end of a callback.
|
|
399
|
+
* This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
|
|
400
|
+
*
|
|
401
|
+
* 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
402
|
*
|
|
174
403
|
*/
|
|
175
404
|
VALUE
|
|
@@ -183,37 +412,6 @@ pgresult_autoclear_p( VALUE self )
|
|
|
183
412
|
* DATA pointer functions
|
|
184
413
|
*/
|
|
185
414
|
|
|
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
415
|
/*
|
|
218
416
|
* Fetch the PG::Result object data pointer and check it's
|
|
219
417
|
* PGresult data pointer for sanity.
|
|
@@ -243,18 +441,30 @@ pgresult_get(VALUE self)
|
|
|
243
441
|
return this->pgresult;
|
|
244
442
|
}
|
|
245
443
|
|
|
246
|
-
|
|
247
|
-
* Document-method: allocate
|
|
248
|
-
*
|
|
249
|
-
* call-seq:
|
|
250
|
-
* PG::Result.allocate -> result
|
|
251
|
-
*/
|
|
252
|
-
static VALUE
|
|
253
|
-
pgresult_s_allocate( VALUE klass )
|
|
444
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
254
445
|
{
|
|
255
|
-
VALUE
|
|
256
|
-
|
|
257
|
-
|
|
446
|
+
VALUE fname;
|
|
447
|
+
#ifdef TRUFFLERUBY
|
|
448
|
+
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
|
449
|
+
#else
|
|
450
|
+
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
451
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
452
|
+
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
|
453
|
+
if( fname == Qnil ){
|
|
454
|
+
fname = rb_str_new2(cstr);
|
|
455
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
456
|
+
fname = rb_str_intern(fname);
|
|
457
|
+
}
|
|
458
|
+
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
459
|
+
#endif
|
|
460
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
461
|
+
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
|
462
|
+
} else {
|
|
463
|
+
fname = rb_str_new2(cstr);
|
|
464
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
465
|
+
fname = rb_obj_freeze(fname);
|
|
466
|
+
}
|
|
467
|
+
return fname;
|
|
258
468
|
}
|
|
259
469
|
|
|
260
470
|
static void pgresult_init_fnames(VALUE self)
|
|
@@ -266,12 +476,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
266
476
|
int nfields = PQnfields(this->pgresult);
|
|
267
477
|
|
|
268
478
|
for( i=0; i<nfields; i++ ){
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this->fnames[i] = rb_obj_freeze(fname);
|
|
479
|
+
char *cfname = PQfname(this->pgresult, i);
|
|
480
|
+
this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
272
481
|
this->nfields = i + 1;
|
|
273
|
-
|
|
274
|
-
RB_GC_GUARD(fname);
|
|
275
482
|
}
|
|
276
483
|
this->nfields = nfields;
|
|
277
484
|
}
|
|
@@ -283,12 +490,15 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
283
490
|
*
|
|
284
491
|
* The class to represent the query result tuples (rows).
|
|
285
492
|
* An instance of this class is created as the result of every query.
|
|
286
|
-
*
|
|
287
|
-
*
|
|
493
|
+
* All result rows and columns are stored in a memory block attached to the PG::Result object.
|
|
494
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
|
495
|
+
*
|
|
496
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
|
497
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
288
498
|
*
|
|
289
499
|
* Example:
|
|
290
500
|
* require 'pg'
|
|
291
|
-
* conn =
|
|
501
|
+
* conn = PG.connect(:dbname => 'test')
|
|
292
502
|
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
293
503
|
* res.getvalue(0,0) # '1'
|
|
294
504
|
* res[0]['b'] # '2'
|
|
@@ -302,7 +512,7 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
302
512
|
|
|
303
513
|
/*
|
|
304
514
|
* call-seq:
|
|
305
|
-
* res.result_status() ->
|
|
515
|
+
* res.result_status() -> Integer
|
|
306
516
|
*
|
|
307
517
|
* Returns the status of the query. The status value is one of:
|
|
308
518
|
* * +PGRES_EMPTY_QUERY+
|
|
@@ -314,6 +524,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
314
524
|
* * +PGRES_NONFATAL_ERROR+
|
|
315
525
|
* * +PGRES_FATAL_ERROR+
|
|
316
526
|
* * +PGRES_COPY_BOTH+
|
|
527
|
+
* * +PGRES_SINGLE_TUPLE+
|
|
528
|
+
* * +PGRES_PIPELINE_SYNC+
|
|
529
|
+
* * +PGRES_PIPELINE_ABORTED+
|
|
317
530
|
*/
|
|
318
531
|
static VALUE
|
|
319
532
|
pgresult_result_status(VALUE self)
|
|
@@ -325,14 +538,15 @@ pgresult_result_status(VALUE self)
|
|
|
325
538
|
* call-seq:
|
|
326
539
|
* res.res_status( status ) -> String
|
|
327
540
|
*
|
|
328
|
-
* Returns the string representation of
|
|
541
|
+
* Returns the string representation of +status+.
|
|
329
542
|
*
|
|
330
543
|
*/
|
|
331
544
|
static VALUE
|
|
332
545
|
pgresult_res_status(VALUE self, VALUE status)
|
|
333
546
|
{
|
|
334
|
-
|
|
335
|
-
|
|
547
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
548
|
+
VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
|
|
549
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
336
550
|
return ret;
|
|
337
551
|
}
|
|
338
552
|
|
|
@@ -345,10 +559,39 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
|
345
559
|
static VALUE
|
|
346
560
|
pgresult_error_message(VALUE self)
|
|
347
561
|
{
|
|
348
|
-
|
|
349
|
-
|
|
562
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
563
|
+
VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
|
|
564
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
565
|
+
return ret;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
569
|
+
/*
|
|
570
|
+
* call-seq:
|
|
571
|
+
* res.verbose_error_message( verbosity, show_context ) -> String
|
|
572
|
+
*
|
|
573
|
+
* Returns a reformatted version of the error message associated with a PGresult object.
|
|
574
|
+
*
|
|
575
|
+
* Available since PostgreSQL-9.6
|
|
576
|
+
*/
|
|
577
|
+
static VALUE
|
|
578
|
+
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
579
|
+
{
|
|
580
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
581
|
+
VALUE ret;
|
|
582
|
+
char *c_str;
|
|
583
|
+
|
|
584
|
+
c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
|
|
585
|
+
if(!c_str)
|
|
586
|
+
rb_raise(rb_eNoMemError, "insufficient memory to format error message");
|
|
587
|
+
|
|
588
|
+
ret = rb_str_new2(c_str);
|
|
589
|
+
PQfreemem(c_str);
|
|
590
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
591
|
+
|
|
350
592
|
return ret;
|
|
351
593
|
}
|
|
594
|
+
#endif
|
|
352
595
|
|
|
353
596
|
/*
|
|
354
597
|
* call-seq:
|
|
@@ -399,14 +642,14 @@ pgresult_error_message(VALUE self)
|
|
|
399
642
|
static VALUE
|
|
400
643
|
pgresult_error_field(VALUE self, VALUE field)
|
|
401
644
|
{
|
|
402
|
-
|
|
645
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
403
646
|
int fieldcode = NUM2INT( field );
|
|
404
|
-
char * fieldstr = PQresultErrorField(
|
|
647
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
|
405
648
|
VALUE ret = Qnil;
|
|
406
649
|
|
|
407
650
|
if ( fieldstr ) {
|
|
408
|
-
ret =
|
|
409
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
|
651
|
+
ret = rb_str_new2( fieldstr );
|
|
652
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
|
410
653
|
}
|
|
411
654
|
|
|
412
655
|
return ret;
|
|
@@ -414,7 +657,7 @@ pgresult_error_field(VALUE self, VALUE field)
|
|
|
414
657
|
|
|
415
658
|
/*
|
|
416
659
|
* call-seq:
|
|
417
|
-
* res.ntuples() ->
|
|
660
|
+
* res.ntuples() -> Integer
|
|
418
661
|
*
|
|
419
662
|
* Returns the number of tuples in the query result.
|
|
420
663
|
*/
|
|
@@ -427,7 +670,7 @@ pgresult_ntuples(VALUE self)
|
|
|
427
670
|
static VALUE
|
|
428
671
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
|
429
672
|
{
|
|
430
|
-
|
|
673
|
+
return pgresult_ntuples(self);
|
|
431
674
|
}
|
|
432
675
|
|
|
433
676
|
/*
|
|
@@ -444,29 +687,30 @@ pgresult_nfields(VALUE self)
|
|
|
444
687
|
|
|
445
688
|
/*
|
|
446
689
|
* call-seq:
|
|
447
|
-
* res.fname( index ) -> String
|
|
690
|
+
* res.fname( index ) -> String or Symbol
|
|
448
691
|
*
|
|
449
692
|
* Returns the name of the column corresponding to _index_.
|
|
693
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
|
694
|
+
*
|
|
450
695
|
*/
|
|
451
696
|
static VALUE
|
|
452
697
|
pgresult_fname(VALUE self, VALUE index)
|
|
453
698
|
{
|
|
454
|
-
|
|
455
|
-
PGresult *result = pgresult_get(self);
|
|
699
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
456
700
|
int i = NUM2INT(index);
|
|
701
|
+
char *cfname;
|
|
457
702
|
|
|
458
|
-
if (i < 0 || i >= PQnfields(
|
|
703
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
|
459
704
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
|
460
705
|
}
|
|
461
706
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return rb_obj_freeze(fname);
|
|
707
|
+
cfname = PQfname(this->pgresult, i);
|
|
708
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
465
709
|
}
|
|
466
710
|
|
|
467
711
|
/*
|
|
468
712
|
* call-seq:
|
|
469
|
-
* res.fnumber( name ) ->
|
|
713
|
+
* res.fnumber( name ) -> Integer
|
|
470
714
|
*
|
|
471
715
|
* Returns the index of the field specified by the string +name+.
|
|
472
716
|
* The given +name+ is treated like an identifier in an SQL command, that is,
|
|
@@ -527,7 +771,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
|
|
|
527
771
|
|
|
528
772
|
/*
|
|
529
773
|
* call-seq:
|
|
530
|
-
* res.ftablecol( column_number ) ->
|
|
774
|
+
* res.ftablecol( column_number ) -> Integer
|
|
531
775
|
*
|
|
532
776
|
* Returns the column number (within its table) of the table from
|
|
533
777
|
* which the column _column_number_ is made up.
|
|
@@ -552,7 +796,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
|
|
|
552
796
|
|
|
553
797
|
/*
|
|
554
798
|
* call-seq:
|
|
555
|
-
* res.fformat( column_number ) ->
|
|
799
|
+
* res.fformat( column_number ) -> Integer
|
|
556
800
|
*
|
|
557
801
|
* Returns the format (0 for text, 1 for binary) of column
|
|
558
802
|
* _column_number_.
|
|
@@ -696,7 +940,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
|
|
|
696
940
|
|
|
697
941
|
/*
|
|
698
942
|
* call-seq:
|
|
699
|
-
* res.getlength( tup_num, field_num ) ->
|
|
943
|
+
* res.getlength( tup_num, field_num ) -> Integer
|
|
700
944
|
*
|
|
701
945
|
* Returns the (String) length of the field in bytes.
|
|
702
946
|
*
|
|
@@ -721,7 +965,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
|
|
|
721
965
|
|
|
722
966
|
/*
|
|
723
967
|
* call-seq:
|
|
724
|
-
* res.nparams() ->
|
|
968
|
+
* res.nparams() -> Integer
|
|
725
969
|
*
|
|
726
970
|
* Returns the number of parameters of a prepared statement.
|
|
727
971
|
* Only useful for the result returned by conn.describePrepared
|
|
@@ -760,8 +1004,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
|
760
1004
|
static VALUE
|
|
761
1005
|
pgresult_cmd_status(VALUE self)
|
|
762
1006
|
{
|
|
763
|
-
|
|
764
|
-
|
|
1007
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
1008
|
+
VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
|
|
1009
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
765
1010
|
return ret;
|
|
766
1011
|
}
|
|
767
1012
|
|
|
@@ -772,11 +1017,17 @@ pgresult_cmd_status(VALUE self)
|
|
|
772
1017
|
* Returns the number of tuples (rows) affected by the SQL command.
|
|
773
1018
|
*
|
|
774
1019
|
* If the SQL command that generated the PG::Result was not one of:
|
|
775
|
-
*
|
|
776
|
-
* *
|
|
777
|
-
* *
|
|
778
|
-
* *
|
|
779
|
-
* *
|
|
1020
|
+
*
|
|
1021
|
+
* * <tt>SELECT</tt>
|
|
1022
|
+
* * <tt>CREATE TABLE AS</tt>
|
|
1023
|
+
* * <tt>INSERT</tt>
|
|
1024
|
+
* * <tt>UPDATE</tt>
|
|
1025
|
+
* * <tt>DELETE</tt>
|
|
1026
|
+
* * <tt>MOVE</tt>
|
|
1027
|
+
* * <tt>FETCH</tt>
|
|
1028
|
+
* * <tt>COPY</tt>
|
|
1029
|
+
* * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
|
|
1030
|
+
*
|
|
780
1031
|
* or if no tuples were affected, <tt>0</tt> is returned.
|
|
781
1032
|
*/
|
|
782
1033
|
static VALUE
|
|
@@ -784,7 +1035,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
|
784
1035
|
{
|
|
785
1036
|
long n;
|
|
786
1037
|
n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
|
|
787
|
-
return
|
|
1038
|
+
return LONG2NUM(n);
|
|
788
1039
|
}
|
|
789
1040
|
|
|
790
1041
|
/*
|
|
@@ -955,8 +1206,12 @@ static VALUE
|
|
|
955
1206
|
pgresult_field_values( VALUE self, VALUE field )
|
|
956
1207
|
{
|
|
957
1208
|
PGresult *result = pgresult_get( self );
|
|
958
|
-
const char *fieldname
|
|
959
|
-
int fnum
|
|
1209
|
+
const char *fieldname;
|
|
1210
|
+
int fnum;
|
|
1211
|
+
|
|
1212
|
+
if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
|
|
1213
|
+
fieldname = StringValueCStr( field );
|
|
1214
|
+
fnum = PQfnumber( result, fieldname );
|
|
960
1215
|
|
|
961
1216
|
if ( fnum < 0 )
|
|
962
1217
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
|
@@ -965,6 +1220,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
|
965
1220
|
}
|
|
966
1221
|
|
|
967
1222
|
|
|
1223
|
+
/*
|
|
1224
|
+
* call-seq:
|
|
1225
|
+
* res.tuple_values( n ) -> array
|
|
1226
|
+
*
|
|
1227
|
+
* Returns an Array of the field values from the nth row of the result.
|
|
1228
|
+
*
|
|
1229
|
+
*/
|
|
1230
|
+
static VALUE
|
|
1231
|
+
pgresult_tuple_values(VALUE self, VALUE index)
|
|
1232
|
+
{
|
|
1233
|
+
int tuple_num = NUM2INT( index );
|
|
1234
|
+
t_pg_result *this;
|
|
1235
|
+
int field;
|
|
1236
|
+
int num_tuples;
|
|
1237
|
+
int num_fields;
|
|
1238
|
+
|
|
1239
|
+
this = pgresult_get_this_safe(self);
|
|
1240
|
+
num_tuples = PQntuples(this->pgresult);
|
|
1241
|
+
num_fields = PQnfields(this->pgresult);
|
|
1242
|
+
|
|
1243
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1244
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1245
|
+
|
|
1246
|
+
{
|
|
1247
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
|
1248
|
+
|
|
1249
|
+
/* populate the row */
|
|
1250
|
+
for ( field = 0; field < num_fields; field++ ) {
|
|
1251
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
|
|
1252
|
+
}
|
|
1253
|
+
return rb_ary_new4( num_fields, row_values );
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
static void ensure_init_for_tuple(VALUE self)
|
|
1258
|
+
{
|
|
1259
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
1260
|
+
|
|
1261
|
+
if( this->field_map == Qnil ){
|
|
1262
|
+
int i;
|
|
1263
|
+
VALUE field_map = rb_hash_new();
|
|
1264
|
+
|
|
1265
|
+
if( this->nfields == -1 )
|
|
1266
|
+
pgresult_init_fnames( self );
|
|
1267
|
+
|
|
1268
|
+
for( i = 0; i < this->nfields; i++ ){
|
|
1269
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
|
1270
|
+
}
|
|
1271
|
+
rb_obj_freeze(field_map);
|
|
1272
|
+
this->field_map = field_map;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/*
|
|
1277
|
+
* call-seq:
|
|
1278
|
+
* res.tuple( n ) -> PG::Tuple
|
|
1279
|
+
*
|
|
1280
|
+
* Returns a PG::Tuple from the nth row of the result.
|
|
1281
|
+
*
|
|
1282
|
+
*/
|
|
1283
|
+
static VALUE
|
|
1284
|
+
pgresult_tuple(VALUE self, VALUE index)
|
|
1285
|
+
{
|
|
1286
|
+
int tuple_num = NUM2INT( index );
|
|
1287
|
+
t_pg_result *this;
|
|
1288
|
+
int num_tuples;
|
|
1289
|
+
|
|
1290
|
+
this = pgresult_get_this_safe(self);
|
|
1291
|
+
num_tuples = PQntuples(this->pgresult);
|
|
1292
|
+
|
|
1293
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1294
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1295
|
+
|
|
1296
|
+
ensure_init_for_tuple(self);
|
|
1297
|
+
|
|
1298
|
+
return pg_tuple_new(self, tuple_num);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
|
|
968
1302
|
/*
|
|
969
1303
|
* call-seq:
|
|
970
1304
|
* res.each{ |tuple| ... }
|
|
@@ -991,7 +1325,7 @@ pgresult_each(VALUE self)
|
|
|
991
1325
|
* call-seq:
|
|
992
1326
|
* res.fields() -> Array
|
|
993
1327
|
*
|
|
994
|
-
*
|
|
1328
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
995
1329
|
*/
|
|
996
1330
|
static VALUE
|
|
997
1331
|
pgresult_fields(VALUE self)
|
|
@@ -1023,14 +1357,11 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1023
1357
|
t_pg_result *this = pgresult_get_this(self);
|
|
1024
1358
|
t_typemap *p_typemap;
|
|
1025
1359
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
rb_obj_classname( typemap ) );
|
|
1029
|
-
}
|
|
1030
|
-
Data_Get_Struct(typemap, t_typemap, p_typemap);
|
|
1360
|
+
/* Check type of method param */
|
|
1361
|
+
TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
|
|
1031
1362
|
|
|
1032
1363
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
|
1033
|
-
this->p_typemap =
|
|
1364
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
1034
1365
|
|
|
1035
1366
|
return typemap;
|
|
1036
1367
|
}
|
|
@@ -1050,40 +1381,64 @@ pgresult_type_map_get(VALUE self)
|
|
|
1050
1381
|
return this->typemap;
|
|
1051
1382
|
}
|
|
1052
1383
|
|
|
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
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1384
|
+
|
|
1385
|
+
static void
|
|
1386
|
+
yield_hash(VALUE self, int ntuples, int nfields, void *data)
|
|
1387
|
+
{
|
|
1388
|
+
int tuple_num;
|
|
1389
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1390
|
+
UNUSED(nfields);
|
|
1391
|
+
|
|
1392
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1393
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
pgresult_clear( this );
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
static void
|
|
1400
|
+
yield_array(VALUE self, int ntuples, int nfields, void *data)
|
|
1401
|
+
{
|
|
1402
|
+
int row;
|
|
1403
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1404
|
+
|
|
1405
|
+
for ( row = 0; row < ntuples; row++ ) {
|
|
1406
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
|
1407
|
+
int field;
|
|
1408
|
+
|
|
1409
|
+
/* populate the row */
|
|
1410
|
+
for ( field = 0; field < nfields; field++ ) {
|
|
1411
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
|
1412
|
+
}
|
|
1413
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
pgresult_clear( this );
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
static void
|
|
1420
|
+
yield_tuple(VALUE self, int ntuples, int nfields, void *data)
|
|
1421
|
+
{
|
|
1422
|
+
int tuple_num;
|
|
1423
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1424
|
+
VALUE copy;
|
|
1425
|
+
UNUSED(nfields);
|
|
1426
|
+
|
|
1427
|
+
/* make a copy of the base result, that is bound to the PG::Tuple */
|
|
1428
|
+
copy = pg_copy_result(this);
|
|
1429
|
+
/* The copy is now owner of the PGresult and is responsible to PQclear it.
|
|
1430
|
+
* We clear the pgresult here, so that it's not double freed on error within yield. */
|
|
1431
|
+
this->pgresult = NULL;
|
|
1432
|
+
|
|
1433
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1434
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
|
1435
|
+
rb_yield( tuple );
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/* Non-static, and data pointer for use by sequel_pg */
|
|
1440
|
+
VALUE
|
|
1441
|
+
pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int, void*), void* data)
|
|
1087
1442
|
{
|
|
1088
1443
|
t_pg_result *this;
|
|
1089
1444
|
int nfields;
|
|
@@ -1098,11 +1453,11 @@ pgresult_stream_each(VALUE self)
|
|
|
1098
1453
|
nfields = PQnfields(pgresult);
|
|
1099
1454
|
|
|
1100
1455
|
for(;;){
|
|
1101
|
-
int tuple_num;
|
|
1102
1456
|
int ntuples = PQntuples(pgresult);
|
|
1103
1457
|
|
|
1104
1458
|
switch( PQresultStatus(pgresult) ){
|
|
1105
1459
|
case PGRES_TUPLES_OK:
|
|
1460
|
+
case PGRES_COMMAND_OK:
|
|
1106
1461
|
if( ntuples == 0 )
|
|
1107
1462
|
return self;
|
|
1108
1463
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
@@ -1112,21 +1467,19 @@ pgresult_stream_each(VALUE self)
|
|
|
1112
1467
|
pg_result_check( self );
|
|
1113
1468
|
}
|
|
1114
1469
|
|
|
1115
|
-
|
|
1116
|
-
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
|
1117
|
-
}
|
|
1470
|
+
yielder( self, ntuples, nfields, data );
|
|
1118
1471
|
|
|
1119
|
-
if(
|
|
1120
|
-
|
|
1121
|
-
this->
|
|
1472
|
+
if( gvl_PQisBusy(pgconn) ){
|
|
1473
|
+
/* wait for input (without blocking) before reading each result */
|
|
1474
|
+
pgconn_block( 0, NULL, this->connection );
|
|
1122
1475
|
}
|
|
1123
1476
|
|
|
1124
1477
|
pgresult = gvl_PQgetResult(pgconn);
|
|
1125
1478
|
if( pgresult == NULL )
|
|
1126
|
-
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another
|
|
1479
|
+
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
|
|
1127
1480
|
|
|
1128
1481
|
if( nfields != PQnfields(pgresult) )
|
|
1129
|
-
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields
|
|
1482
|
+
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, PQnfields(pgresult));
|
|
1130
1483
|
|
|
1131
1484
|
this->pgresult = pgresult;
|
|
1132
1485
|
}
|
|
@@ -1135,6 +1488,44 @@ pgresult_stream_each(VALUE self)
|
|
|
1135
1488
|
return self;
|
|
1136
1489
|
}
|
|
1137
1490
|
|
|
1491
|
+
|
|
1492
|
+
/*
|
|
1493
|
+
* call-seq:
|
|
1494
|
+
* res.stream_each{ |tuple| ... }
|
|
1495
|
+
*
|
|
1496
|
+
* Invokes block for each tuple in the result set in single row mode.
|
|
1497
|
+
*
|
|
1498
|
+
* This is a convenience method for retrieving all result tuples
|
|
1499
|
+
* as they are transferred. It is an alternative to repeated calls of
|
|
1500
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
|
1501
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
|
1502
|
+
* the same speed as with ordinary results.
|
|
1503
|
+
*
|
|
1504
|
+
* The base result must be in status PGRES_SINGLE_TUPLE.
|
|
1505
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
|
1506
|
+
* A PG::Error is raised for any errors from the server.
|
|
1507
|
+
*
|
|
1508
|
+
* Row description data does not change while the iteration. All value retrieval
|
|
1509
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
|
1510
|
+
* the iteration and +0+ after all tuples were yielded.
|
|
1511
|
+
*
|
|
1512
|
+
* Example:
|
|
1513
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
|
1514
|
+
* conn.set_single_row_mode
|
|
1515
|
+
* conn.get_result.stream_each do |row|
|
|
1516
|
+
* # do something with each received row of the first query
|
|
1517
|
+
* end
|
|
1518
|
+
* conn.get_result.stream_each do |row|
|
|
1519
|
+
* # do something with each received row of the second query
|
|
1520
|
+
* end
|
|
1521
|
+
* conn.get_result # => nil (no more results)
|
|
1522
|
+
*/
|
|
1523
|
+
static VALUE
|
|
1524
|
+
pgresult_stream_each(VALUE self)
|
|
1525
|
+
{
|
|
1526
|
+
return pgresult_stream_any(self, yield_hash, NULL);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1138
1529
|
/*
|
|
1139
1530
|
* call-seq:
|
|
1140
1531
|
* res.stream_each_row { |row| ... }
|
|
@@ -1148,70 +1539,92 @@ pgresult_stream_each(VALUE self)
|
|
|
1148
1539
|
static VALUE
|
|
1149
1540
|
pgresult_stream_each_row(VALUE self)
|
|
1150
1541
|
{
|
|
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
|
-
}
|
|
1542
|
+
return pgresult_stream_any(self, yield_array, NULL);
|
|
1543
|
+
}
|
|
1177
1544
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1545
|
+
/*
|
|
1546
|
+
* call-seq:
|
|
1547
|
+
* res.stream_each_tuple { |tuple| ... }
|
|
1548
|
+
*
|
|
1549
|
+
* Yields each row of the result set in single row mode.
|
|
1550
|
+
*
|
|
1551
|
+
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
|
1552
|
+
*/
|
|
1553
|
+
static VALUE
|
|
1554
|
+
pgresult_stream_each_tuple(VALUE self)
|
|
1555
|
+
{
|
|
1556
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
|
1557
|
+
ensure_init_for_tuple(self);
|
|
1181
1558
|
|
|
1182
|
-
|
|
1183
|
-
|
|
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
|
-
}
|
|
1559
|
+
return pgresult_stream_any(self, yield_tuple, NULL);
|
|
1560
|
+
}
|
|
1188
1561
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1562
|
+
/*
|
|
1563
|
+
* call-seq:
|
|
1564
|
+
* res.field_name_type = Symbol
|
|
1565
|
+
*
|
|
1566
|
+
* Set type of field names specific to this result.
|
|
1567
|
+
* It can be set to one of:
|
|
1568
|
+
* * +:string+ to use String based field names
|
|
1569
|
+
* * +:symbol+ to use Symbol based field names
|
|
1570
|
+
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
|
|
1571
|
+
*
|
|
1572
|
+
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
|
1573
|
+
*
|
|
1574
|
+
* This setting affects several result methods:
|
|
1575
|
+
* * keys of Hash returned by #[] , #each and #stream_each
|
|
1576
|
+
* * #fields
|
|
1577
|
+
* * #fname
|
|
1578
|
+
* * field names used by #tuple and #stream_each_tuple
|
|
1579
|
+
*
|
|
1580
|
+
* The type of field names can only be changed before any of the affected methods have been called.
|
|
1581
|
+
*
|
|
1582
|
+
*/
|
|
1583
|
+
static VALUE
|
|
1584
|
+
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
|
1585
|
+
{
|
|
1586
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1587
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
|
1193
1588
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1589
|
+
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
|
1590
|
+
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
|
1591
|
+
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
|
1592
|
+
else if ( sym == sym_string );
|
|
1593
|
+
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
|
1197
1594
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1595
|
+
return sym;
|
|
1596
|
+
}
|
|
1200
1597
|
|
|
1201
|
-
|
|
1598
|
+
/*
|
|
1599
|
+
* call-seq:
|
|
1600
|
+
* res.field_name_type -> Symbol
|
|
1601
|
+
*
|
|
1602
|
+
* Get type of field names.
|
|
1603
|
+
*
|
|
1604
|
+
* See description at #field_name_type=
|
|
1605
|
+
*/
|
|
1606
|
+
static VALUE
|
|
1607
|
+
pgresult_field_name_type_get(VALUE self)
|
|
1608
|
+
{
|
|
1609
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1610
|
+
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
1611
|
+
return sym_symbol;
|
|
1612
|
+
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
1613
|
+
return sym_static_symbol;
|
|
1614
|
+
} else {
|
|
1615
|
+
return sym_string;
|
|
1202
1616
|
}
|
|
1203
|
-
|
|
1204
|
-
/* never reached */
|
|
1205
|
-
return self;
|
|
1206
1617
|
}
|
|
1207
|
-
#endif
|
|
1208
|
-
|
|
1209
1618
|
|
|
1210
1619
|
void
|
|
1211
1620
|
init_pg_result()
|
|
1212
1621
|
{
|
|
1622
|
+
sym_string = ID2SYM(rb_intern("string"));
|
|
1623
|
+
sym_symbol = ID2SYM(rb_intern("symbol"));
|
|
1624
|
+
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
|
1625
|
+
|
|
1213
1626
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
|
1214
|
-
|
|
1627
|
+
rb_undef_alloc_func(rb_cPGresult);
|
|
1215
1628
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
|
1216
1629
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
|
1217
1630
|
|
|
@@ -1220,6 +1633,10 @@ init_pg_result()
|
|
|
1220
1633
|
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
|
1221
1634
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
|
1222
1635
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
|
1636
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
1637
|
+
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
|
1638
|
+
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
|
1639
|
+
#endif
|
|
1223
1640
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
|
1224
1641
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
|
1225
1642
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
|
@@ -1255,17 +1672,19 @@ init_pg_result()
|
|
|
1255
1672
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
|
1256
1673
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
|
1257
1674
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
|
1675
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
|
1676
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
|
1258
1677
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
|
1259
1678
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
|
1260
1679
|
|
|
1261
1680
|
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
|
1262
1681
|
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
|
1263
1682
|
|
|
1264
|
-
#ifdef HAVE_PQSETSINGLEROWMODE
|
|
1265
1683
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
|
1266
1684
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
|
1267
1685
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
|
1268
|
-
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1686
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
|
1271
1687
|
|
|
1688
|
+
rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
|
|
1689
|
+
rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
|
|
1690
|
+
}
|