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.
@@ -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 );
@@ -7,6 +7,7 @@
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 copy_get_data results", rb_obj_classname(self) );
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 self, int tuple, int field)
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(VALUE self, VALUE param_value, int field)
43
+ pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
43
44
  {
44
- rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
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 copy_get_data results" );
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->fit_to_result = pg_typemap_fit_to_result;
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
  }
@@ -9,7 +9,7 @@
9
9
  #include "pg.h"
10
10
 
11
11
  VALUE rb_cTypeMapAllStrings;
12
- VALUE pg_default_typemap;
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(VALUE self, VALUE param_value, int field)
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. It is used when type_map is not set or set to +nil+.
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
- pg_default_typemap = rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 );
112
- rb_gc_register_address( &pg_default_typemap );
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
+ }