pg 0.18.0.pre20141017160319 → 0.18.0.pre20141117110243

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id: pg_type_map_by_column.c,v ec9ef7fb39eb 2014/10/10 18:59:10 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -10,18 +10,41 @@ static VALUE rb_cTypeMapByColumn;
10
10
  static ID s_id_decode;
11
11
  static ID s_id_encode;
12
12
 
13
+ static VALUE pg_tmbc_s_allocate( VALUE klass );
14
+
13
15
  static VALUE
14
16
  pg_tmbc_fit_to_result( VALUE self, VALUE result )
15
17
  {
16
18
  int nfields;
17
19
  t_tmbc *this = DATA_PTR( self );
20
+ t_typemap *default_tm;
21
+ VALUE sub_typemap;
18
22
 
19
23
  nfields = PQnfields( pgresult_get(result) );
20
24
  if ( this->nfields != nfields ) {
21
25
  rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
22
26
  nfields, this->nfields );
23
27
  }
24
- return self;
28
+
29
+ /* Ensure that the default type map fits equaly. */
30
+ default_tm = DATA_PTR( this->typemap.default_typemap );
31
+ sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
32
+
33
+ /* Did the default type return the same object ? */
34
+ if( sub_typemap == this->typemap.default_typemap ){
35
+ return self;
36
+ } else {
37
+ /* Our default type map built a new object, so we need to propagate it
38
+ * and build a copy of this type map and set it as default there.. */
39
+ VALUE new_typemap = pg_tmbc_s_allocate( rb_cTypeMapByColumn );
40
+ size_t struct_size = sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields;
41
+ t_tmbc *p_new_typemap = (t_tmbc *)xmalloc(struct_size);
42
+
43
+ memcpy( p_new_typemap, this, struct_size );
44
+ p_new_typemap->typemap.default_typemap = sub_typemap;
45
+ DATA_PTR(new_typemap) = p_new_typemap;
46
+ return new_typemap;
47
+ }
25
48
  }
26
49
 
27
50
  static VALUE
@@ -29,12 +52,18 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
29
52
  {
30
53
  int nfields;
31
54
  t_tmbc *this = DATA_PTR( self );
55
+ t_typemap *default_tm;
32
56
 
33
57
  nfields = (int)RARRAY_LEN( params );
34
58
  if ( this->nfields != nfields ) {
35
59
  rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
36
60
  nfields, this->nfields );
37
61
  }
62
+
63
+ /* Ensure that the default type map fits equaly. */
64
+ default_tm = DATA_PTR( this->typemap.default_typemap );
65
+ default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
66
+
38
67
  return self;
39
68
  }
40
69
 
@@ -42,47 +71,59 @@ static int
42
71
  pg_tmbc_fit_to_copy_get( VALUE self )
43
72
  {
44
73
  t_tmbc *this = DATA_PTR( self );
74
+
75
+ /* Ensure that the default type map fits equaly. */
76
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
77
+ default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
78
+
45
79
  return this->nfields;
46
80
  }
47
81
 
48
82
 
49
83
  VALUE
50
- pg_tmbc_result_value(VALUE result, int tuple, int field)
84
+ pg_tmbc_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
51
85
  {
52
- char * val;
53
- int len;
54
86
  t_pg_coder *p_coder = NULL;
55
- t_pg_coder_dec_func dec_func;
56
87
  t_pg_result *p_result = pgresult_get_this(result);
57
- t_tmbc *this = (t_tmbc *) p_result->p_typemap;
88
+ t_tmbc *this = (t_tmbc *) p_typemap;
89
+ t_typemap *default_tm;
58
90
 
59
91
  if (PQgetisnull(p_result->pgresult, tuple, field)) {
60
92
  return Qnil;
61
93
  }
62
94
 
63
- val = PQgetvalue( p_result->pgresult, tuple, field );
64
- len = PQgetlength( p_result->pgresult, tuple, field );
95
+ p_coder = this->convs[field].cconv;
65
96
 
66
- if( this ){
67
- p_coder = this->convs[field].cconv;
97
+ if( p_coder ){
98
+ char * val = PQgetvalue( p_result->pgresult, tuple, field );
99
+ int len = PQgetlength( p_result->pgresult, tuple, field );
68
100
 
69
- if( p_coder && p_coder->dec_func ){
101
+ if( p_coder->dec_func ){
70
102
  return p_coder->dec_func(p_coder, val, len, tuple, field, ENCODING_GET(result));
103
+ } else {
104
+ t_pg_coder_dec_func dec_func;
105
+ dec_func = pg_coder_dec_func( p_coder, PQfformat(p_result->pgresult, field) );
106
+ return dec_func(p_coder, val, len, tuple, field, ENCODING_GET(result));
71
107
  }
72
108
  }
73
109
 
74
- dec_func = pg_coder_dec_func( p_coder, PQfformat(p_result->pgresult, field) );
75
- return dec_func( p_coder, val, len, tuple, field, ENCODING_GET(result) );
110
+ default_tm = DATA_PTR( this->typemap.default_typemap );
111
+ return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
76
112
  }
77
113
 
78
114
  static t_pg_coder *
79
- pg_tmbc_typecast_query_param(VALUE self, VALUE param_value, int field)
115
+ pg_tmbc_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
80
116
  {
81
- t_tmbc *this = (t_tmbc *)DATA_PTR(self);
117
+ t_tmbc *this = (t_tmbc *) p_typemap;
82
118
 
83
119
  /* Number of fields were already checked in pg_tmbc_fit_to_query() */
84
120
  t_pg_coder *p_coder = this->convs[field].cconv;
85
121
 
122
+ if( !p_coder ){
123
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
124
+ return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
125
+ }
126
+
86
127
  return p_coder;
87
128
  }
88
129
 
@@ -100,14 +141,19 @@ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, i
100
141
 
101
142
  p_coder = this->convs[fieldno].cconv;
102
143
 
144
+ if( !p_coder ){
145
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
146
+ return default_tm->funcs.typecast_copy_get( default_tm, field_str, fieldno, format, enc_idx );
147
+ }
148
+
103
149
  dec_func = pg_coder_dec_func( p_coder, format );
104
150
 
105
151
  /* Is it a pure String conversion? Then we can directly send field_str to the user. */
106
- if( format == 0 && dec_func == pg_text_dec_string ){
152
+ if( dec_func == pg_text_dec_string ){
107
153
  PG_ENCODING_SET_NOCHECK( field_str, enc_idx );
108
154
  return field_str;
109
155
  }
110
- if( format == 1 && dec_func == pg_bin_dec_bytea ){
156
+ if( dec_func == pg_bin_dec_bytea ){
111
157
  PG_ENCODING_SET_NOCHECK( field_str, rb_ascii8bit_encindex() );
112
158
  return field_str;
113
159
  }
@@ -115,7 +161,7 @@ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, i
115
161
  return dec_func( p_coder, RSTRING_PTR(field_str), RSTRING_LEN(field_str), 0, fieldno, enc_idx );
116
162
  }
117
163
 
118
- const t_typemap pg_tmbc_default_typemap = {
164
+ const struct pg_typemap_funcs pg_tmbc_funcs = {
119
165
  .fit_to_result = pg_tmbc_fit_to_result,
120
166
  .fit_to_query = pg_tmbc_fit_to_query,
121
167
  .fit_to_copy_get = pg_tmbc_fit_to_copy_get,
@@ -130,8 +176,9 @@ pg_tmbc_mark( t_tmbc *this )
130
176
  int i;
131
177
 
132
178
  /* allocated but not initialized ? */
133
- if( !this ) return;
179
+ if( this == (t_tmbc *)&pg_typemap_funcs ) return;
134
180
 
181
+ rb_gc_mark(this->typemap.default_typemap);
135
182
  for( i=0; i<this->nfields; i++){
136
183
  t_pg_coder *p_coder = this->convs[i].cconv;
137
184
  if( p_coder )
@@ -139,10 +186,19 @@ pg_tmbc_mark( t_tmbc *this )
139
186
  }
140
187
  }
141
188
 
189
+ static void
190
+ pg_tmbc_free( t_tmbc *this )
191
+ {
192
+ /* allocated but not initialized ? */
193
+ if( this == (t_tmbc *)&pg_typemap_funcs ) return;
194
+ xfree( this );
195
+ }
196
+
142
197
  static VALUE
143
198
  pg_tmbc_s_allocate( VALUE klass )
144
199
  {
145
- return Data_Wrap_Struct( klass, pg_tmbc_mark, -1, NULL );
200
+ /* Use pg_typemap_funcs as interim struct until #initialize is called. */
201
+ return Data_Wrap_Struct( klass, pg_tmbc_mark, pg_tmbc_free, (t_tmbc *)&pg_typemap_funcs );
146
202
  }
147
203
 
148
204
  VALUE
@@ -160,7 +216,7 @@ pg_tmbc_allocate()
160
216
  * The length of the Array corresponds to
161
217
  * the number of columns or bind parameters this type map is usable for.
162
218
  *
163
- * A +nil+ value will cast the given field to a String object.
219
+ * A +nil+ value will forward the given field to the #default_type_map .
164
220
  */
165
221
  static VALUE
166
222
  pg_tmbc_init(VALUE self, VALUE conv_ary)
@@ -175,7 +231,8 @@ pg_tmbc_init(VALUE self, VALUE conv_ary)
175
231
  this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
176
232
  /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
177
233
  this->nfields = 0;
178
- this->typemap = pg_tmbc_default_typemap;
234
+ this->typemap.funcs = pg_tmbc_funcs;
235
+ this->typemap.default_typemap = pg_typemap_all_strings;
179
236
  DATA_PTR(self) = this;
180
237
 
181
238
  for(i=0; i<conv_ary_len; i++)
@@ -251,4 +308,5 @@ init_pg_type_map_by_column()
251
308
  rb_define_alloc_func( rb_cTypeMapByColumn, pg_tmbc_s_allocate );
252
309
  rb_define_method( rb_cTypeMapByColumn, "initialize", pg_tmbc_init, 1 );
253
310
  rb_define_method( rb_cTypeMapByColumn, "coders", pg_tmbc_coders, 0 );
311
+ rb_include_module( rb_cTypeMapByColumn, rb_mDefaultTypeMappable );
254
312
  }
@@ -1,12 +1,10 @@
1
1
  /*
2
2
  * pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
3
- * $Id: pg_type_map_by_mri_type.c,v 4227fdc5f0ac 2014/10/07 17:42:09 lars $
3
+ * $Id$
4
4
  *
5
5
  * This type map can be used to select value encoders based on the MRI-internal
6
6
  * value type code.
7
7
  *
8
- * This class is planned to get replaced by a PG::TypeMapByClass implementation.
9
- *
10
8
  */
11
9
 
12
10
  #include "pg.h"
@@ -35,7 +33,8 @@ static VALUE rb_cTypeMapByMriType;
35
33
 
36
34
  #define DECLARE_CODER(type) \
37
35
  t_pg_coder *coder_##type; \
38
- VALUE ask_##type;
36
+ VALUE ask_##type; \
37
+ VALUE coder_obj_##type;
39
38
 
40
39
  typedef struct {
41
40
  t_typemap typemap;
@@ -52,9 +51,9 @@ typedef struct {
52
51
  break;
53
52
 
54
53
  static t_pg_coder *
55
- pg_tmbmt_typecast_query_param(VALUE self, VALUE param_value, int field)
54
+ pg_tmbmt_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
56
55
  {
57
- t_tmbmt *this = (t_tmbmt *)DATA_PTR(self);
56
+ t_tmbmt *this = (t_tmbmt *)p_typemap;
58
57
  t_pg_coder *p_coder;
59
58
  VALUE ask_for_coder;
60
59
 
@@ -70,11 +69,7 @@ pg_tmbmt_typecast_query_param(VALUE self, VALUE param_value, int field)
70
69
  /* No static Coder object, but proc/method given to ask for the Coder to use. */
71
70
  VALUE obj;
72
71
 
73
- if( TYPE(ask_for_coder) == T_SYMBOL ){
74
- obj = rb_funcall(self, SYM2ID(ask_for_coder), 1, param_value);
75
- }else{
76
- obj = rb_funcall(ask_for_coder, rb_intern("call"), 1, param_value);
77
- }
72
+ obj = rb_funcall(ask_for_coder, rb_intern("call"), 1, param_value);
78
73
 
79
74
  if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
80
75
  Data_Get_Struct(obj, t_pg_coder, p_coder);
@@ -84,28 +79,39 @@ pg_tmbmt_typecast_query_param(VALUE self, VALUE param_value, int field)
84
79
  }
85
80
  }
86
81
 
82
+ if( !p_coder ){
83
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
84
+ return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
85
+ }
86
+
87
87
  return p_coder;
88
88
  }
89
89
 
90
90
  static VALUE
91
91
  pg_tmbmt_fit_to_query( VALUE self, VALUE params )
92
92
  {
93
+ t_tmbmt *this = (t_tmbmt *)DATA_PTR(self);
94
+ /* Nothing to check at this typemap, but ensure that the default type map fits. */
95
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
96
+ default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
93
97
  return self;
94
98
  }
95
99
 
96
100
  #define GC_MARK_AS_USED(type) \
97
- if(this->coders.coder_##type) rb_gc_mark(this->coders.coder_##type->coder_obj); \
98
- rb_gc_mark( this->coders.ask_##type );
101
+ rb_gc_mark( this->coders.ask_##type ); \
102
+ rb_gc_mark( this->coders.coder_obj_##type );
99
103
 
100
104
  static void
101
105
  pg_tmbmt_mark( t_tmbmt *this )
102
106
  {
107
+ rb_gc_mark(this->typemap.default_typemap);
103
108
  FOR_EACH_MRI_TYPE( GC_MARK_AS_USED );
104
109
  }
105
110
 
106
111
  #define INIT_VARIABLES(type) \
107
112
  this->coders.coder_##type = NULL; \
108
- this->coders.ask_##type = Qnil;
113
+ this->coders.ask_##type = Qnil; \
114
+ this->coders.coder_obj_##type = Qnil;
109
115
 
110
116
  static VALUE
111
117
  pg_tmbmt_s_allocate( VALUE klass )
@@ -114,12 +120,13 @@ pg_tmbmt_s_allocate( VALUE klass )
114
120
  VALUE self;
115
121
 
116
122
  self = Data_Make_Struct( klass, t_tmbmt, pg_tmbmt_mark, -1, this );
117
- this->typemap.fit_to_result = pg_typemap_fit_to_result;
118
- this->typemap.fit_to_query = pg_tmbmt_fit_to_query;
119
- this->typemap.fit_to_copy_get = pg_typemap_fit_to_copy_get;
120
- this->typemap.typecast_result_value = pg_typemap_result_value;
121
- this->typemap.typecast_query_param = pg_tmbmt_typecast_query_param;
122
- this->typemap.typecast_copy_get = pg_typemap_typecast_copy_get;
123
+ this->typemap.funcs.fit_to_result = pg_typemap_fit_to_result;
124
+ this->typemap.funcs.fit_to_query = pg_tmbmt_fit_to_query;
125
+ this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
126
+ this->typemap.funcs.typecast_result_value = pg_typemap_result_value;
127
+ this->typemap.funcs.typecast_query_param = pg_tmbmt_typecast_query_param;
128
+ this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
129
+ this->typemap.default_typemap = pg_typemap_all_strings;
123
130
 
124
131
  FOR_EACH_MRI_TYPE( INIT_VARIABLES );
125
132
 
@@ -128,12 +135,16 @@ pg_tmbmt_s_allocate( VALUE klass )
128
135
 
129
136
  #define COMPARE_AND_ASSIGN(type) \
130
137
  else if(!strcmp(p_mri_type, #type)){ \
138
+ this->coders.coder_obj_##type = coder; \
131
139
  if(NIL_P(coder)){ \
132
140
  this->coders.coder_##type = NULL; \
133
141
  this->coders.ask_##type = Qnil; \
134
142
  }else if(rb_obj_is_kind_of(coder, rb_cPG_Coder)){ \
135
143
  Data_Get_Struct(coder, t_pg_coder, this->coders.coder_##type); \
136
144
  this->coders.ask_##type = Qnil; \
145
+ }else if(RB_TYPE_P(coder, T_SYMBOL)){ \
146
+ this->coders.coder_##type = NULL; \
147
+ this->coders.ask_##type = rb_obj_method( self, coder ); \
137
148
  }else{ \
138
149
  this->coders.coder_##type = NULL; \
139
150
  this->coders.ask_##type = coder; \
@@ -144,11 +155,11 @@ pg_tmbmt_s_allocate( VALUE klass )
144
155
  * call-seq:
145
156
  * typemap.[mri_type] = coder
146
157
  *
147
- * Assigns a new PG::Coder object to the type map. The decoder
158
+ * Assigns a new PG::Coder object to the type map. The encoder
148
159
  * is registered for type casts of the given +mri_type+ .
149
160
  *
150
161
  * +coder+ can be one of the following:
151
- * * +nil+ - Values are encoded by +#to_str+
162
+ * * +nil+ - Values are forwarded to the #default_type_map .
152
163
  * * a PG::Coder - Values are encoded by the given encoder
153
164
  * * a Symbol - The method of this type map (or a derivation) that is called for each value to sent.
154
165
  * It must return a PG::Coder.
@@ -194,7 +205,7 @@ pg_tmbmt_aset( VALUE self, VALUE mri_type, VALUE coder )
194
205
 
195
206
  #define COMPARE_AND_GET(type) \
196
207
  else if(!strcmp(p_mri_type, #type)){ \
197
- coder = this->coders.coder_##type ? this->coders.coder_##type->coder_obj : this->coders.ask_##type; \
208
+ coder = this->coders.coder_obj_##type; \
198
209
  }
199
210
 
200
211
  /*
@@ -225,7 +236,7 @@ pg_tmbmt_aref( VALUE self, VALUE mri_type )
225
236
  }
226
237
 
227
238
  #define ADD_TO_HASH(type) \
228
- rb_hash_aset( hash_coders, rb_obj_freeze(rb_str_new2(#type)), this->coders.coder_##type ? this->coders.coder_##type->coder_obj : this->coders.ask_##type );
239
+ rb_hash_aset( hash_coders, rb_obj_freeze(rb_str_new2(#type)), this->coders.coder_obj_##type );
229
240
 
230
241
 
231
242
  /*
@@ -257,10 +268,17 @@ init_pg_type_map_by_mri_type()
257
268
  * This type map is usable for type casting query bind parameters and COPY data
258
269
  * for PG::Connection#put_copy_data . Therefore only encoders might be assigned by
259
270
  * the #[]= method.
271
+ *
272
+ * _Note_ : This type map is not portable across Ruby implementations and is less flexible
273
+ * than PG::TypeMapByClass.
274
+ * It is kept only for performance comparisons, but PG::TypeMapByClass proved to be equally
275
+ * fast in almost all cases.
276
+ *
260
277
  */
261
278
  rb_cTypeMapByMriType = rb_define_class_under( rb_mPG, "TypeMapByMriType", rb_cTypeMap );
262
279
  rb_define_alloc_func( rb_cTypeMapByMriType, pg_tmbmt_s_allocate );
263
280
  rb_define_method( rb_cTypeMapByMriType, "[]=", pg_tmbmt_aset, 2 );
264
281
  rb_define_method( rb_cTypeMapByMriType, "[]", pg_tmbmt_aref, 1 );
265
282
  rb_define_method( rb_cTypeMapByMriType, "coders", pg_tmbmt_coders, 0 );
283
+ rb_include_module( rb_cTypeMapByMriType, rb_mDefaultTypeMappable );
266
284
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_oid.c - PG::TypeMapByOid class extension
3
- * $Id: pg_type_map_by_oid.c,v 4227fdc5f0ac 2014/10/07 17:42:09 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -23,6 +23,9 @@ typedef struct {
23
23
  } format[2];
24
24
  } t_tmbo;
25
25
 
26
+ static VALUE pg_tmbo_s_allocate( VALUE klass );
27
+
28
+
26
29
  /*
27
30
  * We use the OID's minor 8 Bits as index to a 256 entry cache. This avoids full ruby hash lookups
28
31
  * for each value in most cases.
@@ -63,7 +66,8 @@ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
63
66
  p_colmap = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields);
64
67
  /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
65
68
  p_colmap->nfields = 0;
66
- p_colmap->typemap = pg_tmbc_default_typemap;
69
+ p_colmap->typemap.funcs = pg_tmbc_funcs;
70
+ p_colmap->typemap.default_typemap = pg_typemap_all_strings;
67
71
 
68
72
  colmap = pg_tmbc_allocate();
69
73
  DATA_PTR(colmap) = p_colmap;
@@ -84,30 +88,33 @@ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
84
88
  }
85
89
 
86
90
  static VALUE
87
- pg_tmbo_result_value(VALUE result, int tuple, int field)
91
+ pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
88
92
  {
89
- char * val;
90
- int len;
91
93
  int format;
92
94
  t_pg_coder *p_coder;
93
- t_pg_coder_dec_func dec_func;
94
95
  t_pg_result *p_result = pgresult_get_this(result);
95
- t_tmbo *this = (t_tmbo*) p_result->p_typemap;
96
+ t_tmbo *this = (t_tmbo*) p_typemap;
97
+ t_typemap *default_tm;
96
98
 
97
99
  if (PQgetisnull(p_result->pgresult, tuple, field)) {
98
100
  return Qnil;
99
101
  }
100
102
 
101
- val = PQgetvalue( p_result->pgresult, tuple, field );
102
- len = PQgetlength( p_result->pgresult, tuple, field );
103
103
  format = PQfformat( p_result->pgresult, field );
104
104
 
105
105
  if( format < 0 || format > 1 )
106
106
  rb_raise(rb_eArgError, "result field %d has unsupported format code %d", field+1, format);
107
107
 
108
108
  p_coder = pg_tmbo_lookup_oid( this, format, PQftype(p_result->pgresult, field) );
109
- dec_func = pg_coder_dec_func( p_coder, format );
110
- return dec_func( p_coder, val, len, tuple, field, ENCODING_GET(result) );
109
+ if( p_coder ){
110
+ char * val = PQgetvalue( p_result->pgresult, tuple, field );
111
+ int len = PQgetlength( p_result->pgresult, tuple, field );
112
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func( p_coder, format );
113
+ return dec_func( p_coder, val, len, tuple, field, ENCODING_GET(result) );
114
+ }
115
+
116
+ default_tm = DATA_PTR( this->typemap.default_typemap );
117
+ return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
111
118
  }
112
119
 
113
120
  static VALUE
@@ -116,14 +123,33 @@ pg_tmbo_fit_to_result( VALUE self, VALUE result )
116
123
  t_tmbo *this = DATA_PTR( self );
117
124
  PGresult *pgresult = pgresult_get( result );
118
125
 
126
+ /* Ensure that the default type map fits equaly. */
127
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
128
+ VALUE sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
129
+
119
130
  if( PQntuples( pgresult ) <= this->max_rows_for_online_lookup ){
120
131
  /* Do a hash lookup for each result value in pg_tmbc_result_value() */
121
- return self;
132
+
133
+ /* Did the default type return the same object ? */
134
+ if( sub_typemap == this->typemap.default_typemap ){
135
+ return self;
136
+ } else {
137
+ /* The default type map built a new object, so we need to propagate it
138
+ * and build a copy of this type map. */
139
+ VALUE new_typemap = pg_tmbo_s_allocate( rb_cTypeMapByOid );
140
+ t_tmbo *p_new_typemap = DATA_PTR(new_typemap);
141
+ *p_new_typemap = *this;
142
+ p_new_typemap->typemap.default_typemap = sub_typemap;
143
+ return new_typemap;
144
+ }
122
145
  }else{
123
146
  /* Build a new TypeMapByColumn that fits to the given result and
124
147
  * uses a fast array lookup.
125
148
  */
126
- return pg_tmbo_build_type_map_for_result2( this, pgresult );
149
+ VALUE new_typemap = pg_tmbo_build_type_map_for_result2( this, pgresult );
150
+ t_tmbo *p_new_typemap = DATA_PTR(new_typemap);
151
+ p_new_typemap->typemap.default_typemap = sub_typemap;
152
+ return new_typemap;
127
153
  }
128
154
  }
129
155
 
@@ -132,6 +158,7 @@ pg_tmbo_mark( t_tmbo *this )
132
158
  {
133
159
  int i;
134
160
 
161
+ rb_gc_mark(this->typemap.default_typemap);
135
162
  for( i=0; i<2; i++){
136
163
  rb_gc_mark(this->format[i].oid_to_coder);
137
164
  }
@@ -146,12 +173,13 @@ pg_tmbo_s_allocate( VALUE klass )
146
173
 
147
174
  self = Data_Make_Struct( klass, t_tmbo, pg_tmbo_mark, -1, this );
148
175
 
149
- this->typemap.fit_to_result = pg_tmbo_fit_to_result;
150
- this->typemap.fit_to_query = pg_typemap_fit_to_query;
151
- this->typemap.fit_to_copy_get = pg_typemap_fit_to_copy_get;
152
- this->typemap.typecast_result_value = pg_tmbo_result_value;
153
- this->typemap.typecast_query_param = pg_typemap_typecast_query_param;
154
- this->typemap.typecast_copy_get = pg_typemap_typecast_copy_get;
176
+ this->typemap.funcs.fit_to_result = pg_tmbo_fit_to_result;
177
+ this->typemap.funcs.fit_to_query = pg_typemap_fit_to_query;
178
+ this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
179
+ this->typemap.funcs.typecast_result_value = pg_tmbo_result_value;
180
+ this->typemap.funcs.typecast_query_param = pg_typemap_typecast_query_param;
181
+ this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
182
+ this->typemap.default_typemap = pg_typemap_all_strings;
155
183
  this->max_rows_for_online_lookup = 10;
156
184
 
157
185
  for( i=0; i<2; i++){
@@ -302,7 +330,7 @@ pg_tmbo_fit_to_result_ext( int argc, VALUE *argv, VALUE self )
302
330
 
303
331
  if( NIL_P( online_lookup ) ){
304
332
  /* call super */
305
- return this->typemap.fit_to_result(self, result);
333
+ return this->typemap.funcs.fit_to_result(self, result);
306
334
  } else if( RB_TYPE_P( online_lookup, T_TRUE ) ){
307
335
  return self;
308
336
  } else if( RB_TYPE_P( online_lookup, T_FALSE ) ){
@@ -329,6 +357,9 @@ init_pg_type_map_by_oid()
329
357
  *
330
358
  * This type map is only suitable to cast values from PG::Result objects.
331
359
  * Therefore only decoders might be assigned by the #add_coder method.
360
+ *
361
+ * Fields with no match to any of the registered type OID / format combination
362
+ * are forwarded to the #default_type_map .
332
363
  */
333
364
  rb_cTypeMapByOid = rb_define_class_under( rb_mPG, "TypeMapByOid", rb_cTypeMap );
334
365
  rb_define_alloc_func( rb_cTypeMapByOid, pg_tmbo_s_allocate );
@@ -338,4 +369,5 @@ init_pg_type_map_by_oid()
338
369
  rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
339
370
  rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
340
371
  rb_define_method( rb_cTypeMapByOid, "fit_to_result", pg_tmbo_fit_to_result_ext, -1 );
372
+ rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
341
373
  }