pg 0.17.1 → 0.18.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +2407 -2
  4. data/History.rdoc +68 -0
  5. data/Manifest.txt +29 -1
  6. data/README-Windows.rdoc +15 -26
  7. data/README.rdoc +52 -2
  8. data/Rakefile +56 -18
  9. data/Rakefile.cross +77 -49
  10. data/ext/extconf.rb +33 -26
  11. data/ext/pg.c +142 -21
  12. data/ext/pg.h +242 -6
  13. data/ext/pg_binary_decoder.c +162 -0
  14. data/ext/pg_binary_encoder.c +162 -0
  15. data/ext/pg_coder.c +479 -0
  16. data/ext/pg_connection.c +858 -553
  17. data/ext/pg_copy_coder.c +561 -0
  18. data/ext/pg_errors.c +6 -0
  19. data/ext/pg_result.c +479 -128
  20. data/ext/pg_text_decoder.c +421 -0
  21. data/ext/pg_text_encoder.c +663 -0
  22. data/ext/pg_type_map.c +159 -0
  23. data/ext/pg_type_map_all_strings.c +116 -0
  24. data/ext/pg_type_map_by_class.c +239 -0
  25. data/ext/pg_type_map_by_column.c +312 -0
  26. data/ext/pg_type_map_by_mri_type.c +284 -0
  27. data/ext/pg_type_map_by_oid.c +355 -0
  28. data/ext/pg_type_map_in_ruby.c +299 -0
  29. data/ext/util.c +149 -0
  30. data/ext/util.h +65 -0
  31. data/lib/pg/basic_type_mapping.rb +399 -0
  32. data/lib/pg/coder.rb +83 -0
  33. data/lib/pg/connection.rb +81 -29
  34. data/lib/pg/result.rb +13 -3
  35. data/lib/pg/text_decoder.rb +44 -0
  36. data/lib/pg/text_encoder.rb +27 -0
  37. data/lib/pg/type_map_by_column.rb +15 -0
  38. data/lib/pg.rb +12 -2
  39. data/spec/{lib/helpers.rb → helpers.rb} +101 -39
  40. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  41. data/spec/pg/connection_spec.rb +516 -218
  42. data/spec/pg/result_spec.rb +216 -112
  43. data/spec/pg/type_map_by_class_spec.rb +138 -0
  44. data/spec/pg/type_map_by_column_spec.rb +222 -0
  45. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  46. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  47. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  48. data/spec/pg/type_map_spec.rb +22 -0
  49. data/spec/pg/type_spec.rb +697 -0
  50. data/spec/pg_spec.rb +24 -18
  51. data.tar.gz.sig +0 -0
  52. metadata +111 -45
  53. metadata.gz.sig +0 -0
data/ext/pg_type_map.c ADDED
@@ -0,0 +1,159 @@
1
+ /*
2
+ * pg_column_map.c - PG::ColumnMap class extension
3
+ * $Id: pg_type_map.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ VALUE rb_cTypeMap;
10
+ VALUE rb_mDefaultTypeMappable;
11
+ static ID s_id_fit_to_query;
12
+ static ID s_id_fit_to_result;
13
+
14
+ VALUE
15
+ pg_typemap_fit_to_result( VALUE self, VALUE result )
16
+ {
17
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map result values", rb_obj_classname(self) );
18
+ return Qnil;
19
+ }
20
+
21
+ VALUE
22
+ pg_typemap_fit_to_query( VALUE self, VALUE params )
23
+ {
24
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
25
+ return Qnil;
26
+ }
27
+
28
+ int
29
+ pg_typemap_fit_to_copy_get( VALUE self )
30
+ {
31
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map get_copy_data results", rb_obj_classname(self) );
32
+ return Qnil;
33
+ }
34
+
35
+ VALUE
36
+ pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
37
+ {
38
+ rb_raise( rb_eNotImpError, "type map is not suitable to map result values" );
39
+ return Qnil;
40
+ }
41
+
42
+ t_pg_coder *
43
+ pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
44
+ {
45
+ rb_raise( rb_eNotImpError, "type map is not suitable to map query params" );
46
+ return NULL;
47
+ }
48
+
49
+ VALUE
50
+ pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
51
+ {
52
+ rb_raise( rb_eNotImpError, "type map is not suitable to map get_copy_data results" );
53
+ return Qnil;
54
+ }
55
+
56
+ const struct pg_typemap_funcs pg_typemap_funcs = {
57
+ pg_typemap_fit_to_result,
58
+ pg_typemap_fit_to_query,
59
+ pg_typemap_fit_to_copy_get,
60
+ pg_typemap_result_value,
61
+ pg_typemap_typecast_query_param,
62
+ pg_typemap_typecast_copy_get
63
+ };
64
+
65
+ static VALUE
66
+ pg_typemap_s_allocate( VALUE klass )
67
+ {
68
+ VALUE self;
69
+ t_typemap *this;
70
+
71
+ self = Data_Make_Struct( klass, t_typemap, NULL, -1, this );
72
+ this->funcs = pg_typemap_funcs;
73
+
74
+ return self;
75
+ }
76
+
77
+ /*
78
+ * call-seq:
79
+ * res.default_type_map = typemap
80
+ *
81
+ * Set the default TypeMap that is used for values that could not be
82
+ * casted by this type map.
83
+ *
84
+ * +typemap+ must be a kind of PG::TypeMap
85
+ *
86
+ */
87
+ static VALUE
88
+ pg_typemap_default_type_map_set(VALUE self, VALUE typemap)
89
+ {
90
+ t_typemap *this = DATA_PTR( self );
91
+
92
+ if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
93
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
94
+ rb_obj_classname( typemap ) );
95
+ }
96
+ Check_Type(typemap, T_DATA);
97
+ this->default_typemap = typemap;
98
+
99
+ return typemap;
100
+ }
101
+
102
+ /*
103
+ * call-seq:
104
+ * res.default_type_map -> TypeMap
105
+ *
106
+ * Returns the default TypeMap that is currently set for values that could not be
107
+ * casted by this type map.
108
+ *
109
+ * Returns a kind of PG::TypeMap.
110
+ *
111
+ */
112
+ static VALUE
113
+ pg_typemap_default_type_map_get(VALUE self)
114
+ {
115
+ t_typemap *this = DATA_PTR( self );
116
+
117
+ return this->default_typemap;
118
+ }
119
+
120
+ /*
121
+ * call-seq:
122
+ * res.with_default_type_map( typemap )
123
+ *
124
+ * Set the default TypeMap that is used for values that could not be
125
+ * casted by this type map.
126
+ *
127
+ * +typemap+ must be a kind of PG::TypeMap
128
+ *
129
+ * Returns self.
130
+ */
131
+ static VALUE
132
+ pg_typemap_with_default_type_map(VALUE self, VALUE typemap)
133
+ {
134
+ pg_typemap_default_type_map_set( self, typemap );
135
+ return self;
136
+ }
137
+
138
+ void
139
+ init_pg_type_map()
140
+ {
141
+ s_id_fit_to_query = rb_intern("fit_to_query");
142
+ s_id_fit_to_result = rb_intern("fit_to_result");
143
+
144
+ /*
145
+ * Document-class: PG::TypeMap < Object
146
+ *
147
+ * This is the base class for type maps.
148
+ * See derived classes for implementations of different type cast strategies
149
+ * ( PG::TypeMapByColumn, PG::TypeMapByOid ).
150
+ *
151
+ */
152
+ rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
153
+ rb_define_alloc_func( rb_cTypeMap, pg_typemap_s_allocate );
154
+
155
+ rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable");
156
+ rb_define_method( rb_mDefaultTypeMappable, "default_type_map=", pg_typemap_default_type_map_set, 1 );
157
+ rb_define_method( rb_mDefaultTypeMappable, "default_type_map", pg_typemap_default_type_map_get, 0 );
158
+ rb_define_method( rb_mDefaultTypeMappable, "with_default_type_map", pg_typemap_with_default_type_map, 1 );
159
+ }
@@ -0,0 +1,116 @@
1
+ /*
2
+ * pg_type_map_all_strings.c - PG::TypeMapAllStrings class extension
3
+ * $Id: pg_type_map_all_strings.c,v c53f993a4254 2014/12/12 21:57:29 lars $
4
+ *
5
+ * This is the default typemap.
6
+ *
7
+ */
8
+
9
+ #include "pg.h"
10
+
11
+ VALUE rb_cTypeMapAllStrings;
12
+ VALUE pg_typemap_all_strings;
13
+
14
+ static VALUE
15
+ pg_tmas_fit_to_result( VALUE self, VALUE result )
16
+ {
17
+ return self;
18
+ }
19
+
20
+ static VALUE
21
+ pg_tmas_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
22
+ {
23
+ VALUE ret;
24
+ char * val;
25
+ int len;
26
+ t_pg_result *p_result = pgresult_get_this(result);
27
+
28
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
29
+ return Qnil;
30
+ }
31
+
32
+ val = PQgetvalue( p_result->pgresult, tuple, field );
33
+ len = PQgetlength( p_result->pgresult, tuple, field );
34
+
35
+ if ( 0 == PQfformat(p_result->pgresult, field) ) {
36
+ ret = pg_text_dec_string(NULL, val, len, tuple, field, ENCODING_GET(result));
37
+ } else {
38
+ ret = pg_bin_dec_bytea(NULL, val, len, tuple, field, ENCODING_GET(result));
39
+ }
40
+
41
+ return ret;
42
+ }
43
+
44
+ static VALUE
45
+ pg_tmas_fit_to_query( VALUE self, VALUE params )
46
+ {
47
+ return self;
48
+ }
49
+
50
+ static t_pg_coder *
51
+ pg_tmas_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
52
+ {
53
+ return NULL;
54
+ }
55
+
56
+ static int
57
+ pg_tmas_fit_to_copy_get( VALUE self )
58
+ {
59
+ /* We can not predict the number of columns for copy */
60
+ return 0;
61
+ }
62
+
63
+ static VALUE
64
+ pg_tmas_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
65
+ {
66
+ if( format == 0 ){
67
+ PG_ENCODING_SET_NOCHECK( field_str, enc_idx );
68
+ } else {
69
+ PG_ENCODING_SET_NOCHECK( field_str, rb_ascii8bit_encindex() );
70
+ }
71
+ return field_str;
72
+ }
73
+
74
+ static VALUE
75
+ pg_tmas_s_allocate( VALUE klass )
76
+ {
77
+ t_typemap *this;
78
+ VALUE self;
79
+
80
+ self = Data_Make_Struct( klass, t_typemap, NULL, -1, this );
81
+
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
+
89
+ return self;
90
+ }
91
+
92
+
93
+ void
94
+ init_pg_type_map_all_strings()
95
+ {
96
+ /*
97
+ * Document-class: PG::TypeMapAllStrings < PG::TypeMap
98
+ *
99
+ * This type map casts all values received from the database server to Strings
100
+ * and sends all values to the server after conversion to String by +#to_s+ .
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.
104
+ *
105
+ * It is suitable for type casting query bind parameters, result values and
106
+ * COPY IN/OUT data.
107
+ *
108
+ * This is the default type map for each PG::Connection .
109
+ *
110
+ */
111
+ rb_cTypeMapAllStrings = rb_define_class_under( rb_mPG, "TypeMapAllStrings", rb_cTypeMap );
112
+ rb_define_alloc_func( rb_cTypeMapAllStrings, pg_tmas_s_allocate );
113
+
114
+ pg_typemap_all_strings = rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 );
115
+ rb_gc_register_address( &pg_typemap_all_strings );
116
+ }
@@ -0,0 +1,239 @@
1
+ /*
2
+ * pg_type_map_by_class.c - PG::TypeMapByClass class extension
3
+ * $Id: pg_type_map_by_class.c,v eeb8a82c5328 2014/11/10 19:34:02 lars $
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
+ }