pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +1573 -2
  5. data/History.rdoc +3 -11
  6. data/Manifest.txt +24 -0
  7. data/README.rdoc +51 -4
  8. data/Rakefile +20 -14
  9. data/Rakefile.cross +39 -32
  10. data/ext/extconf.rb +27 -26
  11. data/ext/pg.c +75 -21
  12. data/ext/pg.h +194 -6
  13. data/ext/pg_binary_decoder.c +160 -0
  14. data/ext/pg_binary_encoder.c +160 -0
  15. data/ext/pg_coder.c +454 -0
  16. data/ext/pg_connection.c +815 -518
  17. data/ext/pg_copy_coder.c +557 -0
  18. data/ext/pg_result.c +258 -103
  19. data/ext/pg_text_decoder.c +424 -0
  20. data/ext/pg_text_encoder.c +608 -0
  21. data/ext/pg_type_map.c +113 -0
  22. data/ext/pg_type_map_all_strings.c +113 -0
  23. data/ext/pg_type_map_by_column.c +254 -0
  24. data/ext/pg_type_map_by_mri_type.c +266 -0
  25. data/ext/pg_type_map_by_oid.c +341 -0
  26. data/ext/util.c +121 -0
  27. data/ext/util.h +63 -0
  28. data/lib/pg.rb +11 -1
  29. data/lib/pg/basic_type_mapping.rb +377 -0
  30. data/lib/pg/coder.rb +74 -0
  31. data/lib/pg/connection.rb +38 -5
  32. data/lib/pg/result.rb +13 -3
  33. data/lib/pg/text_decoder.rb +42 -0
  34. data/lib/pg/text_encoder.rb +27 -0
  35. data/lib/pg/type_map_by_column.rb +15 -0
  36. data/spec/helpers.rb +9 -1
  37. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  38. data/spec/pg/connection_spec.rb +232 -13
  39. data/spec/pg/result_spec.rb +52 -0
  40. data/spec/pg/type_map_by_column_spec.rb +135 -0
  41. data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
  42. data/spec/pg/type_map_by_oid_spec.rb +133 -0
  43. data/spec/pg/type_map_spec.rb +39 -0
  44. data/spec/pg/type_spec.rb +620 -0
  45. metadata +40 -4
  46. metadata.gz.sig +0 -0
@@ -0,0 +1,266 @@
1
+ /*
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 $
4
+ *
5
+ * This type map can be used to select value encoders based on the MRI-internal
6
+ * value type code.
7
+ *
8
+ * This class is planned to get replaced by a PG::TypeMapByClass implementation.
9
+ *
10
+ */
11
+
12
+ #include "pg.h"
13
+
14
+ static VALUE rb_cTypeMapByMriType;
15
+
16
+ #define FOR_EACH_MRI_TYPE(func) \
17
+ func(T_FIXNUM) \
18
+ func(T_TRUE) \
19
+ func(T_FALSE) \
20
+ func(T_FLOAT) \
21
+ func(T_BIGNUM) \
22
+ func(T_COMPLEX) \
23
+ func(T_RATIONAL) \
24
+ func(T_ARRAY) \
25
+ func(T_STRING) \
26
+ func(T_SYMBOL) \
27
+ func(T_OBJECT) \
28
+ func(T_CLASS) \
29
+ func(T_MODULE) \
30
+ func(T_REGEXP) \
31
+ func(T_HASH) \
32
+ func(T_STRUCT) \
33
+ func(T_FILE) \
34
+ func(T_DATA)
35
+
36
+ #define DECLARE_CODER(type) \
37
+ t_pg_coder *coder_##type; \
38
+ VALUE ask_##type;
39
+
40
+ typedef struct {
41
+ t_typemap typemap;
42
+ struct pg_tmbmt_converter {
43
+ FOR_EACH_MRI_TYPE( DECLARE_CODER );
44
+ } coders;
45
+ } t_tmbmt;
46
+
47
+
48
+ #define CASE_AND_GET(type) \
49
+ case type: \
50
+ p_coder = this->coders.coder_##type; \
51
+ ask_for_coder = this->coders.ask_##type; \
52
+ break;
53
+
54
+ static t_pg_coder *
55
+ pg_tmbmt_typecast_query_param(VALUE self, VALUE param_value, int field)
56
+ {
57
+ t_tmbmt *this = (t_tmbmt *)DATA_PTR(self);
58
+ t_pg_coder *p_coder;
59
+ VALUE ask_for_coder;
60
+
61
+ switch(TYPE(param_value)){
62
+ FOR_EACH_MRI_TYPE( CASE_AND_GET )
63
+ default:
64
+ /* unknown MRI type */
65
+ p_coder = NULL;
66
+ ask_for_coder = Qnil;
67
+ }
68
+
69
+ if( !NIL_P(ask_for_coder) ){
70
+ /* No static Coder object, but proc/method given to ask for the Coder to use. */
71
+ VALUE obj;
72
+
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
+ }
78
+
79
+ if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
80
+ Data_Get_Struct(obj, t_pg_coder, p_coder);
81
+ }else{
82
+ rb_raise(rb_eTypeError, "argument %d has invalid type %s (should be nil or some kind of PG::Coder)",
83
+ field+1, rb_obj_classname( obj ));
84
+ }
85
+ }
86
+
87
+ return p_coder;
88
+ }
89
+
90
+ static VALUE
91
+ pg_tmbmt_fit_to_query( VALUE self, VALUE params )
92
+ {
93
+ return self;
94
+ }
95
+
96
+ #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 );
99
+
100
+ static void
101
+ pg_tmbmt_mark( t_tmbmt *this )
102
+ {
103
+ FOR_EACH_MRI_TYPE( GC_MARK_AS_USED );
104
+ }
105
+
106
+ #define INIT_VARIABLES(type) \
107
+ this->coders.coder_##type = NULL; \
108
+ this->coders.ask_##type = Qnil;
109
+
110
+ static VALUE
111
+ pg_tmbmt_s_allocate( VALUE klass )
112
+ {
113
+ t_tmbmt *this;
114
+ VALUE self;
115
+
116
+ 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
+
124
+ FOR_EACH_MRI_TYPE( INIT_VARIABLES );
125
+
126
+ return self;
127
+ }
128
+
129
+ #define COMPARE_AND_ASSIGN(type) \
130
+ else if(!strcmp(p_mri_type, #type)){ \
131
+ if(NIL_P(coder)){ \
132
+ this->coders.coder_##type = NULL; \
133
+ this->coders.ask_##type = Qnil; \
134
+ }else if(rb_obj_is_kind_of(coder, rb_cPG_Coder)){ \
135
+ Data_Get_Struct(coder, t_pg_coder, this->coders.coder_##type); \
136
+ this->coders.ask_##type = Qnil; \
137
+ }else{ \
138
+ this->coders.coder_##type = NULL; \
139
+ this->coders.ask_##type = coder; \
140
+ } \
141
+ }
142
+
143
+ /*
144
+ * call-seq:
145
+ * typemap.[mri_type] = coder
146
+ *
147
+ * Assigns a new PG::Coder object to the type map. The decoder
148
+ * is registered for type casts of the given +mri_type+ .
149
+ *
150
+ * +coder+ can be one of the following:
151
+ * * +nil+ - Values are encoded by +#to_str+
152
+ * * a PG::Coder - Values are encoded by the given encoder
153
+ * * a Symbol - The method of this type map (or a derivation) that is called for each value to sent.
154
+ * It must return a PG::Coder.
155
+ * * a Proc - The Proc object is called for each value. It must return a PG::Coder.
156
+ *
157
+ * +mri_type+ must be one of the following strings:
158
+ * * +T_FIXNUM+
159
+ * * +T_TRUE+
160
+ * * +T_FALSE+
161
+ * * +T_FLOAT+
162
+ * * +T_BIGNUM+
163
+ * * +T_COMPLEX+
164
+ * * +T_RATIONAL+
165
+ * * +T_ARRAY+
166
+ * * +T_STRING+
167
+ * * +T_SYMBOL+
168
+ * * +T_OBJECT+
169
+ * * +T_CLASS+
170
+ * * +T_MODULE+
171
+ * * +T_REGEXP+
172
+ * * +T_HASH+
173
+ * * +T_STRUCT+
174
+ * * +T_FILE+
175
+ * * +T_DATA+
176
+ */
177
+ static VALUE
178
+ pg_tmbmt_aset( VALUE self, VALUE mri_type, VALUE coder )
179
+ {
180
+ t_tmbmt *this = DATA_PTR( self );
181
+ char *p_mri_type;
182
+
183
+ p_mri_type = StringValueCStr(mri_type);
184
+
185
+ if(0){}
186
+ FOR_EACH_MRI_TYPE( COMPARE_AND_ASSIGN )
187
+ else{
188
+ VALUE mri_type_inspect = rb_inspect( mri_type );
189
+ rb_raise(rb_eArgError, "unknown mri_type %s", StringValueCStr(mri_type_inspect));
190
+ }
191
+
192
+ return self;
193
+ }
194
+
195
+ #define COMPARE_AND_GET(type) \
196
+ else if(!strcmp(p_mri_type, #type)){ \
197
+ coder = this->coders.coder_##type ? this->coders.coder_##type->coder_obj : this->coders.ask_##type; \
198
+ }
199
+
200
+ /*
201
+ * call-seq:
202
+ * typemap.[mri_type] -> coder
203
+ *
204
+ * Returns the encoder object for the given +mri_type+
205
+ *
206
+ * See #[]= for allowed +mri_type+ .
207
+ */
208
+ static VALUE
209
+ pg_tmbmt_aref( VALUE self, VALUE mri_type )
210
+ {
211
+ VALUE coder;
212
+ t_tmbmt *this = DATA_PTR( self );
213
+ char *p_mri_type;
214
+
215
+ p_mri_type = StringValueCStr(mri_type);
216
+
217
+ if(0){}
218
+ FOR_EACH_MRI_TYPE( COMPARE_AND_GET )
219
+ else{
220
+ VALUE mri_type_inspect = rb_inspect( mri_type );
221
+ rb_raise(rb_eArgError, "unknown mri_type %s", StringValueCStr(mri_type_inspect));
222
+ }
223
+
224
+ return coder;
225
+ }
226
+
227
+ #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 );
229
+
230
+
231
+ /*
232
+ * call-seq:
233
+ * typemap.coders -> Hash
234
+ *
235
+ * Returns all mri types and their assigned encoder object.
236
+ */
237
+ static VALUE
238
+ pg_tmbmt_coders( VALUE self )
239
+ {
240
+ t_tmbmt *this = DATA_PTR( self );
241
+ VALUE hash_coders = rb_hash_new();
242
+
243
+ FOR_EACH_MRI_TYPE( ADD_TO_HASH );
244
+
245
+ return rb_obj_freeze(hash_coders);
246
+ }
247
+
248
+ void
249
+ init_pg_type_map_by_mri_type()
250
+ {
251
+ /*
252
+ * Document-class: PG::TypeMapByMriType < PG::TypeMap
253
+ *
254
+ * This type map casts values based on the Ruby object type code of the given value
255
+ * to be sent.
256
+ *
257
+ * This type map is usable for type casting query bind parameters and COPY data
258
+ * for PG::Connection#put_copy_data . Therefore only encoders might be assigned by
259
+ * the #[]= method.
260
+ */
261
+ rb_cTypeMapByMriType = rb_define_class_under( rb_mPG, "TypeMapByMriType", rb_cTypeMap );
262
+ rb_define_alloc_func( rb_cTypeMapByMriType, pg_tmbmt_s_allocate );
263
+ rb_define_method( rb_cTypeMapByMriType, "[]=", pg_tmbmt_aset, 2 );
264
+ rb_define_method( rb_cTypeMapByMriType, "[]", pg_tmbmt_aref, 1 );
265
+ rb_define_method( rb_cTypeMapByMriType, "coders", pg_tmbmt_coders, 0 );
266
+ }
@@ -0,0 +1,341 @@
1
+ /*
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 $
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
+ /*
27
+ * We use the OID's minor 8 Bits as index to a 256 entry cache. This avoids full ruby hash lookups
28
+ * for each value in most cases.
29
+ */
30
+ #define CACHE_LOOKUP(this, form, oid) ( &this->format[(form)].cache_row[(oid) & 0xff] )
31
+
32
+ static t_pg_coder *
33
+ pg_tmbo_lookup_oid(t_tmbo *this, int format, Oid oid)
34
+ {
35
+ t_pg_coder *conv;
36
+ struct pg_tmbo_oid_cache_entry *p_ce;
37
+
38
+ p_ce = CACHE_LOOKUP(this, format, oid);
39
+
40
+ /* Has the entry the expected OID and is it a non empty entry? */
41
+ if( p_ce->oid == oid && (oid || p_ce->p_coder) ) {
42
+ conv = p_ce->p_coder;
43
+ } else {
44
+ VALUE obj = rb_hash_lookup( this->format[format].oid_to_coder, UINT2NUM( oid ));
45
+ /* obj must be nil or some kind of PG::Coder, this is checked at insertion */
46
+ conv = NIL_P(obj) ? NULL : DATA_PTR(obj);
47
+ /* Write the retrieved coder to the cache */
48
+ p_ce->oid = oid;
49
+ p_ce->p_coder = conv;
50
+ }
51
+ return conv;
52
+ }
53
+
54
+ /* Build a TypeMapByColumn that fits to the given result */
55
+ static VALUE
56
+ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
57
+ {
58
+ t_tmbc *p_colmap;
59
+ int i;
60
+ VALUE colmap;
61
+ int nfields = PQnfields( pgresult );
62
+
63
+ p_colmap = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields);
64
+ /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
65
+ p_colmap->nfields = 0;
66
+ p_colmap->typemap = pg_tmbc_default_typemap;
67
+
68
+ colmap = pg_tmbc_allocate();
69
+ DATA_PTR(colmap) = p_colmap;
70
+
71
+ for(i=0; i<nfields; i++)
72
+ {
73
+ int format = PQfformat(pgresult, i);
74
+
75
+ if( format < 0 || format > 1 )
76
+ rb_raise(rb_eArgError, "result field %d has unsupported format code %d", i+1, format);
77
+
78
+ p_colmap->convs[i].cconv = pg_tmbo_lookup_oid( this, format, PQftype(pgresult, i) );
79
+ }
80
+
81
+ p_colmap->nfields = nfields;
82
+
83
+ return colmap;
84
+ }
85
+
86
+ static VALUE
87
+ pg_tmbo_result_value(VALUE result, int tuple, int field)
88
+ {
89
+ char * val;
90
+ int len;
91
+ int format;
92
+ t_pg_coder *p_coder;
93
+ t_pg_coder_dec_func dec_func;
94
+ t_pg_result *p_result = pgresult_get_this(result);
95
+ t_tmbo *this = (t_tmbo*) p_result->p_typemap;
96
+
97
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
98
+ return Qnil;
99
+ }
100
+
101
+ val = PQgetvalue( p_result->pgresult, tuple, field );
102
+ len = PQgetlength( p_result->pgresult, tuple, field );
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
+ dec_func = pg_coder_dec_func( p_coder, format );
110
+ return dec_func( p_coder, val, len, tuple, field, ENCODING_GET(result) );
111
+ }
112
+
113
+ static VALUE
114
+ pg_tmbo_fit_to_result( VALUE self, VALUE result )
115
+ {
116
+ t_tmbo *this = DATA_PTR( self );
117
+ PGresult *pgresult = pgresult_get( result );
118
+
119
+ if( PQntuples( pgresult ) <= this->max_rows_for_online_lookup ){
120
+ /* Do a hash lookup for each result value in pg_tmbc_result_value() */
121
+ return self;
122
+ }else{
123
+ /* Build a new TypeMapByColumn that fits to the given result and
124
+ * uses a fast array lookup.
125
+ */
126
+ return pg_tmbo_build_type_map_for_result2( this, pgresult );
127
+ }
128
+ }
129
+
130
+ static void
131
+ pg_tmbo_mark( t_tmbo *this )
132
+ {
133
+ int i;
134
+
135
+ for( i=0; i<2; i++){
136
+ rb_gc_mark(this->format[i].oid_to_coder);
137
+ }
138
+ }
139
+
140
+ static VALUE
141
+ pg_tmbo_s_allocate( VALUE klass )
142
+ {
143
+ t_tmbo *this;
144
+ VALUE self;
145
+ int i;
146
+
147
+ self = Data_Make_Struct( klass, t_tmbo, pg_tmbo_mark, -1, this );
148
+
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;
155
+ this->max_rows_for_online_lookup = 10;
156
+
157
+ for( i=0; i<2; i++){
158
+ this->format[i].oid_to_coder = rb_hash_new();
159
+ }
160
+
161
+ return self;
162
+ }
163
+
164
+ /*
165
+ * call-seq:
166
+ * typemap.add_coder( coder )
167
+ *
168
+ * Assigns a new PG::Coder object to the type map. The decoder
169
+ * is registered for type casts based on it's PG::Coder#oid and
170
+ * PG::Coder#format attributes.
171
+ *
172
+ * Later changes of the oid or format code within the coder object
173
+ * will have no effect to the type map.
174
+ *
175
+ */
176
+ static VALUE
177
+ pg_tmbo_add_coder( VALUE self, VALUE coder )
178
+ {
179
+ VALUE hash;
180
+ t_tmbo *this = DATA_PTR( self );
181
+ t_pg_coder *p_coder;
182
+ struct pg_tmbo_oid_cache_entry *p_ce;
183
+
184
+ if( !rb_obj_is_kind_of(coder, rb_cPG_Coder) )
185
+ rb_raise(rb_eArgError, "invalid type %s (should be some kind of PG::Coder)",
186
+ rb_obj_classname( coder ));
187
+
188
+ Data_Get_Struct(coder, t_pg_coder, p_coder);
189
+
190
+ if( p_coder->format < 0 || p_coder->format > 1 )
191
+ rb_raise(rb_eArgError, "invalid format code %d", p_coder->format);
192
+
193
+ /* Update cache entry */
194
+ p_ce = CACHE_LOOKUP(this, p_coder->format, p_coder->oid);
195
+ p_ce->oid = p_coder->oid;
196
+ p_ce->p_coder = p_coder;
197
+ /* Write coder into the hash of the given format */
198
+ hash = this->format[p_coder->format].oid_to_coder;
199
+ rb_hash_aset( hash, UINT2NUM(p_coder->oid), coder);
200
+
201
+ return self;
202
+ }
203
+
204
+ /*
205
+ * call-seq:
206
+ * typemap.rm_coder( format, oid )
207
+ *
208
+ * Removes a PG::Coder object from the type map based on the given
209
+ * oid and format codes.
210
+ *
211
+ * Returns the removed coder object.
212
+ */
213
+ static VALUE
214
+ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
215
+ {
216
+ VALUE hash;
217
+ VALUE coder;
218
+ t_tmbo *this = DATA_PTR( self );
219
+ int i_format = NUM2INT(format);
220
+ struct pg_tmbo_oid_cache_entry *p_ce;
221
+
222
+ if( i_format < 0 || i_format > 1 )
223
+ rb_raise(rb_eArgError, "invalid format code %d", i_format);
224
+
225
+ /* Mark the cache entry as empty */
226
+ p_ce = CACHE_LOOKUP(this, i_format, NUM2UINT(oid));
227
+ p_ce->oid = 0;
228
+ p_ce->p_coder = NULL;
229
+ hash = this->format[i_format].oid_to_coder;
230
+ coder = rb_hash_delete( hash, oid );
231
+
232
+ return coder;
233
+ }
234
+
235
+ /*
236
+ * call-seq:
237
+ * typemap.coders -> Array
238
+ *
239
+ * Array of all assigned PG::Coder objects.
240
+ */
241
+ static VALUE
242
+ pg_tmbo_coders( VALUE self )
243
+ {
244
+ t_tmbo *this = DATA_PTR( self );
245
+
246
+ return rb_ary_concat(
247
+ rb_funcall(this->format[0].oid_to_coder, rb_intern("values"), 0),
248
+ rb_funcall(this->format[1].oid_to_coder, rb_intern("values"), 0));
249
+ }
250
+
251
+ /*
252
+ * call-seq:
253
+ * typemap.max_rows_for_online_lookup = number
254
+ *
255
+ * Threshold for doing Hash lookups versus creation of a dedicated PG::TypeMapByColumn.
256
+ * The type map will do Hash lookups for each result value, if the number of rows
257
+ * is below or equal +number+.
258
+ *
259
+ */
260
+ static VALUE
261
+ pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
262
+ {
263
+ t_tmbo *this = DATA_PTR( self );
264
+ this->max_rows_for_online_lookup = NUM2INT(value);
265
+ return value;
266
+ }
267
+
268
+ /*
269
+ * call-seq:
270
+ * typemap.max_rows_for_online_lookup -> Integer
271
+ */
272
+ static VALUE
273
+ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
274
+ {
275
+ t_tmbo *this = DATA_PTR( self );
276
+ return INT2NUM(this->max_rows_for_online_lookup);
277
+ }
278
+
279
+ /*
280
+ * call-seq:
281
+ * typemap.fit_to_result( result, online_lookup = nil )
282
+ *
283
+ * This is an extended version of PG::TypeMap#fit_to_result that
284
+ * allows explicit selection of online lookup ( online_lookup=true )
285
+ * or building of a new PG::TypeMapByColumn ( online_lookup=false ).
286
+ *
287
+ *
288
+ */
289
+ static VALUE
290
+ pg_tmbo_fit_to_result_ext( int argc, VALUE *argv, VALUE self )
291
+ {
292
+ t_tmbo *this = DATA_PTR( self );
293
+ VALUE result;
294
+ VALUE online_lookup;
295
+
296
+ rb_scan_args(argc, argv, "11", &result, &online_lookup);
297
+
298
+ if ( !rb_obj_is_kind_of(result, rb_cPGresult) ) {
299
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Result)",
300
+ rb_obj_classname( result ) );
301
+ }
302
+
303
+ if( NIL_P( online_lookup ) ){
304
+ /* call super */
305
+ return this->typemap.fit_to_result(self, result);
306
+ } else if( RB_TYPE_P( online_lookup, T_TRUE ) ){
307
+ return self;
308
+ } else if( RB_TYPE_P( online_lookup, T_FALSE ) ){
309
+ PGresult *pgresult = pgresult_get( result );
310
+
311
+ return pg_tmbo_build_type_map_for_result2( this, pgresult );
312
+ } else {
313
+ rb_raise( rb_eArgError, "argument online_lookup %s should be true, false or nil instead",
314
+ rb_obj_classname( result ) );
315
+ }
316
+ }
317
+
318
+
319
+ void
320
+ init_pg_type_map_by_oid()
321
+ {
322
+ s_id_decode = rb_intern("decode");
323
+
324
+ /*
325
+ * Document-class: PG::TypeMapByOid < PG::TypeMap
326
+ *
327
+ * This type map casts values based on the type OID of the given column
328
+ * in the result set.
329
+ *
330
+ * This type map is only suitable to cast values from PG::Result objects.
331
+ * Therefore only decoders might be assigned by the #add_coder method.
332
+ */
333
+ rb_cTypeMapByOid = rb_define_class_under( rb_mPG, "TypeMapByOid", rb_cTypeMap );
334
+ rb_define_alloc_func( rb_cTypeMapByOid, pg_tmbo_s_allocate );
335
+ rb_define_method( rb_cTypeMapByOid, "add_coder", pg_tmbo_add_coder, 1 );
336
+ rb_define_method( rb_cTypeMapByOid, "rm_coder", pg_tmbo_rm_coder, 2 );
337
+ rb_define_method( rb_cTypeMapByOid, "coders", pg_tmbo_coders, 0 );
338
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
339
+ rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
340
+ rb_define_method( rb_cTypeMapByOid, "fit_to_result", pg_tmbo_fit_to_result_ext, -1 );
341
+ }