pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815

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.
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
+ }