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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +2407 -2
  4. data/History.rdoc +68 -0
  5. data/Manifest.txt +29 -1
  6. data/README-Windows.rdoc +15 -26
  7. data/README.rdoc +52 -2
  8. data/Rakefile +56 -18
  9. data/Rakefile.cross +77 -49
  10. data/ext/extconf.rb +33 -26
  11. data/ext/pg.c +142 -21
  12. data/ext/pg.h +242 -6
  13. data/ext/pg_binary_decoder.c +162 -0
  14. data/ext/pg_binary_encoder.c +162 -0
  15. data/ext/pg_coder.c +479 -0
  16. data/ext/pg_connection.c +858 -553
  17. data/ext/pg_copy_coder.c +561 -0
  18. data/ext/pg_errors.c +6 -0
  19. data/ext/pg_result.c +479 -128
  20. data/ext/pg_text_decoder.c +421 -0
  21. data/ext/pg_text_encoder.c +663 -0
  22. data/ext/pg_type_map.c +159 -0
  23. data/ext/pg_type_map_all_strings.c +116 -0
  24. data/ext/pg_type_map_by_class.c +239 -0
  25. data/ext/pg_type_map_by_column.c +312 -0
  26. data/ext/pg_type_map_by_mri_type.c +284 -0
  27. data/ext/pg_type_map_by_oid.c +355 -0
  28. data/ext/pg_type_map_in_ruby.c +299 -0
  29. data/ext/util.c +149 -0
  30. data/ext/util.h +65 -0
  31. data/lib/pg/basic_type_mapping.rb +399 -0
  32. data/lib/pg/coder.rb +83 -0
  33. data/lib/pg/connection.rb +81 -29
  34. data/lib/pg/result.rb +13 -3
  35. data/lib/pg/text_decoder.rb +44 -0
  36. data/lib/pg/text_encoder.rb +27 -0
  37. data/lib/pg/type_map_by_column.rb +15 -0
  38. data/lib/pg.rb +12 -2
  39. data/spec/{lib/helpers.rb → helpers.rb} +101 -39
  40. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  41. data/spec/pg/connection_spec.rb +516 -218
  42. data/spec/pg/result_spec.rb +216 -112
  43. data/spec/pg/type_map_by_class_spec.rb +138 -0
  44. data/spec/pg/type_map_by_column_spec.rb +222 -0
  45. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  46. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  47. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  48. data/spec/pg/type_map_spec.rb +22 -0
  49. data/spec/pg/type_spec.rb +697 -0
  50. data/spec/pg_spec.rb +24 -18
  51. data.tar.gz.sig +0 -0
  52. metadata +111 -45
  53. 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 8255d4f73334 2013/08/18 20:40:08 lars $
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( PGresult * );
13
- static PGresult* pgresult_get( VALUE );
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
- PGconn *conn = pg_get_pgconn( rb_pgconn );
27
- VALUE val = Data_Wrap_Struct(rb_cPGresult, NULL, pgresult_gc_free, result);
28
- #ifdef M17N_SUPPORTED
29
- rb_encoding *enc = pg_conn_enc_get( conn );
30
- ENCODING_SET( val, rb_enc_to_index(enc) );
31
- #endif
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
- rb_iv_set( val, "@connection", rb_pgconn );
45
+ PG_ENCODING_SET_NOCHECK(self, ENCODING_GET(rb_pgconn));
34
46
 
35
- return val;
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
- Data_Get_Struct(self, PGresult, result);
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(result))
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(result) );
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
- #ifdef M17N_SUPPORTED
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( result, PG_DIAG_SQLSTATE );
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", rb_pgconn );
96
- rb_iv_set( exception, "@result", result ? self : Qnil );
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
- PQclear(pgresult_get(self));
120
- DATA_PTR(self) = NULL;
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( PGresult *result )
208
+ pgresult_gc_free( t_pg_result *this )
135
209
  {
136
- if(result != NULL)
137
- PQclear(result);
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 for the result object
218
+ * Fetch the PG::Result object data pointer and check it's
219
+ * PGresult data pointer for sanity.
142
220
  */
143
- static PGresult*
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
- PGresult *result;
147
- Data_Get_Struct(self, PGresult, result);
148
- if (result == NULL) rb_raise(rb_ePGerror, "result has been cleared");
149
- return result;
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
- ASSOCIATE_INDEX(ret, self);
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
- ASSOCIATE_INDEX(ret, self);
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
- ASSOCIATE_INDEX( ret, self );
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() -> Fixnum
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
- ASSOCIATE_INDEX(fname, self);
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), StringValuePtr(name));
497
+ n = PQfnumber(pgresult_get(self), StringValueCStr(name));
365
498
  if (n == -1) {
366
- rb_raise(rb_eArgError,"Unknown field: %s", StringValuePtr(name));
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 ) -> Fixnum
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 INT2FIX(n);
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 INT2NUM(PQftype(result, i));
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
- PGresult *result;
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
- result = pgresult_get(self);
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(result)) {
668
+ if(j < 0 || j >= PQnfields(this->pgresult)) {
561
669
  rb_raise(rb_eArgError,"invalid field number %d", j);
562
670
  }
563
- return pgresult_value(self, result, i, j);
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 INT2FIX(PQparamtype(result,NUM2INT(param_number)));
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
- ASSOCIATE_INDEX(ret, self);
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() -> Fixnum
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() -> Fixnum
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 INT2FIX(n);
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
- PGresult *result = pgresult_get(self);
818
+ t_pg_result *this = pgresult_get_this_safe(self);
711
819
  int tuple_num = NUM2INT(index);
712
820
  int field_num;
713
- VALUE fname;
821
+ int num_tuples = PQntuples(this->pgresult);
714
822
  VALUE tuple;
715
823
 
716
- if ( tuple_num < 0 || tuple_num >= PQntuples(result) )
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
- tuple = rb_hash_new();
720
- for ( field_num = 0; field_num < PQnfields(result); field_num++ ) {
721
- fname = rb_tainted_str_new2( PQfname(result,field_num) );
722
- ASSOCIATE_INDEX(fname, self);
723
- rb_hash_aset( tuple, fname, pgresult_value(self, result, tuple_num, field_num) );
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
- PGresult* result = (PGresult*) pgresult_get(self);
853
+ t_pg_result *this;
738
854
  int row;
739
855
  int field;
740
- int num_rows = PQntuples(result);
741
- int num_fields = PQnfields(result);
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 new_row = rb_ary_new2(num_fields);
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
- rb_ary_store( new_row, field, pgresult_value(self, result, row, field) );
870
+ row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
749
871
  }
750
- rb_yield( new_row );
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
- PGresult *result = pgresult_get( self );
765
- int rows = PQntuples( result );
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(result) )
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 = rb_tainted_str_new( PQgetvalue(result, i, col),
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 = StringValuePtr( field );
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 = pgresult_get(self);
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
- PGresult *result = pgresult_get( self );
858
- int n = PQnfields( result );
859
- VALUE fields = rb_ary_new2( n );
860
- int i;
999
+ t_pg_result *this = pgresult_get_this_safe(self);
1000
+
1001
+ if( this->nfields == -1 )
1002
+ pgresult_init_fnames( self );
861
1003
 
862
- for ( i = 0; i < n; i++ ) {
863
- VALUE val = rb_tainted_str_new2(PQfname(result, i));
864
- ASSOCIATE_INDEX(val, self);
865
- rb_ary_store( fields, i, val );
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 fields;
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