pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +1573 -2
  5. data/History.rdoc +3 -11
  6. data/Manifest.txt +24 -0
  7. data/README.rdoc +51 -4
  8. data/Rakefile +20 -14
  9. data/Rakefile.cross +39 -32
  10. data/ext/extconf.rb +27 -26
  11. data/ext/pg.c +75 -21
  12. data/ext/pg.h +194 -6
  13. data/ext/pg_binary_decoder.c +160 -0
  14. data/ext/pg_binary_encoder.c +160 -0
  15. data/ext/pg_coder.c +454 -0
  16. data/ext/pg_connection.c +815 -518
  17. data/ext/pg_copy_coder.c +557 -0
  18. data/ext/pg_result.c +258 -103
  19. data/ext/pg_text_decoder.c +424 -0
  20. data/ext/pg_text_encoder.c +608 -0
  21. data/ext/pg_type_map.c +113 -0
  22. data/ext/pg_type_map_all_strings.c +113 -0
  23. data/ext/pg_type_map_by_column.c +254 -0
  24. data/ext/pg_type_map_by_mri_type.c +266 -0
  25. data/ext/pg_type_map_by_oid.c +341 -0
  26. data/ext/util.c +121 -0
  27. data/ext/util.h +63 -0
  28. data/lib/pg.rb +11 -1
  29. data/lib/pg/basic_type_mapping.rb +377 -0
  30. data/lib/pg/coder.rb +74 -0
  31. data/lib/pg/connection.rb +38 -5
  32. data/lib/pg/result.rb +13 -3
  33. data/lib/pg/text_decoder.rb +42 -0
  34. data/lib/pg/text_encoder.rb +27 -0
  35. data/lib/pg/type_map_by_column.rb +15 -0
  36. data/spec/helpers.rb +9 -1
  37. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  38. data/spec/pg/connection_spec.rb +232 -13
  39. data/spec/pg/result_spec.rb +52 -0
  40. data/spec/pg/type_map_by_column_spec.rb +135 -0
  41. data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
  42. data/spec/pg/type_map_by_oid_spec.rb +133 -0
  43. data/spec/pg/type_map_spec.rb +39 -0
  44. data/spec/pg/type_spec.rb +620 -0
  45. metadata +40 -4
  46. metadata.gz.sig +0 -0
data/ext/pg_type_map.c ADDED
@@ -0,0 +1,113 @@
1
+ /*
2
+ * pg_column_map.c - PG::ColumnMap class extension
3
+ * $Id: pg_type_map.c,v 4227fdc5f0ac 2014/10/07 17:42:09 lars $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ VALUE rb_cTypeMap;
10
+ static ID s_id_fit_to_query;
11
+ static ID s_id_fit_to_result;
12
+
13
+ VALUE
14
+ pg_typemap_fit_to_result( VALUE self, VALUE result )
15
+ {
16
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map result values", rb_obj_classname(self) );
17
+ return Qnil;
18
+ }
19
+
20
+ VALUE
21
+ pg_typemap_fit_to_query( VALUE self, VALUE params )
22
+ {
23
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
24
+ return Qnil;
25
+ }
26
+
27
+ int
28
+ pg_typemap_fit_to_copy_get( VALUE self )
29
+ {
30
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map copy_get_data results", rb_obj_classname(self) );
31
+ return Qnil;
32
+ }
33
+
34
+ VALUE
35
+ pg_typemap_result_value(VALUE self, int tuple, int field)
36
+ {
37
+ rb_raise( rb_eNotImpError, "type map is not suitable to map result values" );
38
+ return Qnil;
39
+ }
40
+
41
+ t_pg_coder *
42
+ pg_typemap_typecast_query_param(VALUE self, VALUE param_value, int field)
43
+ {
44
+ rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
45
+ return NULL;
46
+ }
47
+
48
+ VALUE
49
+ pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
50
+ {
51
+ rb_raise( rb_eNotImpError, "type map is not suitable to map copy_get_data results" );
52
+ return Qnil;
53
+ }
54
+
55
+ static VALUE
56
+ pg_typemap_s_allocate( VALUE klass )
57
+ {
58
+ VALUE self;
59
+ t_typemap *this;
60
+
61
+ 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;
68
+
69
+ return self;
70
+ }
71
+
72
+ static VALUE
73
+ pg_typemap_fit_to_result_ext( VALUE self, VALUE result )
74
+ {
75
+ t_typemap *this = DATA_PTR( self );
76
+
77
+ if ( !rb_obj_is_kind_of(result, rb_cPGresult) ) {
78
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Result)",
79
+ rb_obj_classname( result ) );
80
+ }
81
+
82
+ return this->fit_to_result( self, result );
83
+ }
84
+
85
+ static VALUE
86
+ pg_typemap_fit_to_query_ext( VALUE self, VALUE params )
87
+ {
88
+ t_typemap *this = DATA_PTR( self );
89
+
90
+ Check_Type( params, T_ARRAY);
91
+
92
+ return this->fit_to_query( self, params );
93
+ }
94
+
95
+ void
96
+ init_pg_type_map()
97
+ {
98
+ s_id_fit_to_query = rb_intern("fit_to_query");
99
+ s_id_fit_to_result = rb_intern("fit_to_result");
100
+
101
+ /*
102
+ * Document-class: PG::TypeMap < Object
103
+ *
104
+ * This is the base class for type maps.
105
+ * See derived classes for implementations of different type cast strategies
106
+ * ( PG::TypeMapByColumn, PG::TypeMapByOid ).
107
+ *
108
+ */
109
+ rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
110
+ rb_define_alloc_func( rb_cTypeMap, pg_typemap_s_allocate );
111
+ rb_define_method( rb_cTypeMap, "fit_to_result", pg_typemap_fit_to_result_ext, 1 );
112
+ rb_define_method( rb_cTypeMap, "fit_to_query", pg_typemap_fit_to_query_ext, 1 );
113
+ }
@@ -0,0 +1,113 @@
1
+ /*
2
+ * pg_type_map_all_strings.c - PG::TypeMapAllStrings class extension
3
+ * $Id: pg_type_map_all_strings.c,v 4227fdc5f0ac 2014/10/07 17:42:09 lars $
4
+ *
5
+ * This is the default typemap.
6
+ *
7
+ */
8
+
9
+ #include "pg.h"
10
+
11
+ VALUE rb_cTypeMapAllStrings;
12
+ VALUE pg_default_typemap;
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(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(VALUE self, 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->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;
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_str+ .
101
+ *
102
+ * It is suitable for type casting query bind parameters, result values and
103
+ * COPY IN/OUT data.
104
+ *
105
+ * This is the default type map. It is used when type_map is not set or set to +nil+.
106
+ *
107
+ */
108
+ rb_cTypeMapAllStrings = rb_define_class_under( rb_mPG, "TypeMapAllStrings", rb_cTypeMap );
109
+ rb_define_alloc_func( rb_cTypeMapAllStrings, pg_tmas_s_allocate );
110
+
111
+ pg_default_typemap = rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 );
112
+ rb_gc_register_address( &pg_default_typemap );
113
+ }
@@ -0,0 +1,254 @@
1
+ /*
2
+ * pg_column_map.c - PG::ColumnMap class extension
3
+ * $Id: pg_type_map_by_column.c,v ec9ef7fb39eb 2014/10/10 18:59:10 lars $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+ static VALUE rb_cTypeMapByColumn;
10
+ static ID s_id_decode;
11
+ static ID s_id_encode;
12
+
13
+ static VALUE
14
+ pg_tmbc_fit_to_result( VALUE self, VALUE result )
15
+ {
16
+ int nfields;
17
+ t_tmbc *this = DATA_PTR( self );
18
+
19
+ nfields = PQnfields( pgresult_get(result) );
20
+ if ( this->nfields != nfields ) {
21
+ rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
22
+ nfields, this->nfields );
23
+ }
24
+ return self;
25
+ }
26
+
27
+ static VALUE
28
+ pg_tmbc_fit_to_query( VALUE self, VALUE params )
29
+ {
30
+ int nfields;
31
+ t_tmbc *this = DATA_PTR( self );
32
+
33
+ nfields = (int)RARRAY_LEN( params );
34
+ if ( this->nfields != nfields ) {
35
+ rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
36
+ nfields, this->nfields );
37
+ }
38
+ return self;
39
+ }
40
+
41
+ static int
42
+ pg_tmbc_fit_to_copy_get( VALUE self )
43
+ {
44
+ t_tmbc *this = DATA_PTR( self );
45
+ return this->nfields;
46
+ }
47
+
48
+
49
+ VALUE
50
+ pg_tmbc_result_value(VALUE result, int tuple, int field)
51
+ {
52
+ char * val;
53
+ int len;
54
+ t_pg_coder *p_coder = NULL;
55
+ t_pg_coder_dec_func dec_func;
56
+ t_pg_result *p_result = pgresult_get_this(result);
57
+ t_tmbc *this = (t_tmbc *) p_result->p_typemap;
58
+
59
+ if (PQgetisnull(p_result->pgresult, tuple, field)) {
60
+ return Qnil;
61
+ }
62
+
63
+ val = PQgetvalue( p_result->pgresult, tuple, field );
64
+ len = PQgetlength( p_result->pgresult, tuple, field );
65
+
66
+ if( this ){
67
+ p_coder = this->convs[field].cconv;
68
+
69
+ if( p_coder && p_coder->dec_func ){
70
+ return p_coder->dec_func(p_coder, val, len, tuple, field, ENCODING_GET(result));
71
+ }
72
+ }
73
+
74
+ dec_func = pg_coder_dec_func( p_coder, PQfformat(p_result->pgresult, field) );
75
+ return dec_func( p_coder, val, len, tuple, field, ENCODING_GET(result) );
76
+ }
77
+
78
+ static t_pg_coder *
79
+ pg_tmbc_typecast_query_param(VALUE self, VALUE param_value, int field)
80
+ {
81
+ t_tmbc *this = (t_tmbc *)DATA_PTR(self);
82
+
83
+ /* Number of fields were already checked in pg_tmbc_fit_to_query() */
84
+ t_pg_coder *p_coder = this->convs[field].cconv;
85
+
86
+ return p_coder;
87
+ }
88
+
89
+ static VALUE
90
+ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
91
+ {
92
+ t_tmbc *this = (t_tmbc *) p_typemap;
93
+ t_pg_coder *p_coder;
94
+ t_pg_coder_dec_func dec_func;
95
+
96
+ if ( fieldno >= this->nfields || fieldno < 0 ) {
97
+ rb_raise( rb_eArgError, "number of copy fields (%d) exceeds number of mapped columns (%d)",
98
+ fieldno, this->nfields );
99
+ }
100
+
101
+ p_coder = this->convs[fieldno].cconv;
102
+
103
+ dec_func = pg_coder_dec_func( p_coder, format );
104
+
105
+ /* Is it a pure String conversion? Then we can directly send field_str to the user. */
106
+ if( format == 0 && dec_func == pg_text_dec_string ){
107
+ PG_ENCODING_SET_NOCHECK( field_str, enc_idx );
108
+ return field_str;
109
+ }
110
+ if( format == 1 && dec_func == pg_bin_dec_bytea ){
111
+ PG_ENCODING_SET_NOCHECK( field_str, rb_ascii8bit_encindex() );
112
+ return field_str;
113
+ }
114
+
115
+ return dec_func( p_coder, RSTRING_PTR(field_str), RSTRING_LEN(field_str), 0, fieldno, enc_idx );
116
+ }
117
+
118
+ const t_typemap pg_tmbc_default_typemap = {
119
+ .fit_to_result = pg_tmbc_fit_to_result,
120
+ .fit_to_query = pg_tmbc_fit_to_query,
121
+ .fit_to_copy_get = pg_tmbc_fit_to_copy_get,
122
+ .typecast_result_value = pg_tmbc_result_value,
123
+ .typecast_query_param = pg_tmbc_typecast_query_param,
124
+ .typecast_copy_get = pg_tmbc_typecast_copy_get
125
+ };
126
+
127
+ static void
128
+ pg_tmbc_mark( t_tmbc *this )
129
+ {
130
+ int i;
131
+
132
+ /* allocated but not initialized ? */
133
+ if( !this ) return;
134
+
135
+ for( i=0; i<this->nfields; i++){
136
+ t_pg_coder *p_coder = this->convs[i].cconv;
137
+ if( p_coder )
138
+ rb_gc_mark(p_coder->coder_obj);
139
+ }
140
+ }
141
+
142
+ static VALUE
143
+ pg_tmbc_s_allocate( VALUE klass )
144
+ {
145
+ return Data_Wrap_Struct( klass, pg_tmbc_mark, -1, NULL );
146
+ }
147
+
148
+ VALUE
149
+ pg_tmbc_allocate()
150
+ {
151
+ return pg_tmbc_s_allocate(rb_cTypeMapByColumn);
152
+ }
153
+
154
+ /*
155
+ * call-seq:
156
+ * PG::TypeMapByColumn.new( coders )
157
+ *
158
+ * Builds a new type map and assigns a list of coders for the given column.
159
+ * +coders+ must be an Array of PG::Coder objects or +nil+ values.
160
+ * The length of the Array corresponds to
161
+ * the number of columns or bind parameters this type map is usable for.
162
+ *
163
+ * A +nil+ value will cast the given field to a String object.
164
+ */
165
+ static VALUE
166
+ pg_tmbc_init(VALUE self, VALUE conv_ary)
167
+ {
168
+ int i;
169
+ t_tmbc *this;
170
+ int conv_ary_len;
171
+
172
+ Check_Type(self, T_DATA);
173
+ Check_Type(conv_ary, T_ARRAY);
174
+ conv_ary_len = RARRAY_LEN(conv_ary);
175
+ this = xmalloc(sizeof(t_tmbc) + sizeof(struct pg_tmbc_converter) * conv_ary_len);
176
+ /* Set nfields to 0 at first, so that GC mark function doesn't access uninitialized memory. */
177
+ this->nfields = 0;
178
+ this->typemap = pg_tmbc_default_typemap;
179
+ DATA_PTR(self) = this;
180
+
181
+ for(i=0; i<conv_ary_len; i++)
182
+ {
183
+ VALUE obj = rb_ary_entry(conv_ary, i);
184
+
185
+ if( obj == Qnil ){
186
+ /* no type cast */
187
+ this->convs[i].cconv = NULL;
188
+ } else if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
189
+ Data_Get_Struct(obj, t_pg_coder, this->convs[i].cconv);
190
+ } else {
191
+ rb_raise(rb_eArgError, "argument %d has invalid type %s (should be nil or some kind of PG::Coder)",
192
+ i+1, rb_obj_classname( obj ));
193
+ }
194
+ }
195
+
196
+ this->nfields = conv_ary_len;
197
+
198
+ return self;
199
+ }
200
+
201
+ /*
202
+ * call-seq:
203
+ * typemap.coders -> Array
204
+ *
205
+ * Array of PG::Coder objects. The length of the Array corresponds to
206
+ * the number of columns or bind parameters this type map is usable for.
207
+ */
208
+ static VALUE
209
+ pg_tmbc_coders(VALUE self)
210
+ {
211
+ int i;
212
+ t_tmbc *this = DATA_PTR( self );
213
+ VALUE ary_coders = rb_ary_new();
214
+
215
+ for( i=0; i<this->nfields; i++){
216
+ t_pg_coder *conv = this->convs[i].cconv;
217
+ if( conv ) {
218
+ rb_ary_push( ary_coders, conv->coder_obj );
219
+ } else {
220
+ rb_ary_push( ary_coders, Qnil );
221
+ }
222
+ }
223
+
224
+ return rb_obj_freeze(ary_coders);
225
+ }
226
+
227
+ void
228
+ init_pg_type_map_by_column()
229
+ {
230
+ s_id_decode = rb_intern("decode");
231
+ s_id_encode = rb_intern("encode");
232
+
233
+ /*
234
+ * Document-class: PG::TypeMapByColumn < PG::TypeMap
235
+ *
236
+ * This type map casts values by a coder assigned per field/column.
237
+ *
238
+ * Each PG:TypeMapByColumn has a fixed list of either encoders or decoders,
239
+ * that is defined at #new . A type map with encoders is usable for type casting
240
+ * query bind parameters and COPY data for PG::Connection#put_copy_data .
241
+ * A type map with decoders is usable for type casting of result values and
242
+ * COPY data from PG::Connection#get_copy_data .
243
+ *
244
+ * PG::TypeMapByColumns are in particular useful in conjunction with prepared statements,
245
+ * since they can be cached alongside with the statement handle.
246
+ *
247
+ * This type map strategy is also used internally by PG::TypeMapByOid, when the
248
+ * number of rows of a result set exceeds a given limit.
249
+ */
250
+ rb_cTypeMapByColumn = rb_define_class_under( rb_mPG, "TypeMapByColumn", rb_cTypeMap );
251
+ rb_define_alloc_func( rb_cTypeMapByColumn, pg_tmbc_s_allocate );
252
+ rb_define_method( rb_cTypeMapByColumn, "initialize", pg_tmbc_init, 1 );
253
+ rb_define_method( rb_cTypeMapByColumn, "coders", pg_tmbc_coders, 0 );
254
+ }