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,355 @@
1
+ /*
2
+ * pg_type_map_by_oid.c - PG::TypeMapByOid class extension
3
+ * $Id: pg_type_map_by_oid.c,v c99d26015e3c 2014/12/12 20:58:25 lars $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ static VALUE rb_cTypeMapByOid;
10
+ static ID s_id_decode;
11
+
12
+ typedef struct {
13
+ t_typemap typemap;
14
+ int max_rows_for_online_lookup;
15
+
16
+ struct pg_tmbo_converter {
17
+ VALUE oid_to_coder;
18
+
19
+ struct pg_tmbo_oid_cache_entry {
20
+ Oid oid;
21
+ t_pg_coder *p_coder;
22
+ } cache_row[0x100];
23
+ } format[2];
24
+ } t_tmbo;
25
+
26
+ static VALUE pg_tmbo_s_allocate( VALUE klass );
27
+
28
+
29
+ /*
30
+ * We use the OID's minor 8 Bits as index to a 256 entry cache. This avoids full ruby hash lookups
31
+ * for each value in most cases.
32
+ */
33
+ #define CACHE_LOOKUP(this, form, oid) ( &this->format[(form)].cache_row[(oid) & 0xff] )
34
+
35
+ static t_pg_coder *
36
+ pg_tmbo_lookup_oid(t_tmbo *this, int format, Oid oid)
37
+ {
38
+ t_pg_coder *conv;
39
+ struct pg_tmbo_oid_cache_entry *p_ce;
40
+
41
+ p_ce = CACHE_LOOKUP(this, format, oid);
42
+
43
+ /* Has the entry the expected OID and is it a non empty entry? */
44
+ if( p_ce->oid == oid && (oid || p_ce->p_coder) ) {
45
+ conv = p_ce->p_coder;
46
+ } else {
47
+ VALUE obj = rb_hash_lookup( this->format[format].oid_to_coder, UINT2NUM( oid ));
48
+ /* obj must be nil or some kind of PG::Coder, this is checked at insertion */
49
+ conv = NIL_P(obj) ? NULL : DATA_PTR(obj);
50
+ /* Write the retrieved coder to the cache */
51
+ p_ce->oid = oid;
52
+ p_ce->p_coder = conv;
53
+ }
54
+ return conv;
55
+ }
56
+
57
+ /* Build a TypeMapByColumn that fits to the given result */
58
+ static VALUE
59
+ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
60
+ {
61
+ t_tmbc *p_colmap;
62
+ int i;
63
+ VALUE colmap;
64
+ int nfields = PQnfields( pgresult );
65
+
66
+ p_colmap = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields);
67
+ /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
68
+ p_colmap->nfields = 0;
69
+ p_colmap->typemap.funcs = pg_tmbc_funcs;
70
+ p_colmap->typemap.default_typemap = pg_typemap_all_strings;
71
+
72
+ colmap = pg_tmbc_allocate();
73
+ DATA_PTR(colmap) = p_colmap;
74
+
75
+ for(i=0; i<nfields; i++)
76
+ {
77
+ int format = PQfformat(pgresult, i);
78
+
79
+ if( format < 0 || format > 1 )
80
+ rb_raise(rb_eArgError, "result field %d has unsupported format code %d", i+1, format);
81
+
82
+ p_colmap->convs[i].cconv = pg_tmbo_lookup_oid( this, format, PQftype(pgresult, i) );
83
+ }
84
+
85
+ p_colmap->nfields = nfields;
86
+
87
+ return colmap;
88
+ }
89
+
90
+ static VALUE
91
+ pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
92
+ {
93
+ int format;
94
+ t_pg_coder *p_coder;
95
+ t_pg_result *p_result = pgresult_get_this(result);
96
+ t_tmbo *this = (t_tmbo*) p_typemap;
97
+ t_typemap *default_tm;
98
+
99
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
100
+ return Qnil;
101
+ }
102
+
103
+ format = PQfformat( p_result->pgresult, field );
104
+
105
+ if( format < 0 || format > 1 )
106
+ rb_raise(rb_eArgError, "result field %d has unsupported format code %d", field+1, format);
107
+
108
+ p_coder = pg_tmbo_lookup_oid( this, format, PQftype(p_result->pgresult, field) );
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 );
118
+ }
119
+
120
+ static VALUE
121
+ pg_tmbo_fit_to_result( VALUE self, VALUE result )
122
+ {
123
+ t_tmbo *this = DATA_PTR( self );
124
+ PGresult *pgresult = pgresult_get( result );
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
+
130
+ if( PQntuples( pgresult ) <= this->max_rows_for_online_lookup ){
131
+ /* Do a hash lookup for each result value in pg_tmbc_result_value() */
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
+ }
145
+ }else{
146
+ /* Build a new TypeMapByColumn that fits to the given result and
147
+ * uses a fast array lookup.
148
+ */
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;
153
+ }
154
+ }
155
+
156
+ static void
157
+ pg_tmbo_mark( t_tmbo *this )
158
+ {
159
+ int i;
160
+
161
+ rb_gc_mark(this->typemap.default_typemap);
162
+ for( i=0; i<2; i++){
163
+ rb_gc_mark(this->format[i].oid_to_coder);
164
+ }
165
+ }
166
+
167
+ static VALUE
168
+ pg_tmbo_s_allocate( VALUE klass )
169
+ {
170
+ t_tmbo *this;
171
+ VALUE self;
172
+ int i;
173
+
174
+ self = Data_Make_Struct( klass, t_tmbo, pg_tmbo_mark, -1, this );
175
+
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;
183
+ this->max_rows_for_online_lookup = 10;
184
+
185
+ for( i=0; i<2; i++){
186
+ this->format[i].oid_to_coder = rb_hash_new();
187
+ }
188
+
189
+ return self;
190
+ }
191
+
192
+ /*
193
+ * call-seq:
194
+ * typemap.add_coder( coder )
195
+ *
196
+ * Assigns a new PG::Coder object to the type map. The decoder
197
+ * is registered for type casts based on it's PG::Coder#oid and
198
+ * PG::Coder#format attributes.
199
+ *
200
+ * Later changes of the oid or format code within the coder object
201
+ * will have no effect to the type map.
202
+ *
203
+ */
204
+ static VALUE
205
+ pg_tmbo_add_coder( VALUE self, VALUE coder )
206
+ {
207
+ VALUE hash;
208
+ t_tmbo *this = DATA_PTR( self );
209
+ t_pg_coder *p_coder;
210
+ struct pg_tmbo_oid_cache_entry *p_ce;
211
+
212
+ if( !rb_obj_is_kind_of(coder, rb_cPG_Coder) )
213
+ rb_raise(rb_eArgError, "invalid type %s (should be some kind of PG::Coder)",
214
+ rb_obj_classname( coder ));
215
+
216
+ Data_Get_Struct(coder, t_pg_coder, p_coder);
217
+
218
+ if( p_coder->format < 0 || p_coder->format > 1 )
219
+ rb_raise(rb_eArgError, "invalid format code %d", p_coder->format);
220
+
221
+ /* Update cache entry */
222
+ p_ce = CACHE_LOOKUP(this, p_coder->format, p_coder->oid);
223
+ p_ce->oid = p_coder->oid;
224
+ p_ce->p_coder = p_coder;
225
+ /* Write coder into the hash of the given format */
226
+ hash = this->format[p_coder->format].oid_to_coder;
227
+ rb_hash_aset( hash, UINT2NUM(p_coder->oid), coder);
228
+
229
+ return self;
230
+ }
231
+
232
+ /*
233
+ * call-seq:
234
+ * typemap.rm_coder( format, oid )
235
+ *
236
+ * Removes a PG::Coder object from the type map based on the given
237
+ * oid and format codes.
238
+ *
239
+ * Returns the removed coder object.
240
+ */
241
+ static VALUE
242
+ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
243
+ {
244
+ VALUE hash;
245
+ VALUE coder;
246
+ t_tmbo *this = DATA_PTR( self );
247
+ int i_format = NUM2INT(format);
248
+ struct pg_tmbo_oid_cache_entry *p_ce;
249
+
250
+ if( i_format < 0 || i_format > 1 )
251
+ rb_raise(rb_eArgError, "invalid format code %d", i_format);
252
+
253
+ /* Mark the cache entry as empty */
254
+ p_ce = CACHE_LOOKUP(this, i_format, NUM2UINT(oid));
255
+ p_ce->oid = 0;
256
+ p_ce->p_coder = NULL;
257
+ hash = this->format[i_format].oid_to_coder;
258
+ coder = rb_hash_delete( hash, oid );
259
+
260
+ return coder;
261
+ }
262
+
263
+ /*
264
+ * call-seq:
265
+ * typemap.coders -> Array
266
+ *
267
+ * Array of all assigned PG::Coder objects.
268
+ */
269
+ static VALUE
270
+ pg_tmbo_coders( VALUE self )
271
+ {
272
+ t_tmbo *this = DATA_PTR( self );
273
+
274
+ return rb_ary_concat(
275
+ rb_funcall(this->format[0].oid_to_coder, rb_intern("values"), 0),
276
+ rb_funcall(this->format[1].oid_to_coder, rb_intern("values"), 0));
277
+ }
278
+
279
+ /*
280
+ * call-seq:
281
+ * typemap.max_rows_for_online_lookup = number
282
+ *
283
+ * Threshold for doing Hash lookups versus creation of a dedicated PG::TypeMapByColumn.
284
+ * The type map will do Hash lookups for each result value, if the number of rows
285
+ * is below or equal +number+.
286
+ *
287
+ */
288
+ static VALUE
289
+ pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
290
+ {
291
+ t_tmbo *this = DATA_PTR( self );
292
+ this->max_rows_for_online_lookup = NUM2INT(value);
293
+ return value;
294
+ }
295
+
296
+ /*
297
+ * call-seq:
298
+ * typemap.max_rows_for_online_lookup -> Integer
299
+ */
300
+ static VALUE
301
+ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
302
+ {
303
+ t_tmbo *this = DATA_PTR( self );
304
+ return INT2NUM(this->max_rows_for_online_lookup);
305
+ }
306
+
307
+ /*
308
+ * call-seq:
309
+ * typemap.build_column_map( result )
310
+ *
311
+ * This builds a PG::TypeMapByColumn that fits to the given PG::Result object
312
+ * based on it's type OIDs.
313
+ *
314
+ */
315
+ static VALUE
316
+ pg_tmbo_build_column_map( VALUE self, VALUE result )
317
+ {
318
+ t_tmbo *this = DATA_PTR( self );
319
+
320
+ if ( !rb_obj_is_kind_of(result, rb_cPGresult) ) {
321
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Result)",
322
+ rb_obj_classname( result ) );
323
+ }
324
+
325
+ return pg_tmbo_build_type_map_for_result2( this, pgresult_get(result) );
326
+ }
327
+
328
+
329
+ void
330
+ init_pg_type_map_by_oid()
331
+ {
332
+ s_id_decode = rb_intern("decode");
333
+
334
+ /*
335
+ * Document-class: PG::TypeMapByOid < PG::TypeMap
336
+ *
337
+ * This type map casts values based on the type OID of the given column
338
+ * in the result set.
339
+ *
340
+ * This type map is only suitable to cast values from PG::Result objects.
341
+ * Therefore only decoders might be assigned by the #add_coder method.
342
+ *
343
+ * Fields with no match to any of the registered type OID / format combination
344
+ * are forwarded to the #default_type_map .
345
+ */
346
+ rb_cTypeMapByOid = rb_define_class_under( rb_mPG, "TypeMapByOid", rb_cTypeMap );
347
+ rb_define_alloc_func( rb_cTypeMapByOid, pg_tmbo_s_allocate );
348
+ rb_define_method( rb_cTypeMapByOid, "add_coder", pg_tmbo_add_coder, 1 );
349
+ rb_define_method( rb_cTypeMapByOid, "rm_coder", pg_tmbo_rm_coder, 2 );
350
+ rb_define_method( rb_cTypeMapByOid, "coders", pg_tmbo_coders, 0 );
351
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
352
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
353
+ rb_define_method( rb_cTypeMapByOid, "build_column_map", pg_tmbo_build_column_map, 1 );
354
+ rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
355
+ }
@@ -0,0 +1,299 @@
1
+ /*
2
+ * pg_type_map_in_ruby.c - PG::TypeMapInRuby class extension
3
+ * $Id: pg_type_map_in_ruby.c,v 3d89d3aae4fd 2015/01/05 16:19:41 kanis $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ VALUE rb_cTypeMapInRuby;
10
+ static VALUE s_id_fit_to_result;
11
+ static VALUE s_id_fit_to_query;
12
+ static VALUE s_id_fit_to_copy_get;
13
+ static VALUE s_id_typecast_result_value;
14
+ static VALUE s_id_typecast_query_param;
15
+ static VALUE s_id_typecast_copy_get;
16
+
17
+ typedef struct {
18
+ t_typemap typemap;
19
+ VALUE self;
20
+ } t_tmir;
21
+
22
+
23
+ /*
24
+ * call-seq:
25
+ * typemap.fit_to_result( result )
26
+ *
27
+ * Check that the type map fits to the result.
28
+ *
29
+ * This method is called, when a type map is assigned to a result.
30
+ * It must return a PG::TypeMap object or raise an Exception.
31
+ * This can be +self+ or some other type map that fits to the result.
32
+ *
33
+ */
34
+ static VALUE
35
+ pg_tmir_fit_to_result( VALUE self, VALUE result )
36
+ {
37
+ t_tmir *this = DATA_PTR( self );
38
+ t_typemap *default_tm;
39
+ t_typemap *p_new_typemap;
40
+ VALUE sub_typemap;
41
+ VALUE new_typemap;
42
+
43
+ if( rb_respond_to(self, s_id_fit_to_result) ){
44
+ new_typemap = rb_funcall( self, s_id_fit_to_result, 1, result );
45
+
46
+ if ( !rb_obj_is_kind_of(new_typemap, rb_cTypeMap) ) {
47
+ rb_raise( rb_eTypeError, "wrong return type from fit_to_result: %s expected kind of PG::TypeMap",
48
+ rb_obj_classname( new_typemap ) );
49
+ }
50
+ Check_Type( new_typemap, T_DATA );
51
+ } else {
52
+ new_typemap = self;
53
+ }
54
+
55
+ /* Ensure that the default type map fits equaly. */
56
+ default_tm = DATA_PTR( this->typemap.default_typemap );
57
+ sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
58
+
59
+ if( sub_typemap != this->typemap.default_typemap ){
60
+ new_typemap = rb_obj_dup( new_typemap );
61
+ }
62
+
63
+ p_new_typemap = DATA_PTR(new_typemap);
64
+ p_new_typemap->default_typemap = sub_typemap;
65
+ return new_typemap;
66
+ }
67
+
68
+ static VALUE
69
+ pg_tmir_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
70
+ {
71
+ t_tmir *this = (t_tmir *) p_typemap;
72
+
73
+ return rb_funcall( this->self, s_id_typecast_result_value, 3, result, INT2NUM(tuple), INT2NUM(field) );
74
+ }
75
+
76
+ /*
77
+ * call-seq:
78
+ * typemap.typecast_result_value( result, tuple, field )
79
+ *
80
+ * Retrieve and cast a field of the given result.
81
+ *
82
+ * This method implementation uses the #default_type_map to get the
83
+ * field value. It can be derived to change this behaviour.
84
+ *
85
+ * Parameters:
86
+ * * +result+ : The PG::Result received from the database.
87
+ * * +tuple+ : The row number to retrieve.
88
+ * * +field+ : The column number to retrieve.
89
+ *
90
+ * Note: Calling any value retrieving methods of +result+ will result
91
+ * in an (endless) recursion. Instead super() can be used to retrieve
92
+ * the value using the default_typemap.
93
+ *
94
+ */
95
+ static VALUE
96
+ pg_tmir_typecast_result_value( VALUE self, VALUE result, VALUE tuple, VALUE field )
97
+ {
98
+ t_tmir *this = DATA_PTR( self );
99
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
100
+ return default_tm->funcs.typecast_result_value( default_tm, result, NUM2INT(tuple), NUM2INT(field) );
101
+ }
102
+
103
+ /*
104
+ * call-seq:
105
+ * typemap.fit_to_query( params )
106
+ *
107
+ * Check that the type map fits to the given user values.
108
+ *
109
+ * This method is called, when a type map is used for sending a query
110
+ * and for encoding of copy data, before the value is casted.
111
+ *
112
+ */
113
+ static VALUE
114
+ pg_tmir_fit_to_query( VALUE self, VALUE params )
115
+ {
116
+ t_tmir *this = DATA_PTR( self );
117
+ t_typemap *default_tm;
118
+
119
+ if( rb_respond_to(self, s_id_fit_to_query) ){
120
+ rb_funcall( self, s_id_fit_to_query, 1, params );
121
+ }
122
+
123
+ /* Ensure that the default type map fits equaly. */
124
+ default_tm = DATA_PTR( this->typemap.default_typemap );
125
+ default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
126
+
127
+ return self;
128
+ }
129
+
130
+ static t_pg_coder *
131
+ pg_tmir_query_param( t_typemap *p_typemap, VALUE param_value, int field )
132
+ {
133
+ t_tmir *this = (t_tmir *) p_typemap;
134
+
135
+ VALUE coder = rb_funcall( this->self, s_id_typecast_query_param, 2, param_value, INT2NUM(field) );
136
+
137
+ if ( NIL_P(coder) ){
138
+ return NULL;
139
+ } else if( rb_obj_is_kind_of(coder, rb_cPG_Coder) ) {
140
+ return DATA_PTR(coder);
141
+ } else {
142
+ rb_raise( rb_eTypeError, "wrong return type from typecast_query_param: %s expected nil or kind of PG::Coder",
143
+ rb_obj_classname( coder ) );
144
+ }
145
+ }
146
+
147
+ /*
148
+ * call-seq:
149
+ * typemap.typecast_query_param( param_value, field )
150
+ *
151
+ * Cast a field string for transmission to the server.
152
+ *
153
+ * This method implementation uses the #default_type_map to cast param_value.
154
+ * It can be derived to change this behaviour.
155
+ *
156
+ * Parameters:
157
+ * * +param_value+ : The value from the user.
158
+ * * +field+ : The field number from left to right.
159
+ *
160
+ */
161
+ static VALUE
162
+ pg_tmir_typecast_query_param( VALUE self, VALUE param_value, VALUE field )
163
+ {
164
+ t_tmir *this = DATA_PTR( self );
165
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
166
+ t_pg_coder *p_coder = default_tm->funcs.typecast_query_param( default_tm, param_value, NUM2INT(field) );
167
+
168
+ return p_coder ? p_coder->coder_obj : Qnil;
169
+ }
170
+
171
+ /* This is to fool rdoc's C parser */
172
+ #if 0
173
+ /*
174
+ * call-seq:
175
+ * typemap.fit_to_copy_get()
176
+ *
177
+ * Check that the type map can be used for PG::Connection#get_copy_data.
178
+ *
179
+ * This method is called, when a type map is used for decoding copy data,
180
+ * before the value is casted.
181
+ *
182
+ */
183
+ static VALUE pg_tmir_fit_to_copy_get_dummy( VALUE self ){}
184
+ #endif
185
+
186
+ static int
187
+ pg_tmir_fit_to_copy_get( VALUE self )
188
+ {
189
+ t_tmir *this = DATA_PTR( self );
190
+ t_typemap *default_tm;
191
+ VALUE num_columns = INT2NUM(0);
192
+
193
+ if( rb_respond_to(self, s_id_fit_to_copy_get) ){
194
+ num_columns = rb_funcall( self, s_id_fit_to_copy_get, 0 );
195
+ }
196
+
197
+ if ( !rb_obj_is_kind_of(num_columns, rb_cInteger) ) {
198
+ rb_raise( rb_eTypeError, "wrong return type from fit_to_copy_get: %s expected kind of Integer",
199
+ rb_obj_classname( num_columns ) );
200
+ }
201
+ /* Ensure that the default type map fits equaly. */
202
+ default_tm = DATA_PTR( this->typemap.default_typemap );
203
+ default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
204
+
205
+ return NUM2INT(num_columns);;
206
+ }
207
+
208
+ static VALUE
209
+ pg_tmir_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
210
+ {
211
+ t_tmir *this = (t_tmir *) p_typemap;
212
+ rb_encoding *p_encoding = rb_enc_from_index(enc_idx);
213
+ VALUE enc = rb_enc_from_encoding(p_encoding);
214
+ /* field_str is reused in-place by pg_text_dec_copy_row(), so we need to make
215
+ * a copy of the string buffer for use in ruby space. */
216
+ VALUE field_str_copy = rb_str_dup(field_str);
217
+ rb_str_modify(field_str_copy);
218
+
219
+ return rb_funcall( this->self, s_id_typecast_copy_get, 4, field_str_copy, INT2NUM(fieldno), INT2NUM(format), enc );
220
+ }
221
+
222
+ /*
223
+ * call-seq:
224
+ * typemap.typecast_copy_get( field_str, fieldno, format, encoding )
225
+ *
226
+ * Cast a field string received by PG::Connection#get_copy_data.
227
+ *
228
+ * This method implementation uses the #default_type_map to cast field_str.
229
+ * It can be derived to change this behaviour.
230
+ *
231
+ * Parameters:
232
+ * * +field_str+ : The String received from the server.
233
+ * * +fieldno+ : The field number from left to right.
234
+ * * +format+ : The format code (0 = text, 1 = binary)
235
+ * * +encoding+ : The encoding of the connection and encoding the returned
236
+ * value should get.
237
+ *
238
+ */
239
+ static VALUE
240
+ pg_tmir_typecast_copy_get( VALUE self, VALUE field_str, VALUE fieldno, VALUE format, VALUE enc )
241
+ {
242
+ t_tmir *this = DATA_PTR( self );
243
+ t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
244
+ int enc_idx = rb_to_encoding_index( enc );
245
+
246
+ return default_tm->funcs.typecast_copy_get( default_tm, field_str, NUM2INT(fieldno), NUM2INT(format), enc_idx );
247
+ }
248
+
249
+ static VALUE
250
+ pg_tmir_s_allocate( VALUE klass )
251
+ {
252
+ t_tmir *this;
253
+ VALUE self;
254
+
255
+ self = Data_Make_Struct( klass, t_tmir, NULL, -1, this );
256
+
257
+ this->typemap.funcs.fit_to_result = pg_tmir_fit_to_result;
258
+ this->typemap.funcs.fit_to_query = pg_tmir_fit_to_query;
259
+ this->typemap.funcs.fit_to_copy_get = pg_tmir_fit_to_copy_get;
260
+ this->typemap.funcs.typecast_result_value = pg_tmir_result_value;
261
+ this->typemap.funcs.typecast_query_param = pg_tmir_query_param;
262
+ this->typemap.funcs.typecast_copy_get = pg_tmir_copy_get;
263
+ this->typemap.default_typemap = pg_typemap_all_strings;
264
+ this->self = self;
265
+
266
+ return self;
267
+ }
268
+
269
+
270
+ void
271
+ init_pg_type_map_in_ruby()
272
+ {
273
+ s_id_fit_to_result = rb_intern("fit_to_result");
274
+ s_id_fit_to_query = rb_intern("fit_to_query");
275
+ s_id_fit_to_copy_get = rb_intern("fit_to_copy_get");
276
+ s_id_typecast_result_value = rb_intern("typecast_result_value");
277
+ s_id_typecast_query_param = rb_intern("typecast_query_param");
278
+ s_id_typecast_copy_get = rb_intern("typecast_copy_get");
279
+
280
+ /*
281
+ * Document-class: PG::TypeMapInRuby < PG::TypeMap
282
+ *
283
+ * This class can be used to implement a type map in ruby, typically as a
284
+ * #default_type_map in a type map chain.
285
+ *
286
+ * This API is EXPERIMENTAL and could change in the future.
287
+ *
288
+ */
289
+ rb_cTypeMapInRuby = rb_define_class_under( rb_mPG, "TypeMapInRuby", rb_cTypeMap );
290
+ rb_define_alloc_func( rb_cTypeMapInRuby, pg_tmir_s_allocate );
291
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_result", pg_tmir_fit_to_result, 1 ); */
292
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_query", pg_tmir_fit_to_query, 1 ); */
293
+ /* rb_define_method( rb_cTypeMapInRuby, "fit_to_copy_get", pg_tmir_fit_to_copy_get_dummy, 0 ); */
294
+ rb_define_method( rb_cTypeMapInRuby, "typecast_result_value", pg_tmir_typecast_result_value, 3 );
295
+ rb_define_method( rb_cTypeMapInRuby, "typecast_query_param", pg_tmir_typecast_query_param, 2 );
296
+ rb_define_method( rb_cTypeMapInRuby, "typecast_copy_get", pg_tmir_typecast_copy_get, 4 );
297
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
298
+ rb_include_module( rb_cTypeMapInRuby, rb_mDefaultTypeMappable );
299
+ }