pg 1.1.4

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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gemtest +0 -0
  5. data/BSDL +22 -0
  6. data/ChangeLog +6595 -0
  7. data/Contributors.rdoc +46 -0
  8. data/History.rdoc +492 -0
  9. data/LICENSE +56 -0
  10. data/Manifest.txt +72 -0
  11. data/POSTGRES +23 -0
  12. data/README-OS_X.rdoc +68 -0
  13. data/README-Windows.rdoc +56 -0
  14. data/README.ja.rdoc +14 -0
  15. data/README.rdoc +178 -0
  16. data/Rakefile +215 -0
  17. data/Rakefile.cross +298 -0
  18. data/ext/errorcodes.def +968 -0
  19. data/ext/errorcodes.rb +45 -0
  20. data/ext/errorcodes.txt +478 -0
  21. data/ext/extconf.rb +94 -0
  22. data/ext/gvl_wrappers.c +17 -0
  23. data/ext/gvl_wrappers.h +241 -0
  24. data/ext/pg.c +640 -0
  25. data/ext/pg.h +365 -0
  26. data/ext/pg_binary_decoder.c +229 -0
  27. data/ext/pg_binary_encoder.c +162 -0
  28. data/ext/pg_coder.c +549 -0
  29. data/ext/pg_connection.c +4252 -0
  30. data/ext/pg_copy_coder.c +596 -0
  31. data/ext/pg_errors.c +95 -0
  32. data/ext/pg_result.c +1501 -0
  33. data/ext/pg_text_decoder.c +981 -0
  34. data/ext/pg_text_encoder.c +682 -0
  35. data/ext/pg_tuple.c +541 -0
  36. data/ext/pg_type_map.c +166 -0
  37. data/ext/pg_type_map_all_strings.c +116 -0
  38. data/ext/pg_type_map_by_class.c +239 -0
  39. data/ext/pg_type_map_by_column.c +312 -0
  40. data/ext/pg_type_map_by_mri_type.c +284 -0
  41. data/ext/pg_type_map_by_oid.c +355 -0
  42. data/ext/pg_type_map_in_ruby.c +299 -0
  43. data/ext/util.c +149 -0
  44. data/ext/util.h +65 -0
  45. data/ext/vc/pg.sln +26 -0
  46. data/ext/vc/pg_18/pg.vcproj +216 -0
  47. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  48. data/lib/pg.rb +74 -0
  49. data/lib/pg/basic_type_mapping.rb +459 -0
  50. data/lib/pg/binary_decoder.rb +22 -0
  51. data/lib/pg/coder.rb +83 -0
  52. data/lib/pg/connection.rb +291 -0
  53. data/lib/pg/constants.rb +11 -0
  54. data/lib/pg/exceptions.rb +11 -0
  55. data/lib/pg/result.rb +31 -0
  56. data/lib/pg/text_decoder.rb +47 -0
  57. data/lib/pg/text_encoder.rb +69 -0
  58. data/lib/pg/tuple.rb +30 -0
  59. data/lib/pg/type_map_by_column.rb +15 -0
  60. data/spec/data/expected_trace.out +26 -0
  61. data/spec/data/random_binary_data +0 -0
  62. data/spec/helpers.rb +380 -0
  63. data/spec/pg/basic_type_mapping_spec.rb +508 -0
  64. data/spec/pg/connection_spec.rb +1872 -0
  65. data/spec/pg/connection_sync_spec.rb +41 -0
  66. data/spec/pg/result_spec.rb +491 -0
  67. data/spec/pg/tuple_spec.rb +280 -0
  68. data/spec/pg/type_map_by_class_spec.rb +138 -0
  69. data/spec/pg/type_map_by_column_spec.rb +222 -0
  70. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  71. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  72. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  73. data/spec/pg/type_map_spec.rb +22 -0
  74. data/spec/pg/type_spec.rb +949 -0
  75. data/spec/pg_spec.rb +50 -0
  76. metadata +322 -0
  77. 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
+ }