pg 0.15.1 → 1.2.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.tar.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +0 -3022
- data/History.rdoc +370 -4
- data/Manifest.txt +39 -19
- data/README-Windows.rdoc +17 -28
- data/README.ja.rdoc +1 -2
- data/README.rdoc +113 -14
- data/Rakefile +97 -36
- data/Rakefile.cross +109 -83
- data/ext/errorcodes.def +1032 -0
- data/ext/errorcodes.rb +45 -0
- data/ext/errorcodes.txt +494 -0
- data/ext/extconf.rb +55 -52
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +94 -38
- data/ext/pg.c +273 -121
- data/ext/pg.h +292 -50
- data/ext/pg_binary_decoder.c +229 -0
- data/ext/pg_binary_encoder.c +163 -0
- data/ext/pg_coder.c +561 -0
- data/ext/pg_connection.c +1811 -1051
- data/ext/pg_copy_coder.c +599 -0
- data/ext/pg_errors.c +95 -0
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +917 -203
- data/ext/pg_text_decoder.c +987 -0
- data/ext/pg_text_encoder.c +814 -0
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +166 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +244 -0
- data/ext/pg_type_map_by_column.c +313 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +356 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/pg_util.c +149 -0
- data/ext/pg_util.h +65 -0
- data/lib/pg.rb +31 -9
- data/lib/pg/basic_type_mapping.rb +522 -0
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +104 -0
- data/lib/pg/connection.rb +235 -30
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +33 -6
- data/lib/pg/text_decoder.rb +46 -0
- data/lib/pg/text_encoder.rb +59 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +16 -0
- data/spec/{lib/helpers.rb → helpers.rb} +154 -52
- data/spec/pg/basic_type_mapping_spec.rb +630 -0
- data/spec/pg/connection_spec.rb +1352 -426
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +508 -105
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +226 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
- data/spec/pg/type_map_by_oid_spec.rb +149 -0
- data/spec/pg/type_map_in_ruby_spec.rb +164 -0
- data/spec/pg/type_map_spec.rb +22 -0
- data/spec/pg/type_spec.rb +1123 -0
- data/spec/pg_spec.rb +35 -16
- metadata +163 -84
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
data/ext/pg_result.c
CHANGED
@@ -1,17 +1,177 @@
|
|
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
|
+
|
12
|
+
static VALUE pgresult_type_map_set( VALUE, VALUE );
|
13
|
+
static t_pg_result *pgresult_get_this( VALUE );
|
14
|
+
static t_pg_result *pgresult_get_this_safe( VALUE );
|
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
|
+
}
|
11
84
|
|
12
|
-
|
13
|
-
|
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
|
+
);
|
14
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( t_pg_result *this )
|
111
|
+
{
|
112
|
+
int i;
|
113
|
+
|
114
|
+
rb_gc_mark( this->connection );
|
115
|
+
rb_gc_mark( this->typemap );
|
116
|
+
rb_gc_mark( this->tuple_hash );
|
117
|
+
rb_gc_mark( this->field_map );
|
118
|
+
|
119
|
+
for( i=0; i < this->nfields; i++ ){
|
120
|
+
rb_gc_mark( this->fnames[i] );
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
/*
|
125
|
+
* GC Free function
|
126
|
+
*/
|
127
|
+
static void
|
128
|
+
pgresult_clear( t_pg_result *this )
|
129
|
+
{
|
130
|
+
if( this->pgresult && !this->autoclear ){
|
131
|
+
PQclear(this->pgresult);
|
132
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
133
|
+
rb_gc_adjust_memory_usage(-this->result_size);
|
134
|
+
#endif
|
135
|
+
}
|
136
|
+
this->result_size = 0;
|
137
|
+
this->nfields = -1;
|
138
|
+
this->pgresult = NULL;
|
139
|
+
}
|
140
|
+
|
141
|
+
static void
|
142
|
+
pgresult_gc_free( t_pg_result *this )
|
143
|
+
{
|
144
|
+
pgresult_clear( this );
|
145
|
+
xfree(this);
|
146
|
+
}
|
147
|
+
|
148
|
+
static size_t
|
149
|
+
pgresult_memsize( t_pg_result *this )
|
150
|
+
{
|
151
|
+
/* Ideally the memory 'this' is pointing to should be taken into account as well.
|
152
|
+
* However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
|
153
|
+
*/
|
154
|
+
return this->result_size;
|
155
|
+
}
|
156
|
+
|
157
|
+
static const rb_data_type_t pgresult_type = {
|
158
|
+
"pg",
|
159
|
+
{
|
160
|
+
(void (*)(void*))pgresult_gc_mark,
|
161
|
+
(void (*)(void*))pgresult_gc_free,
|
162
|
+
(size_t (*)(const void *))pgresult_memsize,
|
163
|
+
},
|
164
|
+
0, 0,
|
165
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
166
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
167
|
+
#endif
|
168
|
+
};
|
169
|
+
|
170
|
+
/* Needed by sequel_pg gem, do not delete */
|
171
|
+
int pg_get_result_enc_idx(VALUE self)
|
172
|
+
{
|
173
|
+
return pgresult_get_this(self)->enc_idx;
|
174
|
+
}
|
15
175
|
|
16
176
|
/*
|
17
177
|
* Global functions
|
@@ -20,19 +180,90 @@ static PGresult* pgresult_get( VALUE );
|
|
20
180
|
/*
|
21
181
|
* Result constructor
|
22
182
|
*/
|
183
|
+
static VALUE
|
184
|
+
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
185
|
+
{
|
186
|
+
int nfields = result ? PQnfields(result) : 0;
|
187
|
+
VALUE self;
|
188
|
+
t_pg_result *this;
|
189
|
+
|
190
|
+
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
191
|
+
this->pgresult = result;
|
192
|
+
this->connection = rb_pgconn;
|
193
|
+
this->typemap = pg_typemap_all_strings;
|
194
|
+
this->p_typemap = DATA_PTR( this->typemap );
|
195
|
+
this->nfields = -1;
|
196
|
+
this->tuple_hash = Qnil;
|
197
|
+
this->field_map = Qnil;
|
198
|
+
this->flags = 0;
|
199
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
200
|
+
|
201
|
+
if( result ){
|
202
|
+
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
203
|
+
VALUE typemap = p_conn->type_map_for_results;
|
204
|
+
/* Type check is done when assigned to PG::Connection. */
|
205
|
+
t_typemap *p_typemap = DATA_PTR(typemap);
|
206
|
+
|
207
|
+
this->enc_idx = p_conn->enc_idx;
|
208
|
+
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
209
|
+
this->p_typemap = DATA_PTR( this->typemap );
|
210
|
+
this->flags = p_conn->flags;
|
211
|
+
} else {
|
212
|
+
this->enc_idx = rb_locale_encindex();
|
213
|
+
}
|
214
|
+
|
215
|
+
return self;
|
216
|
+
}
|
217
|
+
|
23
218
|
VALUE
|
24
219
|
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
25
220
|
{
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
221
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
222
|
+
t_pg_result *this = pgresult_get_this(self);
|
223
|
+
|
224
|
+
this->autoclear = 0;
|
225
|
+
|
226
|
+
/* Estimate size of underlying pgresult memory storage and account to ruby GC.
|
227
|
+
* There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
|
228
|
+
*/
|
229
|
+
/* 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.
|
230
|
+
* For now the memory savings don't justify the ifdefs necessary to support both cases.
|
231
|
+
*/
|
232
|
+
this->result_size = pgresult_approx_size(result);
|
233
|
+
|
234
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
235
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
31
236
|
#endif
|
32
237
|
|
33
|
-
|
238
|
+
return self;
|
239
|
+
}
|
240
|
+
|
241
|
+
static VALUE
|
242
|
+
pg_copy_result(t_pg_result *this)
|
243
|
+
{
|
244
|
+
int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
|
245
|
+
size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
|
246
|
+
t_pg_result *copy;
|
247
|
+
|
248
|
+
copy = (t_pg_result *)xmalloc(len);
|
249
|
+
memcpy(copy, this, len);
|
250
|
+
this->result_size = 0;
|
251
|
+
|
252
|
+
return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
|
253
|
+
}
|
34
254
|
|
35
|
-
|
255
|
+
VALUE
|
256
|
+
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
257
|
+
{
|
258
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
259
|
+
t_pg_result *this = pgresult_get_this(self);
|
260
|
+
|
261
|
+
/* Autocleared results are freed implicit instead of by PQclear().
|
262
|
+
* So it's not very useful to be accounted by ruby GC.
|
263
|
+
*/
|
264
|
+
this->result_size = 0;
|
265
|
+
this->autoclear = 1;
|
266
|
+
return self;
|
36
267
|
}
|
37
268
|
|
38
269
|
/*
|
@@ -44,56 +275,48 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
44
275
|
VALUE
|
45
276
|
pg_result_check( VALUE self )
|
46
277
|
{
|
47
|
-
|
48
|
-
VALUE
|
49
|
-
|
50
|
-
PGresult *result;
|
51
|
-
#ifdef M17N_SUPPORTED
|
52
|
-
rb_encoding *enc = pg_conn_enc_get( conn );
|
53
|
-
#endif
|
54
|
-
|
55
|
-
Data_Get_Struct(self, PGresult, result);
|
278
|
+
t_pg_result *this = pgresult_get_this(self);
|
279
|
+
VALUE error, exception, klass;
|
280
|
+
char * sqlstate;
|
56
281
|
|
57
|
-
if(
|
282
|
+
if(this->pgresult == NULL)
|
58
283
|
{
|
284
|
+
PGconn *conn = pg_get_pgconn(this->connection);
|
59
285
|
error = rb_str_new2( PQerrorMessage(conn) );
|
60
286
|
}
|
61
287
|
else
|
62
288
|
{
|
63
|
-
switch (PQresultStatus(
|
289
|
+
switch (PQresultStatus(this->pgresult))
|
64
290
|
{
|
65
291
|
case PGRES_TUPLES_OK:
|
66
292
|
case PGRES_COPY_OUT:
|
67
293
|
case PGRES_COPY_IN:
|
68
|
-
#ifdef HAVE_CONST_PGRES_COPY_BOTH
|
69
294
|
case PGRES_COPY_BOTH:
|
70
|
-
#endif
|
71
|
-
#ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
|
72
295
|
case PGRES_SINGLE_TUPLE:
|
73
|
-
#endif
|
74
296
|
case PGRES_EMPTY_QUERY:
|
75
297
|
case PGRES_COMMAND_OK:
|
76
|
-
return
|
298
|
+
return self;
|
77
299
|
case PGRES_BAD_RESPONSE:
|
78
300
|
case PGRES_FATAL_ERROR:
|
79
301
|
case PGRES_NONFATAL_ERROR:
|
80
|
-
error = rb_str_new2( PQresultErrorMessage(
|
302
|
+
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
81
303
|
break;
|
82
304
|
default:
|
83
305
|
error = rb_str_new2( "internal error : unknown result status." );
|
84
306
|
}
|
85
307
|
}
|
86
308
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
rb_iv_set( exception, "@
|
309
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
310
|
+
|
311
|
+
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
312
|
+
klass = lookup_error_class( sqlstate );
|
313
|
+
exception = rb_exc_new3( klass, error );
|
314
|
+
rb_iv_set( exception, "@connection", this->connection );
|
315
|
+
rb_iv_set( exception, "@result", this->pgresult ? self : Qnil );
|
93
316
|
rb_exc_raise( exception );
|
94
317
|
|
95
318
|
/* Not reached */
|
96
|
-
return
|
319
|
+
return self;
|
97
320
|
}
|
98
321
|
|
99
322
|
|
@@ -107,44 +330,130 @@ pg_result_check( VALUE self )
|
|
107
330
|
* call-seq:
|
108
331
|
* res.clear() -> nil
|
109
332
|
*
|
110
|
-
* Clears the PG::Result object as the result of
|
333
|
+
* Clears the PG::Result object as the result of a query.
|
334
|
+
* This frees all underlying memory consumed by the result object.
|
335
|
+
* Afterwards access to result methods raises PG::Error "result has been cleared".
|
336
|
+
*
|
337
|
+
* Explicit calling #clear can lead to better memory performance, but is not generally necessary.
|
338
|
+
* Special care must be taken when PG::Tuple objects are used.
|
339
|
+
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
340
|
+
*
|
341
|
+
* 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.
|
342
|
+
*
|
111
343
|
*/
|
112
344
|
VALUE
|
113
345
|
pg_result_clear(VALUE self)
|
114
346
|
{
|
115
|
-
|
116
|
-
|
347
|
+
t_pg_result *this = pgresult_get_this(self);
|
348
|
+
pgresult_clear( this );
|
117
349
|
return Qnil;
|
118
350
|
}
|
119
351
|
|
352
|
+
/*
|
353
|
+
* call-seq:
|
354
|
+
* res.cleared? -> boolean
|
355
|
+
*
|
356
|
+
* Returns +true+ if the backend result memory has been free'd.
|
357
|
+
*/
|
358
|
+
VALUE
|
359
|
+
pgresult_cleared_p( VALUE self )
|
360
|
+
{
|
361
|
+
t_pg_result *this = pgresult_get_this(self);
|
362
|
+
return this->pgresult ? Qfalse : Qtrue;
|
363
|
+
}
|
120
364
|
|
365
|
+
/*
|
366
|
+
* call-seq:
|
367
|
+
* res.autoclear? -> boolean
|
368
|
+
*
|
369
|
+
* Returns +true+ if the underlying C struct will be cleared at the end of a callback.
|
370
|
+
* This applies only to Result objects received by the block to PG::Cinnection#set_notice_receiver .
|
371
|
+
*
|
372
|
+
* All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
|
373
|
+
*
|
374
|
+
*/
|
375
|
+
VALUE
|
376
|
+
pgresult_autoclear_p( VALUE self )
|
377
|
+
{
|
378
|
+
t_pg_result *this = pgresult_get_this(self);
|
379
|
+
return this->autoclear ? Qtrue : Qfalse;
|
380
|
+
}
|
121
381
|
|
122
382
|
/*
|
123
383
|
* DATA pointer functions
|
124
384
|
*/
|
125
385
|
|
126
386
|
/*
|
127
|
-
*
|
387
|
+
* Fetch the PG::Result object data pointer and check it's
|
388
|
+
* PGresult data pointer for sanity.
|
128
389
|
*/
|
129
|
-
static
|
130
|
-
|
390
|
+
static t_pg_result *
|
391
|
+
pgresult_get_this_safe( VALUE self )
|
131
392
|
{
|
132
|
-
|
133
|
-
|
393
|
+
t_pg_result *this = pgresult_get_this(self);
|
394
|
+
|
395
|
+
if (this->pgresult == NULL) rb_raise(rb_ePGerror, "result has been cleared");
|
396
|
+
return this;
|
134
397
|
}
|
135
398
|
|
136
399
|
/*
|
137
|
-
* Fetch the
|
400
|
+
* Fetch the PGresult pointer for the result object and check validity
|
401
|
+
*
|
402
|
+
* Note: This function is used externally by the sequel_pg gem,
|
403
|
+
* so do changes carefully.
|
404
|
+
*
|
138
405
|
*/
|
139
|
-
|
406
|
+
PGresult*
|
140
407
|
pgresult_get(VALUE self)
|
141
408
|
{
|
142
|
-
|
143
|
-
|
144
|
-
if (
|
145
|
-
return
|
409
|
+
t_pg_result *this = pgresult_get_this(self);
|
410
|
+
|
411
|
+
if (this->pgresult == NULL) rb_raise(rb_ePGerror, "result has been cleared");
|
412
|
+
return this->pgresult;
|
146
413
|
}
|
147
414
|
|
415
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
416
|
+
{
|
417
|
+
VALUE fname;
|
418
|
+
#ifdef TRUFFLERUBY
|
419
|
+
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
420
|
+
#else
|
421
|
+
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
422
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
423
|
+
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
424
|
+
if( fname == Qnil ){
|
425
|
+
fname = rb_str_new2(cstr);
|
426
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
427
|
+
fname = rb_str_intern(fname);
|
428
|
+
}
|
429
|
+
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
430
|
+
#endif
|
431
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
432
|
+
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
433
|
+
} else {
|
434
|
+
fname = rb_str_new2(cstr);
|
435
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
436
|
+
fname = rb_obj_freeze(fname);
|
437
|
+
}
|
438
|
+
return fname;
|
439
|
+
}
|
440
|
+
|
441
|
+
static void pgresult_init_fnames(VALUE self)
|
442
|
+
{
|
443
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
444
|
+
|
445
|
+
if( this->nfields == -1 ){
|
446
|
+
int i;
|
447
|
+
int nfields = PQnfields(this->pgresult);
|
448
|
+
|
449
|
+
for( i=0; i<nfields; i++ ){
|
450
|
+
char *cfname = PQfname(this->pgresult, i);
|
451
|
+
this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
452
|
+
this->nfields = i + 1;
|
453
|
+
}
|
454
|
+
this->nfields = nfields;
|
455
|
+
}
|
456
|
+
}
|
148
457
|
|
149
458
|
/********************************************************************
|
150
459
|
*
|
@@ -152,12 +461,15 @@ pgresult_get(VALUE self)
|
|
152
461
|
*
|
153
462
|
* The class to represent the query result tuples (rows).
|
154
463
|
* An instance of this class is created as the result of every query.
|
155
|
-
*
|
156
|
-
*
|
464
|
+
* All result rows and columns are stored in a memory block attached to the PG::Result object.
|
465
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
466
|
+
*
|
467
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
468
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
157
469
|
*
|
158
470
|
* Example:
|
159
471
|
* require 'pg'
|
160
|
-
* conn =
|
472
|
+
* conn = PG.connect(:dbname => 'test')
|
161
473
|
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
162
474
|
* res.getvalue(0,0) # '1'
|
163
475
|
* res[0]['b'] # '2'
|
@@ -171,7 +483,7 @@ pgresult_get(VALUE self)
|
|
171
483
|
|
172
484
|
/*
|
173
485
|
* call-seq:
|
174
|
-
* res.result_status() ->
|
486
|
+
* res.result_status() -> Integer
|
175
487
|
*
|
176
488
|
* Returns the status of the query. The status value is one of:
|
177
489
|
* * +PGRES_EMPTY_QUERY+
|
@@ -200,8 +512,9 @@ pgresult_result_status(VALUE self)
|
|
200
512
|
static VALUE
|
201
513
|
pgresult_res_status(VALUE self, VALUE status)
|
202
514
|
{
|
203
|
-
|
204
|
-
|
515
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
516
|
+
VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
|
517
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
205
518
|
return ret;
|
206
519
|
}
|
207
520
|
|
@@ -214,11 +527,40 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
214
527
|
static VALUE
|
215
528
|
pgresult_error_message(VALUE self)
|
216
529
|
{
|
217
|
-
|
218
|
-
|
530
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
531
|
+
VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
|
532
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
219
533
|
return ret;
|
220
534
|
}
|
221
535
|
|
536
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
537
|
+
/*
|
538
|
+
* call-seq:
|
539
|
+
* res.verbose_error_message( verbosity, show_context ) -> String
|
540
|
+
*
|
541
|
+
* Returns a reformatted version of the error message associated with a PGresult object.
|
542
|
+
*
|
543
|
+
* Available since PostgreSQL-9.6
|
544
|
+
*/
|
545
|
+
static VALUE
|
546
|
+
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
547
|
+
{
|
548
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
549
|
+
VALUE ret;
|
550
|
+
char *c_str;
|
551
|
+
|
552
|
+
c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
|
553
|
+
if(!c_str)
|
554
|
+
rb_raise(rb_eNoMemError, "insufficient memory to format error message");
|
555
|
+
|
556
|
+
ret = rb_str_new2(c_str);
|
557
|
+
PQfreemem(c_str);
|
558
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
559
|
+
|
560
|
+
return ret;
|
561
|
+
}
|
562
|
+
#endif
|
563
|
+
|
222
564
|
/*
|
223
565
|
* call-seq:
|
224
566
|
* res.error_field(fieldcode) -> String
|
@@ -245,18 +587,18 @@ pgresult_error_message(VALUE self)
|
|
245
587
|
* conn.exec( "SELECT * FROM nonexistant_table" )
|
246
588
|
* rescue PG::Error => err
|
247
589
|
* p [
|
248
|
-
* result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
249
|
-
* result.error_field( PG::Result::PG_DIAG_SQLSTATE ),
|
250
|
-
* result.error_field( PG::Result::PG_DIAG_MESSAGE_PRIMARY ),
|
251
|
-
* result.error_field( PG::Result::PG_DIAG_MESSAGE_DETAIL ),
|
252
|
-
* result.error_field( PG::Result::PG_DIAG_MESSAGE_HINT ),
|
253
|
-
* result.error_field( PG::Result::PG_DIAG_STATEMENT_POSITION ),
|
254
|
-
* result.error_field( PG::Result::PG_DIAG_INTERNAL_POSITION ),
|
255
|
-
* result.error_field( PG::Result::PG_DIAG_INTERNAL_QUERY ),
|
256
|
-
* result.error_field( PG::Result::PG_DIAG_CONTEXT ),
|
257
|
-
* result.error_field( PG::Result::PG_DIAG_SOURCE_FILE ),
|
258
|
-
* result.error_field( PG::Result::PG_DIAG_SOURCE_LINE ),
|
259
|
-
* result.error_field( PG::Result::PG_DIAG_SOURCE_FUNCTION ),
|
590
|
+
* err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
591
|
+
* err.result.error_field( PG::Result::PG_DIAG_SQLSTATE ),
|
592
|
+
* err.result.error_field( PG::Result::PG_DIAG_MESSAGE_PRIMARY ),
|
593
|
+
* err.result.error_field( PG::Result::PG_DIAG_MESSAGE_DETAIL ),
|
594
|
+
* err.result.error_field( PG::Result::PG_DIAG_MESSAGE_HINT ),
|
595
|
+
* err.result.error_field( PG::Result::PG_DIAG_STATEMENT_POSITION ),
|
596
|
+
* err.result.error_field( PG::Result::PG_DIAG_INTERNAL_POSITION ),
|
597
|
+
* err.result.error_field( PG::Result::PG_DIAG_INTERNAL_QUERY ),
|
598
|
+
* err.result.error_field( PG::Result::PG_DIAG_CONTEXT ),
|
599
|
+
* err.result.error_field( PG::Result::PG_DIAG_SOURCE_FILE ),
|
600
|
+
* err.result.error_field( PG::Result::PG_DIAG_SOURCE_LINE ),
|
601
|
+
* err.result.error_field( PG::Result::PG_DIAG_SOURCE_FUNCTION ),
|
260
602
|
* ]
|
261
603
|
* end
|
262
604
|
*
|
@@ -268,14 +610,14 @@ pgresult_error_message(VALUE self)
|
|
268
610
|
static VALUE
|
269
611
|
pgresult_error_field(VALUE self, VALUE field)
|
270
612
|
{
|
271
|
-
|
613
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
272
614
|
int fieldcode = NUM2INT( field );
|
273
|
-
char * fieldstr = PQresultErrorField(
|
615
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
274
616
|
VALUE ret = Qnil;
|
275
617
|
|
276
618
|
if ( fieldstr ) {
|
277
|
-
ret =
|
278
|
-
|
619
|
+
ret = rb_str_new2( fieldstr );
|
620
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
279
621
|
}
|
280
622
|
|
281
623
|
return ret;
|
@@ -283,7 +625,7 @@ pgresult_error_field(VALUE self, VALUE field)
|
|
283
625
|
|
284
626
|
/*
|
285
627
|
* call-seq:
|
286
|
-
* res.ntuples() ->
|
628
|
+
* res.ntuples() -> Integer
|
287
629
|
*
|
288
630
|
* Returns the number of tuples in the query result.
|
289
631
|
*/
|
@@ -293,9 +635,15 @@ pgresult_ntuples(VALUE self)
|
|
293
635
|
return INT2FIX(PQntuples(pgresult_get(self)));
|
294
636
|
}
|
295
637
|
|
638
|
+
static VALUE
|
639
|
+
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
640
|
+
{
|
641
|
+
return pgresult_ntuples(self);
|
642
|
+
}
|
643
|
+
|
296
644
|
/*
|
297
645
|
* call-seq:
|
298
|
-
* res.nfields() ->
|
646
|
+
* res.nfields() -> Integer
|
299
647
|
*
|
300
648
|
* Returns the number of columns in the query result.
|
301
649
|
*/
|
@@ -307,29 +655,30 @@ pgresult_nfields(VALUE self)
|
|
307
655
|
|
308
656
|
/*
|
309
657
|
* call-seq:
|
310
|
-
* res.fname( index ) -> String
|
658
|
+
* res.fname( index ) -> String or Symbol
|
311
659
|
*
|
312
660
|
* Returns the name of the column corresponding to _index_.
|
661
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
662
|
+
*
|
313
663
|
*/
|
314
664
|
static VALUE
|
315
665
|
pgresult_fname(VALUE self, VALUE index)
|
316
666
|
{
|
317
|
-
|
318
|
-
PGresult *result;
|
667
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
319
668
|
int i = NUM2INT(index);
|
669
|
+
char *cfname;
|
320
670
|
|
321
|
-
|
322
|
-
if (i < 0 || i >= PQnfields(result)) {
|
671
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
323
672
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
324
673
|
}
|
325
|
-
|
326
|
-
|
327
|
-
return
|
674
|
+
|
675
|
+
cfname = PQfname(this->pgresult, i);
|
676
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
328
677
|
}
|
329
678
|
|
330
679
|
/*
|
331
680
|
* call-seq:
|
332
|
-
* res.fnumber( name ) ->
|
681
|
+
* res.fnumber( name ) -> Integer
|
333
682
|
*
|
334
683
|
* Returns the index of the field specified by the string +name+.
|
335
684
|
* The given +name+ is treated like an identifier in an SQL command, that is,
|
@@ -357,16 +706,16 @@ pgresult_fnumber(VALUE self, VALUE name)
|
|
357
706
|
|
358
707
|
Check_Type(name, T_STRING);
|
359
708
|
|
360
|
-
n = PQfnumber(pgresult_get(self),
|
709
|
+
n = PQfnumber(pgresult_get(self), StringValueCStr(name));
|
361
710
|
if (n == -1) {
|
362
|
-
rb_raise(rb_eArgError,"Unknown field: %s",
|
711
|
+
rb_raise(rb_eArgError,"Unknown field: %s", StringValueCStr(name));
|
363
712
|
}
|
364
713
|
return INT2FIX(n);
|
365
714
|
}
|
366
715
|
|
367
716
|
/*
|
368
717
|
* call-seq:
|
369
|
-
* res.ftable( column_number ) ->
|
718
|
+
* res.ftable( column_number ) -> Integer
|
370
719
|
*
|
371
720
|
* Returns the Oid of the table from which the column _column_number_
|
372
721
|
* was fetched.
|
@@ -385,12 +734,12 @@ pgresult_ftable(VALUE self, VALUE column_number)
|
|
385
734
|
rb_raise(rb_eArgError,"Invalid column index: %d", col_number);
|
386
735
|
|
387
736
|
n = PQftable(pgresult, col_number);
|
388
|
-
return
|
737
|
+
return UINT2NUM(n);
|
389
738
|
}
|
390
739
|
|
391
740
|
/*
|
392
741
|
* call-seq:
|
393
|
-
* res.ftablecol( column_number ) ->
|
742
|
+
* res.ftablecol( column_number ) -> Integer
|
394
743
|
*
|
395
744
|
* Returns the column number (within its table) of the table from
|
396
745
|
* which the column _column_number_ is made up.
|
@@ -415,7 +764,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
|
|
415
764
|
|
416
765
|
/*
|
417
766
|
* call-seq:
|
418
|
-
* res.fformat( column_number ) ->
|
767
|
+
* res.fformat( column_number ) -> Integer
|
419
768
|
*
|
420
769
|
* Returns the format (0 for text, 1 for binary) of column
|
421
770
|
* _column_number_.
|
@@ -436,7 +785,7 @@ pgresult_fformat(VALUE self, VALUE column_number)
|
|
436
785
|
|
437
786
|
/*
|
438
787
|
* call-seq:
|
439
|
-
* res.ftype( column_number )
|
788
|
+
* res.ftype( column_number ) -> Integer
|
440
789
|
*
|
441
790
|
* Returns the data type associated with _column_number_.
|
442
791
|
*
|
@@ -460,7 +809,7 @@ pgresult_ftype(VALUE self, VALUE index)
|
|
460
809
|
if (i < 0 || i >= PQnfields(result)) {
|
461
810
|
rb_raise(rb_eArgError, "invalid field number %d", i);
|
462
811
|
}
|
463
|
-
return
|
812
|
+
return UINT2NUM(PQftype(result, i));
|
464
813
|
}
|
465
814
|
|
466
815
|
/*
|
@@ -510,6 +859,7 @@ pgresult_fsize(VALUE self, VALUE index)
|
|
510
859
|
return INT2NUM(PQfsize(result, i));
|
511
860
|
}
|
512
861
|
|
862
|
+
|
513
863
|
/*
|
514
864
|
* call-seq:
|
515
865
|
* res.getvalue( tup_num, field_num )
|
@@ -520,33 +870,17 @@ pgresult_fsize(VALUE self, VALUE index)
|
|
520
870
|
static VALUE
|
521
871
|
pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
|
522
872
|
{
|
523
|
-
|
524
|
-
PGresult *result;
|
873
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
525
874
|
int i = NUM2INT(tup_num);
|
526
875
|
int j = NUM2INT(field_num);
|
527
876
|
|
528
|
-
|
529
|
-
if(i < 0 || i >= PQntuples(result)) {
|
877
|
+
if(i < 0 || i >= PQntuples(this->pgresult)) {
|
530
878
|
rb_raise(rb_eArgError,"invalid tuple number %d", i);
|
531
879
|
}
|
532
|
-
if(j < 0 || j >= PQnfields(
|
880
|
+
if(j < 0 || j >= PQnfields(this->pgresult)) {
|
533
881
|
rb_raise(rb_eArgError,"invalid field number %d", j);
|
534
882
|
}
|
535
|
-
|
536
|
-
return Qnil;
|
537
|
-
val = rb_tainted_str_new(PQgetvalue(result, i, j),
|
538
|
-
PQgetlength(result, i, j));
|
539
|
-
|
540
|
-
#ifdef M17N_SUPPORTED
|
541
|
-
/* associate client encoding for text format only */
|
542
|
-
if ( 0 == PQfformat(result, j) ) {
|
543
|
-
ASSOCIATE_INDEX( val, self );
|
544
|
-
} else {
|
545
|
-
rb_enc_associate( val, rb_ascii8bit_encoding() );
|
546
|
-
}
|
547
|
-
#endif
|
548
|
-
|
549
|
-
return val;
|
883
|
+
return this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, j);
|
550
884
|
}
|
551
885
|
|
552
886
|
/*
|
@@ -574,7 +908,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
|
|
574
908
|
|
575
909
|
/*
|
576
910
|
* call-seq:
|
577
|
-
* res.getlength( tup_num, field_num ) ->
|
911
|
+
* res.getlength( tup_num, field_num ) -> Integer
|
578
912
|
*
|
579
913
|
* Returns the (String) length of the field in bytes.
|
580
914
|
*
|
@@ -599,7 +933,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
|
|
599
933
|
|
600
934
|
/*
|
601
935
|
* call-seq:
|
602
|
-
* res.nparams() ->
|
936
|
+
* res.nparams() -> Integer
|
603
937
|
*
|
604
938
|
* Returns the number of parameters of a prepared statement.
|
605
939
|
* Only useful for the result returned by conn.describePrepared
|
@@ -626,7 +960,7 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
626
960
|
PGresult *result;
|
627
961
|
|
628
962
|
result = pgresult_get(self);
|
629
|
-
return
|
963
|
+
return UINT2NUM(PQparamtype(result,NUM2INT(param_number)));
|
630
964
|
}
|
631
965
|
|
632
966
|
/*
|
@@ -638,23 +972,30 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
638
972
|
static VALUE
|
639
973
|
pgresult_cmd_status(VALUE self)
|
640
974
|
{
|
641
|
-
|
642
|
-
|
975
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
976
|
+
VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
|
977
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
643
978
|
return ret;
|
644
979
|
}
|
645
980
|
|
646
981
|
/*
|
647
982
|
* call-seq:
|
648
|
-
* res.cmd_tuples() ->
|
983
|
+
* res.cmd_tuples() -> Integer
|
649
984
|
*
|
650
985
|
* Returns the number of tuples (rows) affected by the SQL command.
|
651
986
|
*
|
652
987
|
* If the SQL command that generated the PG::Result was not one of:
|
653
|
-
*
|
654
|
-
* *
|
655
|
-
* *
|
656
|
-
* *
|
657
|
-
* *
|
988
|
+
*
|
989
|
+
* * <tt>SELECT</tt>
|
990
|
+
* * <tt>CREATE TABLE AS</tt>
|
991
|
+
* * <tt>INSERT</tt>
|
992
|
+
* * <tt>UPDATE</tt>
|
993
|
+
* * <tt>DELETE</tt>
|
994
|
+
* * <tt>MOVE</tt>
|
995
|
+
* * <tt>FETCH</tt>
|
996
|
+
* * <tt>COPY</tt>
|
997
|
+
* * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
|
998
|
+
*
|
658
999
|
* or if no tuples were affected, <tt>0</tt> is returned.
|
659
1000
|
*/
|
660
1001
|
static VALUE
|
@@ -667,7 +1008,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
667
1008
|
|
668
1009
|
/*
|
669
1010
|
* call-seq:
|
670
|
-
* res.oid_value() ->
|
1011
|
+
* res.oid_value() -> Integer
|
671
1012
|
*
|
672
1013
|
* Returns the +oid+ of the inserted row if applicable,
|
673
1014
|
* otherwise +nil+.
|
@@ -679,7 +1020,7 @@ pgresult_oid_value(VALUE self)
|
|
679
1020
|
if (n == InvalidOid)
|
680
1021
|
return Qnil;
|
681
1022
|
else
|
682
|
-
return
|
1023
|
+
return UINT2NUM(n);
|
683
1024
|
}
|
684
1025
|
|
685
1026
|
/* Utility methods not in libpq */
|
@@ -693,38 +1034,29 @@ pgresult_oid_value(VALUE self)
|
|
693
1034
|
static VALUE
|
694
1035
|
pgresult_aref(VALUE self, VALUE index)
|
695
1036
|
{
|
696
|
-
|
1037
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
697
1038
|
int tuple_num = NUM2INT(index);
|
698
1039
|
int field_num;
|
699
|
-
|
1040
|
+
int num_tuples = PQntuples(this->pgresult);
|
700
1041
|
VALUE tuple;
|
701
1042
|
|
702
|
-
if
|
703
|
-
|
1043
|
+
if( this->nfields == -1 )
|
1044
|
+
pgresult_init_fnames( self );
|
704
1045
|
|
705
|
-
|
706
|
-
|
707
|
-
fname = rb_tainted_str_new2( PQfname(result,field_num) );
|
708
|
-
ASSOCIATE_INDEX(fname, self);
|
709
|
-
if ( PQgetisnull(result, tuple_num, field_num) ) {
|
710
|
-
rb_hash_aset( tuple, fname, Qnil );
|
711
|
-
}
|
712
|
-
else {
|
713
|
-
val = rb_tainted_str_new( PQgetvalue(result, tuple_num, field_num ),
|
714
|
-
PQgetlength(result, tuple_num, field_num) );
|
715
|
-
|
716
|
-
#ifdef M17N_SUPPORTED
|
717
|
-
/* associate client encoding for text format only */
|
718
|
-
if ( 0 == PQfformat(result, field_num) ) {
|
719
|
-
ASSOCIATE_INDEX( val, self );
|
720
|
-
} else {
|
721
|
-
rb_enc_associate( val, rb_ascii8bit_encoding() );
|
722
|
-
}
|
723
|
-
#endif
|
1046
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1047
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
724
1048
|
|
725
|
-
|
726
|
-
|
1049
|
+
/* We reuse the Hash of the previous output for larger row counts.
|
1050
|
+
* This is somewhat faster than populating an empty Hash object. */
|
1051
|
+
tuple = NIL_P(this->tuple_hash) ? rb_hash_new() : this->tuple_hash;
|
1052
|
+
for ( field_num = 0; field_num < this->nfields; field_num++ ) {
|
1053
|
+
VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field_num);
|
1054
|
+
rb_hash_aset( tuple, this->fnames[field_num], val );
|
727
1055
|
}
|
1056
|
+
/* Store a copy of the filled hash for use at the next row. */
|
1057
|
+
if( num_tuples > 10 )
|
1058
|
+
this->tuple_hash = rb_hash_dup(tuple);
|
1059
|
+
|
728
1060
|
return tuple;
|
729
1061
|
}
|
730
1062
|
|
@@ -737,41 +1069,59 @@ pgresult_aref(VALUE self, VALUE index)
|
|
737
1069
|
static VALUE
|
738
1070
|
pgresult_each_row(VALUE self)
|
739
1071
|
{
|
740
|
-
|
1072
|
+
t_pg_result *this;
|
741
1073
|
int row;
|
742
1074
|
int field;
|
743
|
-
int num_rows
|
744
|
-
int num_fields
|
1075
|
+
int num_rows;
|
1076
|
+
int num_fields;
|
1077
|
+
|
1078
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
|
1079
|
+
|
1080
|
+
this = pgresult_get_this_safe(self);
|
1081
|
+
num_rows = PQntuples(this->pgresult);
|
1082
|
+
num_fields = PQnfields(this->pgresult);
|
745
1083
|
|
746
1084
|
for ( row = 0; row < num_rows; row++ ) {
|
747
|
-
VALUE
|
1085
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
748
1086
|
|
749
1087
|
/* populate the row */
|
750
1088
|
for ( field = 0; field < num_fields; field++ ) {
|
751
|
-
|
752
|
-
rb_ary_store( new_row, field, Qnil );
|
753
|
-
}
|
754
|
-
else {
|
755
|
-
VALUE val = rb_tainted_str_new( PQgetvalue(result, row, field),
|
756
|
-
PQgetlength(result, row, field) );
|
757
|
-
|
758
|
-
#ifdef M17N_SUPPORTED
|
759
|
-
/* associate client encoding for text format only */
|
760
|
-
if ( 0 == PQfformat(result, field) ) {
|
761
|
-
ASSOCIATE_INDEX( val, self );
|
762
|
-
} else {
|
763
|
-
rb_enc_associate( val, rb_ascii8bit_encoding() );
|
764
|
-
}
|
765
|
-
#endif
|
766
|
-
rb_ary_store( new_row, field, val );
|
767
|
-
}
|
1089
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
768
1090
|
}
|
769
|
-
rb_yield(
|
1091
|
+
rb_yield( rb_ary_new4( num_fields, row_values ));
|
770
1092
|
}
|
771
1093
|
|
772
1094
|
return Qnil;
|
773
1095
|
}
|
774
1096
|
|
1097
|
+
/*
|
1098
|
+
* call-seq:
|
1099
|
+
* res.values -> Array
|
1100
|
+
*
|
1101
|
+
* Returns all tuples as an array of arrays.
|
1102
|
+
*/
|
1103
|
+
static VALUE
|
1104
|
+
pgresult_values(VALUE self)
|
1105
|
+
{
|
1106
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1107
|
+
int row;
|
1108
|
+
int field;
|
1109
|
+
int num_rows = PQntuples(this->pgresult);
|
1110
|
+
int num_fields = PQnfields(this->pgresult);
|
1111
|
+
VALUE results = rb_ary_new2( num_rows );
|
1112
|
+
|
1113
|
+
for ( row = 0; row < num_rows; row++ ) {
|
1114
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
1115
|
+
|
1116
|
+
/* populate the row */
|
1117
|
+
for ( field = 0; field < num_fields; field++ ) {
|
1118
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1119
|
+
}
|
1120
|
+
rb_ary_store( results, row, rb_ary_new4( num_fields, row_values ) );
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
return results;
|
1124
|
+
}
|
775
1125
|
|
776
1126
|
/*
|
777
1127
|
* Make a Ruby array out of the encoded values from the specified
|
@@ -780,28 +1130,16 @@ pgresult_each_row(VALUE self)
|
|
780
1130
|
static VALUE
|
781
1131
|
make_column_result_array( VALUE self, int col )
|
782
1132
|
{
|
783
|
-
|
784
|
-
int rows = PQntuples(
|
1133
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1134
|
+
int rows = PQntuples( this->pgresult );
|
785
1135
|
int i;
|
786
|
-
VALUE val = Qnil;
|
787
1136
|
VALUE results = rb_ary_new2( rows );
|
788
1137
|
|
789
|
-
if ( col >= PQnfields(
|
1138
|
+
if ( col >= PQnfields(this->pgresult) )
|
790
1139
|
rb_raise( rb_eIndexError, "no column %d in result", col );
|
791
1140
|
|
792
1141
|
for ( i=0; i < rows; i++ ) {
|
793
|
-
val =
|
794
|
-
PQgetlength(result, i, col) );
|
795
|
-
|
796
|
-
#ifdef M17N_SUPPORTED
|
797
|
-
/* associate client encoding for text format only */
|
798
|
-
if ( 0 == PQfformat(result, col) ) {
|
799
|
-
ASSOCIATE_INDEX( val, self );
|
800
|
-
} else {
|
801
|
-
rb_enc_associate( val, rb_ascii8bit_encoding() );
|
802
|
-
}
|
803
|
-
#endif
|
804
|
-
|
1142
|
+
VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, col);
|
805
1143
|
rb_ary_store( results, i, val );
|
806
1144
|
}
|
807
1145
|
|
@@ -836,8 +1174,12 @@ static VALUE
|
|
836
1174
|
pgresult_field_values( VALUE self, VALUE field )
|
837
1175
|
{
|
838
1176
|
PGresult *result = pgresult_get( self );
|
839
|
-
const char *fieldname
|
840
|
-
int fnum
|
1177
|
+
const char *fieldname;
|
1178
|
+
int fnum;
|
1179
|
+
|
1180
|
+
if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
|
1181
|
+
fieldname = StringValueCStr( field );
|
1182
|
+
fnum = PQfnumber( result, fieldname );
|
841
1183
|
|
842
1184
|
if ( fnum < 0 )
|
843
1185
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
@@ -846,6 +1188,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
846
1188
|
}
|
847
1189
|
|
848
1190
|
|
1191
|
+
/*
|
1192
|
+
* call-seq:
|
1193
|
+
* res.tuple_values( n ) -> array
|
1194
|
+
*
|
1195
|
+
* Returns an Array of the field values from the nth row of the result.
|
1196
|
+
*
|
1197
|
+
*/
|
1198
|
+
static VALUE
|
1199
|
+
pgresult_tuple_values(VALUE self, VALUE index)
|
1200
|
+
{
|
1201
|
+
int tuple_num = NUM2INT( index );
|
1202
|
+
t_pg_result *this;
|
1203
|
+
int field;
|
1204
|
+
int num_tuples;
|
1205
|
+
int num_fields;
|
1206
|
+
|
1207
|
+
this = pgresult_get_this_safe(self);
|
1208
|
+
num_tuples = PQntuples(this->pgresult);
|
1209
|
+
num_fields = PQnfields(this->pgresult);
|
1210
|
+
|
1211
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1212
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1213
|
+
|
1214
|
+
{
|
1215
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
1216
|
+
|
1217
|
+
/* populate the row */
|
1218
|
+
for ( field = 0; field < num_fields; field++ ) {
|
1219
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
|
1220
|
+
}
|
1221
|
+
return rb_ary_new4( num_fields, row_values );
|
1222
|
+
}
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
static void ensure_init_for_tuple(VALUE self)
|
1226
|
+
{
|
1227
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1228
|
+
|
1229
|
+
if( this->field_map == Qnil ){
|
1230
|
+
int i;
|
1231
|
+
VALUE field_map = rb_hash_new();
|
1232
|
+
|
1233
|
+
if( this->nfields == -1 )
|
1234
|
+
pgresult_init_fnames( self );
|
1235
|
+
|
1236
|
+
for( i = 0; i < this->nfields; i++ ){
|
1237
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
1238
|
+
}
|
1239
|
+
rb_obj_freeze(field_map);
|
1240
|
+
this->field_map = field_map;
|
1241
|
+
}
|
1242
|
+
}
|
1243
|
+
|
1244
|
+
/*
|
1245
|
+
* call-seq:
|
1246
|
+
* res.tuple( n ) -> PG::Tuple
|
1247
|
+
*
|
1248
|
+
* Returns a PG::Tuple from the nth row of the result.
|
1249
|
+
*
|
1250
|
+
*/
|
1251
|
+
static VALUE
|
1252
|
+
pgresult_tuple(VALUE self, VALUE index)
|
1253
|
+
{
|
1254
|
+
int tuple_num = NUM2INT( index );
|
1255
|
+
t_pg_result *this;
|
1256
|
+
int num_tuples;
|
1257
|
+
|
1258
|
+
this = pgresult_get_this_safe(self);
|
1259
|
+
num_tuples = PQntuples(this->pgresult);
|
1260
|
+
|
1261
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1262
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1263
|
+
|
1264
|
+
ensure_init_for_tuple(self);
|
1265
|
+
|
1266
|
+
return pg_tuple_new(self, tuple_num);
|
1267
|
+
}
|
1268
|
+
|
1269
|
+
|
849
1270
|
/*
|
850
1271
|
* call-seq:
|
851
1272
|
* res.each{ |tuple| ... }
|
@@ -855,9 +1276,13 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
855
1276
|
static VALUE
|
856
1277
|
pgresult_each(VALUE self)
|
857
1278
|
{
|
858
|
-
PGresult *result
|
1279
|
+
PGresult *result;
|
859
1280
|
int tuple_num;
|
860
1281
|
|
1282
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
|
1283
|
+
|
1284
|
+
result = pgresult_get(self);
|
1285
|
+
|
861
1286
|
for(tuple_num = 0; tuple_num < PQntuples(result); tuple_num++) {
|
862
1287
|
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
863
1288
|
}
|
@@ -868,30 +1293,301 @@ pgresult_each(VALUE self)
|
|
868
1293
|
* call-seq:
|
869
1294
|
* res.fields() -> Array
|
870
1295
|
*
|
871
|
-
*
|
1296
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
872
1297
|
*/
|
873
1298
|
static VALUE
|
874
1299
|
pgresult_fields(VALUE self)
|
875
1300
|
{
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
1301
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1302
|
+
|
1303
|
+
if( this->nfields == -1 )
|
1304
|
+
pgresult_init_fnames( self );
|
1305
|
+
|
1306
|
+
return rb_ary_new4( this->nfields, this->fnames );
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
/*
|
1310
|
+
* call-seq:
|
1311
|
+
* res.type_map = typemap
|
1312
|
+
*
|
1313
|
+
* Set the TypeMap that is used for type casts of result values to ruby objects.
|
1314
|
+
*
|
1315
|
+
* All value retrieval methods will respect the type map and will do the
|
1316
|
+
* type casts from PostgreSQL's wire format to Ruby objects on the fly,
|
1317
|
+
* according to the rules and decoders defined in the given typemap.
|
1318
|
+
*
|
1319
|
+
* +typemap+ must be a kind of PG::TypeMap .
|
1320
|
+
*
|
1321
|
+
*/
|
1322
|
+
static VALUE
|
1323
|
+
pgresult_type_map_set(VALUE self, VALUE typemap)
|
1324
|
+
{
|
1325
|
+
t_pg_result *this = pgresult_get_this(self);
|
1326
|
+
t_typemap *p_typemap;
|
1327
|
+
|
1328
|
+
if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
|
1329
|
+
rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
|
1330
|
+
rb_obj_classname( typemap ) );
|
1331
|
+
}
|
1332
|
+
Data_Get_Struct(typemap, t_typemap, p_typemap);
|
1333
|
+
|
1334
|
+
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
1335
|
+
this->p_typemap = DATA_PTR( this->typemap );
|
1336
|
+
|
1337
|
+
return typemap;
|
1338
|
+
}
|
1339
|
+
|
1340
|
+
/*
|
1341
|
+
* call-seq:
|
1342
|
+
* res.type_map -> value
|
1343
|
+
*
|
1344
|
+
* Returns the TypeMap that is currently set for type casts of result values to ruby objects.
|
1345
|
+
*
|
1346
|
+
*/
|
1347
|
+
static VALUE
|
1348
|
+
pgresult_type_map_get(VALUE self)
|
1349
|
+
{
|
1350
|
+
t_pg_result *this = pgresult_get_this(self);
|
1351
|
+
|
1352
|
+
return this->typemap;
|
1353
|
+
}
|
1354
|
+
|
1355
|
+
|
1356
|
+
static void
|
1357
|
+
yield_hash(VALUE self, int ntuples, int nfields)
|
1358
|
+
{
|
1359
|
+
int tuple_num;
|
1360
|
+
t_pg_result *this = pgresult_get_this(self);
|
1361
|
+
UNUSED(nfields);
|
880
1362
|
|
881
|
-
for
|
882
|
-
|
883
|
-
ASSOCIATE_INDEX(val, self);
|
884
|
-
rb_ary_store( fields, i, val );
|
1363
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1364
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
885
1365
|
}
|
886
1366
|
|
887
|
-
|
1367
|
+
pgresult_clear( this );
|
888
1368
|
}
|
889
1369
|
|
1370
|
+
static void
|
1371
|
+
yield_array(VALUE self, int ntuples, int nfields)
|
1372
|
+
{
|
1373
|
+
int row;
|
1374
|
+
t_pg_result *this = pgresult_get_this(self);
|
1375
|
+
|
1376
|
+
for ( row = 0; row < ntuples; row++ ) {
|
1377
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
1378
|
+
int field;
|
1379
|
+
|
1380
|
+
/* populate the row */
|
1381
|
+
for ( field = 0; field < nfields; field++ ) {
|
1382
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1383
|
+
}
|
1384
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
1385
|
+
}
|
1386
|
+
|
1387
|
+
pgresult_clear( this );
|
1388
|
+
}
|
1389
|
+
|
1390
|
+
static void
|
1391
|
+
yield_tuple(VALUE self, int ntuples, int nfields)
|
1392
|
+
{
|
1393
|
+
int tuple_num;
|
1394
|
+
t_pg_result *this = pgresult_get_this(self);
|
1395
|
+
VALUE copy;
|
1396
|
+
UNUSED(nfields);
|
1397
|
+
|
1398
|
+
/* make a copy of the base result, that is bound to the PG::Tuple */
|
1399
|
+
copy = pg_copy_result(this);
|
1400
|
+
/* The copy is now owner of the PGresult and is responsible to PQclear it.
|
1401
|
+
* We clear the pgresult here, so that it's not double freed on error within yield. */
|
1402
|
+
this->pgresult = NULL;
|
1403
|
+
|
1404
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1405
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
1406
|
+
rb_yield( tuple );
|
1407
|
+
}
|
1408
|
+
}
|
1409
|
+
|
1410
|
+
static VALUE
|
1411
|
+
pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
1412
|
+
{
|
1413
|
+
t_pg_result *this;
|
1414
|
+
int nfields;
|
1415
|
+
PGconn *pgconn;
|
1416
|
+
PGresult *pgresult;
|
1417
|
+
|
1418
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
1419
|
+
|
1420
|
+
this = pgresult_get_this_safe(self);
|
1421
|
+
pgconn = pg_get_pgconn(this->connection);
|
1422
|
+
pgresult = this->pgresult;
|
1423
|
+
nfields = PQnfields(pgresult);
|
1424
|
+
|
1425
|
+
for(;;){
|
1426
|
+
int ntuples = PQntuples(pgresult);
|
1427
|
+
|
1428
|
+
switch( PQresultStatus(pgresult) ){
|
1429
|
+
case PGRES_TUPLES_OK:
|
1430
|
+
if( ntuples == 0 )
|
1431
|
+
return self;
|
1432
|
+
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
1433
|
+
case PGRES_SINGLE_TUPLE:
|
1434
|
+
break;
|
1435
|
+
default:
|
1436
|
+
pg_result_check( self );
|
1437
|
+
}
|
1438
|
+
|
1439
|
+
yielder( self, ntuples, nfields );
|
1440
|
+
|
1441
|
+
pgresult = gvl_PQgetResult(pgconn);
|
1442
|
+
if( pgresult == NULL )
|
1443
|
+
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
|
1444
|
+
|
1445
|
+
if( nfields != PQnfields(pgresult) )
|
1446
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
|
1447
|
+
|
1448
|
+
this->pgresult = pgresult;
|
1449
|
+
}
|
1450
|
+
|
1451
|
+
/* never reached */
|
1452
|
+
return self;
|
1453
|
+
}
|
1454
|
+
|
1455
|
+
|
1456
|
+
/*
|
1457
|
+
* call-seq:
|
1458
|
+
* res.stream_each{ |tuple| ... }
|
1459
|
+
*
|
1460
|
+
* Invokes block for each tuple in the result set in single row mode.
|
1461
|
+
*
|
1462
|
+
* This is a convenience method for retrieving all result tuples
|
1463
|
+
* as they are transferred. It is an alternative to repeated calls of
|
1464
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
1465
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
1466
|
+
* the same speed as with ordinary results.
|
1467
|
+
*
|
1468
|
+
* The base result must be in status PGRES_SINGLE_TUPLE.
|
1469
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
1470
|
+
* A PG::Error is raised for any errors from the server.
|
1471
|
+
*
|
1472
|
+
* Row description data does not change while the iteration. All value retrieval
|
1473
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
1474
|
+
* the iteration and +0+ after all tuples were yielded.
|
1475
|
+
*
|
1476
|
+
* Example:
|
1477
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
1478
|
+
* conn.set_single_row_mode
|
1479
|
+
* conn.get_result.stream_each do |row|
|
1480
|
+
* # do something with each received row of the first query
|
1481
|
+
* end
|
1482
|
+
* conn.get_result.stream_each do |row|
|
1483
|
+
* # do something with each received row of the second query
|
1484
|
+
* end
|
1485
|
+
* conn.get_result # => nil (no more results)
|
1486
|
+
*/
|
1487
|
+
static VALUE
|
1488
|
+
pgresult_stream_each(VALUE self)
|
1489
|
+
{
|
1490
|
+
return pgresult_stream_any(self, yield_hash);
|
1491
|
+
}
|
1492
|
+
|
1493
|
+
/*
|
1494
|
+
* call-seq:
|
1495
|
+
* res.stream_each_row { |row| ... }
|
1496
|
+
*
|
1497
|
+
* Yields each row of the result set in single row mode.
|
1498
|
+
* The row is a list of column values.
|
1499
|
+
*
|
1500
|
+
* This method works equally to #stream_each , but yields an Array of
|
1501
|
+
* values.
|
1502
|
+
*/
|
1503
|
+
static VALUE
|
1504
|
+
pgresult_stream_each_row(VALUE self)
|
1505
|
+
{
|
1506
|
+
return pgresult_stream_any(self, yield_array);
|
1507
|
+
}
|
1508
|
+
|
1509
|
+
/*
|
1510
|
+
* call-seq:
|
1511
|
+
* res.stream_each_tuple { |tuple| ... }
|
1512
|
+
*
|
1513
|
+
* Yields each row of the result set in single row mode.
|
1514
|
+
*
|
1515
|
+
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
1516
|
+
*/
|
1517
|
+
static VALUE
|
1518
|
+
pgresult_stream_each_tuple(VALUE self)
|
1519
|
+
{
|
1520
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
1521
|
+
ensure_init_for_tuple(self);
|
1522
|
+
|
1523
|
+
return pgresult_stream_any(self, yield_tuple);
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
/*
|
1527
|
+
* call-seq:
|
1528
|
+
* res.field_name_type = Symbol
|
1529
|
+
*
|
1530
|
+
* Set type of field names specific to this result.
|
1531
|
+
* It can be set to one of:
|
1532
|
+
* * +:string+ to use String based field names
|
1533
|
+
* * +:symbol+ to use Symbol based field names
|
1534
|
+
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably removed in future.
|
1535
|
+
*
|
1536
|
+
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
1537
|
+
*
|
1538
|
+
* This setting affects several result methods:
|
1539
|
+
* * keys of Hash returned by #[] , #each and #stream_each
|
1540
|
+
* * #fields
|
1541
|
+
* * #fname
|
1542
|
+
* * field names used by #tuple and #stream_each_tuple
|
1543
|
+
*
|
1544
|
+
* The type of field names can only be changed before any of the affected methods have been called.
|
1545
|
+
*
|
1546
|
+
*/
|
1547
|
+
static VALUE
|
1548
|
+
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
1549
|
+
{
|
1550
|
+
t_pg_result *this = pgresult_get_this(self);
|
1551
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
1552
|
+
|
1553
|
+
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
1554
|
+
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
1555
|
+
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
1556
|
+
else if ( sym == sym_string );
|
1557
|
+
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
1558
|
+
|
1559
|
+
return sym;
|
1560
|
+
}
|
1561
|
+
|
1562
|
+
/*
|
1563
|
+
* call-seq:
|
1564
|
+
* res.field_name_type -> Symbol
|
1565
|
+
*
|
1566
|
+
* Get type of field names.
|
1567
|
+
*
|
1568
|
+
* See description at #field_name_type=
|
1569
|
+
*/
|
1570
|
+
static VALUE
|
1571
|
+
pgresult_field_name_type_get(VALUE self)
|
1572
|
+
{
|
1573
|
+
t_pg_result *this = pgresult_get_this(self);
|
1574
|
+
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
1575
|
+
return sym_symbol;
|
1576
|
+
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
1577
|
+
return sym_static_symbol;
|
1578
|
+
} else {
|
1579
|
+
return sym_string;
|
1580
|
+
}
|
1581
|
+
}
|
890
1582
|
|
891
1583
|
void
|
892
1584
|
init_pg_result()
|
893
1585
|
{
|
894
|
-
|
1586
|
+
sym_string = ID2SYM(rb_intern("string"));
|
1587
|
+
sym_symbol = ID2SYM(rb_intern("symbol"));
|
1588
|
+
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
1589
|
+
|
1590
|
+
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cData );
|
895
1591
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
896
1592
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
897
1593
|
|
@@ -900,6 +1596,10 @@ init_pg_result()
|
|
900
1596
|
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
901
1597
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
902
1598
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
1599
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
1600
|
+
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
1601
|
+
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
1602
|
+
#endif
|
903
1603
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
904
1604
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
905
1605
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
@@ -932,8 +1632,22 @@ init_pg_result()
|
|
932
1632
|
rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
|
933
1633
|
rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
|
934
1634
|
rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
|
1635
|
+
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
935
1636
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
936
1637
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
937
|
-
|
1638
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
1639
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
1640
|
+
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
1641
|
+
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
1642
|
+
|
1643
|
+
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
1644
|
+
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
938
1645
|
|
1646
|
+
/****** PG::Result INSTANCE METHODS: streaming ******/
|
1647
|
+
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1648
|
+
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1649
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
939
1650
|
|
1651
|
+
rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
|
1652
|
+
rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
|
1653
|
+
}
|