pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815

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 (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
+ }