pg 0.18.0.pre20141017160319 → 0.18.0.pre20141117110243

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.
@@ -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
  }