pg 0.18.0.pre20141017160319 → 0.18.0.pre20141117110243
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/.gemtest +0 -0
- data/ChangeLog +213 -15
- data/History.rdoc +15 -0
- data/Manifest.txt +2 -0
- data/README.rdoc +1 -1
- data/Rakefile +0 -17
- data/ext/extconf.rb +2 -0
- data/ext/pg.c +67 -1
- data/ext/pg.h +50 -13
- data/ext/pg_binary_decoder.c +4 -4
- data/ext/pg_binary_encoder.c +1 -1
- data/ext/pg_coder.c +6 -0
- data/ext/pg_connection.c +50 -55
- data/ext/pg_copy_coder.c +13 -29
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +273 -77
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +44 -1
- data/ext/pg_type_map.c +85 -14
- data/ext/pg_type_map_all_strings.c +16 -13
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +81 -23
- data/ext/pg_type_map_by_mri_type.c +42 -24
- data/ext/pg_type_map_by_oid.c +52 -20
- data/ext/util.c +1 -1
- data/lib/pg.rb +3 -3
- data/lib/pg/basic_type_mapping.rb +13 -13
- data/lib/pg/coder.rb +9 -0
- data/sample/disk_usage_report.rb +1 -1
- data/sample/pg_statistics.rb +1 -1
- data/sample/replication_monitor.rb +1 -1
- 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
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_column_map.c - PG::ColumnMap class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -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
|
}
|
@@ -1,12 +1,10 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
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
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_by_oid.c - PG::TypeMapByOid class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -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
|
}
|