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_text_decoder.c
CHANGED
data/ext/pg_text_encoder.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_text_encoder.c - PG::TextEncoder module
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -42,6 +42,7 @@
|
|
42
42
|
#include "pg.h"
|
43
43
|
#include "util.h"
|
44
44
|
#include <inttypes.h>
|
45
|
+
#include <math.h>
|
45
46
|
|
46
47
|
VALUE rb_mPG_TextEncoder;
|
47
48
|
static ID s_id_encode;
|
@@ -246,6 +247,46 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
|
246
247
|
}
|
247
248
|
}
|
248
249
|
|
250
|
+
static const char hextab[] = {
|
251
|
+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
252
|
+
};
|
253
|
+
|
254
|
+
/*
|
255
|
+
* Document-class: PG::TextEncoder::Bytea < PG::SimpleEncoder
|
256
|
+
*
|
257
|
+
* This is an encoder class for the PostgreSQL bytea type for server version 9.0
|
258
|
+
* or newer.
|
259
|
+
*
|
260
|
+
* The binary String is converted to hexadecimal representation for transmission
|
261
|
+
* in text format. For query bind parameters it is recommended to use
|
262
|
+
* PG::BinaryEncoder::Bytea instead, in order to decrease network traffic and
|
263
|
+
* CPU usage.
|
264
|
+
*
|
265
|
+
*/
|
266
|
+
static int
|
267
|
+
pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
|
268
|
+
{
|
269
|
+
if(out){
|
270
|
+
size_t strlen = RSTRING_LEN(*intermediate);
|
271
|
+
char *iptr = RSTRING_PTR(*intermediate);
|
272
|
+
char *eptr = iptr + strlen;
|
273
|
+
char *optr = out;
|
274
|
+
*optr++ = '\\';
|
275
|
+
*optr++ = 'x';
|
276
|
+
|
277
|
+
for( ; iptr < eptr; iptr++ ){
|
278
|
+
unsigned char c = *iptr;
|
279
|
+
*optr++ = hextab[c >> 4];
|
280
|
+
*optr++ = hextab[c & 0xf];
|
281
|
+
}
|
282
|
+
return optr - out;
|
283
|
+
}else{
|
284
|
+
*intermediate = rb_obj_as_string(value);
|
285
|
+
/* The output starts with "\x" and each character is converted to hex. */
|
286
|
+
return 2 + RSTRING_LEN(*intermediate) * 2;
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
249
290
|
typedef int (*t_quote_func)( void *_this, char *p_in, int strlen, char *p_out );
|
250
291
|
|
251
292
|
static int
|
@@ -619,6 +660,8 @@ init_pg_text_encoder()
|
|
619
660
|
pg_define_coder( "Float", pg_text_enc_float, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
620
661
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
|
621
662
|
pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
663
|
+
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
|
664
|
+
pg_define_coder( "Bytea", pg_text_enc_bytea, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
622
665
|
|
623
666
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Array", rb_cPG_CompositeEncoder ); */
|
624
667
|
pg_define_coder( "Array", pg_text_enc_array, rb_cPG_CompositeEncoder, rb_mPG_TextEncoder );
|
data/ext/pg_type_map.c
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
/*
|
2
2
|
* pg_column_map.c - PG::ColumnMap class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
7
7
|
#include "pg.h"
|
8
8
|
|
9
9
|
VALUE rb_cTypeMap;
|
10
|
+
VALUE rb_mDefaultTypeMappable;
|
10
11
|
static ID s_id_fit_to_query;
|
11
12
|
static ID s_id_fit_to_result;
|
12
13
|
|
@@ -27,31 +28,40 @@ pg_typemap_fit_to_query( VALUE self, VALUE params )
|
|
27
28
|
int
|
28
29
|
pg_typemap_fit_to_copy_get( VALUE self )
|
29
30
|
{
|
30
|
-
rb_raise( rb_eNotImpError, "type map %s is not suitable to map
|
31
|
+
rb_raise( rb_eNotImpError, "type map %s is not suitable to map get_copy_data results", rb_obj_classname(self) );
|
31
32
|
return Qnil;
|
32
33
|
}
|
33
34
|
|
34
35
|
VALUE
|
35
|
-
pg_typemap_result_value(VALUE
|
36
|
+
pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
|
36
37
|
{
|
37
38
|
rb_raise( rb_eNotImpError, "type map is not suitable to map result values" );
|
38
39
|
return Qnil;
|
39
40
|
}
|
40
41
|
|
41
42
|
t_pg_coder *
|
42
|
-
pg_typemap_typecast_query_param(
|
43
|
+
pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
|
43
44
|
{
|
44
|
-
rb_raise( rb_eNotImpError, "type map
|
45
|
+
rb_raise( rb_eNotImpError, "type map is not suitable to map query params" );
|
45
46
|
return NULL;
|
46
47
|
}
|
47
48
|
|
48
49
|
VALUE
|
49
50
|
pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
|
50
51
|
{
|
51
|
-
rb_raise( rb_eNotImpError, "type map is not suitable to map
|
52
|
+
rb_raise( rb_eNotImpError, "type map is not suitable to map get_copy_data results" );
|
52
53
|
return Qnil;
|
53
54
|
}
|
54
55
|
|
56
|
+
const struct pg_typemap_funcs pg_typemap_funcs = {
|
57
|
+
.fit_to_result = pg_typemap_fit_to_result,
|
58
|
+
.fit_to_query = pg_typemap_fit_to_query,
|
59
|
+
.fit_to_copy_get = pg_typemap_fit_to_copy_get,
|
60
|
+
.typecast_result_value = pg_typemap_result_value,
|
61
|
+
.typecast_query_param = pg_typemap_typecast_query_param,
|
62
|
+
.typecast_copy_get = pg_typemap_typecast_copy_get
|
63
|
+
};
|
64
|
+
|
55
65
|
static VALUE
|
56
66
|
pg_typemap_s_allocate( VALUE klass )
|
57
67
|
{
|
@@ -59,12 +69,7 @@ pg_typemap_s_allocate( VALUE klass )
|
|
59
69
|
t_typemap *this;
|
60
70
|
|
61
71
|
self = Data_Make_Struct( klass, t_typemap, NULL, -1, this );
|
62
|
-
this->
|
63
|
-
this->fit_to_query = pg_typemap_fit_to_query;
|
64
|
-
this->fit_to_copy_get = pg_typemap_fit_to_copy_get;
|
65
|
-
this->typecast_result_value = pg_typemap_result_value;
|
66
|
-
this->typecast_query_param = pg_typemap_typecast_query_param;
|
67
|
-
this->typecast_copy_get = pg_typemap_typecast_copy_get;
|
72
|
+
this->funcs = pg_typemap_funcs;
|
68
73
|
|
69
74
|
return self;
|
70
75
|
}
|
@@ -79,7 +84,7 @@ pg_typemap_fit_to_result_ext( VALUE self, VALUE result )
|
|
79
84
|
rb_obj_classname( result ) );
|
80
85
|
}
|
81
86
|
|
82
|
-
return this->fit_to_result( self, result );
|
87
|
+
return this->funcs.fit_to_result( self, result );
|
83
88
|
}
|
84
89
|
|
85
90
|
static VALUE
|
@@ -89,7 +94,68 @@ pg_typemap_fit_to_query_ext( VALUE self, VALUE params )
|
|
89
94
|
|
90
95
|
Check_Type( params, T_ARRAY);
|
91
96
|
|
92
|
-
return this->fit_to_query( self, params );
|
97
|
+
return this->funcs.fit_to_query( self, params );
|
98
|
+
}
|
99
|
+
|
100
|
+
/*
|
101
|
+
* call-seq:
|
102
|
+
* res.default_type_map = typemap
|
103
|
+
*
|
104
|
+
* Set the default TypeMap that is used for values that could not be
|
105
|
+
* casted by this type map.
|
106
|
+
*
|
107
|
+
* +typemap+ must be a kind of PG::TypeMap
|
108
|
+
*
|
109
|
+
*/
|
110
|
+
static VALUE
|
111
|
+
pg_typemap_default_type_map_set(VALUE self, VALUE typemap)
|
112
|
+
{
|
113
|
+
t_typemap *this = DATA_PTR( self );
|
114
|
+
|
115
|
+
if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
|
116
|
+
rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
|
117
|
+
rb_obj_classname( typemap ) );
|
118
|
+
}
|
119
|
+
Check_Type(typemap, T_DATA);
|
120
|
+
this->default_typemap = typemap;
|
121
|
+
|
122
|
+
return typemap;
|
123
|
+
}
|
124
|
+
|
125
|
+
/*
|
126
|
+
* call-seq:
|
127
|
+
* res.default_type_map -> TypeMap
|
128
|
+
*
|
129
|
+
* Returns the default TypeMap that is currently set for values that could not be
|
130
|
+
* casted by this type map.
|
131
|
+
*
|
132
|
+
* Returns a kind of PG::TypeMap.
|
133
|
+
*
|
134
|
+
*/
|
135
|
+
static VALUE
|
136
|
+
pg_typemap_default_type_map_get(VALUE self)
|
137
|
+
{
|
138
|
+
t_typemap *this = DATA_PTR( self );
|
139
|
+
|
140
|
+
return this->default_typemap;
|
141
|
+
}
|
142
|
+
|
143
|
+
/*
|
144
|
+
* call-seq:
|
145
|
+
* res.with_default_type_map( typemap )
|
146
|
+
*
|
147
|
+
* Set the default TypeMap that is used for values that could not be
|
148
|
+
* casted by this type map.
|
149
|
+
*
|
150
|
+
* +typemap+ must be a kind of PG::TypeMap
|
151
|
+
*
|
152
|
+
* Returns self.
|
153
|
+
*/
|
154
|
+
static VALUE
|
155
|
+
pg_typemap_with_default_type_map(VALUE self, VALUE typemap)
|
156
|
+
{
|
157
|
+
pg_typemap_default_type_map_set( self, typemap );
|
158
|
+
return self;
|
93
159
|
}
|
94
160
|
|
95
161
|
void
|
@@ -110,4 +176,9 @@ init_pg_type_map()
|
|
110
176
|
rb_define_alloc_func( rb_cTypeMap, pg_typemap_s_allocate );
|
111
177
|
rb_define_method( rb_cTypeMap, "fit_to_result", pg_typemap_fit_to_result_ext, 1 );
|
112
178
|
rb_define_method( rb_cTypeMap, "fit_to_query", pg_typemap_fit_to_query_ext, 1 );
|
179
|
+
|
180
|
+
rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable");
|
181
|
+
rb_define_method( rb_mDefaultTypeMappable, "default_type_map=", pg_typemap_default_type_map_set, 1 );
|
182
|
+
rb_define_method( rb_mDefaultTypeMappable, "default_type_map", pg_typemap_default_type_map_get, 0 );
|
183
|
+
rb_define_method( rb_mDefaultTypeMappable, "with_default_type_map", pg_typemap_with_default_type_map, 1 );
|
113
184
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_all_strings.c - PG::TypeMapAllStrings class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
* This is the default typemap.
|
6
6
|
*
|
@@ -9,7 +9,7 @@
|
|
9
9
|
#include "pg.h"
|
10
10
|
|
11
11
|
VALUE rb_cTypeMapAllStrings;
|
12
|
-
VALUE
|
12
|
+
VALUE pg_typemap_all_strings;
|
13
13
|
|
14
14
|
static VALUE
|
15
15
|
pg_tmas_fit_to_result( VALUE self, VALUE result )
|
@@ -18,7 +18,7 @@ pg_tmas_fit_to_result( VALUE self, VALUE result )
|
|
18
18
|
}
|
19
19
|
|
20
20
|
static VALUE
|
21
|
-
pg_tmas_result_value(VALUE result, int tuple, int field)
|
21
|
+
pg_tmas_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
|
22
22
|
{
|
23
23
|
VALUE ret;
|
24
24
|
char * val;
|
@@ -48,7 +48,7 @@ pg_tmas_fit_to_query( VALUE self, VALUE params )
|
|
48
48
|
}
|
49
49
|
|
50
50
|
static t_pg_coder *
|
51
|
-
pg_tmas_typecast_query_param(
|
51
|
+
pg_tmas_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
|
52
52
|
{
|
53
53
|
return NULL;
|
54
54
|
}
|
@@ -79,12 +79,12 @@ pg_tmas_s_allocate( VALUE klass )
|
|
79
79
|
|
80
80
|
self = Data_Make_Struct( klass, t_typemap, NULL, -1, this );
|
81
81
|
|
82
|
-
this->fit_to_result = pg_tmas_fit_to_result;
|
83
|
-
this->fit_to_query = pg_tmas_fit_to_query;
|
84
|
-
this->fit_to_copy_get = pg_tmas_fit_to_copy_get;
|
85
|
-
this->typecast_result_value = pg_tmas_result_value;
|
86
|
-
this->typecast_query_param = pg_tmas_typecast_query_param;
|
87
|
-
this->typecast_copy_get = pg_tmas_typecast_copy_get;
|
82
|
+
this->funcs.fit_to_result = pg_tmas_fit_to_result;
|
83
|
+
this->funcs.fit_to_query = pg_tmas_fit_to_query;
|
84
|
+
this->funcs.fit_to_copy_get = pg_tmas_fit_to_copy_get;
|
85
|
+
this->funcs.typecast_result_value = pg_tmas_result_value;
|
86
|
+
this->funcs.typecast_query_param = pg_tmas_typecast_query_param;
|
87
|
+
this->funcs.typecast_copy_get = pg_tmas_typecast_copy_get;
|
88
88
|
|
89
89
|
return self;
|
90
90
|
}
|
@@ -98,16 +98,19 @@ init_pg_type_map_all_strings()
|
|
98
98
|
*
|
99
99
|
* This type map casts all values received from the database server to Strings
|
100
100
|
* and sends all values to the server after conversion to String by +#to_str+ .
|
101
|
+
* That means, it is hard coded to PG::TextEncoder::String for value encoding
|
102
|
+
* and to PG::TextDecoder::String for text format respectivly PG::BinaryDecoder::Bytea
|
103
|
+
* for binary format received from the server.
|
101
104
|
*
|
102
105
|
* It is suitable for type casting query bind parameters, result values and
|
103
106
|
* COPY IN/OUT data.
|
104
107
|
*
|
105
|
-
* This is the default type map
|
108
|
+
* This is the default type map for each PG::Connection .
|
106
109
|
*
|
107
110
|
*/
|
108
111
|
rb_cTypeMapAllStrings = rb_define_class_under( rb_mPG, "TypeMapAllStrings", rb_cTypeMap );
|
109
112
|
rb_define_alloc_func( rb_cTypeMapAllStrings, pg_tmas_s_allocate );
|
110
113
|
|
111
|
-
|
112
|
-
rb_gc_register_address( &
|
114
|
+
pg_typemap_all_strings = rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 );
|
115
|
+
rb_gc_register_address( &pg_typemap_all_strings );
|
113
116
|
}
|
@@ -0,0 +1,239 @@
|
|
1
|
+
/*
|
2
|
+
* pg_type_map_by_class.c - PG::TypeMapByClass class extension
|
3
|
+
* $Id$
|
4
|
+
*
|
5
|
+
* This type map can be used to select value encoders based on the class
|
6
|
+
* of the given value to be send.
|
7
|
+
*
|
8
|
+
*/
|
9
|
+
|
10
|
+
#include "pg.h"
|
11
|
+
|
12
|
+
static VALUE rb_cTypeMapByClass;
|
13
|
+
static ID s_id_ancestors;
|
14
|
+
|
15
|
+
typedef struct {
|
16
|
+
t_typemap typemap;
|
17
|
+
|
18
|
+
VALUE klass_to_coder;
|
19
|
+
VALUE self;
|
20
|
+
|
21
|
+
struct pg_tmbk_coder_cache_entry {
|
22
|
+
VALUE klass;
|
23
|
+
t_pg_coder *p_coder;
|
24
|
+
} cache_row[0x100];
|
25
|
+
} t_tmbk;
|
26
|
+
|
27
|
+
/*
|
28
|
+
* We use 8 Bits of the klass object id as index to a 256 entry cache.
|
29
|
+
* This avoids full lookups in most cases.
|
30
|
+
*/
|
31
|
+
#define CACHE_LOOKUP(this, klass) ( &this->cache_row[(klass >> 8) & 0xff] )
|
32
|
+
|
33
|
+
|
34
|
+
static t_pg_coder *
|
35
|
+
pg_tmbk_lookup_klass(t_tmbk *this, VALUE klass, VALUE param_value)
|
36
|
+
{
|
37
|
+
t_pg_coder *p_coder;
|
38
|
+
struct pg_tmbk_coder_cache_entry *p_ce;
|
39
|
+
|
40
|
+
p_ce = CACHE_LOOKUP(this, klass);
|
41
|
+
|
42
|
+
/* Is the cache entry for the expected klass? */
|
43
|
+
if( p_ce->klass == klass ) {
|
44
|
+
p_coder = p_ce->p_coder;
|
45
|
+
} else {
|
46
|
+
/* No, then do a full lookup based on the ancestors. */
|
47
|
+
VALUE obj = rb_hash_lookup( this->klass_to_coder, klass );
|
48
|
+
|
49
|
+
if( NIL_P(obj) ){
|
50
|
+
int i;
|
51
|
+
VALUE ancestors = rb_funcall( klass, s_id_ancestors, 0 );
|
52
|
+
|
53
|
+
Check_Type( ancestors, T_ARRAY );
|
54
|
+
/* Don't look at the first element, it's expected to equal klass. */
|
55
|
+
for( i=1; i<RARRAY_LEN(ancestors); i++ ){
|
56
|
+
obj = rb_hash_lookup( this->klass_to_coder, rb_ary_entry( ancestors, i) );
|
57
|
+
|
58
|
+
if( !NIL_P(obj) )
|
59
|
+
break;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
if(NIL_P(obj)){
|
64
|
+
p_coder = NULL;
|
65
|
+
}else if(rb_obj_is_kind_of(obj, rb_cPG_Coder)){
|
66
|
+
Data_Get_Struct(obj, t_pg_coder, p_coder);
|
67
|
+
}else{
|
68
|
+
if( RB_TYPE_P(obj, T_SYMBOL) ){
|
69
|
+
/* A Proc object (or something that responds to #call). */
|
70
|
+
obj = rb_funcall(this->self, SYM2ID(obj), 1, param_value);
|
71
|
+
}else{
|
72
|
+
/* A Proc object (or something that responds to #call). */
|
73
|
+
obj = rb_funcall(obj, rb_intern("call"), 1, param_value);
|
74
|
+
}
|
75
|
+
|
76
|
+
if( NIL_P(obj) ){
|
77
|
+
p_coder = NULL;
|
78
|
+
}else if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
|
79
|
+
Data_Get_Struct(obj, t_pg_coder, p_coder);
|
80
|
+
}else{
|
81
|
+
rb_raise(rb_eTypeError, "argument has invalid type %s (should be nil or some kind of PG::Coder)",
|
82
|
+
rb_obj_classname( obj ));
|
83
|
+
}
|
84
|
+
|
85
|
+
/* We can not cache coders retrieved by ruby code, because we can not anticipate
|
86
|
+
* the returned Coder object. */
|
87
|
+
return p_coder;
|
88
|
+
}
|
89
|
+
|
90
|
+
/* Write the retrieved coder to the cache */
|
91
|
+
p_ce->klass = klass;
|
92
|
+
p_ce->p_coder = p_coder;
|
93
|
+
}
|
94
|
+
return p_coder;
|
95
|
+
}
|
96
|
+
|
97
|
+
|
98
|
+
static t_pg_coder *
|
99
|
+
pg_tmbk_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
|
100
|
+
{
|
101
|
+
t_tmbk *this = (t_tmbk *)p_typemap;
|
102
|
+
t_pg_coder *p_coder;
|
103
|
+
|
104
|
+
p_coder = pg_tmbk_lookup_klass( this, rb_obj_class(param_value), param_value );
|
105
|
+
|
106
|
+
if( !p_coder ){
|
107
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
108
|
+
return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
|
109
|
+
}
|
110
|
+
|
111
|
+
return p_coder;
|
112
|
+
}
|
113
|
+
|
114
|
+
static VALUE
|
115
|
+
pg_tmbk_fit_to_query( VALUE self, VALUE params )
|
116
|
+
{
|
117
|
+
t_tmbk *this = (t_tmbk *)DATA_PTR(self);
|
118
|
+
/* Nothing to check at this typemap, but ensure that the default type map fits. */
|
119
|
+
t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
|
120
|
+
default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
|
121
|
+
return self;
|
122
|
+
}
|
123
|
+
|
124
|
+
static void
|
125
|
+
pg_tmbk_mark( t_tmbk *this )
|
126
|
+
{
|
127
|
+
rb_gc_mark(this->typemap.default_typemap);
|
128
|
+
rb_gc_mark(this->klass_to_coder);
|
129
|
+
/* All coders are in the Hash, so no need to mark the cache. */
|
130
|
+
}
|
131
|
+
|
132
|
+
static VALUE
|
133
|
+
pg_tmbk_s_allocate( VALUE klass )
|
134
|
+
{
|
135
|
+
t_tmbk *this;
|
136
|
+
VALUE self;
|
137
|
+
|
138
|
+
self = Data_Make_Struct( klass, t_tmbk, pg_tmbk_mark, -1, this );
|
139
|
+
this->typemap.funcs.fit_to_result = pg_typemap_fit_to_result;
|
140
|
+
this->typemap.funcs.fit_to_query = pg_tmbk_fit_to_query;
|
141
|
+
this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
|
142
|
+
this->typemap.funcs.typecast_result_value = pg_typemap_result_value;
|
143
|
+
this->typemap.funcs.typecast_query_param = pg_tmbk_typecast_query_param;
|
144
|
+
this->typemap.funcs.typecast_copy_get = pg_typemap_typecast_copy_get;
|
145
|
+
this->typemap.default_typemap = pg_typemap_all_strings;
|
146
|
+
|
147
|
+
/* We need to store self in the this-struct, because pg_tmbk_typecast_query_param(),
|
148
|
+
* is called with the this-pointer only. */
|
149
|
+
this->self = self;
|
150
|
+
this->klass_to_coder = rb_hash_new();
|
151
|
+
|
152
|
+
/* The cache is properly initialized by Data_Make_Struct(). */
|
153
|
+
|
154
|
+
return self;
|
155
|
+
}
|
156
|
+
|
157
|
+
/*
|
158
|
+
* call-seq:
|
159
|
+
* typemap.[class] = coder
|
160
|
+
*
|
161
|
+
* Assigns a new PG::Coder object to the type map. The encoder
|
162
|
+
* is chosen for all values that are a kind of the given +class+ .
|
163
|
+
*
|
164
|
+
* +coder+ can be one of the following:
|
165
|
+
* * +nil+ - Values are forwarded to the #default_type_map .
|
166
|
+
* * a PG::Coder - Values are encoded by the given encoder
|
167
|
+
* * a Symbol - The method of this type map (or a derivation) that is called for each value to sent.
|
168
|
+
* It must return a PG::Coder or +nil+ .
|
169
|
+
* * a Proc - The Proc object is called for each value. It must return a PG::Coder or +nil+ .
|
170
|
+
*
|
171
|
+
*/
|
172
|
+
static VALUE
|
173
|
+
pg_tmbk_aset( VALUE self, VALUE klass, VALUE coder )
|
174
|
+
{
|
175
|
+
t_tmbk *this = DATA_PTR( self );
|
176
|
+
|
177
|
+
if(NIL_P(coder)){
|
178
|
+
rb_hash_delete( this->klass_to_coder, klass );
|
179
|
+
}else{
|
180
|
+
rb_hash_aset( this->klass_to_coder, klass, coder );
|
181
|
+
}
|
182
|
+
|
183
|
+
/* The cache lookup key can be a derivation of the klass.
|
184
|
+
* So we can not expire the cache selectively. */
|
185
|
+
memset( &this->cache_row, 0, sizeof(this->cache_row) );
|
186
|
+
|
187
|
+
return coder;
|
188
|
+
}
|
189
|
+
|
190
|
+
/*
|
191
|
+
* call-seq:
|
192
|
+
* typemap.[class] -> coder
|
193
|
+
*
|
194
|
+
* Returns the encoder object for the given +class+
|
195
|
+
*/
|
196
|
+
static VALUE
|
197
|
+
pg_tmbk_aref( VALUE self, VALUE klass )
|
198
|
+
{
|
199
|
+
t_tmbk *this = DATA_PTR( self );
|
200
|
+
|
201
|
+
return rb_hash_lookup(this->klass_to_coder, klass);
|
202
|
+
}
|
203
|
+
|
204
|
+
/*
|
205
|
+
* call-seq:
|
206
|
+
* typemap.coders -> Hash
|
207
|
+
*
|
208
|
+
* Returns all classes and their assigned encoder object.
|
209
|
+
*/
|
210
|
+
static VALUE
|
211
|
+
pg_tmbk_coders( VALUE self )
|
212
|
+
{
|
213
|
+
t_tmbk *this = DATA_PTR( self );
|
214
|
+
|
215
|
+
return rb_obj_freeze(rb_hash_dup(this->klass_to_coder));
|
216
|
+
}
|
217
|
+
|
218
|
+
void
|
219
|
+
init_pg_type_map_by_class()
|
220
|
+
{
|
221
|
+
s_id_ancestors = rb_intern("ancestors");
|
222
|
+
|
223
|
+
/*
|
224
|
+
* Document-class: PG::TypeMapByClass < PG::TypeMap
|
225
|
+
*
|
226
|
+
* This type map casts values based on the class or the ancestors of the given value
|
227
|
+
* to be sent.
|
228
|
+
*
|
229
|
+
* This type map is usable for type casting query bind parameters and COPY data
|
230
|
+
* for PG::Connection#put_copy_data . Therefore only encoders might be assigned by
|
231
|
+
* the #[]= method.
|
232
|
+
*/
|
233
|
+
rb_cTypeMapByClass = rb_define_class_under( rb_mPG, "TypeMapByClass", rb_cTypeMap );
|
234
|
+
rb_define_alloc_func( rb_cTypeMapByClass, pg_tmbk_s_allocate );
|
235
|
+
rb_define_method( rb_cTypeMapByClass, "[]=", pg_tmbk_aset, 2 );
|
236
|
+
rb_define_method( rb_cTypeMapByClass, "[]", pg_tmbk_aref, 1 );
|
237
|
+
rb_define_method( rb_cTypeMapByClass, "coders", pg_tmbk_coders, 0 );
|
238
|
+
rb_include_module( rb_cTypeMapByClass, rb_mDefaultTypeMappable );
|
239
|
+
}
|