pg 1.1.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +110 -1
- data/Manifest.txt +3 -2
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +44 -9
- data/Rakefile +8 -6
- data/Rakefile.cross +57 -56
- data/ext/errorcodes.def +68 -0
- data/ext/errorcodes.txt +19 -2
- data/ext/extconf.rb +6 -6
- data/ext/pg.c +138 -99
- data/ext/pg.h +34 -26
- data/ext/pg_binary_decoder.c +20 -16
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +21 -9
- data/ext/pg_connection.c +413 -321
- data/ext/pg_copy_coder.c +6 -3
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +282 -128
- data/ext/pg_text_decoder.c +14 -8
- data/ext/pg_text_encoder.c +180 -48
- data/ext/pg_tuple.c +14 -6
- 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} +5 -5
- data/ext/{util.h → pg_util.h} +0 -0
- data/lib/pg.rb +5 -5
- data/lib/pg/basic_type_mapping.rb +81 -18
- data/lib/pg/binary_decoder.rb +1 -0
- data/lib/pg/coder.rb +22 -1
- data/lib/pg/connection.rb +2 -2
- data/lib/pg/constants.rb +1 -0
- data/lib/pg/exceptions.rb +1 -0
- data/lib/pg/result.rb +13 -1
- data/lib/pg/text_decoder.rb +2 -3
- data/lib/pg/text_encoder.rb +8 -18
- data/lib/pg/type_map_by_column.rb +2 -1
- data/spec/helpers.rb +19 -19
- data/spec/pg/basic_type_mapping_spec.rb +141 -19
- data/spec/pg/connection_spec.rb +239 -93
- data/spec/pg/result_spec.rb +194 -4
- data/spec/pg/tuple_spec.rb +55 -2
- data/spec/pg/type_map_by_class_spec.rb +1 -1
- data/spec/pg/type_map_by_column_spec.rb +5 -1
- data/spec/pg/type_map_by_oid_spec.rb +2 -2
- data/spec/pg/type_spec.rb +180 -6
- metadata +41 -47
- metadata.gz.sig +0 -0
data/ext/pg_result.c
CHANGED
@@ -1,20 +1,27 @@
|
|
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
|
18
25
|
|
19
26
|
#define PGRESULT_DATA_BLOCKSIZE 2048
|
20
27
|
typedef struct pgresAttValue
|
@@ -44,7 +51,7 @@ count_leading_zero_bits(unsigned int x)
|
|
44
51
|
}
|
45
52
|
|
46
53
|
static ssize_t
|
47
|
-
pgresult_approx_size(PGresult *result)
|
54
|
+
pgresult_approx_size(const PGresult *result)
|
48
55
|
{
|
49
56
|
int num_fields = PQnfields(result);
|
50
57
|
ssize_t size = 0;
|
@@ -94,7 +101,29 @@ pgresult_approx_size(PGresult *result)
|
|
94
101
|
|
95
102
|
return size;
|
96
103
|
}
|
104
|
+
#endif
|
97
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
|
+
*/
|
98
127
|
static void
|
99
128
|
pgresult_clear( t_pg_result *this )
|
100
129
|
{
|
@@ -104,15 +133,46 @@ pgresult_clear( t_pg_result *this )
|
|
104
133
|
rb_gc_adjust_memory_usage(-this->result_size);
|
105
134
|
#endif
|
106
135
|
}
|
136
|
+
this->result_size = 0;
|
137
|
+
this->nfields = -1;
|
107
138
|
this->pgresult = NULL;
|
108
139
|
}
|
109
140
|
|
141
|
+
static void
|
142
|
+
pgresult_gc_free( t_pg_result *this )
|
143
|
+
{
|
144
|
+
pgresult_clear( this );
|
145
|
+
xfree(this);
|
146
|
+
}
|
147
|
+
|
110
148
|
static size_t
|
111
149
|
pgresult_memsize( t_pg_result *this )
|
112
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
|
+
*/
|
113
154
|
return this->result_size;
|
114
155
|
}
|
115
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
|
+
}
|
175
|
+
|
116
176
|
/*
|
117
177
|
* Global functions
|
118
178
|
*/
|
@@ -124,12 +184,10 @@ static VALUE
|
|
124
184
|
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
125
185
|
{
|
126
186
|
int nfields = result ? PQnfields(result) : 0;
|
127
|
-
VALUE self
|
187
|
+
VALUE self;
|
128
188
|
t_pg_result *this;
|
129
189
|
|
130
190
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
131
|
-
RTYPEDDATA_DATA(self) = this;
|
132
|
-
|
133
191
|
this->pgresult = result;
|
134
192
|
this->connection = rb_pgconn;
|
135
193
|
this->typemap = pg_typemap_all_strings;
|
@@ -137,18 +195,21 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
137
195
|
this->nfields = -1;
|
138
196
|
this->tuple_hash = Qnil;
|
139
197
|
this->field_map = Qnil;
|
140
|
-
|
141
|
-
|
198
|
+
this->flags = 0;
|
199
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
142
200
|
|
143
201
|
if( result ){
|
144
202
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
145
203
|
VALUE typemap = p_conn->type_map_for_results;
|
146
|
-
|
147
204
|
/* Type check is done when assigned to PG::Connection. */
|
148
205
|
t_typemap *p_typemap = DATA_PTR(typemap);
|
149
206
|
|
207
|
+
this->enc_idx = p_conn->enc_idx;
|
150
208
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
151
209
|
this->p_typemap = DATA_PTR( this->typemap );
|
210
|
+
this->flags = p_conn->flags;
|
211
|
+
} else {
|
212
|
+
this->enc_idx = rb_locale_encindex();
|
152
213
|
}
|
153
214
|
|
154
215
|
return self;
|
@@ -159,22 +220,38 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
159
220
|
{
|
160
221
|
VALUE self = pg_new_result2(result, rb_pgconn);
|
161
222
|
t_pg_result *this = pgresult_get_this(self);
|
162
|
-
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
163
223
|
|
164
224
|
this->autoclear = 0;
|
165
225
|
|
166
|
-
|
167
|
-
|
168
|
-
|
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);
|
169
233
|
|
170
234
|
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
171
|
-
|
235
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
172
236
|
#endif
|
173
|
-
}
|
174
237
|
|
175
238
|
return self;
|
176
239
|
}
|
177
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
|
+
|
178
255
|
VALUE
|
179
256
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
180
257
|
{
|
@@ -229,7 +306,7 @@ pg_result_check( VALUE self )
|
|
229
306
|
}
|
230
307
|
}
|
231
308
|
|
232
|
-
PG_ENCODING_SET_NOCHECK( error,
|
309
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
233
310
|
|
234
311
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
235
312
|
klass = lookup_error_class( sqlstate );
|
@@ -261,8 +338,7 @@ pg_result_check( VALUE self )
|
|
261
338
|
* Special care must be taken when PG::Tuple objects are used.
|
262
339
|
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
263
340
|
*
|
264
|
-
* If PG::Result#autoclear? is true then the result is marked as cleared
|
265
|
-
* 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.
|
266
342
|
*
|
267
343
|
*/
|
268
344
|
VALUE
|
@@ -290,8 +366,10 @@ pgresult_cleared_p( VALUE self )
|
|
290
366
|
* call-seq:
|
291
367
|
* res.autoclear? -> boolean
|
292
368
|
*
|
293
|
-
* Returns +true+ if the underlying C struct will be cleared
|
294
|
-
*
|
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 .
|
295
373
|
*
|
296
374
|
*/
|
297
375
|
VALUE
|
@@ -305,37 +383,6 @@ pgresult_autoclear_p( VALUE self )
|
|
305
383
|
* DATA pointer functions
|
306
384
|
*/
|
307
385
|
|
308
|
-
/*
|
309
|
-
* GC Mark function
|
310
|
-
*/
|
311
|
-
static void
|
312
|
-
pgresult_gc_mark( t_pg_result *this )
|
313
|
-
{
|
314
|
-
int i;
|
315
|
-
|
316
|
-
if( !this ) return;
|
317
|
-
rb_gc_mark( this->connection );
|
318
|
-
rb_gc_mark( this->typemap );
|
319
|
-
rb_gc_mark( this->tuple_hash );
|
320
|
-
rb_gc_mark( this->field_map );
|
321
|
-
|
322
|
-
for( i=0; i < this->nfields; i++ ){
|
323
|
-
rb_gc_mark( this->fnames[i] );
|
324
|
-
}
|
325
|
-
}
|
326
|
-
|
327
|
-
/*
|
328
|
-
* GC Free function
|
329
|
-
*/
|
330
|
-
static void
|
331
|
-
pgresult_gc_free( t_pg_result *this )
|
332
|
-
{
|
333
|
-
if( !this ) return;
|
334
|
-
pgresult_clear( this );
|
335
|
-
|
336
|
-
xfree(this);
|
337
|
-
}
|
338
|
-
|
339
386
|
/*
|
340
387
|
* Fetch the PG::Result object data pointer and check it's
|
341
388
|
* PGresult data pointer for sanity.
|
@@ -365,32 +412,30 @@ pgresult_get(VALUE self)
|
|
365
412
|
return this->pgresult;
|
366
413
|
}
|
367
414
|
|
368
|
-
|
369
|
-
static const rb_data_type_t pgresult_type = {
|
370
|
-
"pg",
|
371
|
-
{
|
372
|
-
(void (*)(void*))pgresult_gc_mark,
|
373
|
-
(void (*)(void*))pgresult_gc_free,
|
374
|
-
(size_t (*)(const void *))pgresult_memsize,
|
375
|
-
},
|
376
|
-
0, 0,
|
377
|
-
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
378
|
-
RUBY_TYPED_FREE_IMMEDIATELY,
|
379
|
-
#endif
|
380
|
-
};
|
381
|
-
|
382
|
-
/*
|
383
|
-
* Document-method: allocate
|
384
|
-
*
|
385
|
-
* call-seq:
|
386
|
-
* PG::Result.allocate -> result
|
387
|
-
*/
|
388
|
-
static VALUE
|
389
|
-
pgresult_s_allocate( VALUE klass )
|
415
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
390
416
|
{
|
391
|
-
VALUE
|
392
|
-
|
393
|
-
|
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;
|
394
439
|
}
|
395
440
|
|
396
441
|
static void pgresult_init_fnames(VALUE self)
|
@@ -402,12 +447,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
402
447
|
int nfields = PQnfields(this->pgresult);
|
403
448
|
|
404
449
|
for( i=0; i<nfields; i++ ){
|
405
|
-
|
406
|
-
|
407
|
-
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);
|
408
452
|
this->nfields = i + 1;
|
409
|
-
|
410
|
-
RB_GC_GUARD(fname);
|
411
453
|
}
|
412
454
|
this->nfields = nfields;
|
413
455
|
}
|
@@ -419,6 +461,8 @@ static void pgresult_init_fnames(VALUE self)
|
|
419
461
|
*
|
420
462
|
* The class to represent the query result tuples (rows).
|
421
463
|
* An instance of this class is created as the result of every query.
|
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 .
|
422
466
|
*
|
423
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.
|
424
468
|
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
@@ -468,8 +512,9 @@ pgresult_result_status(VALUE self)
|
|
468
512
|
static VALUE
|
469
513
|
pgresult_res_status(VALUE self, VALUE status)
|
470
514
|
{
|
471
|
-
|
472
|
-
|
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);
|
473
518
|
return ret;
|
474
519
|
}
|
475
520
|
|
@@ -482,11 +527,40 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
482
527
|
static VALUE
|
483
528
|
pgresult_error_message(VALUE self)
|
484
529
|
{
|
485
|
-
|
486
|
-
|
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);
|
487
533
|
return ret;
|
488
534
|
}
|
489
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
|
+
|
490
564
|
/*
|
491
565
|
* call-seq:
|
492
566
|
* res.error_field(fieldcode) -> String
|
@@ -536,14 +610,14 @@ pgresult_error_message(VALUE self)
|
|
536
610
|
static VALUE
|
537
611
|
pgresult_error_field(VALUE self, VALUE field)
|
538
612
|
{
|
539
|
-
|
613
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
540
614
|
int fieldcode = NUM2INT( field );
|
541
|
-
char * fieldstr = PQresultErrorField(
|
615
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
542
616
|
VALUE ret = Qnil;
|
543
617
|
|
544
618
|
if ( fieldstr ) {
|
545
|
-
ret =
|
546
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
619
|
+
ret = rb_str_new2( fieldstr );
|
620
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
547
621
|
}
|
548
622
|
|
549
623
|
return ret;
|
@@ -581,24 +655,25 @@ pgresult_nfields(VALUE self)
|
|
581
655
|
|
582
656
|
/*
|
583
657
|
* call-seq:
|
584
|
-
* res.fname( index ) -> String
|
658
|
+
* res.fname( index ) -> String or Symbol
|
585
659
|
*
|
586
660
|
* Returns the name of the column corresponding to _index_.
|
661
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
662
|
+
*
|
587
663
|
*/
|
588
664
|
static VALUE
|
589
665
|
pgresult_fname(VALUE self, VALUE index)
|
590
666
|
{
|
591
|
-
|
592
|
-
PGresult *result = pgresult_get(self);
|
667
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
593
668
|
int i = NUM2INT(index);
|
669
|
+
char *cfname;
|
594
670
|
|
595
|
-
if (i < 0 || i >= PQnfields(
|
671
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
596
672
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
597
673
|
}
|
598
674
|
|
599
|
-
|
600
|
-
|
601
|
-
return rb_obj_freeze(fname);
|
675
|
+
cfname = PQfname(this->pgresult, i);
|
676
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
602
677
|
}
|
603
678
|
|
604
679
|
/*
|
@@ -897,8 +972,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
897
972
|
static VALUE
|
898
973
|
pgresult_cmd_status(VALUE self)
|
899
974
|
{
|
900
|
-
|
901
|
-
|
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);
|
902
978
|
return ret;
|
903
979
|
}
|
904
980
|
|
@@ -1098,8 +1174,12 @@ static VALUE
|
|
1098
1174
|
pgresult_field_values( VALUE self, VALUE field )
|
1099
1175
|
{
|
1100
1176
|
PGresult *result = pgresult_get( self );
|
1101
|
-
const char *fieldname
|
1102
|
-
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 );
|
1103
1183
|
|
1104
1184
|
if ( fnum < 0 )
|
1105
1185
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
@@ -1142,6 +1222,25 @@ pgresult_tuple_values(VALUE self, VALUE index)
|
|
1142
1222
|
}
|
1143
1223
|
}
|
1144
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
|
+
|
1145
1244
|
/*
|
1146
1245
|
* call-seq:
|
1147
1246
|
* res.tuple( n ) -> PG::Tuple
|
@@ -1162,19 +1261,7 @@ pgresult_tuple(VALUE self, VALUE index)
|
|
1162
1261
|
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1163
1262
|
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1164
1263
|
|
1165
|
-
|
1166
|
-
int i;
|
1167
|
-
VALUE field_map = rb_hash_new();
|
1168
|
-
|
1169
|
-
if( this->nfields == -1 )
|
1170
|
-
pgresult_init_fnames( self );
|
1171
|
-
|
1172
|
-
for( i = 0; i < this->nfields; i++ ){
|
1173
|
-
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
1174
|
-
}
|
1175
|
-
rb_obj_freeze(field_map);
|
1176
|
-
this->field_map = field_map;
|
1177
|
-
}
|
1264
|
+
ensure_init_for_tuple(self);
|
1178
1265
|
|
1179
1266
|
return pg_tuple_new(self, tuple_num);
|
1180
1267
|
}
|
@@ -1206,7 +1293,7 @@ pgresult_each(VALUE self)
|
|
1206
1293
|
* call-seq:
|
1207
1294
|
* res.fields() -> Array
|
1208
1295
|
*
|
1209
|
-
*
|
1296
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
1210
1297
|
*/
|
1211
1298
|
static VALUE
|
1212
1299
|
pgresult_fields(VALUE self)
|
@@ -1305,11 +1392,17 @@ yield_tuple(VALUE self, int ntuples, int nfields)
|
|
1305
1392
|
{
|
1306
1393
|
int tuple_num;
|
1307
1394
|
t_pg_result *this = pgresult_get_this(self);
|
1308
|
-
VALUE
|
1395
|
+
VALUE copy;
|
1309
1396
|
UNUSED(nfields);
|
1310
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
|
+
|
1311
1404
|
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1312
|
-
VALUE tuple = pgresult_tuple(
|
1405
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
1313
1406
|
rb_yield( tuple );
|
1314
1407
|
}
|
1315
1408
|
}
|
@@ -1390,8 +1483,6 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
|
1390
1483
|
* # do something with each received row of the second query
|
1391
1484
|
* end
|
1392
1485
|
* conn.get_result # => nil (no more results)
|
1393
|
-
*
|
1394
|
-
* Available since PostgreSQL-9.2
|
1395
1486
|
*/
|
1396
1487
|
static VALUE
|
1397
1488
|
pgresult_stream_each(VALUE self)
|
@@ -1408,8 +1499,6 @@ pgresult_stream_each(VALUE self)
|
|
1408
1499
|
*
|
1409
1500
|
* This method works equally to #stream_each , but yields an Array of
|
1410
1501
|
* values.
|
1411
|
-
*
|
1412
|
-
* Available since PostgreSQL-9.2
|
1413
1502
|
*/
|
1414
1503
|
static VALUE
|
1415
1504
|
pgresult_stream_each_row(VALUE self)
|
@@ -1424,21 +1513,81 @@ pgresult_stream_each_row(VALUE self)
|
|
1424
1513
|
* Yields each row of the result set in single row mode.
|
1425
1514
|
*
|
1426
1515
|
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
1427
|
-
*
|
1428
|
-
* Available since PostgreSQL-9.2
|
1429
1516
|
*/
|
1430
1517
|
static VALUE
|
1431
1518
|
pgresult_stream_each_tuple(VALUE self)
|
1432
1519
|
{
|
1520
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
1521
|
+
ensure_init_for_tuple(self);
|
1522
|
+
|
1433
1523
|
return pgresult_stream_any(self, yield_tuple);
|
1434
1524
|
}
|
1435
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
|
+
}
|
1436
1582
|
|
1437
1583
|
void
|
1438
1584
|
init_pg_result()
|
1439
1585
|
{
|
1440
|
-
|
1441
|
-
|
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 );
|
1442
1591
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
1443
1592
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
1444
1593
|
|
@@ -1447,6 +1596,10 @@ init_pg_result()
|
|
1447
1596
|
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
1448
1597
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
1449
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
|
1450
1603
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
1451
1604
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
1452
1605
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
@@ -1494,6 +1647,7 @@ init_pg_result()
|
|
1494
1647
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1495
1648
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1496
1649
|
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
1497
|
-
}
|
1498
|
-
|
1499
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
|
+
}
|