pg 0.17.1 → 0.18.4
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/ChangeLog +2407 -2
- data/History.rdoc +68 -0
- data/Manifest.txt +29 -1
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +52 -2
- data/Rakefile +56 -18
- data/Rakefile.cross +77 -49
- data/ext/extconf.rb +33 -26
- data/ext/pg.c +142 -21
- data/ext/pg.h +242 -6
- data/ext/pg_binary_decoder.c +162 -0
- data/ext/pg_binary_encoder.c +162 -0
- data/ext/pg_coder.c +479 -0
- data/ext/pg_connection.c +858 -553
- data/ext/pg_copy_coder.c +561 -0
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +479 -128
- data/ext/pg_text_decoder.c +421 -0
- data/ext/pg_text_encoder.c +663 -0
- data/ext/pg_type_map.c +159 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +312 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +355 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/util.c +149 -0
- data/ext/util.h +65 -0
- data/lib/pg/basic_type_mapping.rb +399 -0
- data/lib/pg/coder.rb +83 -0
- data/lib/pg/connection.rb +81 -29
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +44 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/lib/pg.rb +12 -2
- data/spec/{lib/helpers.rb → helpers.rb} +101 -39
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +516 -218
- data/spec/pg/result_spec.rb +216 -112
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +222 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
- data/spec/pg/type_map_by_oid_spec.rb +149 -0
- data/spec/pg/type_map_in_ruby_spec.rb +164 -0
- data/spec/pg/type_map_spec.rb +22 -0
- data/spec/pg/type_spec.rb +697 -0
- data/spec/pg_spec.rb +24 -18
- data.tar.gz.sig +0 -0
- metadata +111 -45
- metadata.gz.sig +0 -0
data/ext/pg_result.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_result.c - PG::Result class extension
|
3
|
-
* $Id: pg_result.c,v
|
3
|
+
* $Id: pg_result.c,v 1269b8ad77b8 2015/02/06 16:38:23 lars $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -9,8 +9,12 @@
|
|
9
9
|
|
10
10
|
VALUE rb_cPGresult;
|
11
11
|
|
12
|
-
static void pgresult_gc_free(
|
13
|
-
static
|
12
|
+
static void pgresult_gc_free( t_pg_result * );
|
13
|
+
static VALUE pgresult_type_map_set( VALUE, VALUE );
|
14
|
+
static VALUE pgresult_s_allocate( VALUE );
|
15
|
+
static t_pg_result *pgresult_get_this( VALUE );
|
16
|
+
static t_pg_result *pgresult_get_this_safe( VALUE );
|
17
|
+
|
14
18
|
|
15
19
|
|
16
20
|
/*
|
@@ -23,16 +27,44 @@ static PGresult* pgresult_get( VALUE );
|
|
23
27
|
VALUE
|
24
28
|
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
25
29
|
{
|
26
|
-
|
27
|
-
VALUE
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
int nfields = result ? PQnfields(result) : 0;
|
31
|
+
VALUE self = pgresult_s_allocate( rb_cPGresult );
|
32
|
+
t_pg_result *this;
|
33
|
+
|
34
|
+
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
35
|
+
DATA_PTR(self) = this;
|
36
|
+
|
37
|
+
this->pgresult = result;
|
38
|
+
this->connection = rb_pgconn;
|
39
|
+
this->typemap = pg_typemap_all_strings;
|
40
|
+
this->p_typemap = DATA_PTR( this->typemap );
|
41
|
+
this->autoclear = 0;
|
42
|
+
this->nfields = -1;
|
43
|
+
this->tuple_hash = Qnil;
|
32
44
|
|
33
|
-
|
45
|
+
PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
|
34
46
|
|
35
|
-
|
47
|
+
if( result ){
|
48
|
+
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
49
|
+
VALUE typemap = p_conn->type_map_for_results;
|
50
|
+
|
51
|
+
/* Type check is done when assigned to PG::Connection. */
|
52
|
+
t_typemap *p_typemap = DATA_PTR(typemap);
|
53
|
+
|
54
|
+
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
55
|
+
this->p_typemap = DATA_PTR( this->typemap );
|
56
|
+
}
|
57
|
+
|
58
|
+
return self;
|
59
|
+
}
|
60
|
+
|
61
|
+
VALUE
|
62
|
+
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
63
|
+
{
|
64
|
+
VALUE self = pg_new_result(result, rb_pgconn);
|
65
|
+
t_pg_result *this = pgresult_get_this(self);
|
66
|
+
this->autoclear = 1;
|
67
|
+
return self;
|
36
68
|
}
|
37
69
|
|
38
70
|
/*
|
@@ -44,24 +76,18 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
44
76
|
VALUE
|
45
77
|
pg_result_check( VALUE self )
|
46
78
|
{
|
79
|
+
t_pg_result *this = pgresult_get_this(self);
|
47
80
|
VALUE error, exception, klass;
|
48
|
-
VALUE rb_pgconn = rb_iv_get( self, "@connection" );
|
49
|
-
PGconn *conn = pg_get_pgconn(rb_pgconn);
|
50
|
-
PGresult *result;
|
51
|
-
#ifdef M17N_SUPPORTED
|
52
|
-
rb_encoding *enc = pg_conn_enc_get( conn );
|
53
|
-
#endif
|
54
81
|
char * sqlstate;
|
55
82
|
|
56
|
-
|
57
|
-
|
58
|
-
if(result == NULL)
|
83
|
+
if(this->pgresult == NULL)
|
59
84
|
{
|
85
|
+
PGconn *conn = pg_get_pgconn(this->connection);
|
60
86
|
error = rb_str_new2( PQerrorMessage(conn) );
|
61
87
|
}
|
62
88
|
else
|
63
89
|
{
|
64
|
-
switch (PQresultStatus(
|
90
|
+
switch (PQresultStatus(this->pgresult))
|
65
91
|
{
|
66
92
|
case PGRES_TUPLES_OK:
|
67
93
|
case PGRES_COPY_OUT:
|
@@ -78,22 +104,20 @@ pg_result_check( VALUE self )
|
|
78
104
|
case PGRES_BAD_RESPONSE:
|
79
105
|
case PGRES_FATAL_ERROR:
|
80
106
|
case PGRES_NONFATAL_ERROR:
|
81
|
-
error = rb_str_new2( PQresultErrorMessage(
|
107
|
+
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
82
108
|
break;
|
83
109
|
default:
|
84
110
|
error = rb_str_new2( "internal error : unknown result status." );
|
85
111
|
}
|
86
112
|
}
|
87
113
|
|
88
|
-
|
89
|
-
rb_enc_set_index( error, rb_enc_to_index(enc) );
|
90
|
-
#endif
|
114
|
+
PG_ENCODING_SET_NOCHECK( error, ENCODING_GET(self) );
|
91
115
|
|
92
|
-
sqlstate = PQresultErrorField(
|
116
|
+
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
93
117
|
klass = lookup_error_class( sqlstate );
|
94
118
|
exception = rb_exc_new3( klass, error );
|
95
|
-
rb_iv_set( exception, "@connection",
|
96
|
-
rb_iv_set( exception, "@result",
|
119
|
+
rb_iv_set( exception, "@connection", this->connection );
|
120
|
+
rb_iv_set( exception, "@result", this->pgresult ? self : Qnil );
|
97
121
|
rb_exc_raise( exception );
|
98
122
|
|
99
123
|
/* Not reached */
|
@@ -112,43 +136,146 @@ pg_result_check( VALUE self )
|
|
112
136
|
* res.clear() -> nil
|
113
137
|
*
|
114
138
|
* Clears the PG::Result object as the result of the query.
|
139
|
+
*
|
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.
|
142
|
+
*
|
115
143
|
*/
|
116
144
|
VALUE
|
117
145
|
pg_result_clear(VALUE self)
|
118
146
|
{
|
119
|
-
|
120
|
-
|
147
|
+
t_pg_result *this = pgresult_get_this(self);
|
148
|
+
if( !this->autoclear )
|
149
|
+
PQclear(pgresult_get(self));
|
150
|
+
this->pgresult = NULL;
|
121
151
|
return Qnil;
|
122
152
|
}
|
123
153
|
|
154
|
+
/*
|
155
|
+
* call-seq:
|
156
|
+
* res.cleared? -> boolean
|
157
|
+
*
|
158
|
+
* Returns +true+ if the backend result memory has been free'd.
|
159
|
+
*/
|
160
|
+
VALUE
|
161
|
+
pgresult_cleared_p( VALUE self )
|
162
|
+
{
|
163
|
+
t_pg_result *this = pgresult_get_this(self);
|
164
|
+
return this->pgresult ? Qfalse : Qtrue;
|
165
|
+
}
|
124
166
|
|
167
|
+
/*
|
168
|
+
* call-seq:
|
169
|
+
* res.autoclear? -> boolean
|
170
|
+
*
|
171
|
+
* Returns +true+ if the underlying C struct will be cleared automatically by libpq.
|
172
|
+
* Elsewise the result is cleared by PG::Result#clear or by the GC when it's no longer in use.
|
173
|
+
*
|
174
|
+
*/
|
175
|
+
VALUE
|
176
|
+
pgresult_autoclear_p( VALUE self )
|
177
|
+
{
|
178
|
+
t_pg_result *this = pgresult_get_this(self);
|
179
|
+
return this->autoclear ? Qtrue : Qfalse;
|
180
|
+
}
|
125
181
|
|
126
182
|
/*
|
127
183
|
* DATA pointer functions
|
128
184
|
*/
|
129
185
|
|
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
|
+
|
130
204
|
/*
|
131
205
|
* GC Free function
|
132
206
|
*/
|
133
207
|
static void
|
134
|
-
pgresult_gc_free(
|
208
|
+
pgresult_gc_free( t_pg_result *this )
|
135
209
|
{
|
136
|
-
if(
|
137
|
-
|
210
|
+
if( !this ) return;
|
211
|
+
if(this->pgresult != NULL && !this->autoclear)
|
212
|
+
PQclear(this->pgresult);
|
213
|
+
|
214
|
+
xfree(this);
|
138
215
|
}
|
139
216
|
|
140
217
|
/*
|
141
|
-
* Fetch the data pointer
|
218
|
+
* Fetch the PG::Result object data pointer and check it's
|
219
|
+
* PGresult data pointer for sanity.
|
142
220
|
*/
|
143
|
-
static
|
221
|
+
static t_pg_result *
|
222
|
+
pgresult_get_this_safe( VALUE self )
|
223
|
+
{
|
224
|
+
t_pg_result *this = pgresult_get_this(self);
|
225
|
+
|
226
|
+
if (this->pgresult == NULL) rb_raise(rb_ePGerror, "result has been cleared");
|
227
|
+
return this;
|
228
|
+
}
|
229
|
+
|
230
|
+
/*
|
231
|
+
* Fetch the PGresult pointer for the result object and check validity
|
232
|
+
*
|
233
|
+
* Note: This function is used externally by the sequel_pg gem,
|
234
|
+
* so do changes carefully.
|
235
|
+
*
|
236
|
+
*/
|
237
|
+
PGresult*
|
144
238
|
pgresult_get(VALUE self)
|
145
239
|
{
|
146
|
-
|
147
|
-
|
148
|
-
if (
|
149
|
-
return
|
240
|
+
t_pg_result *this = pgresult_get_this(self);
|
241
|
+
|
242
|
+
if (this->pgresult == NULL) rb_raise(rb_ePGerror, "result has been cleared");
|
243
|
+
return this->pgresult;
|
150
244
|
}
|
151
245
|
|
246
|
+
/*
|
247
|
+
* Document-method: allocate
|
248
|
+
*
|
249
|
+
* call-seq:
|
250
|
+
* PG::Result.allocate -> result
|
251
|
+
*/
|
252
|
+
static VALUE
|
253
|
+
pgresult_s_allocate( VALUE klass )
|
254
|
+
{
|
255
|
+
VALUE self = Data_Wrap_Struct( klass, pgresult_gc_mark, pgresult_gc_free, NULL );
|
256
|
+
|
257
|
+
return self;
|
258
|
+
}
|
259
|
+
|
260
|
+
static void pgresult_init_fnames(VALUE self)
|
261
|
+
{
|
262
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
263
|
+
|
264
|
+
if( this->nfields == -1 ){
|
265
|
+
int i;
|
266
|
+
int nfields = PQnfields(this->pgresult);
|
267
|
+
|
268
|
+
for( i=0; i<nfields; i++ ){
|
269
|
+
VALUE fname = rb_tainted_str_new2(PQfname(this->pgresult, i));
|
270
|
+
PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
|
271
|
+
this->fnames[i] = rb_obj_freeze(fname);
|
272
|
+
this->nfields = i + 1;
|
273
|
+
|
274
|
+
RB_GC_GUARD(fname);
|
275
|
+
}
|
276
|
+
this->nfields = nfields;
|
277
|
+
}
|
278
|
+
}
|
152
279
|
|
153
280
|
/********************************************************************
|
154
281
|
*
|
@@ -205,7 +332,7 @@ static VALUE
|
|
205
332
|
pgresult_res_status(VALUE self, VALUE status)
|
206
333
|
{
|
207
334
|
VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
|
208
|
-
|
335
|
+
PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
|
209
336
|
return ret;
|
210
337
|
}
|
211
338
|
|
@@ -219,7 +346,7 @@ static VALUE
|
|
219
346
|
pgresult_error_message(VALUE self)
|
220
347
|
{
|
221
348
|
VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
|
222
|
-
|
349
|
+
PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
|
223
350
|
return ret;
|
224
351
|
}
|
225
352
|
|
@@ -279,7 +406,7 @@ pgresult_error_field(VALUE self, VALUE field)
|
|
279
406
|
|
280
407
|
if ( fieldstr ) {
|
281
408
|
ret = rb_tainted_str_new2( fieldstr );
|
282
|
-
|
409
|
+
PG_ENCODING_SET_NOCHECK( ret, ENCODING_GET(self ));
|
283
410
|
}
|
284
411
|
|
285
412
|
return ret;
|
@@ -297,9 +424,15 @@ pgresult_ntuples(VALUE self)
|
|
297
424
|
return INT2FIX(PQntuples(pgresult_get(self)));
|
298
425
|
}
|
299
426
|
|
427
|
+
static VALUE
|
428
|
+
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
429
|
+
{
|
430
|
+
return pgresult_ntuples(self);
|
431
|
+
}
|
432
|
+
|
300
433
|
/*
|
301
434
|
* call-seq:
|
302
|
-
* res.nfields() ->
|
435
|
+
* res.nfields() -> Integer
|
303
436
|
*
|
304
437
|
* Returns the number of columns in the query result.
|
305
438
|
*/
|
@@ -319,16 +452,16 @@ static VALUE
|
|
319
452
|
pgresult_fname(VALUE self, VALUE index)
|
320
453
|
{
|
321
454
|
VALUE fname;
|
322
|
-
PGresult *result;
|
455
|
+
PGresult *result = pgresult_get(self);
|
323
456
|
int i = NUM2INT(index);
|
324
457
|
|
325
|
-
result = pgresult_get(self);
|
326
458
|
if (i < 0 || i >= PQnfields(result)) {
|
327
459
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
328
460
|
}
|
461
|
+
|
329
462
|
fname = rb_tainted_str_new2(PQfname(result, i));
|
330
|
-
|
331
|
-
return fname;
|
463
|
+
PG_ENCODING_SET_NOCHECK(fname, ENCODING_GET(self));
|
464
|
+
return rb_obj_freeze(fname);
|
332
465
|
}
|
333
466
|
|
334
467
|
/*
|
@@ -361,16 +494,16 @@ pgresult_fnumber(VALUE self, VALUE name)
|
|
361
494
|
|
362
495
|
Check_Type(name, T_STRING);
|
363
496
|
|
364
|
-
n = PQfnumber(pgresult_get(self),
|
497
|
+
n = PQfnumber(pgresult_get(self), StringValueCStr(name));
|
365
498
|
if (n == -1) {
|
366
|
-
rb_raise(rb_eArgError,"Unknown field: %s",
|
499
|
+
rb_raise(rb_eArgError,"Unknown field: %s", StringValueCStr(name));
|
367
500
|
}
|
368
501
|
return INT2FIX(n);
|
369
502
|
}
|
370
503
|
|
371
504
|
/*
|
372
505
|
* call-seq:
|
373
|
-
* res.ftable( column_number ) ->
|
506
|
+
* res.ftable( column_number ) -> Integer
|
374
507
|
*
|
375
508
|
* Returns the Oid of the table from which the column _column_number_
|
376
509
|
* was fetched.
|
@@ -389,7 +522,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
|
|
389
522
|
rb_raise(rb_eArgError,"Invalid column index: %d", col_number);
|
390
523
|
|
391
524
|
n = PQftable(pgresult, col_number);
|
392
|
-
return
|
525
|
+
return UINT2NUM(n);
|
393
526
|
}
|
394
527
|
|
395
528
|
/*
|
@@ -440,7 +573,7 @@ pgresult_fformat(VALUE self, VALUE column_number)
|
|
440
573
|
|
441
574
|
/*
|
442
575
|
* call-seq:
|
443
|
-
* res.ftype( column_number )
|
576
|
+
* res.ftype( column_number ) -> Integer
|
444
577
|
*
|
445
578
|
* Returns the data type associated with _column_number_.
|
446
579
|
*
|
@@ -464,7 +597,7 @@ pgresult_ftype(VALUE self, VALUE index)
|
|
464
597
|
if (i < 0 || i >= PQnfields(result)) {
|
465
598
|
rb_raise(rb_eArgError, "invalid field number %d", i);
|
466
599
|
}
|
467
|
-
return
|
600
|
+
return UINT2NUM(PQftype(result, i));
|
468
601
|
}
|
469
602
|
|
470
603
|
/*
|
@@ -515,30 +648,6 @@ pgresult_fsize(VALUE self, VALUE index)
|
|
515
648
|
}
|
516
649
|
|
517
650
|
|
518
|
-
static VALUE
|
519
|
-
pgresult_value(VALUE self, PGresult *result, int tuple_num, int field_num)
|
520
|
-
{
|
521
|
-
VALUE val;
|
522
|
-
if ( PQgetisnull(result, tuple_num, field_num) ) {
|
523
|
-
return Qnil;
|
524
|
-
}
|
525
|
-
else {
|
526
|
-
val = rb_tainted_str_new( PQgetvalue(result, tuple_num, field_num ),
|
527
|
-
PQgetlength(result, tuple_num, field_num) );
|
528
|
-
|
529
|
-
#ifdef M17N_SUPPORTED
|
530
|
-
/* associate client encoding for text format only */
|
531
|
-
if ( 0 == PQfformat(result, field_num) ) {
|
532
|
-
ASSOCIATE_INDEX( val, self );
|
533
|
-
} else {
|
534
|
-
rb_enc_associate( val, rb_ascii8bit_encoding() );
|
535
|
-
}
|
536
|
-
#endif
|
537
|
-
|
538
|
-
return val;
|
539
|
-
}
|
540
|
-
}
|
541
|
-
|
542
651
|
/*
|
543
652
|
* call-seq:
|
544
653
|
* res.getvalue( tup_num, field_num )
|
@@ -549,18 +658,17 @@ pgresult_value(VALUE self, PGresult *result, int tuple_num, int field_num)
|
|
549
658
|
static VALUE
|
550
659
|
pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
|
551
660
|
{
|
552
|
-
|
661
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
553
662
|
int i = NUM2INT(tup_num);
|
554
663
|
int j = NUM2INT(field_num);
|
555
664
|
|
556
|
-
|
557
|
-
if(i < 0 || i >= PQntuples(result)) {
|
665
|
+
if(i < 0 || i >= PQntuples(this->pgresult)) {
|
558
666
|
rb_raise(rb_eArgError,"invalid tuple number %d", i);
|
559
667
|
}
|
560
|
-
if(j < 0 || j >= PQnfields(
|
668
|
+
if(j < 0 || j >= PQnfields(this->pgresult)) {
|
561
669
|
rb_raise(rb_eArgError,"invalid field number %d", j);
|
562
670
|
}
|
563
|
-
return
|
671
|
+
return this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, j);
|
564
672
|
}
|
565
673
|
|
566
674
|
/*
|
@@ -640,7 +748,7 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
640
748
|
PGresult *result;
|
641
749
|
|
642
750
|
result = pgresult_get(self);
|
643
|
-
return
|
751
|
+
return UINT2NUM(PQparamtype(result,NUM2INT(param_number)));
|
644
752
|
}
|
645
753
|
|
646
754
|
/*
|
@@ -653,13 +761,13 @@ static VALUE
|
|
653
761
|
pgresult_cmd_status(VALUE self)
|
654
762
|
{
|
655
763
|
VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
|
656
|
-
|
764
|
+
PG_ENCODING_SET_NOCHECK(ret, ENCODING_GET(self));
|
657
765
|
return ret;
|
658
766
|
}
|
659
767
|
|
660
768
|
/*
|
661
769
|
* call-seq:
|
662
|
-
* res.cmd_tuples() ->
|
770
|
+
* res.cmd_tuples() -> Integer
|
663
771
|
*
|
664
772
|
* Returns the number of tuples (rows) affected by the SQL command.
|
665
773
|
*
|
@@ -681,7 +789,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
681
789
|
|
682
790
|
/*
|
683
791
|
* call-seq:
|
684
|
-
* res.oid_value() ->
|
792
|
+
* res.oid_value() -> Integer
|
685
793
|
*
|
686
794
|
* Returns the +oid+ of the inserted row if applicable,
|
687
795
|
* otherwise +nil+.
|
@@ -693,7 +801,7 @@ pgresult_oid_value(VALUE self)
|
|
693
801
|
if (n == InvalidOid)
|
694
802
|
return Qnil;
|
695
803
|
else
|
696
|
-
return
|
804
|
+
return UINT2NUM(n);
|
697
805
|
}
|
698
806
|
|
699
807
|
/* Utility methods not in libpq */
|
@@ -707,21 +815,29 @@ pgresult_oid_value(VALUE self)
|
|
707
815
|
static VALUE
|
708
816
|
pgresult_aref(VALUE self, VALUE index)
|
709
817
|
{
|
710
|
-
|
818
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
711
819
|
int tuple_num = NUM2INT(index);
|
712
820
|
int field_num;
|
713
|
-
|
821
|
+
int num_tuples = PQntuples(this->pgresult);
|
714
822
|
VALUE tuple;
|
715
823
|
|
716
|
-
if
|
824
|
+
if( this->nfields == -1 )
|
825
|
+
pgresult_init_fnames( self );
|
826
|
+
|
827
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
717
828
|
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
718
829
|
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
830
|
+
/* We reuse the Hash of the previous output for larger row counts.
|
831
|
+
* This is somewhat faster than populating an empty Hash object. */
|
832
|
+
tuple = NIL_P(this->tuple_hash) ? rb_hash_new() : this->tuple_hash;
|
833
|
+
for ( field_num = 0; field_num < this->nfields; field_num++ ) {
|
834
|
+
VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field_num);
|
835
|
+
rb_hash_aset( tuple, this->fnames[field_num], val );
|
724
836
|
}
|
837
|
+
/* Store a copy of the filled hash for use at the next row. */
|
838
|
+
if( num_tuples > 10 )
|
839
|
+
this->tuple_hash = rb_hash_dup(tuple);
|
840
|
+
|
725
841
|
return tuple;
|
726
842
|
}
|
727
843
|
|
@@ -734,25 +850,59 @@ pgresult_aref(VALUE self, VALUE index)
|
|
734
850
|
static VALUE
|
735
851
|
pgresult_each_row(VALUE self)
|
736
852
|
{
|
737
|
-
|
853
|
+
t_pg_result *this;
|
738
854
|
int row;
|
739
855
|
int field;
|
740
|
-
int num_rows
|
741
|
-
int num_fields
|
856
|
+
int num_rows;
|
857
|
+
int num_fields;
|
858
|
+
|
859
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
|
860
|
+
|
861
|
+
this = pgresult_get_this_safe(self);
|
862
|
+
num_rows = PQntuples(this->pgresult);
|
863
|
+
num_fields = PQnfields(this->pgresult);
|
742
864
|
|
743
865
|
for ( row = 0; row < num_rows; row++ ) {
|
744
|
-
VALUE
|
866
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
745
867
|
|
746
868
|
/* populate the row */
|
747
869
|
for ( field = 0; field < num_fields; field++ ) {
|
748
|
-
|
870
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
749
871
|
}
|
750
|
-
rb_yield(
|
872
|
+
rb_yield( rb_ary_new4( num_fields, row_values ));
|
751
873
|
}
|
752
874
|
|
753
875
|
return Qnil;
|
754
876
|
}
|
755
877
|
|
878
|
+
/*
|
879
|
+
* call-seq:
|
880
|
+
* res.values -> Array
|
881
|
+
*
|
882
|
+
* Returns all tuples as an array of arrays.
|
883
|
+
*/
|
884
|
+
static VALUE
|
885
|
+
pgresult_values(VALUE self)
|
886
|
+
{
|
887
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
888
|
+
int row;
|
889
|
+
int field;
|
890
|
+
int num_rows = PQntuples(this->pgresult);
|
891
|
+
int num_fields = PQnfields(this->pgresult);
|
892
|
+
VALUE results = rb_ary_new2( num_rows );
|
893
|
+
|
894
|
+
for ( row = 0; row < num_rows; row++ ) {
|
895
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
896
|
+
|
897
|
+
/* populate the row */
|
898
|
+
for ( field = 0; field < num_fields; field++ ) {
|
899
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
900
|
+
}
|
901
|
+
rb_ary_store( results, row, rb_ary_new4( num_fields, row_values ) );
|
902
|
+
}
|
903
|
+
|
904
|
+
return results;
|
905
|
+
}
|
756
906
|
|
757
907
|
/*
|
758
908
|
* Make a Ruby array out of the encoded values from the specified
|
@@ -761,28 +911,16 @@ pgresult_each_row(VALUE self)
|
|
761
911
|
static VALUE
|
762
912
|
make_column_result_array( VALUE self, int col )
|
763
913
|
{
|
764
|
-
|
765
|
-
int rows = PQntuples(
|
914
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
915
|
+
int rows = PQntuples( this->pgresult );
|
766
916
|
int i;
|
767
|
-
VALUE val = Qnil;
|
768
917
|
VALUE results = rb_ary_new2( rows );
|
769
918
|
|
770
|
-
if ( col >= PQnfields(
|
919
|
+
if ( col >= PQnfields(this->pgresult) )
|
771
920
|
rb_raise( rb_eIndexError, "no column %d in result", col );
|
772
921
|
|
773
922
|
for ( i=0; i < rows; i++ ) {
|
774
|
-
val =
|
775
|
-
PQgetlength(result, i, col) );
|
776
|
-
|
777
|
-
#ifdef M17N_SUPPORTED
|
778
|
-
/* associate client encoding for text format only */
|
779
|
-
if ( 0 == PQfformat(result, col) ) {
|
780
|
-
ASSOCIATE_INDEX( val, self );
|
781
|
-
} else {
|
782
|
-
rb_enc_associate( val, rb_ascii8bit_encoding() );
|
783
|
-
}
|
784
|
-
#endif
|
785
|
-
|
923
|
+
VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, i, col);
|
786
924
|
rb_ary_store( results, i, val );
|
787
925
|
}
|
788
926
|
|
@@ -817,7 +955,7 @@ static VALUE
|
|
817
955
|
pgresult_field_values( VALUE self, VALUE field )
|
818
956
|
{
|
819
957
|
PGresult *result = pgresult_get( self );
|
820
|
-
const char *fieldname =
|
958
|
+
const char *fieldname = StringValueCStr( field );
|
821
959
|
int fnum = PQfnumber( result, fieldname );
|
822
960
|
|
823
961
|
if ( fnum < 0 )
|
@@ -836,9 +974,13 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
836
974
|
static VALUE
|
837
975
|
pgresult_each(VALUE self)
|
838
976
|
{
|
839
|
-
PGresult *result
|
977
|
+
PGresult *result;
|
840
978
|
int tuple_num;
|
841
979
|
|
980
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
|
981
|
+
|
982
|
+
result = pgresult_get(self);
|
983
|
+
|
842
984
|
for(tuple_num = 0; tuple_num < PQntuples(result); tuple_num++) {
|
843
985
|
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
844
986
|
}
|
@@ -854,25 +996,222 @@ pgresult_each(VALUE self)
|
|
854
996
|
static VALUE
|
855
997
|
pgresult_fields(VALUE self)
|
856
998
|
{
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
999
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1000
|
+
|
1001
|
+
if( this->nfields == -1 )
|
1002
|
+
pgresult_init_fnames( self );
|
861
1003
|
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
1004
|
+
return rb_ary_new4( this->nfields, this->fnames );
|
1005
|
+
}
|
1006
|
+
|
1007
|
+
/*
|
1008
|
+
* call-seq:
|
1009
|
+
* res.type_map = typemap
|
1010
|
+
*
|
1011
|
+
* Set the TypeMap that is used for type casts of result values to ruby objects.
|
1012
|
+
*
|
1013
|
+
* All value retrieval methods will respect the type map and will do the
|
1014
|
+
* type casts from PostgreSQL's wire format to Ruby objects on the fly,
|
1015
|
+
* according to the rules and decoders defined in the given typemap.
|
1016
|
+
*
|
1017
|
+
* +typemap+ must be a kind of PG::TypeMap .
|
1018
|
+
*
|
1019
|
+
*/
|
1020
|
+
static VALUE
|
1021
|
+
pgresult_type_map_set(VALUE self, VALUE typemap)
|
1022
|
+
{
|
1023
|
+
t_pg_result *this = pgresult_get_this(self);
|
1024
|
+
t_typemap *p_typemap;
|
1025
|
+
|
1026
|
+
if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
|
1027
|
+
rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
|
1028
|
+
rb_obj_classname( typemap ) );
|
866
1029
|
}
|
1030
|
+
Data_Get_Struct(typemap, t_typemap, p_typemap);
|
1031
|
+
|
1032
|
+
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
1033
|
+
this->p_typemap = DATA_PTR( this->typemap );
|
867
1034
|
|
868
|
-
return
|
1035
|
+
return typemap;
|
869
1036
|
}
|
870
1037
|
|
1038
|
+
/*
|
1039
|
+
* call-seq:
|
1040
|
+
* res.type_map -> value
|
1041
|
+
*
|
1042
|
+
* Returns the TypeMap that is currently set for type casts of result values to ruby objects.
|
1043
|
+
*
|
1044
|
+
*/
|
1045
|
+
static VALUE
|
1046
|
+
pgresult_type_map_get(VALUE self)
|
1047
|
+
{
|
1048
|
+
t_pg_result *this = pgresult_get_this(self);
|
1049
|
+
|
1050
|
+
return this->typemap;
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
#ifdef HAVE_PQSETSINGLEROWMODE
|
1054
|
+
/*
|
1055
|
+
* call-seq:
|
1056
|
+
* res.stream_each{ |tuple| ... }
|
1057
|
+
*
|
1058
|
+
* Invokes block for each tuple in the result set in single row mode.
|
1059
|
+
*
|
1060
|
+
* This is a convenience method for retrieving all result tuples
|
1061
|
+
* as they are transferred. It is an alternative to repeated calls of
|
1062
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
1063
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
1064
|
+
* the same speed as with ordinary results.
|
1065
|
+
*
|
1066
|
+
* The result must be in status PGRES_SINGLE_TUPLE.
|
1067
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
1068
|
+
* A PG::Error is raised for any errors from the server.
|
1069
|
+
*
|
1070
|
+
* Row description data does not change while the iteration. All value retrieval
|
1071
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
1072
|
+
* the iteration and +0+ after all tuples were yielded.
|
1073
|
+
*
|
1074
|
+
* Example:
|
1075
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
1076
|
+
* conn.set_single_row_mode
|
1077
|
+
* conn.get_result.stream_each do |row|
|
1078
|
+
* # do something with the received row of the first query
|
1079
|
+
* end
|
1080
|
+
* conn.get_result.stream_each do |row|
|
1081
|
+
* # do something with the received row of the second query
|
1082
|
+
* end
|
1083
|
+
* conn.get_result # => nil (no more results)
|
1084
|
+
*/
|
1085
|
+
static VALUE
|
1086
|
+
pgresult_stream_each(VALUE self)
|
1087
|
+
{
|
1088
|
+
t_pg_result *this;
|
1089
|
+
int nfields;
|
1090
|
+
PGconn *pgconn;
|
1091
|
+
PGresult *pgresult;
|
1092
|
+
|
1093
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
1094
|
+
|
1095
|
+
this = pgresult_get_this_safe(self);
|
1096
|
+
pgconn = pg_get_pgconn(this->connection);
|
1097
|
+
pgresult = this->pgresult;
|
1098
|
+
nfields = PQnfields(pgresult);
|
1099
|
+
|
1100
|
+
for(;;){
|
1101
|
+
int tuple_num;
|
1102
|
+
int ntuples = PQntuples(pgresult);
|
1103
|
+
|
1104
|
+
switch( PQresultStatus(pgresult) ){
|
1105
|
+
case PGRES_TUPLES_OK:
|
1106
|
+
if( ntuples == 0 )
|
1107
|
+
return self;
|
1108
|
+
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
1109
|
+
case PGRES_SINGLE_TUPLE:
|
1110
|
+
break;
|
1111
|
+
default:
|
1112
|
+
pg_result_check( self );
|
1113
|
+
}
|
1114
|
+
|
1115
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1116
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
if( !this->autoclear ){
|
1120
|
+
PQclear( pgresult );
|
1121
|
+
this->pgresult = NULL;
|
1122
|
+
}
|
1123
|
+
|
1124
|
+
pgresult = gvl_PQgetResult(pgconn);
|
1125
|
+
if( pgresult == NULL )
|
1126
|
+
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
|
1127
|
+
|
1128
|
+
if( nfields != PQnfields(pgresult) )
|
1129
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
|
1130
|
+
|
1131
|
+
this->pgresult = pgresult;
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
/* never reached */
|
1135
|
+
return self;
|
1136
|
+
}
|
1137
|
+
|
1138
|
+
/*
|
1139
|
+
* call-seq:
|
1140
|
+
* res.stream_each_row { |row| ... }
|
1141
|
+
*
|
1142
|
+
* Yields each row of the result set in single row mode.
|
1143
|
+
* The row is a list of column values.
|
1144
|
+
*
|
1145
|
+
* This method works equally to #stream_each , but yields an Array of
|
1146
|
+
* values.
|
1147
|
+
*/
|
1148
|
+
static VALUE
|
1149
|
+
pgresult_stream_each_row(VALUE self)
|
1150
|
+
{
|
1151
|
+
t_pg_result *this;
|
1152
|
+
int row;
|
1153
|
+
int nfields;
|
1154
|
+
PGconn *pgconn;
|
1155
|
+
PGresult *pgresult;
|
1156
|
+
|
1157
|
+
RETURN_ENUMERATOR(self, 0, NULL);
|
1158
|
+
|
1159
|
+
this = pgresult_get_this_safe(self);
|
1160
|
+
pgconn = pg_get_pgconn(this->connection);
|
1161
|
+
pgresult = this->pgresult;
|
1162
|
+
nfields = PQnfields(pgresult);
|
1163
|
+
|
1164
|
+
for(;;){
|
1165
|
+
int ntuples = PQntuples(pgresult);
|
1166
|
+
|
1167
|
+
switch( PQresultStatus(pgresult) ){
|
1168
|
+
case PGRES_TUPLES_OK:
|
1169
|
+
if( ntuples == 0 )
|
1170
|
+
return self;
|
1171
|
+
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
1172
|
+
case PGRES_SINGLE_TUPLE:
|
1173
|
+
break;
|
1174
|
+
default:
|
1175
|
+
pg_result_check( self );
|
1176
|
+
}
|
1177
|
+
|
1178
|
+
for ( row = 0; row < ntuples; row++ ) {
|
1179
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
1180
|
+
int field;
|
1181
|
+
|
1182
|
+
/* populate the row */
|
1183
|
+
for ( field = 0; field < nfields; field++ ) {
|
1184
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1185
|
+
}
|
1186
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
1187
|
+
}
|
1188
|
+
|
1189
|
+
if( !this->autoclear ){
|
1190
|
+
PQclear( pgresult );
|
1191
|
+
this->pgresult = NULL;
|
1192
|
+
}
|
1193
|
+
|
1194
|
+
pgresult = gvl_PQgetResult(pgconn);
|
1195
|
+
if( pgresult == NULL )
|
1196
|
+
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
|
1197
|
+
|
1198
|
+
if( nfields != PQnfields(pgresult) )
|
1199
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
|
1200
|
+
|
1201
|
+
this->pgresult = pgresult;
|
1202
|
+
}
|
1203
|
+
|
1204
|
+
/* never reached */
|
1205
|
+
return self;
|
1206
|
+
}
|
1207
|
+
#endif
|
1208
|
+
|
871
1209
|
|
872
1210
|
void
|
873
1211
|
init_pg_result()
|
874
1212
|
{
|
875
1213
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
1214
|
+
rb_define_alloc_func( rb_cPGresult, pgresult_s_allocate );
|
876
1215
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
877
1216
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
878
1217
|
|
@@ -913,8 +1252,20 @@ init_pg_result()
|
|
913
1252
|
rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
|
914
1253
|
rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
|
915
1254
|
rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
|
1255
|
+
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
916
1256
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
917
1257
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
1258
|
+
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
1259
|
+
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
1260
|
+
|
1261
|
+
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
1262
|
+
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
1263
|
+
|
1264
|
+
#ifdef HAVE_PQSETSINGLEROWMODE
|
1265
|
+
/****** PG::Result INSTANCE METHODS: streaming ******/
|
1266
|
+
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1267
|
+
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1268
|
+
#endif
|
918
1269
|
}
|
919
1270
|
|
920
1271
|
|