pg 0.17.1 → 0.18.4

Sign up to get free protection for your applications and to get access to all the features.
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