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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +1573 -2
- data/History.rdoc +3 -11
- data/Manifest.txt +24 -0
- data/README.rdoc +51 -4
- data/Rakefile +20 -14
- data/Rakefile.cross +39 -32
- data/ext/extconf.rb +27 -26
- data/ext/pg.c +75 -21
- data/ext/pg.h +194 -6
- data/ext/pg_binary_decoder.c +160 -0
- data/ext/pg_binary_encoder.c +160 -0
- data/ext/pg_coder.c +454 -0
- data/ext/pg_connection.c +815 -518
- data/ext/pg_copy_coder.c +557 -0
- data/ext/pg_result.c +258 -103
- data/ext/pg_text_decoder.c +424 -0
- data/ext/pg_text_encoder.c +608 -0
- data/ext/pg_type_map.c +113 -0
- data/ext/pg_type_map_all_strings.c +113 -0
- data/ext/pg_type_map_by_column.c +254 -0
- data/ext/pg_type_map_by_mri_type.c +266 -0
- data/ext/pg_type_map_by_oid.c +341 -0
- data/ext/util.c +121 -0
- data/ext/util.h +63 -0
- data/lib/pg.rb +11 -1
- data/lib/pg/basic_type_mapping.rb +377 -0
- data/lib/pg/coder.rb +74 -0
- data/lib/pg/connection.rb +38 -5
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +42 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/spec/helpers.rb +9 -1
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +232 -13
- data/spec/pg/result_spec.rb +52 -0
- data/spec/pg/type_map_by_column_spec.rb +135 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
- data/spec/pg/type_map_by_oid_spec.rb +133 -0
- data/spec/pg/type_map_spec.rb +39 -0
- data/spec/pg/type_spec.rb +620 -0
- metadata +40 -4
- 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
|
+
}
|