pg 0.18.0.pre20141017160319-x64-mingw32 → 0.18.0.pre20141117110243-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +317 -235
- data/History.rdoc +15 -0
- data/Manifest.txt +2 -0
- data/README.rdoc +1 -1
- data/ext/extconf.rb +2 -0
- data/ext/pg.c +66 -0
- data/ext/pg.h +50 -13
- data/ext/pg_binary_decoder.c +3 -3
- data/ext/pg_coder.c +6 -0
- data/ext/pg_connection.c +49 -54
- data/ext/pg_copy_coder.c +13 -29
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +272 -76
- data/ext/pg_text_encoder.c +43 -0
- data/ext/pg_type_map.c +84 -13
- data/ext/pg_type_map_all_strings.c +15 -12
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +80 -22
- data/ext/pg_type_map_by_mri_type.c +41 -23
- data/ext/pg_type_map_by_oid.c +51 -19
- data/lib/2.0/pg_ext.so +0 -0
- data/lib/2.1/pg_ext.so +0 -0
- data/lib/pg.rb +2 -2
- data/lib/pg/basic_type_mapping.rb +13 -13
- data/lib/pg/coder.rb +9 -0
- data/lib/x64-mingw32/libpq.dll +0 -0
- data/spec/helpers.rb +5 -3
- data/spec/pg/basic_type_mapping_spec.rb +1 -1
- data/spec/pg/connection_spec.rb +16 -5
- data/spec/pg/result_spec.rb +77 -3
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +87 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +14 -0
- data/spec/pg/type_map_by_oid_spec.rb +21 -0
- data/spec/pg/type_spec.rb +27 -7
- data/spec/pg_spec.rb +14 -0
- metadata +24 -21
- metadata.gz.sig +0 -0
data/ext/pg_type_map_by_column.c
CHANGED
@@ -10,18 +10,41 @@ static VALUE rb_cTypeMapByColumn;
|
|
10
10
|
static ID s_id_decode;
|
11
11
|
static ID s_id_encode;
|
12
12
|
|
13
|
+
static VALUE pg_tmbc_s_allocate( VALUE klass );
|
14
|
+
|
13
15
|
static VALUE
|
14
16
|
pg_tmbc_fit_to_result( VALUE self, VALUE result )
|
15
17
|
{
|
16
18
|
int nfields;
|
17
19
|
t_tmbc *this = DATA_PTR( self );
|
20
|
+
t_typemap *default_tm;
|
21
|
+
VALUE sub_typemap;
|
18
22
|
|
19
23
|
nfields = PQnfields( pgresult_get(result) );
|
20
24
|
if ( this->nfields != nfields ) {
|
21
25
|
rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
|
22
26
|
nfields, this->nfields );
|
23
27
|
}
|
24
|
-
|
28
|
+
|
29
|
+
/* Ensure that the default type map fits equaly. */
|
30
|
+
default_tm = DATA_PTR( this->typemap.default_typemap );
|
31
|
+
sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
|
32
|
+
|
33
|
+
/* Did the default type return the same object ? */
|
34
|
+
if( sub_typemap == this->typemap.default_typemap ){
|
35
|
+
return self;
|
36
|
+
} else {
|
37
|
+
/* Our default type map built a new object, so we need to propagate it
|
38
|
+
* and build a copy of this type map and set it as default there.. */
|
39
|
+
VALUE new_typemap = pg_tmbc_s_allocate( rb_cTypeMapByColumn );
|
40
|
+
size_t struct_size = sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields;
|
41
|
+
t_tmbc *p_new_typemap = (t_tmbc *)xmalloc(struct_size);
|
42
|
+
|
43
|
+
memcpy( p_new_typemap, this, struct_size );
|
44
|
+
p_new_typemap->typemap.default_typemap = sub_typemap;
|
45
|
+
DATA_PTR(new_typemap) = p_new_typemap;
|
46
|
+
return new_typemap;
|
47
|
+
}
|
25
48
|
}
|
26
49
|
|
27
50
|
static VALUE
|
@@ -29,12 +52,18 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
|
|
29
52
|
{
|
30
53
|
int nfields;
|
31
54
|
t_tmbc *this = DATA_PTR( self );
|
55
|
+
t_typemap *default_tm;
|
32
56
|
|
33
57
|
nfields = (int)RARRAY_LEN( params );
|
34
58
|
if ( this->nfields != nfields ) {
|
35
59
|
rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
|
36
60
|
nfields, this->nfields );
|
37
61
|
}
|
62
|
+
|
63
|
+
/* Ensure that the default type map fits equaly. */
|
64
|
+
default_tm = DATA_PTR( this->typemap.default_typemap );
|
65
|
+
default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
|
66
|
+
|
38
67
|
return self;
|
39
68
|
}
|
40
69
|
|
@@ -42,47 +71,59 @@ static int
|
|
42
71
|
pg_tmbc_fit_to_copy_get( VALUE self )
|
43
72
|
{
|
44
73
|
t_tmbc *this = DATA_PTR( self );
|
74
|
+
|
75
|
+
/* Ensure that the default type map fits equaly. */
|
76
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
77
|
+
default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
|
78
|
+
|
45
79
|
return this->nfields;
|
46
80
|
}
|
47
81
|
|
48
82
|
|
49
83
|
VALUE
|
50
|
-
pg_tmbc_result_value(VALUE result, int tuple, int field)
|
84
|
+
pg_tmbc_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
|
51
85
|
{
|
52
|
-
char * val;
|
53
|
-
int len;
|
54
86
|
t_pg_coder *p_coder = NULL;
|
55
|
-
t_pg_coder_dec_func dec_func;
|
56
87
|
t_pg_result *p_result = pgresult_get_this(result);
|
57
|
-
t_tmbc *this = (t_tmbc *)
|
88
|
+
t_tmbc *this = (t_tmbc *) p_typemap;
|
89
|
+
t_typemap *default_tm;
|
58
90
|
|
59
91
|
if (PQgetisnull(p_result->pgresult, tuple, field)) {
|
60
92
|
return Qnil;
|
61
93
|
}
|
62
94
|
|
63
|
-
|
64
|
-
len = PQgetlength( p_result->pgresult, tuple, field );
|
95
|
+
p_coder = this->convs[field].cconv;
|
65
96
|
|
66
|
-
if(
|
67
|
-
|
97
|
+
if( p_coder ){
|
98
|
+
char * val = PQgetvalue( p_result->pgresult, tuple, field );
|
99
|
+
int len = PQgetlength( p_result->pgresult, tuple, field );
|
68
100
|
|
69
|
-
if( p_coder
|
101
|
+
if( p_coder->dec_func ){
|
70
102
|
return p_coder->dec_func(p_coder, val, len, tuple, field, ENCODING_GET(result));
|
103
|
+
} else {
|
104
|
+
t_pg_coder_dec_func dec_func;
|
105
|
+
dec_func = pg_coder_dec_func( p_coder, PQfformat(p_result->pgresult, field) );
|
106
|
+
return dec_func(p_coder, val, len, tuple, field, ENCODING_GET(result));
|
71
107
|
}
|
72
108
|
}
|
73
109
|
|
74
|
-
|
75
|
-
return
|
110
|
+
default_tm = DATA_PTR( this->typemap.default_typemap );
|
111
|
+
return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
|
76
112
|
}
|
77
113
|
|
78
114
|
static t_pg_coder *
|
79
|
-
pg_tmbc_typecast_query_param(
|
115
|
+
pg_tmbc_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
|
80
116
|
{
|
81
|
-
t_tmbc *this = (t_tmbc *)
|
117
|
+
t_tmbc *this = (t_tmbc *) p_typemap;
|
82
118
|
|
83
119
|
/* Number of fields were already checked in pg_tmbc_fit_to_query() */
|
84
120
|
t_pg_coder *p_coder = this->convs[field].cconv;
|
85
121
|
|
122
|
+
if( !p_coder ){
|
123
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
124
|
+
return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
|
125
|
+
}
|
126
|
+
|
86
127
|
return p_coder;
|
87
128
|
}
|
88
129
|
|
@@ -100,14 +141,19 @@ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, i
|
|
100
141
|
|
101
142
|
p_coder = this->convs[fieldno].cconv;
|
102
143
|
|
144
|
+
if( !p_coder ){
|
145
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
146
|
+
return default_tm->funcs.typecast_copy_get( default_tm, field_str, fieldno, format, enc_idx );
|
147
|
+
}
|
148
|
+
|
103
149
|
dec_func = pg_coder_dec_func( p_coder, format );
|
104
150
|
|
105
151
|
/* Is it a pure String conversion? Then we can directly send field_str to the user. */
|
106
|
-
if(
|
152
|
+
if( dec_func == pg_text_dec_string ){
|
107
153
|
PG_ENCODING_SET_NOCHECK( field_str, enc_idx );
|
108
154
|
return field_str;
|
109
155
|
}
|
110
|
-
if(
|
156
|
+
if( dec_func == pg_bin_dec_bytea ){
|
111
157
|
PG_ENCODING_SET_NOCHECK( field_str, rb_ascii8bit_encindex() );
|
112
158
|
return field_str;
|
113
159
|
}
|
@@ -115,7 +161,7 @@ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, i
|
|
115
161
|
return dec_func( p_coder, RSTRING_PTR(field_str), RSTRING_LEN(field_str), 0, fieldno, enc_idx );
|
116
162
|
}
|
117
163
|
|
118
|
-
const
|
164
|
+
const struct pg_typemap_funcs pg_tmbc_funcs = {
|
119
165
|
.fit_to_result = pg_tmbc_fit_to_result,
|
120
166
|
.fit_to_query = pg_tmbc_fit_to_query,
|
121
167
|
.fit_to_copy_get = pg_tmbc_fit_to_copy_get,
|
@@ -130,8 +176,9 @@ pg_tmbc_mark( t_tmbc *this )
|
|
130
176
|
int i;
|
131
177
|
|
132
178
|
/* allocated but not initialized ? */
|
133
|
-
if(
|
179
|
+
if( this == (t_tmbc *)&pg_typemap_funcs ) return;
|
134
180
|
|
181
|
+
rb_gc_mark(this->typemap.default_typemap);
|
135
182
|
for( i=0; i<this->nfields; i++){
|
136
183
|
t_pg_coder *p_coder = this->convs[i].cconv;
|
137
184
|
if( p_coder )
|
@@ -139,10 +186,19 @@ pg_tmbc_mark( t_tmbc *this )
|
|
139
186
|
}
|
140
187
|
}
|
141
188
|
|
189
|
+
static void
|
190
|
+
pg_tmbc_free( t_tmbc *this )
|
191
|
+
{
|
192
|
+
/* allocated but not initialized ? */
|
193
|
+
if( this == (t_tmbc *)&pg_typemap_funcs ) return;
|
194
|
+
xfree( this );
|
195
|
+
}
|
196
|
+
|
142
197
|
static VALUE
|
143
198
|
pg_tmbc_s_allocate( VALUE klass )
|
144
199
|
{
|
145
|
-
|
200
|
+
/* Use pg_typemap_funcs as interim struct until #initialize is called. */
|
201
|
+
return Data_Wrap_Struct( klass, pg_tmbc_mark, pg_tmbc_free, (t_tmbc *)&pg_typemap_funcs );
|
146
202
|
}
|
147
203
|
|
148
204
|
VALUE
|
@@ -160,7 +216,7 @@ pg_tmbc_allocate()
|
|
160
216
|
* The length of the Array corresponds to
|
161
217
|
* the number of columns or bind parameters this type map is usable for.
|
162
218
|
*
|
163
|
-
* A +nil+ value will
|
219
|
+
* A +nil+ value will forward the given field to the #default_type_map .
|
164
220
|
*/
|
165
221
|
static VALUE
|
166
222
|
pg_tmbc_init(VALUE self, VALUE conv_ary)
|
@@ -175,7 +231,8 @@ pg_tmbc_init(VALUE self, VALUE conv_ary)
|
|
175
231
|
this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
|
176
232
|
/* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
|
177
233
|
this->nfields = 0;
|
178
|
-
this->typemap =
|
234
|
+
this->typemap.funcs = pg_tmbc_funcs;
|
235
|
+
this->typemap.default_typemap = pg_typemap_all_strings;
|
179
236
|
DATA_PTR(self) = this;
|
180
237
|
|
181
238
|
for(i=0; i<conv_ary_len; i++)
|
@@ -251,4 +308,5 @@ init_pg_type_map_by_column()
|
|
251
308
|
rb_define_alloc_func( rb_cTypeMapByColumn, pg_tmbc_s_allocate );
|
252
309
|
rb_define_method( rb_cTypeMapByColumn, "initialize", pg_tmbc_init, 1 );
|
253
310
|
rb_define_method( rb_cTypeMapByColumn, "coders", pg_tmbc_coders, 0 );
|
311
|
+
rb_include_module( rb_cTypeMapByColumn, rb_mDefaultTypeMappable );
|
254
312
|
}
|
@@ -5,8 +5,6 @@
|
|
5
5
|
* This type map can be used to select value encoders based on the MRI-internal
|
6
6
|
* value type code.
|
7
7
|
*
|
8
|
-
* This class is planned to get replaced by a PG::TypeMapByClass implementation.
|
9
|
-
*
|
10
8
|
*/
|
11
9
|
|
12
10
|
#include "pg.h"
|
@@ -35,7 +33,8 @@ static VALUE rb_cTypeMapByMriType;
|
|
35
33
|
|
36
34
|
#define DECLARE_CODER(type) \
|
37
35
|
t_pg_coder *coder_##type; \
|
38
|
-
VALUE ask_##type;
|
36
|
+
VALUE ask_##type; \
|
37
|
+
VALUE coder_obj_##type;
|
39
38
|
|
40
39
|
typedef struct {
|
41
40
|
t_typemap typemap;
|
@@ -52,9 +51,9 @@ typedef struct {
|
|
52
51
|
break;
|
53
52
|
|
54
53
|
static t_pg_coder *
|
55
|
-
pg_tmbmt_typecast_query_param(
|
54
|
+
pg_tmbmt_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
|
56
55
|
{
|
57
|
-
t_tmbmt *this = (t_tmbmt *)
|
56
|
+
t_tmbmt *this = (t_tmbmt *)p_typemap;
|
58
57
|
t_pg_coder *p_coder;
|
59
58
|
VALUE ask_for_coder;
|
60
59
|
|
@@ -70,11 +69,7 @@ pg_tmbmt_typecast_query_param(VALUE self, VALUE param_value, int field)
|
|
70
69
|
/* No static Coder object, but proc/method given to ask for the Coder to use. */
|
71
70
|
VALUE obj;
|
72
71
|
|
73
|
-
|
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
|
-
}
|
72
|
+
obj = rb_funcall(ask_for_coder, rb_intern("call"), 1, param_value);
|
78
73
|
|
79
74
|
if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
|
80
75
|
Data_Get_Struct(obj, t_pg_coder, p_coder);
|
@@ -84,28 +79,39 @@ pg_tmbmt_typecast_query_param(VALUE self, VALUE param_value, int field)
|
|
84
79
|
}
|
85
80
|
}
|
86
81
|
|
82
|
+
if( !p_coder ){
|
83
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
84
|
+
return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
|
85
|
+
}
|
86
|
+
|
87
87
|
return p_coder;
|
88
88
|
}
|
89
89
|
|
90
90
|
static VALUE
|
91
91
|
pg_tmbmt_fit_to_query( VALUE self, VALUE params )
|
92
92
|
{
|
93
|
+
t_tmbmt *this = (t_tmbmt *)DATA_PTR(self);
|
94
|
+
/* Nothing to check at this typemap, but ensure that the default type map fits. */
|
95
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
96
|
+
default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
|
93
97
|
return self;
|
94
98
|
}
|
95
99
|
|
96
100
|
#define GC_MARK_AS_USED(type) \
|
97
|
-
|
98
|
-
rb_gc_mark( this->coders.
|
101
|
+
rb_gc_mark( this->coders.ask_##type ); \
|
102
|
+
rb_gc_mark( this->coders.coder_obj_##type );
|
99
103
|
|
100
104
|
static void
|
101
105
|
pg_tmbmt_mark( t_tmbmt *this )
|
102
106
|
{
|
107
|
+
rb_gc_mark(this->typemap.default_typemap);
|
103
108
|
FOR_EACH_MRI_TYPE( GC_MARK_AS_USED );
|
104
109
|
}
|
105
110
|
|
106
111
|
#define INIT_VARIABLES(type) \
|
107
112
|
this->coders.coder_##type = NULL; \
|
108
|
-
this->coders.ask_##type = Qnil;
|
113
|
+
this->coders.ask_##type = Qnil; \
|
114
|
+
this->coders.coder_obj_##type = Qnil;
|
109
115
|
|
110
116
|
static VALUE
|
111
117
|
pg_tmbmt_s_allocate( VALUE klass )
|
@@ -114,12 +120,13 @@ pg_tmbmt_s_allocate( VALUE klass )
|
|
114
120
|
VALUE self;
|
115
121
|
|
116
122
|
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
|
+
this->typemap.funcs.fit_to_result = pg_typemap_fit_to_result;
|
124
|
+
this->typemap.funcs.fit_to_query = pg_tmbmt_fit_to_query;
|
125
|
+
this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
|
126
|
+
this->typemap.funcs.typecast_result_value = pg_typemap_result_value;
|
127
|
+
this->typemap.funcs.typecast_query_param = pg_tmbmt_typecast_query_param;
|
128
|
+
this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
|
129
|
+
this->typemap.default_typemap = pg_typemap_all_strings;
|
123
130
|
|
124
131
|
FOR_EACH_MRI_TYPE( INIT_VARIABLES );
|
125
132
|
|
@@ -128,12 +135,16 @@ pg_tmbmt_s_allocate( VALUE klass )
|
|
128
135
|
|
129
136
|
#define COMPARE_AND_ASSIGN(type) \
|
130
137
|
else if(!strcmp(p_mri_type, #type)){ \
|
138
|
+
this->coders.coder_obj_##type = coder; \
|
131
139
|
if(NIL_P(coder)){ \
|
132
140
|
this->coders.coder_##type = NULL; \
|
133
141
|
this->coders.ask_##type = Qnil; \
|
134
142
|
}else if(rb_obj_is_kind_of(coder, rb_cPG_Coder)){ \
|
135
143
|
Data_Get_Struct(coder, t_pg_coder, this->coders.coder_##type); \
|
136
144
|
this->coders.ask_##type = Qnil; \
|
145
|
+
}else if(RB_TYPE_P(coder, T_SYMBOL)){ \
|
146
|
+
this->coders.coder_##type = NULL; \
|
147
|
+
this->coders.ask_##type = rb_obj_method( self, coder ); \
|
137
148
|
}else{ \
|
138
149
|
this->coders.coder_##type = NULL; \
|
139
150
|
this->coders.ask_##type = coder; \
|
@@ -144,11 +155,11 @@ pg_tmbmt_s_allocate( VALUE klass )
|
|
144
155
|
* call-seq:
|
145
156
|
* typemap.[mri_type] = coder
|
146
157
|
*
|
147
|
-
* Assigns a new PG::Coder object to the type map. The
|
158
|
+
* Assigns a new PG::Coder object to the type map. The encoder
|
148
159
|
* is registered for type casts of the given +mri_type+ .
|
149
160
|
*
|
150
161
|
* +coder+ can be one of the following:
|
151
|
-
* * +nil+ - Values are
|
162
|
+
* * +nil+ - Values are forwarded to the #default_type_map .
|
152
163
|
* * a PG::Coder - Values are encoded by the given encoder
|
153
164
|
* * a Symbol - The method of this type map (or a derivation) that is called for each value to sent.
|
154
165
|
* It must return a PG::Coder.
|
@@ -194,7 +205,7 @@ pg_tmbmt_aset( VALUE self, VALUE mri_type, VALUE coder )
|
|
194
205
|
|
195
206
|
#define COMPARE_AND_GET(type) \
|
196
207
|
else if(!strcmp(p_mri_type, #type)){ \
|
197
|
-
coder = this->coders.
|
208
|
+
coder = this->coders.coder_obj_##type; \
|
198
209
|
}
|
199
210
|
|
200
211
|
/*
|
@@ -225,7 +236,7 @@ pg_tmbmt_aref( VALUE self, VALUE mri_type )
|
|
225
236
|
}
|
226
237
|
|
227
238
|
#define ADD_TO_HASH(type) \
|
228
|
-
rb_hash_aset( hash_coders, rb_obj_freeze(rb_str_new2(#type)), this->coders.
|
239
|
+
rb_hash_aset( hash_coders, rb_obj_freeze(rb_str_new2(#type)), this->coders.coder_obj_##type );
|
229
240
|
|
230
241
|
|
231
242
|
/*
|
@@ -257,10 +268,17 @@ init_pg_type_map_by_mri_type()
|
|
257
268
|
* This type map is usable for type casting query bind parameters and COPY data
|
258
269
|
* for PG::Connection#put_copy_data . Therefore only encoders might be assigned by
|
259
270
|
* the #[]= method.
|
271
|
+
*
|
272
|
+
* _Note_ : This type map is not portable across Ruby implementations and is less flexible
|
273
|
+
* than PG::TypeMapByClass.
|
274
|
+
* It is kept only for performance comparisons, but PG::TypeMapByClass proved to be equally
|
275
|
+
* fast in almost all cases.
|
276
|
+
*
|
260
277
|
*/
|
261
278
|
rb_cTypeMapByMriType = rb_define_class_under( rb_mPG, "TypeMapByMriType", rb_cTypeMap );
|
262
279
|
rb_define_alloc_func( rb_cTypeMapByMriType, pg_tmbmt_s_allocate );
|
263
280
|
rb_define_method( rb_cTypeMapByMriType, "[]=", pg_tmbmt_aset, 2 );
|
264
281
|
rb_define_method( rb_cTypeMapByMriType, "[]", pg_tmbmt_aref, 1 );
|
265
282
|
rb_define_method( rb_cTypeMapByMriType, "coders", pg_tmbmt_coders, 0 );
|
283
|
+
rb_include_module( rb_cTypeMapByMriType, rb_mDefaultTypeMappable );
|
266
284
|
}
|
data/ext/pg_type_map_by_oid.c
CHANGED
@@ -23,6 +23,9 @@ typedef struct {
|
|
23
23
|
} format[2];
|
24
24
|
} t_tmbo;
|
25
25
|
|
26
|
+
static VALUE pg_tmbo_s_allocate( VALUE klass );
|
27
|
+
|
28
|
+
|
26
29
|
/*
|
27
30
|
* We use the OID's minor 8 Bits as index to a 256 entry cache. This avoids full ruby hash lookups
|
28
31
|
* for each value in most cases.
|
@@ -63,7 +66,8 @@ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
|
|
63
66
|
p_colmap = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * nfields);
|
64
67
|
/* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
|
65
68
|
p_colmap->nfields = 0;
|
66
|
-
p_colmap->typemap =
|
69
|
+
p_colmap->typemap.funcs = pg_tmbc_funcs;
|
70
|
+
p_colmap->typemap.default_typemap = pg_typemap_all_strings;
|
67
71
|
|
68
72
|
colmap = pg_tmbc_allocate();
|
69
73
|
DATA_PTR(colmap) = p_colmap;
|
@@ -84,30 +88,33 @@ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
|
|
84
88
|
}
|
85
89
|
|
86
90
|
static VALUE
|
87
|
-
pg_tmbo_result_value(VALUE result, int tuple, int field)
|
91
|
+
pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
|
88
92
|
{
|
89
|
-
char * val;
|
90
|
-
int len;
|
91
93
|
int format;
|
92
94
|
t_pg_coder *p_coder;
|
93
|
-
t_pg_coder_dec_func dec_func;
|
94
95
|
t_pg_result *p_result = pgresult_get_this(result);
|
95
|
-
t_tmbo *this = (t_tmbo*)
|
96
|
+
t_tmbo *this = (t_tmbo*) p_typemap;
|
97
|
+
t_typemap *default_tm;
|
96
98
|
|
97
99
|
if (PQgetisnull(p_result->pgresult, tuple, field)) {
|
98
100
|
return Qnil;
|
99
101
|
}
|
100
102
|
|
101
|
-
val = PQgetvalue( p_result->pgresult, tuple, field );
|
102
|
-
len = PQgetlength( p_result->pgresult, tuple, field );
|
103
103
|
format = PQfformat( p_result->pgresult, field );
|
104
104
|
|
105
105
|
if( format < 0 || format > 1 )
|
106
106
|
rb_raise(rb_eArgError, "result field %d has unsupported format code %d", field+1, format);
|
107
107
|
|
108
108
|
p_coder = pg_tmbo_lookup_oid( this, format, PQftype(p_result->pgresult, field) );
|
109
|
-
|
110
|
-
|
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 );
|
111
118
|
}
|
112
119
|
|
113
120
|
static VALUE
|
@@ -116,14 +123,33 @@ pg_tmbo_fit_to_result( VALUE self, VALUE result )
|
|
116
123
|
t_tmbo *this = DATA_PTR( self );
|
117
124
|
PGresult *pgresult = pgresult_get( result );
|
118
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
|
+
|
119
130
|
if( PQntuples( pgresult ) <= this->max_rows_for_online_lookup ){
|
120
131
|
/* Do a hash lookup for each result value in pg_tmbc_result_value() */
|
121
|
-
|
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
|
+
}
|
122
145
|
}else{
|
123
146
|
/* Build a new TypeMapByColumn that fits to the given result and
|
124
147
|
* uses a fast array lookup.
|
125
148
|
*/
|
126
|
-
|
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;
|
127
153
|
}
|
128
154
|
}
|
129
155
|
|
@@ -132,6 +158,7 @@ pg_tmbo_mark( t_tmbo *this )
|
|
132
158
|
{
|
133
159
|
int i;
|
134
160
|
|
161
|
+
rb_gc_mark(this->typemap.default_typemap);
|
135
162
|
for( i=0; i<2; i++){
|
136
163
|
rb_gc_mark(this->format[i].oid_to_coder);
|
137
164
|
}
|
@@ -146,12 +173,13 @@ pg_tmbo_s_allocate( VALUE klass )
|
|
146
173
|
|
147
174
|
self = Data_Make_Struct( klass, t_tmbo, pg_tmbo_mark, -1, this );
|
148
175
|
|
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;
|
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;
|
155
183
|
this->max_rows_for_online_lookup = 10;
|
156
184
|
|
157
185
|
for( i=0; i<2; i++){
|
@@ -302,7 +330,7 @@ pg_tmbo_fit_to_result_ext( int argc, VALUE *argv, VALUE self )
|
|
302
330
|
|
303
331
|
if( NIL_P( online_lookup ) ){
|
304
332
|
/* call super */
|
305
|
-
return this->typemap.fit_to_result(self, result);
|
333
|
+
return this->typemap.funcs.fit_to_result(self, result);
|
306
334
|
} else if( RB_TYPE_P( online_lookup, T_TRUE ) ){
|
307
335
|
return self;
|
308
336
|
} else if( RB_TYPE_P( online_lookup, T_FALSE ) ){
|
@@ -329,6 +357,9 @@ init_pg_type_map_by_oid()
|
|
329
357
|
*
|
330
358
|
* This type map is only suitable to cast values from PG::Result objects.
|
331
359
|
* Therefore only decoders might be assigned by the #add_coder method.
|
360
|
+
*
|
361
|
+
* Fields with no match to any of the registered type OID / format combination
|
362
|
+
* are forwarded to the #default_type_map .
|
332
363
|
*/
|
333
364
|
rb_cTypeMapByOid = rb_define_class_under( rb_mPG, "TypeMapByOid", rb_cTypeMap );
|
334
365
|
rb_define_alloc_func( rb_cTypeMapByOid, pg_tmbo_s_allocate );
|
@@ -338,4 +369,5 @@ init_pg_type_map_by_oid()
|
|
338
369
|
rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
|
339
370
|
rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
|
340
371
|
rb_define_method( rb_cTypeMapByOid, "fit_to_result", pg_tmbo_fit_to_result_ext, -1 );
|
372
|
+
rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
|
341
373
|
}
|