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
@@ -0,0 +1,312 @@
1
+ /*
2
+ * pg_column_map.c - PG::ColumnMap class extension
3
+ * $Id: pg_type_map_by_column.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ static VALUE rb_cTypeMapByColumn;
10
+ static ID s_id_decode;
11
+ static ID s_id_encode;
12
+
13
+ static VALUE pg_tmbc_s_allocate( VALUE klass );
14
+
15
+ static VALUE
16
+ pg_tmbc_fit_to_result( VALUE self, VALUE result )
17
+ {
18
+ int nfields;
19
+ t_tmbc *this = DATA_PTR( self );
20
+ t_typemap *default_tm;
21
+ VALUE sub_typemap;
22
+
23
+ nfields = PQnfields( pgresult_get(result) );
24
+ if ( this->nfields != nfields ) {
25
+ rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
26
+ nfields, this->nfields );
27
+ }
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
+ }
48
+ }
49
+
50
+ static VALUE
51
+ pg_tmbc_fit_to_query( VALUE self, VALUE params )
52
+ {
53
+ int nfields;
54
+ t_tmbc *this = DATA_PTR( self );
55
+ t_typemap *default_tm;
56
+
57
+ nfields = (int)RARRAY_LEN( params );
58
+ if ( this->nfields != nfields ) {
59
+ rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
60
+ nfields, this->nfields );
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
+
67
+ return self;
68
+ }
69
+
70
+ static int
71
+ pg_tmbc_fit_to_copy_get( VALUE self )
72
+ {
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
+
79
+ return this->nfields;
80
+ }
81
+
82
+
83
+ VALUE
84
+ pg_tmbc_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
85
+ {
86
+ t_pg_coder *p_coder = NULL;
87
+ t_pg_result *p_result = pgresult_get_this(result);
88
+ t_tmbc *this = (t_tmbc *) p_typemap;
89
+ t_typemap *default_tm;
90
+
91
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
92
+ return Qnil;
93
+ }
94
+
95
+ p_coder = this->convs[field].cconv;
96
+
97
+ if( p_coder ){
98
+ char * val = PQgetvalue( p_result->pgresult, tuple, field );
99
+ int len = PQgetlength( p_result->pgresult, tuple, field );
100
+
101
+ if( p_coder->dec_func ){
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));
107
+ }
108
+ }
109
+
110
+ default_tm = DATA_PTR( this->typemap.default_typemap );
111
+ return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
112
+ }
113
+
114
+ static t_pg_coder *
115
+ pg_tmbc_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
116
+ {
117
+ t_tmbc *this = (t_tmbc *) p_typemap;
118
+
119
+ /* Number of fields were already checked in pg_tmbc_fit_to_query() */
120
+ t_pg_coder *p_coder = this->convs[field].cconv;
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
+
127
+ return p_coder;
128
+ }
129
+
130
+ static VALUE
131
+ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
132
+ {
133
+ t_tmbc *this = (t_tmbc *) p_typemap;
134
+ t_pg_coder *p_coder;
135
+ t_pg_coder_dec_func dec_func;
136
+
137
+ if ( fieldno >= this->nfields || fieldno < 0 ) {
138
+ rb_raise( rb_eArgError, "number of copy fields (%d) exceeds number of mapped columns (%d)",
139
+ fieldno, this->nfields );
140
+ }
141
+
142
+ p_coder = this->convs[fieldno].cconv;
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
+
149
+ dec_func = pg_coder_dec_func( p_coder, format );
150
+
151
+ /* Is it a pure String conversion? Then we can directly send field_str to the user. */
152
+ if( dec_func == pg_text_dec_string ){
153
+ PG_ENCODING_SET_NOCHECK( field_str, enc_idx );
154
+ return field_str;
155
+ }
156
+ if( dec_func == pg_bin_dec_bytea ){
157
+ PG_ENCODING_SET_NOCHECK( field_str, rb_ascii8bit_encindex() );
158
+ return field_str;
159
+ }
160
+
161
+ return dec_func( p_coder, RSTRING_PTR(field_str), RSTRING_LEN(field_str), 0, fieldno, enc_idx );
162
+ }
163
+
164
+ const struct pg_typemap_funcs pg_tmbc_funcs = {
165
+ pg_tmbc_fit_to_result,
166
+ pg_tmbc_fit_to_query,
167
+ pg_tmbc_fit_to_copy_get,
168
+ pg_tmbc_result_value,
169
+ pg_tmbc_typecast_query_param,
170
+ pg_tmbc_typecast_copy_get
171
+ };
172
+
173
+ static void
174
+ pg_tmbc_mark( t_tmbc *this )
175
+ {
176
+ int i;
177
+
178
+ /* allocated but not initialized ? */
179
+ if( this == (t_tmbc *)&pg_typemap_funcs ) return;
180
+
181
+ rb_gc_mark(this->typemap.default_typemap);
182
+ for( i=0; i<this->nfields; i++){
183
+ t_pg_coder *p_coder = this->convs[i].cconv;
184
+ if( p_coder )
185
+ rb_gc_mark(p_coder->coder_obj);
186
+ }
187
+ }
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
+
197
+ static VALUE
198
+ pg_tmbc_s_allocate( VALUE klass )
199
+ {
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 );
202
+ }
203
+
204
+ VALUE
205
+ pg_tmbc_allocate()
206
+ {
207
+ return pg_tmbc_s_allocate(rb_cTypeMapByColumn);
208
+ }
209
+
210
+ /*
211
+ * call-seq:
212
+ * PG::TypeMapByColumn.new( coders )
213
+ *
214
+ * Builds a new type map and assigns a list of coders for the given column.
215
+ * +coders+ must be an Array of PG::Coder objects or +nil+ values.
216
+ * The length of the Array corresponds to
217
+ * the number of columns or bind parameters this type map is usable for.
218
+ *
219
+ * A +nil+ value will forward the given field to the #default_type_map .
220
+ */
221
+ static VALUE
222
+ pg_tmbc_init(VALUE self, VALUE conv_ary)
223
+ {
224
+ int i;
225
+ t_tmbc *this;
226
+ int conv_ary_len;
227
+
228
+ Check_Type(self, T_DATA);
229
+ Check_Type(conv_ary, T_ARRAY);
230
+ conv_ary_len = RARRAY_LEN(conv_ary);
231
+ this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
232
+ /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
233
+ this->nfields = 0;
234
+ this->typemap.funcs = pg_tmbc_funcs;
235
+ this->typemap.default_typemap = pg_typemap_all_strings;
236
+ DATA_PTR(self) = this;
237
+
238
+ for(i=0; i<conv_ary_len; i++)
239
+ {
240
+ VALUE obj = rb_ary_entry(conv_ary, i);
241
+
242
+ if( obj == Qnil ){
243
+ /* no type cast */
244
+ this->convs[i].cconv = NULL;
245
+ } else if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
246
+ Data_Get_Struct(obj, t_pg_coder, this->convs[i].cconv);
247
+ } else {
248
+ rb_raise(rb_eArgError, "argument %d has invalid type %s (should be nil or some kind of PG::Coder)",
249
+ i+1, rb_obj_classname( obj ));
250
+ }
251
+ }
252
+
253
+ this->nfields = conv_ary_len;
254
+
255
+ return self;
256
+ }
257
+
258
+ /*
259
+ * call-seq:
260
+ * typemap.coders -> Array
261
+ *
262
+ * Array of PG::Coder objects. The length of the Array corresponds to
263
+ * the number of columns or bind parameters this type map is usable for.
264
+ */
265
+ static VALUE
266
+ pg_tmbc_coders(VALUE self)
267
+ {
268
+ int i;
269
+ t_tmbc *this = DATA_PTR( self );
270
+ VALUE ary_coders = rb_ary_new();
271
+
272
+ for( i=0; i<this->nfields; i++){
273
+ t_pg_coder *conv = this->convs[i].cconv;
274
+ if( conv ) {
275
+ rb_ary_push( ary_coders, conv->coder_obj );
276
+ } else {
277
+ rb_ary_push( ary_coders, Qnil );
278
+ }
279
+ }
280
+
281
+ return rb_obj_freeze(ary_coders);
282
+ }
283
+
284
+ void
285
+ init_pg_type_map_by_column()
286
+ {
287
+ s_id_decode = rb_intern("decode");
288
+ s_id_encode = rb_intern("encode");
289
+
290
+ /*
291
+ * Document-class: PG::TypeMapByColumn < PG::TypeMap
292
+ *
293
+ * This type map casts values by a coder assigned per field/column.
294
+ *
295
+ * Each PG:TypeMapByColumn has a fixed list of either encoders or decoders,
296
+ * that is defined at #new . A type map with encoders is usable for type casting
297
+ * query bind parameters and COPY data for PG::Connection#put_copy_data .
298
+ * A type map with decoders is usable for type casting of result values and
299
+ * COPY data from PG::Connection#get_copy_data .
300
+ *
301
+ * PG::TypeMapByColumns are in particular useful in conjunction with prepared statements,
302
+ * since they can be cached alongside with the statement handle.
303
+ *
304
+ * This type map strategy is also used internally by PG::TypeMapByOid, when the
305
+ * number of rows of a result set exceeds a given limit.
306
+ */
307
+ rb_cTypeMapByColumn = rb_define_class_under( rb_mPG, "TypeMapByColumn", rb_cTypeMap );
308
+ rb_define_alloc_func( rb_cTypeMapByColumn, pg_tmbc_s_allocate );
309
+ rb_define_method( rb_cTypeMapByColumn, "initialize", pg_tmbc_init, 1 );
310
+ rb_define_method( rb_cTypeMapByColumn, "coders", pg_tmbc_coders, 0 );
311
+ rb_include_module( rb_cTypeMapByColumn, rb_mDefaultTypeMappable );
312
+ }
@@ -0,0 +1,284 @@
1
+ /*
2
+ * pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
3
+ * $Id: pg_type_map_by_mri_type.c,v 1269b8ad77b8 2015/02/06 16:38:23 lars $
4
+ *
5
+ * This type map can be used to select value encoders based on the MRI-internal
6
+ * value type code.
7
+ *
8
+ */
9
+
10
+ #include "pg.h"
11
+
12
+ static VALUE rb_cTypeMapByMriType;
13
+
14
+ #define FOR_EACH_MRI_TYPE(func) \
15
+ func(T_FIXNUM) \
16
+ func(T_TRUE) \
17
+ func(T_FALSE) \
18
+ func(T_FLOAT) \
19
+ func(T_BIGNUM) \
20
+ func(T_COMPLEX) \
21
+ func(T_RATIONAL) \
22
+ func(T_ARRAY) \
23
+ func(T_STRING) \
24
+ func(T_SYMBOL) \
25
+ func(T_OBJECT) \
26
+ func(T_CLASS) \
27
+ func(T_MODULE) \
28
+ func(T_REGEXP) \
29
+ func(T_HASH) \
30
+ func(T_STRUCT) \
31
+ func(T_FILE) \
32
+ func(T_DATA)
33
+
34
+ #define DECLARE_CODER(type) \
35
+ t_pg_coder *coder_##type; \
36
+ VALUE ask_##type; \
37
+ VALUE coder_obj_##type;
38
+
39
+ typedef struct {
40
+ t_typemap typemap;
41
+ struct pg_tmbmt_converter {
42
+ FOR_EACH_MRI_TYPE( DECLARE_CODER )
43
+ } coders;
44
+ } t_tmbmt;
45
+
46
+
47
+ #define CASE_AND_GET(type) \
48
+ case type: \
49
+ p_coder = this->coders.coder_##type; \
50
+ ask_for_coder = this->coders.ask_##type; \
51
+ break;
52
+
53
+ static t_pg_coder *
54
+ pg_tmbmt_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
55
+ {
56
+ t_tmbmt *this = (t_tmbmt *)p_typemap;
57
+ t_pg_coder *p_coder;
58
+ VALUE ask_for_coder;
59
+
60
+ switch(TYPE(param_value)){
61
+ FOR_EACH_MRI_TYPE( CASE_AND_GET )
62
+ default:
63
+ /* unknown MRI type */
64
+ p_coder = NULL;
65
+ ask_for_coder = Qnil;
66
+ }
67
+
68
+ if( !NIL_P(ask_for_coder) ){
69
+ /* No static Coder object, but proc/method given to ask for the Coder to use. */
70
+ VALUE obj;
71
+
72
+ obj = rb_funcall(ask_for_coder, rb_intern("call"), 1, param_value);
73
+
74
+ if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
75
+ Data_Get_Struct(obj, t_pg_coder, p_coder);
76
+ }else{
77
+ rb_raise(rb_eTypeError, "argument %d has invalid type %s (should be nil or some kind of PG::Coder)",
78
+ field+1, rb_obj_classname( obj ));
79
+ }
80
+ }
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
+ return p_coder;
88
+ }
89
+
90
+ static VALUE
91
+ pg_tmbmt_fit_to_query( VALUE self, VALUE params )
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 );
97
+ return self;
98
+ }
99
+
100
+ #define GC_MARK_AS_USED(type) \
101
+ rb_gc_mark( this->coders.ask_##type ); \
102
+ rb_gc_mark( this->coders.coder_obj_##type );
103
+
104
+ static void
105
+ pg_tmbmt_mark( t_tmbmt *this )
106
+ {
107
+ rb_gc_mark(this->typemap.default_typemap);
108
+ FOR_EACH_MRI_TYPE( GC_MARK_AS_USED );
109
+ }
110
+
111
+ #define INIT_VARIABLES(type) \
112
+ this->coders.coder_##type = NULL; \
113
+ this->coders.ask_##type = Qnil; \
114
+ this->coders.coder_obj_##type = Qnil;
115
+
116
+ static VALUE
117
+ pg_tmbmt_s_allocate( VALUE klass )
118
+ {
119
+ t_tmbmt *this;
120
+ VALUE self;
121
+
122
+ self = Data_Make_Struct( klass, t_tmbmt, pg_tmbmt_mark, -1, this );
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;
130
+
131
+ FOR_EACH_MRI_TYPE( INIT_VARIABLES );
132
+
133
+ return self;
134
+ }
135
+
136
+ #define COMPARE_AND_ASSIGN(type) \
137
+ else if(!strcmp(p_mri_type, #type)){ \
138
+ this->coders.coder_obj_##type = coder; \
139
+ if(NIL_P(coder)){ \
140
+ this->coders.coder_##type = NULL; \
141
+ this->coders.ask_##type = Qnil; \
142
+ }else if(rb_obj_is_kind_of(coder, rb_cPG_Coder)){ \
143
+ Data_Get_Struct(coder, t_pg_coder, this->coders.coder_##type); \
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 ); \
148
+ }else{ \
149
+ this->coders.coder_##type = NULL; \
150
+ this->coders.ask_##type = coder; \
151
+ } \
152
+ }
153
+
154
+ /*
155
+ * call-seq:
156
+ * typemap.[mri_type] = coder
157
+ *
158
+ * Assigns a new PG::Coder object to the type map. The encoder
159
+ * is registered for type casts of the given +mri_type+ .
160
+ *
161
+ * +coder+ can be one of the following:
162
+ * * +nil+ - Values are forwarded to the #default_type_map .
163
+ * * a PG::Coder - Values are encoded by the given encoder
164
+ * * a Symbol - The method of this type map (or a derivation) that is called for each value to sent.
165
+ * It must return a PG::Coder.
166
+ * * a Proc - The Proc object is called for each value. It must return a PG::Coder.
167
+ *
168
+ * +mri_type+ must be one of the following strings:
169
+ * * +T_FIXNUM+
170
+ * * +T_TRUE+
171
+ * * +T_FALSE+
172
+ * * +T_FLOAT+
173
+ * * +T_BIGNUM+
174
+ * * +T_COMPLEX+
175
+ * * +T_RATIONAL+
176
+ * * +T_ARRAY+
177
+ * * +T_STRING+
178
+ * * +T_SYMBOL+
179
+ * * +T_OBJECT+
180
+ * * +T_CLASS+
181
+ * * +T_MODULE+
182
+ * * +T_REGEXP+
183
+ * * +T_HASH+
184
+ * * +T_STRUCT+
185
+ * * +T_FILE+
186
+ * * +T_DATA+
187
+ */
188
+ static VALUE
189
+ pg_tmbmt_aset( VALUE self, VALUE mri_type, VALUE coder )
190
+ {
191
+ t_tmbmt *this = DATA_PTR( self );
192
+ char *p_mri_type;
193
+
194
+ p_mri_type = StringValueCStr(mri_type);
195
+
196
+ if(0){}
197
+ FOR_EACH_MRI_TYPE( COMPARE_AND_ASSIGN )
198
+ else{
199
+ VALUE mri_type_inspect = rb_inspect( mri_type );
200
+ rb_raise(rb_eArgError, "unknown mri_type %s", StringValueCStr(mri_type_inspect));
201
+ }
202
+
203
+ return self;
204
+ }
205
+
206
+ #define COMPARE_AND_GET(type) \
207
+ else if(!strcmp(p_mri_type, #type)){ \
208
+ coder = this->coders.coder_obj_##type; \
209
+ }
210
+
211
+ /*
212
+ * call-seq:
213
+ * typemap.[mri_type] -> coder
214
+ *
215
+ * Returns the encoder object for the given +mri_type+
216
+ *
217
+ * See #[]= for allowed +mri_type+ .
218
+ */
219
+ static VALUE
220
+ pg_tmbmt_aref( VALUE self, VALUE mri_type )
221
+ {
222
+ VALUE coder;
223
+ t_tmbmt *this = DATA_PTR( self );
224
+ char *p_mri_type;
225
+
226
+ p_mri_type = StringValueCStr(mri_type);
227
+
228
+ if(0){}
229
+ FOR_EACH_MRI_TYPE( COMPARE_AND_GET )
230
+ else{
231
+ VALUE mri_type_inspect = rb_inspect( mri_type );
232
+ rb_raise(rb_eArgError, "unknown mri_type %s", StringValueCStr(mri_type_inspect));
233
+ }
234
+
235
+ return coder;
236
+ }
237
+
238
+ #define ADD_TO_HASH(type) \
239
+ rb_hash_aset( hash_coders, rb_obj_freeze(rb_str_new2(#type)), this->coders.coder_obj_##type );
240
+
241
+
242
+ /*
243
+ * call-seq:
244
+ * typemap.coders -> Hash
245
+ *
246
+ * Returns all mri types and their assigned encoder object.
247
+ */
248
+ static VALUE
249
+ pg_tmbmt_coders( VALUE self )
250
+ {
251
+ t_tmbmt *this = DATA_PTR( self );
252
+ VALUE hash_coders = rb_hash_new();
253
+
254
+ FOR_EACH_MRI_TYPE( ADD_TO_HASH );
255
+
256
+ return rb_obj_freeze(hash_coders);
257
+ }
258
+
259
+ void
260
+ init_pg_type_map_by_mri_type()
261
+ {
262
+ /*
263
+ * Document-class: PG::TypeMapByMriType < PG::TypeMap
264
+ *
265
+ * This type map casts values based on the Ruby object type code of the given value
266
+ * to be sent.
267
+ *
268
+ * This type map is usable for type casting query bind parameters and COPY data
269
+ * for PG::Connection#put_copy_data . Therefore only encoders might be assigned by
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
+ *
277
+ */
278
+ rb_cTypeMapByMriType = rb_define_class_under( rb_mPG, "TypeMapByMriType", rb_cTypeMap );
279
+ rb_define_alloc_func( rb_cTypeMapByMriType, pg_tmbmt_s_allocate );
280
+ rb_define_method( rb_cTypeMapByMriType, "[]=", pg_tmbmt_aset, 2 );
281
+ rb_define_method( rb_cTypeMapByMriType, "[]", pg_tmbmt_aref, 1 );
282
+ rb_define_method( rb_cTypeMapByMriType, "coders", pg_tmbmt_coders, 0 );
283
+ rb_include_module( rb_cTypeMapByMriType, rb_mDefaultTypeMappable );
284
+ }