pg 0.21.0 → 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/ChangeLog +0 -6595
- data/History.rdoc +184 -0
- data/Manifest.txt +8 -3
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +58 -13
- data/Rakefile +10 -9
- data/Rakefile.cross +68 -71
- data/ext/errorcodes.def +76 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +21 -2
- data/ext/extconf.rb +18 -36
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +154 -144
- data/ext/pg.h +68 -95
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +73 -12
- data/ext/pg_connection.c +699 -459
- data/ext/pg_copy_coder.c +16 -8
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +571 -195
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +185 -54
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +4 -4
- data/ext/pg_type_map_by_class.c +9 -4
- data/ext/pg_type_map_by_column.c +7 -6
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -2
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/{util.c → pg_util.c} +10 -10
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg.rb +8 -10
- data/lib/pg/basic_type_mapping.rb +121 -25
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +23 -2
- data/lib/pg/connection.rb +28 -4
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder.rb +21 -26
- data/lib/pg/text_encoder.rb +32 -8
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/spec/helpers.rb +61 -33
- data/spec/pg/basic_type_mapping_spec.rb +362 -37
- data/spec/pg/connection_spec.rb +602 -329
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +242 -17
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +6 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +3 -3
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +364 -18
- data/spec/pg_spec.rb +2 -2
- metadata +48 -43
- metadata.gz.sig +0 -0
- data/lib/pg/deprecated_constants.rb +0 -21
data/ext/pg_result.c
CHANGED
@@ -1,21 +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
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( 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
|
+
}
|
18
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
|
+
}
|
19
175
|
|
20
176
|
/*
|
21
177
|
* Global functions
|
@@ -24,45 +180,88 @@ static t_pg_result *pgresult_get_this_safe( VALUE );
|
|
24
180
|
/*
|
25
181
|
* Result constructor
|
26
182
|
*/
|
27
|
-
VALUE
|
28
|
-
|
183
|
+
static VALUE
|
184
|
+
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
29
185
|
{
|
30
186
|
int nfields = result ? PQnfields(result) : 0;
|
31
|
-
VALUE self
|
187
|
+
VALUE self;
|
32
188
|
t_pg_result *this;
|
33
189
|
|
34
190
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
35
|
-
DATA_PTR(self) = this;
|
36
|
-
|
37
191
|
this->pgresult = result;
|
38
192
|
this->connection = rb_pgconn;
|
39
193
|
this->typemap = pg_typemap_all_strings;
|
40
194
|
this->p_typemap = DATA_PTR( this->typemap );
|
41
|
-
this->autoclear = 0;
|
42
195
|
this->nfields = -1;
|
43
196
|
this->tuple_hash = Qnil;
|
44
|
-
|
45
|
-
|
197
|
+
this->field_map = Qnil;
|
198
|
+
this->flags = 0;
|
199
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
46
200
|
|
47
201
|
if( result ){
|
48
202
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
49
203
|
VALUE typemap = p_conn->type_map_for_results;
|
50
|
-
|
51
204
|
/* Type check is done when assigned to PG::Connection. */
|
52
205
|
t_typemap *p_typemap = DATA_PTR(typemap);
|
53
206
|
|
207
|
+
this->enc_idx = p_conn->enc_idx;
|
54
208
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
55
209
|
this->p_typemap = DATA_PTR( this->typemap );
|
210
|
+
this->flags = p_conn->flags;
|
211
|
+
} else {
|
212
|
+
this->enc_idx = rb_locale_encindex();
|
56
213
|
}
|
57
214
|
|
58
215
|
return self;
|
59
216
|
}
|
60
217
|
|
218
|
+
VALUE
|
219
|
+
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
220
|
+
{
|
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);
|
236
|
+
#endif
|
237
|
+
|
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
|
+
}
|
254
|
+
|
61
255
|
VALUE
|
62
256
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
63
257
|
{
|
64
|
-
VALUE self =
|
258
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
65
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;
|
66
265
|
this->autoclear = 1;
|
67
266
|
return self;
|
68
267
|
}
|
@@ -92,12 +291,8 @@ pg_result_check( VALUE self )
|
|
92
291
|
case PGRES_TUPLES_OK:
|
93
292
|
case PGRES_COPY_OUT:
|
94
293
|
case PGRES_COPY_IN:
|
95
|
-
#ifdef HAVE_CONST_PGRES_COPY_BOTH
|
96
294
|
case PGRES_COPY_BOTH:
|
97
|
-
#endif
|
98
|
-
#ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
|
99
295
|
case PGRES_SINGLE_TUPLE:
|
100
|
-
#endif
|
101
296
|
case PGRES_EMPTY_QUERY:
|
102
297
|
case PGRES_COMMAND_OK:
|
103
298
|
return self;
|
@@ -111,7 +306,7 @@ pg_result_check( VALUE self )
|
|
111
306
|
}
|
112
307
|
}
|
113
308
|
|
114
|
-
PG_ENCODING_SET_NOCHECK( error,
|
309
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
115
310
|
|
116
311
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
117
312
|
klass = lookup_error_class( sqlstate );
|
@@ -135,19 +330,22 @@ pg_result_check( VALUE self )
|
|
135
330
|
* call-seq:
|
136
331
|
* res.clear() -> nil
|
137
332
|
*
|
138
|
-
* 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.
|
139
340
|
*
|
140
|
-
* If PG::Result#autoclear? is true then the result is marked as cleared
|
141
|
-
* and the underlying C struct will be cleared automatically by libpq.
|
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.
|
142
342
|
*
|
143
343
|
*/
|
144
344
|
VALUE
|
145
345
|
pg_result_clear(VALUE self)
|
146
346
|
{
|
147
347
|
t_pg_result *this = pgresult_get_this(self);
|
148
|
-
|
149
|
-
PQclear(pgresult_get(self));
|
150
|
-
this->pgresult = NULL;
|
348
|
+
pgresult_clear( this );
|
151
349
|
return Qnil;
|
152
350
|
}
|
153
351
|
|
@@ -168,8 +366,10 @@ pgresult_cleared_p( VALUE self )
|
|
168
366
|
* call-seq:
|
169
367
|
* res.autoclear? -> boolean
|
170
368
|
*
|
171
|
-
* Returns +true+ if the underlying C struct will be cleared
|
172
|
-
*
|
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 .
|
173
373
|
*
|
174
374
|
*/
|
175
375
|
VALUE
|
@@ -183,37 +383,6 @@ pgresult_autoclear_p( VALUE self )
|
|
183
383
|
* DATA pointer functions
|
184
384
|
*/
|
185
385
|
|
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
386
|
/*
|
218
387
|
* Fetch the PG::Result object data pointer and check it's
|
219
388
|
* PGresult data pointer for sanity.
|
@@ -243,18 +412,30 @@ pgresult_get(VALUE self)
|
|
243
412
|
return this->pgresult;
|
244
413
|
}
|
245
414
|
|
246
|
-
|
247
|
-
* Document-method: allocate
|
248
|
-
*
|
249
|
-
* call-seq:
|
250
|
-
* PG::Result.allocate -> result
|
251
|
-
*/
|
252
|
-
static VALUE
|
253
|
-
pgresult_s_allocate( VALUE klass )
|
415
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
254
416
|
{
|
255
|
-
VALUE
|
256
|
-
|
257
|
-
|
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;
|
258
439
|
}
|
259
440
|
|
260
441
|
static void pgresult_init_fnames(VALUE self)
|
@@ -266,12 +447,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
266
447
|
int nfields = PQnfields(this->pgresult);
|
267
448
|
|
268
449
|
for( i=0; i<nfields; i++ ){
|
269
|
-
|
270
|
-
|
271
|
-
this->fnames[i] = rb_obj_freeze(fname);
|
450
|
+
char *cfname = PQfname(this->pgresult, i);
|
451
|
+
this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
272
452
|
this->nfields = i + 1;
|
273
|
-
|
274
|
-
RB_GC_GUARD(fname);
|
275
453
|
}
|
276
454
|
this->nfields = nfields;
|
277
455
|
}
|
@@ -283,8 +461,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
283
461
|
*
|
284
462
|
* The class to represent the query result tuples (rows).
|
285
463
|
* An instance of this class is created as the result of every query.
|
286
|
-
*
|
287
|
-
*
|
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.
|
288
469
|
*
|
289
470
|
* Example:
|
290
471
|
* require 'pg'
|
@@ -331,8 +512,9 @@ pgresult_result_status(VALUE self)
|
|
331
512
|
static VALUE
|
332
513
|
pgresult_res_status(VALUE self, VALUE status)
|
333
514
|
{
|
334
|
-
|
335
|
-
|
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);
|
336
518
|
return ret;
|
337
519
|
}
|
338
520
|
|
@@ -345,11 +527,40 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
345
527
|
static VALUE
|
346
528
|
pgresult_error_message(VALUE self)
|
347
529
|
{
|
348
|
-
|
349
|
-
|
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);
|
350
533
|
return ret;
|
351
534
|
}
|
352
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
|
+
|
353
564
|
/*
|
354
565
|
* call-seq:
|
355
566
|
* res.error_field(fieldcode) -> String
|
@@ -399,14 +610,14 @@ pgresult_error_message(VALUE self)
|
|
399
610
|
static VALUE
|
400
611
|
pgresult_error_field(VALUE self, VALUE field)
|
401
612
|
{
|
402
|
-
|
613
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
403
614
|
int fieldcode = NUM2INT( field );
|
404
|
-
char * fieldstr = PQresultErrorField(
|
615
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
405
616
|
VALUE ret = Qnil;
|
406
617
|
|
407
618
|
if ( fieldstr ) {
|
408
|
-
ret =
|
409
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
619
|
+
ret = rb_str_new2( fieldstr );
|
620
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
410
621
|
}
|
411
622
|
|
412
623
|
return ret;
|
@@ -427,7 +638,7 @@ pgresult_ntuples(VALUE self)
|
|
427
638
|
static VALUE
|
428
639
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
429
640
|
{
|
430
|
-
|
641
|
+
return pgresult_ntuples(self);
|
431
642
|
}
|
432
643
|
|
433
644
|
/*
|
@@ -444,24 +655,25 @@ pgresult_nfields(VALUE self)
|
|
444
655
|
|
445
656
|
/*
|
446
657
|
* call-seq:
|
447
|
-
* res.fname( index ) -> String
|
658
|
+
* res.fname( index ) -> String or Symbol
|
448
659
|
*
|
449
660
|
* Returns the name of the column corresponding to _index_.
|
661
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
662
|
+
*
|
450
663
|
*/
|
451
664
|
static VALUE
|
452
665
|
pgresult_fname(VALUE self, VALUE index)
|
453
666
|
{
|
454
|
-
|
455
|
-
PGresult *result = pgresult_get(self);
|
667
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
456
668
|
int i = NUM2INT(index);
|
669
|
+
char *cfname;
|
457
670
|
|
458
|
-
if (i < 0 || i >= PQnfields(
|
671
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
459
672
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
460
673
|
}
|
461
674
|
|
462
|
-
|
463
|
-
|
464
|
-
return rb_obj_freeze(fname);
|
675
|
+
cfname = PQfname(this->pgresult, i);
|
676
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
465
677
|
}
|
466
678
|
|
467
679
|
/*
|
@@ -760,8 +972,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
760
972
|
static VALUE
|
761
973
|
pgresult_cmd_status(VALUE self)
|
762
974
|
{
|
763
|
-
|
764
|
-
|
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);
|
765
978
|
return ret;
|
766
979
|
}
|
767
980
|
|
@@ -961,8 +1174,12 @@ static VALUE
|
|
961
1174
|
pgresult_field_values( VALUE self, VALUE field )
|
962
1175
|
{
|
963
1176
|
PGresult *result = pgresult_get( self );
|
964
|
-
const char *fieldname
|
965
|
-
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 );
|
966
1183
|
|
967
1184
|
if ( fnum < 0 )
|
968
1185
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
@@ -971,6 +1188,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
971
1188
|
}
|
972
1189
|
|
973
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
|
+
|
974
1270
|
/*
|
975
1271
|
* call-seq:
|
976
1272
|
* res.each{ |tuple| ... }
|
@@ -997,7 +1293,7 @@ pgresult_each(VALUE self)
|
|
997
1293
|
* call-seq:
|
998
1294
|
* res.fields() -> Array
|
999
1295
|
*
|
1000
|
-
*
|
1296
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
1001
1297
|
*/
|
1002
1298
|
static VALUE
|
1003
1299
|
pgresult_fields(VALUE self)
|
@@ -1056,40 +1352,63 @@ pgresult_type_map_get(VALUE self)
|
|
1056
1352
|
return this->typemap;
|
1057
1353
|
}
|
1058
1354
|
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
*
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
*
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
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);
|
1362
|
+
|
1363
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1364
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
pgresult_clear( this );
|
1368
|
+
}
|
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
|
+
|
1091
1410
|
static VALUE
|
1092
|
-
|
1411
|
+
pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
1093
1412
|
{
|
1094
1413
|
t_pg_result *this;
|
1095
1414
|
int nfields;
|
@@ -1104,7 +1423,6 @@ pgresult_stream_each(VALUE self)
|
|
1104
1423
|
nfields = PQnfields(pgresult);
|
1105
1424
|
|
1106
1425
|
for(;;){
|
1107
|
-
int tuple_num;
|
1108
1426
|
int ntuples = PQntuples(pgresult);
|
1109
1427
|
|
1110
1428
|
switch( PQresultStatus(pgresult) ){
|
@@ -1118,14 +1436,7 @@ pgresult_stream_each(VALUE self)
|
|
1118
1436
|
pg_result_check( self );
|
1119
1437
|
}
|
1120
1438
|
|
1121
|
-
|
1122
|
-
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
1123
|
-
}
|
1124
|
-
|
1125
|
-
if( !this->autoclear ){
|
1126
|
-
PQclear( pgresult );
|
1127
|
-
this->pgresult = NULL;
|
1128
|
-
}
|
1439
|
+
yielder( self, ntuples, nfields );
|
1129
1440
|
|
1130
1441
|
pgresult = gvl_PQgetResult(pgconn);
|
1131
1442
|
if( pgresult == NULL )
|
@@ -1141,6 +1452,44 @@ pgresult_stream_each(VALUE self)
|
|
1141
1452
|
return self;
|
1142
1453
|
}
|
1143
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
|
+
|
1144
1493
|
/*
|
1145
1494
|
* call-seq:
|
1146
1495
|
* res.stream_each_row { |row| ... }
|
@@ -1154,70 +1503,91 @@ pgresult_stream_each(VALUE self)
|
|
1154
1503
|
static VALUE
|
1155
1504
|
pgresult_stream_each_row(VALUE self)
|
1156
1505
|
{
|
1157
|
-
|
1158
|
-
|
1159
|
-
int nfields;
|
1160
|
-
PGconn *pgconn;
|
1161
|
-
PGresult *pgresult;
|
1162
|
-
|
1163
|
-
RETURN_ENUMERATOR(self, 0, NULL);
|
1164
|
-
|
1165
|
-
this = pgresult_get_this_safe(self);
|
1166
|
-
pgconn = pg_get_pgconn(this->connection);
|
1167
|
-
pgresult = this->pgresult;
|
1168
|
-
nfields = PQnfields(pgresult);
|
1169
|
-
|
1170
|
-
for(;;){
|
1171
|
-
int ntuples = PQntuples(pgresult);
|
1172
|
-
|
1173
|
-
switch( PQresultStatus(pgresult) ){
|
1174
|
-
case PGRES_TUPLES_OK:
|
1175
|
-
if( ntuples == 0 )
|
1176
|
-
return self;
|
1177
|
-
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
1178
|
-
case PGRES_SINGLE_TUPLE:
|
1179
|
-
break;
|
1180
|
-
default:
|
1181
|
-
pg_result_check( self );
|
1182
|
-
}
|
1506
|
+
return pgresult_stream_any(self, yield_array);
|
1507
|
+
}
|
1183
1508
|
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
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);
|
1187
1522
|
|
1188
|
-
|
1189
|
-
|
1190
|
-
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1191
|
-
}
|
1192
|
-
rb_yield( rb_ary_new4( nfields, row_values ));
|
1193
|
-
}
|
1523
|
+
return pgresult_stream_any(self, yield_tuple);
|
1524
|
+
}
|
1194
1525
|
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
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");
|
1199
1552
|
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
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);
|
1203
1558
|
|
1204
|
-
|
1205
|
-
|
1559
|
+
return sym;
|
1560
|
+
}
|
1206
1561
|
|
1207
|
-
|
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;
|
1208
1580
|
}
|
1209
|
-
|
1210
|
-
/* never reached */
|
1211
|
-
return self;
|
1212
1581
|
}
|
1213
|
-
#endif
|
1214
|
-
|
1215
1582
|
|
1216
1583
|
void
|
1217
1584
|
init_pg_result()
|
1218
1585
|
{
|
1219
|
-
|
1220
|
-
|
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 );
|
1221
1591
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
1222
1592
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
1223
1593
|
|
@@ -1226,6 +1596,10 @@ init_pg_result()
|
|
1226
1596
|
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
1227
1597
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
1228
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
|
1229
1603
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
1230
1604
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
1231
1605
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
@@ -1261,17 +1635,19 @@ init_pg_result()
|
|
1261
1635
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
1262
1636
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
1263
1637
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
1638
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
1639
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
1264
1640
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
1265
1641
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
1266
1642
|
|
1267
1643
|
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
1268
1644
|
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
1269
1645
|
|
1270
|
-
#ifdef HAVE_PQSETSINGLEROWMODE
|
1271
1646
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
1272
1647
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1273
1648
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1274
|
-
|
1275
|
-
}
|
1276
|
-
|
1649
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
1277
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
|
+
}
|