pg 1.1.3 → 1.2.0
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 +70 -0
- data/Manifest.txt +3 -2
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +43 -8
- data/Rakefile +4 -4
- data/Rakefile.cross +7 -4
- data/ext/errorcodes.def +68 -0
- data/ext/errorcodes.txt +19 -2
- data/ext/extconf.rb +6 -6
- data/ext/pg.c +132 -95
- data/ext/pg.h +24 -17
- data/ext/pg_binary_decoder.c +20 -16
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +5 -5
- data/ext/pg_connection.c +395 -301
- data/ext/pg_copy_coder.c +5 -3
- data/ext/pg_record_coder.c +490 -0
- data/ext/pg_result.c +272 -124
- 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 +4 -3
- 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 +2 -3
- data/lib/pg/basic_type_mapping.rb +79 -16
- 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 +17 -16
- data/spec/pg/basic_type_mapping_spec.rb +151 -14
- data/spec/pg/connection_spec.rb +117 -55
- data/spec/pg/result_spec.rb +193 -3
- data/spec/pg/tuple_spec.rb +55 -2
- data/spec/pg/type_map_by_column_spec.rb +5 -1
- data/spec/pg/type_spec.rb +180 -6
- metadata +40 -45
- 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,41 @@ 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
|
+
|
116
171
|
/*
|
117
172
|
* Global functions
|
118
173
|
*/
|
@@ -124,12 +179,10 @@ static VALUE
|
|
124
179
|
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
125
180
|
{
|
126
181
|
int nfields = result ? PQnfields(result) : 0;
|
127
|
-
VALUE self
|
182
|
+
VALUE self;
|
128
183
|
t_pg_result *this;
|
129
184
|
|
130
185
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
131
|
-
RTYPEDDATA_DATA(self) = this;
|
132
|
-
|
133
186
|
this->pgresult = result;
|
134
187
|
this->connection = rb_pgconn;
|
135
188
|
this->typemap = pg_typemap_all_strings;
|
@@ -137,18 +190,21 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
137
190
|
this->nfields = -1;
|
138
191
|
this->tuple_hash = Qnil;
|
139
192
|
this->field_map = Qnil;
|
140
|
-
|
141
|
-
|
193
|
+
this->flags = 0;
|
194
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
142
195
|
|
143
196
|
if( result ){
|
144
197
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
145
198
|
VALUE typemap = p_conn->type_map_for_results;
|
146
|
-
|
147
199
|
/* Type check is done when assigned to PG::Connection. */
|
148
200
|
t_typemap *p_typemap = DATA_PTR(typemap);
|
149
201
|
|
202
|
+
this->enc_idx = p_conn->enc_idx;
|
150
203
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
151
204
|
this->p_typemap = DATA_PTR( this->typemap );
|
205
|
+
this->flags = p_conn->flags;
|
206
|
+
} else {
|
207
|
+
this->enc_idx = rb_locale_encindex();
|
152
208
|
}
|
153
209
|
|
154
210
|
return self;
|
@@ -159,22 +215,38 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
159
215
|
{
|
160
216
|
VALUE self = pg_new_result2(result, rb_pgconn);
|
161
217
|
t_pg_result *this = pgresult_get_this(self);
|
162
|
-
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
163
218
|
|
164
219
|
this->autoclear = 0;
|
165
220
|
|
166
|
-
|
167
|
-
|
168
|
-
|
221
|
+
/* Estimate size of underlying pgresult memory storage and account to ruby GC.
|
222
|
+
* There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
|
223
|
+
*/
|
224
|
+
/* 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.
|
225
|
+
* For now the memory savings don't justify the ifdefs necessary to support both cases.
|
226
|
+
*/
|
227
|
+
this->result_size = pgresult_approx_size(result);
|
169
228
|
|
170
229
|
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
171
|
-
|
230
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
172
231
|
#endif
|
173
|
-
}
|
174
232
|
|
175
233
|
return self;
|
176
234
|
}
|
177
235
|
|
236
|
+
static VALUE
|
237
|
+
pg_copy_result(t_pg_result *this)
|
238
|
+
{
|
239
|
+
int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
|
240
|
+
size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
|
241
|
+
t_pg_result *copy;
|
242
|
+
|
243
|
+
copy = (t_pg_result *)xmalloc(len);
|
244
|
+
memcpy(copy, this, len);
|
245
|
+
this->result_size = 0;
|
246
|
+
|
247
|
+
return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
|
248
|
+
}
|
249
|
+
|
178
250
|
VALUE
|
179
251
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
180
252
|
{
|
@@ -229,7 +301,7 @@ pg_result_check( VALUE self )
|
|
229
301
|
}
|
230
302
|
}
|
231
303
|
|
232
|
-
PG_ENCODING_SET_NOCHECK( error,
|
304
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
233
305
|
|
234
306
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
235
307
|
klass = lookup_error_class( sqlstate );
|
@@ -305,37 +377,6 @@ pgresult_autoclear_p( VALUE self )
|
|
305
377
|
* DATA pointer functions
|
306
378
|
*/
|
307
379
|
|
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
380
|
/*
|
340
381
|
* Fetch the PG::Result object data pointer and check it's
|
341
382
|
* PGresult data pointer for sanity.
|
@@ -365,32 +406,30 @@ pgresult_get(VALUE self)
|
|
365
406
|
return this->pgresult;
|
366
407
|
}
|
367
408
|
|
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 )
|
409
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
390
410
|
{
|
391
|
-
VALUE
|
392
|
-
|
393
|
-
|
411
|
+
VALUE fname;
|
412
|
+
#ifdef TRUFFLERUBY
|
413
|
+
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
414
|
+
#else
|
415
|
+
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
416
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
417
|
+
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
418
|
+
if( fname == Qnil ){
|
419
|
+
fname = rb_str_new2(cstr);
|
420
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
421
|
+
fname = rb_str_intern(fname);
|
422
|
+
}
|
423
|
+
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
424
|
+
#endif
|
425
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
426
|
+
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
427
|
+
} else {
|
428
|
+
fname = rb_str_new2(cstr);
|
429
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
430
|
+
fname = rb_obj_freeze(fname);
|
431
|
+
}
|
432
|
+
return fname;
|
394
433
|
}
|
395
434
|
|
396
435
|
static void pgresult_init_fnames(VALUE self)
|
@@ -402,12 +441,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
402
441
|
int nfields = PQnfields(this->pgresult);
|
403
442
|
|
404
443
|
for( i=0; i<nfields; i++ ){
|
405
|
-
|
406
|
-
|
407
|
-
this->fnames[i] = rb_obj_freeze(fname);
|
444
|
+
char *cfname = PQfname(this->pgresult, i);
|
445
|
+
this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
408
446
|
this->nfields = i + 1;
|
409
|
-
|
410
|
-
RB_GC_GUARD(fname);
|
411
447
|
}
|
412
448
|
this->nfields = nfields;
|
413
449
|
}
|
@@ -419,6 +455,8 @@ static void pgresult_init_fnames(VALUE self)
|
|
419
455
|
*
|
420
456
|
* The class to represent the query result tuples (rows).
|
421
457
|
* An instance of this class is created as the result of every query.
|
458
|
+
* All result rows and columns are stored in a memory block attached to the PG::Result object.
|
459
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
422
460
|
*
|
423
461
|
* 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
462
|
* 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 +506,9 @@ pgresult_result_status(VALUE self)
|
|
468
506
|
static VALUE
|
469
507
|
pgresult_res_status(VALUE self, VALUE status)
|
470
508
|
{
|
471
|
-
|
472
|
-
|
509
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
510
|
+
VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
|
511
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
473
512
|
return ret;
|
474
513
|
}
|
475
514
|
|
@@ -482,10 +521,39 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
482
521
|
static VALUE
|
483
522
|
pgresult_error_message(VALUE self)
|
484
523
|
{
|
485
|
-
|
486
|
-
|
524
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
525
|
+
VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
|
526
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
527
|
+
return ret;
|
528
|
+
}
|
529
|
+
|
530
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
531
|
+
/*
|
532
|
+
* call-seq:
|
533
|
+
* res.verbose_error_message( verbosity, show_context ) -> String
|
534
|
+
*
|
535
|
+
* Returns a reformatted version of the error message associated with a PGresult object.
|
536
|
+
*
|
537
|
+
* Available since PostgreSQL-9.6
|
538
|
+
*/
|
539
|
+
static VALUE
|
540
|
+
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
541
|
+
{
|
542
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
543
|
+
VALUE ret;
|
544
|
+
char *c_str;
|
545
|
+
|
546
|
+
c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
|
547
|
+
if(!c_str)
|
548
|
+
rb_raise(rb_eNoMemError, "insufficient memory to format error message");
|
549
|
+
|
550
|
+
ret = rb_str_new2(c_str);
|
551
|
+
PQfreemem(c_str);
|
552
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
553
|
+
|
487
554
|
return ret;
|
488
555
|
}
|
556
|
+
#endif
|
489
557
|
|
490
558
|
/*
|
491
559
|
* call-seq:
|
@@ -536,14 +604,14 @@ pgresult_error_message(VALUE self)
|
|
536
604
|
static VALUE
|
537
605
|
pgresult_error_field(VALUE self, VALUE field)
|
538
606
|
{
|
539
|
-
|
607
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
540
608
|
int fieldcode = NUM2INT( field );
|
541
|
-
char * fieldstr = PQresultErrorField(
|
609
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
542
610
|
VALUE ret = Qnil;
|
543
611
|
|
544
612
|
if ( fieldstr ) {
|
545
|
-
ret =
|
546
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
613
|
+
ret = rb_str_new2( fieldstr );
|
614
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
547
615
|
}
|
548
616
|
|
549
617
|
return ret;
|
@@ -581,24 +649,25 @@ pgresult_nfields(VALUE self)
|
|
581
649
|
|
582
650
|
/*
|
583
651
|
* call-seq:
|
584
|
-
* res.fname( index ) -> String
|
652
|
+
* res.fname( index ) -> String or Symbol
|
585
653
|
*
|
586
654
|
* Returns the name of the column corresponding to _index_.
|
655
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
656
|
+
*
|
587
657
|
*/
|
588
658
|
static VALUE
|
589
659
|
pgresult_fname(VALUE self, VALUE index)
|
590
660
|
{
|
591
|
-
|
592
|
-
PGresult *result = pgresult_get(self);
|
661
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
593
662
|
int i = NUM2INT(index);
|
663
|
+
char *cfname;
|
594
664
|
|
595
|
-
if (i < 0 || i >= PQnfields(
|
665
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
596
666
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
597
667
|
}
|
598
668
|
|
599
|
-
|
600
|
-
|
601
|
-
return rb_obj_freeze(fname);
|
669
|
+
cfname = PQfname(this->pgresult, i);
|
670
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
602
671
|
}
|
603
672
|
|
604
673
|
/*
|
@@ -897,8 +966,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
897
966
|
static VALUE
|
898
967
|
pgresult_cmd_status(VALUE self)
|
899
968
|
{
|
900
|
-
|
901
|
-
|
969
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
970
|
+
VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
|
971
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
902
972
|
return ret;
|
903
973
|
}
|
904
974
|
|
@@ -1098,8 +1168,12 @@ static VALUE
|
|
1098
1168
|
pgresult_field_values( VALUE self, VALUE field )
|
1099
1169
|
{
|
1100
1170
|
PGresult *result = pgresult_get( self );
|
1101
|
-
const char *fieldname
|
1102
|
-
int fnum
|
1171
|
+
const char *fieldname;
|
1172
|
+
int fnum;
|
1173
|
+
|
1174
|
+
if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
|
1175
|
+
fieldname = StringValueCStr( field );
|
1176
|
+
fnum = PQfnumber( result, fieldname );
|
1103
1177
|
|
1104
1178
|
if ( fnum < 0 )
|
1105
1179
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
@@ -1142,6 +1216,25 @@ pgresult_tuple_values(VALUE self, VALUE index)
|
|
1142
1216
|
}
|
1143
1217
|
}
|
1144
1218
|
|
1219
|
+
static void ensure_init_for_tuple(VALUE self)
|
1220
|
+
{
|
1221
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1222
|
+
|
1223
|
+
if( this->field_map == Qnil ){
|
1224
|
+
int i;
|
1225
|
+
VALUE field_map = rb_hash_new();
|
1226
|
+
|
1227
|
+
if( this->nfields == -1 )
|
1228
|
+
pgresult_init_fnames( self );
|
1229
|
+
|
1230
|
+
for( i = 0; i < this->nfields; i++ ){
|
1231
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
1232
|
+
}
|
1233
|
+
rb_obj_freeze(field_map);
|
1234
|
+
this->field_map = field_map;
|
1235
|
+
}
|
1236
|
+
}
|
1237
|
+
|
1145
1238
|
/*
|
1146
1239
|
* call-seq:
|
1147
1240
|
* res.tuple( n ) -> PG::Tuple
|
@@ -1162,19 +1255,7 @@ pgresult_tuple(VALUE self, VALUE index)
|
|
1162
1255
|
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1163
1256
|
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1164
1257
|
|
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
|
-
}
|
1258
|
+
ensure_init_for_tuple(self);
|
1178
1259
|
|
1179
1260
|
return pg_tuple_new(self, tuple_num);
|
1180
1261
|
}
|
@@ -1206,7 +1287,7 @@ pgresult_each(VALUE self)
|
|
1206
1287
|
* call-seq:
|
1207
1288
|
* res.fields() -> Array
|
1208
1289
|
*
|
1209
|
-
*
|
1290
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
1210
1291
|
*/
|
1211
1292
|
static VALUE
|
1212
1293
|
pgresult_fields(VALUE self)
|
@@ -1305,11 +1386,17 @@ yield_tuple(VALUE self, int ntuples, int nfields)
|
|
1305
1386
|
{
|
1306
1387
|
int tuple_num;
|
1307
1388
|
t_pg_result *this = pgresult_get_this(self);
|
1308
|
-
VALUE
|
1389
|
+
VALUE copy;
|
1309
1390
|
UNUSED(nfields);
|
1310
1391
|
|
1392
|
+
/* make a copy of the base result, that is bound to the PG::Tuple */
|
1393
|
+
copy = pg_copy_result(this);
|
1394
|
+
/* The copy is now owner of the PGresult and is responsible to PQclear it.
|
1395
|
+
* We clear the pgresult here, so that it's not double freed on error within yield. */
|
1396
|
+
this->pgresult = NULL;
|
1397
|
+
|
1311
1398
|
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1312
|
-
VALUE tuple = pgresult_tuple(
|
1399
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
1313
1400
|
rb_yield( tuple );
|
1314
1401
|
}
|
1315
1402
|
}
|
@@ -1390,8 +1477,6 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
|
1390
1477
|
* # do something with each received row of the second query
|
1391
1478
|
* end
|
1392
1479
|
* conn.get_result # => nil (no more results)
|
1393
|
-
*
|
1394
|
-
* Available since PostgreSQL-9.2
|
1395
1480
|
*/
|
1396
1481
|
static VALUE
|
1397
1482
|
pgresult_stream_each(VALUE self)
|
@@ -1408,8 +1493,6 @@ pgresult_stream_each(VALUE self)
|
|
1408
1493
|
*
|
1409
1494
|
* This method works equally to #stream_each , but yields an Array of
|
1410
1495
|
* values.
|
1411
|
-
*
|
1412
|
-
* Available since PostgreSQL-9.2
|
1413
1496
|
*/
|
1414
1497
|
static VALUE
|
1415
1498
|
pgresult_stream_each_row(VALUE self)
|
@@ -1424,21 +1507,81 @@ pgresult_stream_each_row(VALUE self)
|
|
1424
1507
|
* Yields each row of the result set in single row mode.
|
1425
1508
|
*
|
1426
1509
|
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
1427
|
-
*
|
1428
|
-
* Available since PostgreSQL-9.2
|
1429
1510
|
*/
|
1430
1511
|
static VALUE
|
1431
1512
|
pgresult_stream_each_tuple(VALUE self)
|
1432
1513
|
{
|
1514
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
1515
|
+
ensure_init_for_tuple(self);
|
1516
|
+
|
1433
1517
|
return pgresult_stream_any(self, yield_tuple);
|
1434
1518
|
}
|
1435
1519
|
|
1520
|
+
/*
|
1521
|
+
* call-seq:
|
1522
|
+
* res.field_name_type = Symbol
|
1523
|
+
*
|
1524
|
+
* Set type of field names specific to this result.
|
1525
|
+
* It can be set to one of:
|
1526
|
+
* * +:string+ to use String based field names
|
1527
|
+
* * +:symbol+ to use Symbol based field names
|
1528
|
+
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably removed in future.
|
1529
|
+
*
|
1530
|
+
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
1531
|
+
*
|
1532
|
+
* This setting affects several result methods:
|
1533
|
+
* * keys of Hash returned by #[] , #each and #stream_each
|
1534
|
+
* * #fields
|
1535
|
+
* * #fname
|
1536
|
+
* * field names used by #tuple and #stream_each_tuple
|
1537
|
+
*
|
1538
|
+
* The type of field names can only be changed before any of the affected methods have been called.
|
1539
|
+
*
|
1540
|
+
*/
|
1541
|
+
static VALUE
|
1542
|
+
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
1543
|
+
{
|
1544
|
+
t_pg_result *this = pgresult_get_this(self);
|
1545
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
1546
|
+
|
1547
|
+
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
1548
|
+
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
1549
|
+
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
1550
|
+
else if ( sym == sym_string );
|
1551
|
+
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
1552
|
+
|
1553
|
+
return sym;
|
1554
|
+
}
|
1555
|
+
|
1556
|
+
/*
|
1557
|
+
* call-seq:
|
1558
|
+
* res.field_name_type -> Symbol
|
1559
|
+
*
|
1560
|
+
* Get type of field names.
|
1561
|
+
*
|
1562
|
+
* See description at #field_name_type=
|
1563
|
+
*/
|
1564
|
+
static VALUE
|
1565
|
+
pgresult_field_name_type_get(VALUE self)
|
1566
|
+
{
|
1567
|
+
t_pg_result *this = pgresult_get_this(self);
|
1568
|
+
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
1569
|
+
return sym_symbol;
|
1570
|
+
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
1571
|
+
return sym_static_symbol;
|
1572
|
+
} else {
|
1573
|
+
return sym_string;
|
1574
|
+
}
|
1575
|
+
}
|
1436
1576
|
|
1437
1577
|
void
|
1438
1578
|
init_pg_result()
|
1439
1579
|
{
|
1440
|
-
|
1441
|
-
|
1580
|
+
sym_string = ID2SYM(rb_intern("string"));
|
1581
|
+
sym_symbol = ID2SYM(rb_intern("symbol"));
|
1582
|
+
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
1583
|
+
|
1584
|
+
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cData );
|
1442
1585
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
1443
1586
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
1444
1587
|
|
@@ -1447,6 +1590,10 @@ init_pg_result()
|
|
1447
1590
|
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
1448
1591
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
1449
1592
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
1593
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
1594
|
+
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
1595
|
+
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
1596
|
+
#endif
|
1450
1597
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
1451
1598
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
1452
1599
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
@@ -1494,6 +1641,7 @@ init_pg_result()
|
|
1494
1641
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1495
1642
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1496
1643
|
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
1497
|
-
}
|
1498
|
-
|
1499
1644
|
|
1645
|
+
rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
|
1646
|
+
rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
|
1647
|
+
}
|