pg 0.15.1 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/BSDL +2 -2
  5. data/ChangeLog +0 -3022
  6. data/History.rdoc +370 -4
  7. data/Manifest.txt +39 -19
  8. data/README-Windows.rdoc +17 -28
  9. data/README.ja.rdoc +1 -2
  10. data/README.rdoc +113 -14
  11. data/Rakefile +97 -36
  12. data/Rakefile.cross +109 -83
  13. data/ext/errorcodes.def +1032 -0
  14. data/ext/errorcodes.rb +45 -0
  15. data/ext/errorcodes.txt +494 -0
  16. data/ext/extconf.rb +55 -52
  17. data/ext/gvl_wrappers.c +4 -0
  18. data/ext/gvl_wrappers.h +94 -38
  19. data/ext/pg.c +273 -121
  20. data/ext/pg.h +292 -50
  21. data/ext/pg_binary_decoder.c +229 -0
  22. data/ext/pg_binary_encoder.c +163 -0
  23. data/ext/pg_coder.c +561 -0
  24. data/ext/pg_connection.c +1811 -1051
  25. data/ext/pg_copy_coder.c +599 -0
  26. data/ext/pg_errors.c +95 -0
  27. data/ext/pg_record_coder.c +491 -0
  28. data/ext/pg_result.c +917 -203
  29. data/ext/pg_text_decoder.c +987 -0
  30. data/ext/pg_text_encoder.c +814 -0
  31. data/ext/pg_tuple.c +549 -0
  32. data/ext/pg_type_map.c +166 -0
  33. data/ext/pg_type_map_all_strings.c +116 -0
  34. data/ext/pg_type_map_by_class.c +244 -0
  35. data/ext/pg_type_map_by_column.c +313 -0
  36. data/ext/pg_type_map_by_mri_type.c +284 -0
  37. data/ext/pg_type_map_by_oid.c +356 -0
  38. data/ext/pg_type_map_in_ruby.c +299 -0
  39. data/ext/pg_util.c +149 -0
  40. data/ext/pg_util.h +65 -0
  41. data/lib/pg.rb +31 -9
  42. data/lib/pg/basic_type_mapping.rb +522 -0
  43. data/lib/pg/binary_decoder.rb +23 -0
  44. data/lib/pg/coder.rb +104 -0
  45. data/lib/pg/connection.rb +235 -30
  46. data/lib/pg/constants.rb +2 -1
  47. data/lib/pg/exceptions.rb +2 -1
  48. data/lib/pg/result.rb +33 -6
  49. data/lib/pg/text_decoder.rb +46 -0
  50. data/lib/pg/text_encoder.rb +59 -0
  51. data/lib/pg/tuple.rb +30 -0
  52. data/lib/pg/type_map_by_column.rb +16 -0
  53. data/spec/{lib/helpers.rb → helpers.rb} +154 -52
  54. data/spec/pg/basic_type_mapping_spec.rb +630 -0
  55. data/spec/pg/connection_spec.rb +1352 -426
  56. data/spec/pg/connection_sync_spec.rb +41 -0
  57. data/spec/pg/result_spec.rb +508 -105
  58. data/spec/pg/tuple_spec.rb +333 -0
  59. data/spec/pg/type_map_by_class_spec.rb +138 -0
  60. data/spec/pg/type_map_by_column_spec.rb +226 -0
  61. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  62. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  63. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  64. data/spec/pg/type_map_spec.rb +22 -0
  65. data/spec/pg/type_spec.rb +1123 -0
  66. data/spec/pg_spec.rb +35 -16
  67. metadata +163 -84
  68. metadata.gz.sig +0 -0
  69. data/sample/array_insert.rb +0 -20
  70. data/sample/async_api.rb +0 -106
  71. data/sample/async_copyto.rb +0 -39
  72. data/sample/async_mixed.rb +0 -56
  73. data/sample/check_conn.rb +0 -21
  74. data/sample/copyfrom.rb +0 -81
  75. data/sample/copyto.rb +0 -19
  76. data/sample/cursor.rb +0 -21
  77. data/sample/disk_usage_report.rb +0 -186
  78. data/sample/issue-119.rb +0 -94
  79. data/sample/losample.rb +0 -69
  80. data/sample/minimal-testcase.rb +0 -17
  81. data/sample/notify_wait.rb +0 -72
  82. data/sample/pg_statistics.rb +0 -294
  83. data/sample/replication_monitor.rb +0 -231
  84. data/sample/test_binary_values.rb +0 -33
  85. data/sample/wal_shipper.rb +0 -434
  86. data/sample/warehouse_partitions.rb +0 -320
@@ -0,0 +1,163 @@
1
+ /*
2
+ * pg_column_map.c - PG::ColumnMap class extension
3
+ * $Id$
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+ #include "pg_util.h"
9
+ #ifdef HAVE_INTTYPES_H
10
+ #include <inttypes.h>
11
+ #endif
12
+
13
+ VALUE rb_mPG_BinaryEncoder;
14
+
15
+
16
+ /*
17
+ * Document-class: PG::BinaryEncoder::Boolean < PG::SimpleEncoder
18
+ *
19
+ * This is the encoder class for the PostgreSQL boolean type.
20
+ *
21
+ * It accepts true and false. Other values will raise an exception.
22
+ *
23
+ */
24
+ static int
25
+ pg_bin_enc_boolean(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
26
+ {
27
+ char mybool;
28
+ if (value == Qtrue) {
29
+ mybool = 1;
30
+ } else if (value == Qfalse) {
31
+ mybool = 0;
32
+ } else {
33
+ rb_raise( rb_eTypeError, "wrong data for binary boolean converter" );
34
+ }
35
+ if(out) *out = mybool;
36
+ return 1;
37
+ }
38
+
39
+ /*
40
+ * Document-class: PG::BinaryEncoder::Int2 < PG::SimpleEncoder
41
+ *
42
+ * This is the encoder class for the PostgreSQL +int2+ (alias +smallint+) type.
43
+ *
44
+ * Non-Number values are expected to have method +to_i+ defined.
45
+ *
46
+ */
47
+ static int
48
+ pg_bin_enc_int2(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
49
+ {
50
+ if(out){
51
+ write_nbo16(NUM2INT(*intermediate), out);
52
+ }else{
53
+ *intermediate = pg_obj_to_i(value);
54
+ }
55
+ return 2;
56
+ }
57
+
58
+ /*
59
+ * Document-class: PG::BinaryEncoder::Int4 < PG::SimpleEncoder
60
+ *
61
+ * This is the encoder class for the PostgreSQL +int4+ (alias +integer+) type.
62
+ *
63
+ * Non-Number values are expected to have method +to_i+ defined.
64
+ *
65
+ */
66
+ static int
67
+ pg_bin_enc_int4(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
68
+ {
69
+ if(out){
70
+ write_nbo32(NUM2LONG(*intermediate), out);
71
+ }else{
72
+ *intermediate = pg_obj_to_i(value);
73
+ }
74
+ return 4;
75
+ }
76
+
77
+ /*
78
+ * Document-class: PG::BinaryEncoder::Int8 < PG::SimpleEncoder
79
+ *
80
+ * This is the encoder class for the PostgreSQL +int8+ (alias +bigint+) type.
81
+ *
82
+ * Non-Number values are expected to have method +to_i+ defined.
83
+ *
84
+ */
85
+ static int
86
+ pg_bin_enc_int8(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
87
+ {
88
+ if(out){
89
+ write_nbo64(NUM2LL(*intermediate), out);
90
+ }else{
91
+ *intermediate = pg_obj_to_i(value);
92
+ }
93
+ return 8;
94
+ }
95
+
96
+ /*
97
+ * Document-class: PG::BinaryEncoder::FromBase64 < PG::CompositeEncoder
98
+ *
99
+ * This is an encoder class for conversion of base64 encoded data
100
+ * to it's binary representation.
101
+ *
102
+ */
103
+ static int
104
+ pg_bin_enc_from_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
105
+ {
106
+ int strlen;
107
+ VALUE subint;
108
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
109
+ t_pg_coder_enc_func enc_func = pg_coder_enc_func(this->elem);
110
+
111
+ if(out){
112
+ /* Second encoder pass, if required */
113
+ strlen = enc_func(this->elem, value, out, intermediate, enc_idx);
114
+ strlen = base64_decode( out, out, strlen );
115
+
116
+ return strlen;
117
+ } else {
118
+ /* First encoder pass */
119
+ strlen = enc_func(this->elem, value, NULL, &subint, enc_idx);
120
+
121
+ if( strlen == -1 ){
122
+ /* Encoded string is returned in subint */
123
+ VALUE out_str;
124
+
125
+ strlen = RSTRING_LENINT(subint);
126
+ out_str = rb_str_new(NULL, BASE64_DECODED_SIZE(strlen));
127
+
128
+ strlen = base64_decode( RSTRING_PTR(out_str), RSTRING_PTR(subint), strlen);
129
+ rb_str_set_len( out_str, strlen );
130
+ *intermediate = out_str;
131
+
132
+ return -1;
133
+ } else {
134
+ *intermediate = subint;
135
+
136
+ return BASE64_DECODED_SIZE(strlen);
137
+ }
138
+ }
139
+ }
140
+
141
+ void
142
+ init_pg_binary_encoder()
143
+ {
144
+ /* This module encapsulates all encoder classes with binary output format */
145
+ rb_mPG_BinaryEncoder = rb_define_module_under( rb_mPG, "BinaryEncoder" );
146
+
147
+ /* Make RDoc aware of the encoder classes... */
148
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
149
+ pg_define_coder( "Boolean", pg_bin_enc_boolean, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
150
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Int2", rb_cPG_SimpleEncoder ); */
151
+ pg_define_coder( "Int2", pg_bin_enc_int2, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
152
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Int4", rb_cPG_SimpleEncoder ); */
153
+ pg_define_coder( "Int4", pg_bin_enc_int4, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
154
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Int8", rb_cPG_SimpleEncoder ); */
155
+ pg_define_coder( "Int8", pg_bin_enc_int8, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
156
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "String", rb_cPG_SimpleEncoder ); */
157
+ pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
158
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
159
+ pg_define_coder( "Bytea", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_BinaryEncoder );
160
+
161
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "FromBase64", rb_cPG_CompositeEncoder ); */
162
+ pg_define_coder( "FromBase64", pg_bin_enc_from_base64, rb_cPG_CompositeEncoder, rb_mPG_BinaryEncoder );
163
+ }
data/ext/pg_coder.c ADDED
@@ -0,0 +1,561 @@
1
+ /*
2
+ * pg_coder.c - PG::Coder class extension
3
+ *
4
+ */
5
+
6
+ #include "pg.h"
7
+
8
+ VALUE rb_cPG_Coder;
9
+ VALUE rb_cPG_SimpleCoder;
10
+ VALUE rb_cPG_SimpleEncoder;
11
+ VALUE rb_cPG_SimpleDecoder;
12
+ VALUE rb_cPG_CompositeCoder;
13
+ VALUE rb_cPG_CompositeEncoder;
14
+ VALUE rb_cPG_CompositeDecoder;
15
+ VALUE rb_mPG_BinaryFormatting;
16
+ static ID s_id_encode;
17
+ static ID s_id_decode;
18
+ static ID s_id_CFUNC;
19
+
20
+ static VALUE
21
+ pg_coder_allocate( VALUE klass )
22
+ {
23
+ rb_raise( rb_eTypeError, "PG::Coder cannot be instantiated directly");
24
+ }
25
+
26
+ void
27
+ pg_coder_init_encoder( VALUE self )
28
+ {
29
+ t_pg_coder *this = DATA_PTR( self );
30
+ VALUE klass = rb_class_of(self);
31
+ if( rb_const_defined( klass, s_id_CFUNC ) ){
32
+ VALUE cfunc = rb_const_get( klass, s_id_CFUNC );
33
+ this->enc_func = DATA_PTR(cfunc);
34
+ } else {
35
+ this->enc_func = NULL;
36
+ }
37
+ this->dec_func = NULL;
38
+ this->coder_obj = self;
39
+ this->oid = 0;
40
+ this->format = 0;
41
+ this->flags = 0;
42
+ rb_iv_set( self, "@name", Qnil );
43
+ }
44
+
45
+ void
46
+ pg_coder_init_decoder( VALUE self )
47
+ {
48
+ t_pg_coder *this = DATA_PTR( self );
49
+ VALUE klass = rb_class_of(self);
50
+ this->enc_func = NULL;
51
+ if( rb_const_defined( klass, s_id_CFUNC ) ){
52
+ VALUE cfunc = rb_const_get( klass, s_id_CFUNC );
53
+ this->dec_func = DATA_PTR(cfunc);
54
+ } else {
55
+ this->dec_func = NULL;
56
+ }
57
+ this->coder_obj = self;
58
+ this->oid = 0;
59
+ this->format = 0;
60
+ this->flags = 0;
61
+ rb_iv_set( self, "@name", Qnil );
62
+ }
63
+
64
+ void
65
+ pg_coder_mark(t_pg_coder *this)
66
+ {
67
+ rb_gc_mark(this->coder_obj);
68
+ }
69
+
70
+ static void
71
+ pg_composite_coder_mark(t_pg_composite_coder *this)
72
+ {
73
+ pg_coder_mark(&this->comp);
74
+ }
75
+
76
+ static VALUE
77
+ pg_simple_encoder_allocate( VALUE klass )
78
+ {
79
+ t_pg_coder *this;
80
+ VALUE self = Data_Make_Struct( klass, t_pg_coder, pg_coder_mark, -1, this );
81
+ pg_coder_init_encoder( self );
82
+ return self;
83
+ }
84
+
85
+ static VALUE
86
+ pg_composite_encoder_allocate( VALUE klass )
87
+ {
88
+ t_pg_composite_coder *this;
89
+ VALUE self = Data_Make_Struct( klass, t_pg_composite_coder, pg_composite_coder_mark, -1, this );
90
+ pg_coder_init_encoder( self );
91
+ this->elem = NULL;
92
+ this->needs_quotation = 1;
93
+ this->delimiter = ',';
94
+ rb_iv_set( self, "@elements_type", Qnil );
95
+ return self;
96
+ }
97
+
98
+ static VALUE
99
+ pg_simple_decoder_allocate( VALUE klass )
100
+ {
101
+ t_pg_coder *this;
102
+ VALUE self = Data_Make_Struct( klass, t_pg_coder, pg_coder_mark, -1, this );
103
+ pg_coder_init_decoder( self );
104
+ return self;
105
+ }
106
+
107
+ static VALUE
108
+ pg_composite_decoder_allocate( VALUE klass )
109
+ {
110
+ t_pg_composite_coder *this;
111
+ VALUE self = Data_Make_Struct( klass, t_pg_composite_coder, pg_composite_coder_mark, -1, this );
112
+ pg_coder_init_decoder( self );
113
+ this->elem = NULL;
114
+ this->needs_quotation = 1;
115
+ this->delimiter = ',';
116
+ rb_iv_set( self, "@elements_type", Qnil );
117
+ return self;
118
+ }
119
+
120
+ /*
121
+ * call-seq:
122
+ * coder.encode( value [, encoding] )
123
+ *
124
+ * Encodes the given Ruby object into string representation, without
125
+ * sending data to/from the database server.
126
+ *
127
+ * A nil value is passed through.
128
+ *
129
+ */
130
+ static VALUE
131
+ pg_coder_encode(int argc, VALUE *argv, VALUE self)
132
+ {
133
+ VALUE res;
134
+ VALUE intermediate;
135
+ VALUE value;
136
+ int len, len2;
137
+ int enc_idx;
138
+ t_pg_coder *this = DATA_PTR(self);
139
+
140
+ if(argc < 1 || argc > 2){
141
+ rb_raise(rb_eArgError, "wrong number of arguments (%i for 1..2)", argc);
142
+ }else if(argc == 1){
143
+ enc_idx = rb_ascii8bit_encindex();
144
+ }else{
145
+ enc_idx = rb_to_encoding_index(argv[1]);
146
+ }
147
+ value = argv[0];
148
+
149
+ if( NIL_P(value) )
150
+ return Qnil;
151
+
152
+ if( !this->enc_func ){
153
+ rb_raise(rb_eRuntimeError, "no encoder function defined");
154
+ }
155
+
156
+ len = this->enc_func( this, value, NULL, &intermediate, enc_idx );
157
+
158
+ if( len == -1 ){
159
+ /* The intermediate value is a String that can be used directly. */
160
+ return intermediate;
161
+ }
162
+
163
+ res = rb_str_new(NULL, len);
164
+ PG_ENCODING_SET_NOCHECK(res, enc_idx);
165
+ len2 = this->enc_func( this, value, RSTRING_PTR(res), &intermediate, enc_idx );
166
+ if( len < len2 ){
167
+ rb_bug("%s: result length of first encoder run (%i) is less than second run (%i)",
168
+ rb_obj_classname( self ), len, len2 );
169
+ }
170
+ rb_str_set_len( res, len2 );
171
+
172
+ RB_GC_GUARD(intermediate);
173
+
174
+ return res;
175
+ }
176
+
177
+ /*
178
+ * call-seq:
179
+ * coder.decode( string, tuple=nil, field=nil )
180
+ *
181
+ * Decodes the given string representation into a Ruby object, without
182
+ * sending data to/from the database server.
183
+ *
184
+ * A nil value is passed through and non String values are expected to have
185
+ * #to_str defined.
186
+ *
187
+ */
188
+ static VALUE
189
+ pg_coder_decode(int argc, VALUE *argv, VALUE self)
190
+ {
191
+ char *val;
192
+ int tuple = -1;
193
+ int field = -1;
194
+ VALUE res;
195
+ t_pg_coder *this = DATA_PTR(self);
196
+
197
+ if(argc < 1 || argc > 3){
198
+ rb_raise(rb_eArgError, "wrong number of arguments (%i for 1..3)", argc);
199
+ }else if(argc >= 3){
200
+ tuple = NUM2INT(argv[1]);
201
+ field = NUM2INT(argv[2]);
202
+ }
203
+
204
+ if( NIL_P(argv[0]) )
205
+ return Qnil;
206
+
207
+ if( this->format == 0 ){
208
+ val = StringValueCStr(argv[0]);
209
+ }else{
210
+ val = StringValuePtr(argv[0]);
211
+ }
212
+ if( !this->dec_func ){
213
+ rb_raise(rb_eRuntimeError, "no decoder function defined");
214
+ }
215
+
216
+ res = this->dec_func(this, val, RSTRING_LEN(argv[0]), tuple, field, ENCODING_GET(argv[0]));
217
+
218
+ return res;
219
+ }
220
+
221
+ /*
222
+ * call-seq:
223
+ * coder.oid = Integer
224
+ *
225
+ * Specifies the type OID that is sent alongside with an encoded
226
+ * query parameter value.
227
+ *
228
+ * The default is +0+.
229
+ */
230
+ static VALUE
231
+ pg_coder_oid_set(VALUE self, VALUE oid)
232
+ {
233
+ t_pg_coder *this = DATA_PTR(self);
234
+ this->oid = NUM2UINT(oid);
235
+ return oid;
236
+ }
237
+
238
+ /*
239
+ * call-seq:
240
+ * coder.oid -> Integer
241
+ *
242
+ * The type OID that is sent alongside with an encoded
243
+ * query parameter value.
244
+ */
245
+ static VALUE
246
+ pg_coder_oid_get(VALUE self)
247
+ {
248
+ t_pg_coder *this = DATA_PTR(self);
249
+ return UINT2NUM(this->oid);
250
+ }
251
+
252
+ /*
253
+ * call-seq:
254
+ * coder.format = Integer
255
+ *
256
+ * Specifies the format code that is sent alongside with an encoded
257
+ * query parameter value.
258
+ *
259
+ * The default is +0+.
260
+ */
261
+ static VALUE
262
+ pg_coder_format_set(VALUE self, VALUE format)
263
+ {
264
+ t_pg_coder *this = DATA_PTR(self);
265
+ this->format = NUM2INT(format);
266
+ return format;
267
+ }
268
+
269
+ /*
270
+ * call-seq:
271
+ * coder.format -> Integer
272
+ *
273
+ * The format code that is sent alongside with an encoded
274
+ * query parameter value.
275
+ */
276
+ static VALUE
277
+ pg_coder_format_get(VALUE self)
278
+ {
279
+ t_pg_coder *this = DATA_PTR(self);
280
+ return INT2NUM(this->format);
281
+ }
282
+
283
+ /*
284
+ * call-seq:
285
+ * coder.flags = Integer
286
+ *
287
+ * Set coder specific bitwise OR-ed flags.
288
+ * See the particular en- or decoder description for available flags.
289
+ *
290
+ * The default is +0+.
291
+ */
292
+ static VALUE
293
+ pg_coder_flags_set(VALUE self, VALUE flags)
294
+ {
295
+ t_pg_coder *this = DATA_PTR(self);
296
+ this->flags = NUM2INT(flags);
297
+ return flags;
298
+ }
299
+
300
+ /*
301
+ * call-seq:
302
+ * coder.flags -> Integer
303
+ *
304
+ * Get current bitwise OR-ed coder flags.
305
+ */
306
+ static VALUE
307
+ pg_coder_flags_get(VALUE self)
308
+ {
309
+ t_pg_coder *this = DATA_PTR(self);
310
+ return INT2NUM(this->flags);
311
+ }
312
+
313
+ /*
314
+ * call-seq:
315
+ * coder.needs_quotation = Boolean
316
+ *
317
+ * Specifies whether the assigned #elements_type requires quotation marks to
318
+ * be transferred safely. Encoding with #needs_quotation=false is somewhat
319
+ * faster.
320
+ *
321
+ * The default is +true+. This option is ignored for decoding of values.
322
+ */
323
+ static VALUE
324
+ pg_coder_needs_quotation_set(VALUE self, VALUE needs_quotation)
325
+ {
326
+ t_pg_composite_coder *this = DATA_PTR(self);
327
+ this->needs_quotation = RTEST(needs_quotation);
328
+ return needs_quotation;
329
+ }
330
+
331
+ /*
332
+ * call-seq:
333
+ * coder.needs_quotation -> Boolean
334
+ *
335
+ * Specifies whether the assigned #elements_type requires quotation marks to
336
+ * be transferred safely.
337
+ */
338
+ static VALUE
339
+ pg_coder_needs_quotation_get(VALUE self)
340
+ {
341
+ t_pg_composite_coder *this = DATA_PTR(self);
342
+ return this->needs_quotation ? Qtrue : Qfalse;
343
+ }
344
+
345
+ /*
346
+ * call-seq:
347
+ * coder.delimiter = String
348
+ *
349
+ * Specifies the character that separates values within the composite type.
350
+ * The default is a comma.
351
+ * This must be a single one-byte character.
352
+ */
353
+ static VALUE
354
+ pg_coder_delimiter_set(VALUE self, VALUE delimiter)
355
+ {
356
+ t_pg_composite_coder *this = DATA_PTR(self);
357
+ StringValue(delimiter);
358
+ if(RSTRING_LEN(delimiter) != 1)
359
+ rb_raise( rb_eArgError, "delimiter size must be one byte");
360
+ this->delimiter = *RSTRING_PTR(delimiter);
361
+ return delimiter;
362
+ }
363
+
364
+ /*
365
+ * call-seq:
366
+ * coder.delimiter -> String
367
+ *
368
+ * The character that separates values within the composite type.
369
+ */
370
+ static VALUE
371
+ pg_coder_delimiter_get(VALUE self)
372
+ {
373
+ t_pg_composite_coder *this = DATA_PTR(self);
374
+ return rb_str_new(&this->delimiter, 1);
375
+ }
376
+
377
+ /*
378
+ * call-seq:
379
+ * coder.elements_type = coder
380
+ *
381
+ * Specifies the PG::Coder object that is used to encode or decode
382
+ * the single elementes of this composite type.
383
+ *
384
+ * If set to +nil+ all values are encoded and decoded as String objects.
385
+ */
386
+ static VALUE
387
+ pg_coder_elements_type_set(VALUE self, VALUE elem_type)
388
+ {
389
+ t_pg_composite_coder *this = DATA_PTR( self );
390
+
391
+ if ( NIL_P(elem_type) ){
392
+ this->elem = NULL;
393
+ } else if ( rb_obj_is_kind_of(elem_type, rb_cPG_Coder) ){
394
+ this->elem = DATA_PTR( elem_type );
395
+ } else {
396
+ rb_raise( rb_eTypeError, "wrong elements type %s (expected some kind of PG::Coder)",
397
+ rb_obj_classname( elem_type ) );
398
+ }
399
+
400
+ rb_iv_set( self, "@elements_type", elem_type );
401
+ return elem_type;
402
+ }
403
+
404
+ void
405
+ pg_define_coder( const char *name, void *func, VALUE base_klass, VALUE nsp )
406
+ {
407
+ VALUE cfunc_obj = Data_Wrap_Struct( rb_cObject, NULL, NULL, func );
408
+ VALUE coder_klass = rb_define_class_under( nsp, name, base_klass );
409
+ if( nsp==rb_mPG_BinaryEncoder || nsp==rb_mPG_BinaryDecoder )
410
+ rb_include_module( coder_klass, rb_mPG_BinaryFormatting );
411
+
412
+ if( nsp==rb_mPG_BinaryEncoder || nsp==rb_mPG_TextEncoder )
413
+ rb_define_method( coder_klass, "encode", pg_coder_encode, -1 );
414
+ if( nsp==rb_mPG_BinaryDecoder || nsp==rb_mPG_TextDecoder )
415
+ rb_define_method( coder_klass, "decode", pg_coder_decode, -1 );
416
+
417
+ rb_define_const( coder_klass, "CFUNC", cfunc_obj );
418
+
419
+ RB_GC_GUARD(cfunc_obj);
420
+ }
421
+
422
+
423
+ static int
424
+ pg_text_enc_in_ruby(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
425
+ {
426
+ int arity = rb_obj_method_arity(conv->coder_obj, s_id_encode);
427
+ if( arity == 1 ){
428
+ VALUE out_str = rb_funcall( conv->coder_obj, s_id_encode, 1, value );
429
+ StringValue( out_str );
430
+ *intermediate = rb_str_export_to_enc(out_str, rb_enc_from_index(enc_idx));
431
+ }else{
432
+ VALUE enc = rb_enc_from_encoding(rb_enc_from_index(enc_idx));
433
+ VALUE out_str = rb_funcall( conv->coder_obj, s_id_encode, 2, value, enc );
434
+ StringValue( out_str );
435
+ *intermediate = out_str;
436
+ }
437
+ return -1;
438
+ }
439
+
440
+ t_pg_coder_enc_func
441
+ pg_coder_enc_func(t_pg_coder *this)
442
+ {
443
+ if( this ){
444
+ if( this->enc_func ){
445
+ return this->enc_func;
446
+ }else{
447
+ return pg_text_enc_in_ruby;
448
+ }
449
+ }else{
450
+ /* no element encoder defined -> use std to_str conversion */
451
+ return pg_coder_enc_to_s;
452
+ }
453
+ }
454
+
455
+ static VALUE
456
+ pg_text_dec_in_ruby(t_pg_coder *this, const char *val, int len, int tuple, int field, int enc_idx)
457
+ {
458
+ VALUE string = pg_text_dec_string(this, val, len, tuple, field, enc_idx);
459
+ return rb_funcall( this->coder_obj, s_id_decode, 3, string, INT2NUM(tuple), INT2NUM(field) );
460
+ }
461
+
462
+ static VALUE
463
+ pg_bin_dec_in_ruby(t_pg_coder *this, const char *val, int len, int tuple, int field, int enc_idx)
464
+ {
465
+ VALUE string = pg_bin_dec_bytea(this, val, len, tuple, field, enc_idx);
466
+ return rb_funcall( this->coder_obj, s_id_decode, 3, string, INT2NUM(tuple), INT2NUM(field) );
467
+ }
468
+
469
+ t_pg_coder_dec_func
470
+ pg_coder_dec_func(t_pg_coder *this, int binary)
471
+ {
472
+ if( this ){
473
+ if( this->dec_func ){
474
+ return this->dec_func;
475
+ }else{
476
+ return binary ? pg_bin_dec_in_ruby : pg_text_dec_in_ruby;
477
+ }
478
+ }else{
479
+ /* no element decoder defined -> use std String conversion */
480
+ return binary ? pg_bin_dec_bytea : pg_text_dec_string;
481
+ }
482
+ }
483
+
484
+
485
+ void
486
+ init_pg_coder()
487
+ {
488
+ s_id_encode = rb_intern("encode");
489
+ s_id_decode = rb_intern("decode");
490
+ s_id_CFUNC = rb_intern("CFUNC");
491
+
492
+ /* Document-class: PG::Coder < Object
493
+ *
494
+ * This is the base class for all type cast encoder and decoder classes.
495
+ *
496
+ * It can be used for implicit type casts by a PG::TypeMap or to
497
+ * convert single values to/from their string representation by #encode
498
+ * and #decode.
499
+ *
500
+ * Ruby +nil+ values are not handled by encoders, but are always transmitted
501
+ * as SQL +NULL+ value. Vice versa SQL +NULL+ values are not handled by decoders,
502
+ * but are always returned as a +nil+ value.
503
+ */
504
+ rb_cPG_Coder = rb_define_class_under( rb_mPG, "Coder", rb_cObject );
505
+ rb_define_alloc_func( rb_cPG_Coder, pg_coder_allocate );
506
+ rb_define_method( rb_cPG_Coder, "oid=", pg_coder_oid_set, 1 );
507
+ rb_define_method( rb_cPG_Coder, "oid", pg_coder_oid_get, 0 );
508
+ rb_define_method( rb_cPG_Coder, "format=", pg_coder_format_set, 1 );
509
+ rb_define_method( rb_cPG_Coder, "format", pg_coder_format_get, 0 );
510
+ rb_define_method( rb_cPG_Coder, "flags=", pg_coder_flags_set, 1 );
511
+ rb_define_method( rb_cPG_Coder, "flags", pg_coder_flags_get, 0 );
512
+
513
+ /* define flags to be used with PG::Coder#flags= */
514
+ rb_define_const( rb_cPG_Coder, "TIMESTAMP_DB_UTC", INT2NUM(PG_CODER_TIMESTAMP_DB_UTC));
515
+ rb_define_const( rb_cPG_Coder, "TIMESTAMP_DB_LOCAL", INT2NUM(PG_CODER_TIMESTAMP_DB_LOCAL));
516
+ rb_define_const( rb_cPG_Coder, "TIMESTAMP_APP_UTC", INT2NUM(PG_CODER_TIMESTAMP_APP_UTC));
517
+ rb_define_const( rb_cPG_Coder, "TIMESTAMP_APP_LOCAL", INT2NUM(PG_CODER_TIMESTAMP_APP_LOCAL));
518
+ rb_define_const( rb_cPG_Coder, "FORMAT_ERROR_MASK", INT2NUM(PG_CODER_FORMAT_ERROR_MASK));
519
+ rb_define_const( rb_cPG_Coder, "FORMAT_ERROR_TO_RAISE", INT2NUM(PG_CODER_FORMAT_ERROR_TO_RAISE));
520
+ rb_define_const( rb_cPG_Coder, "FORMAT_ERROR_TO_STRING", INT2NUM(PG_CODER_FORMAT_ERROR_TO_STRING));
521
+ rb_define_const( rb_cPG_Coder, "FORMAT_ERROR_TO_PARTIAL", INT2NUM(PG_CODER_FORMAT_ERROR_TO_PARTIAL));
522
+
523
+ /*
524
+ * Name of the coder or the corresponding data type.
525
+ *
526
+ * This accessor is only used in PG::Coder#inspect .
527
+ */
528
+ rb_define_attr( rb_cPG_Coder, "name", 1, 1 );
529
+
530
+ /* Document-class: PG::SimpleCoder < PG::Coder */
531
+ rb_cPG_SimpleCoder = rb_define_class_under( rb_mPG, "SimpleCoder", rb_cPG_Coder );
532
+
533
+ /* Document-class: PG::SimpleEncoder < PG::SimpleCoder */
534
+ rb_cPG_SimpleEncoder = rb_define_class_under( rb_mPG, "SimpleEncoder", rb_cPG_SimpleCoder );
535
+ rb_define_alloc_func( rb_cPG_SimpleEncoder, pg_simple_encoder_allocate );
536
+ /* Document-class: PG::SimpleDecoder < PG::SimpleCoder */
537
+ rb_cPG_SimpleDecoder = rb_define_class_under( rb_mPG, "SimpleDecoder", rb_cPG_SimpleCoder );
538
+ rb_define_alloc_func( rb_cPG_SimpleDecoder, pg_simple_decoder_allocate );
539
+
540
+ /* Document-class: PG::CompositeCoder < PG::Coder
541
+ *
542
+ * This is the base class for all type cast classes of PostgreSQL types,
543
+ * that are made up of some sub type.
544
+ */
545
+ rb_cPG_CompositeCoder = rb_define_class_under( rb_mPG, "CompositeCoder", rb_cPG_Coder );
546
+ rb_define_method( rb_cPG_CompositeCoder, "elements_type=", pg_coder_elements_type_set, 1 );
547
+ rb_define_attr( rb_cPG_CompositeCoder, "elements_type", 1, 0 );
548
+ rb_define_method( rb_cPG_CompositeCoder, "needs_quotation=", pg_coder_needs_quotation_set, 1 );
549
+ rb_define_method( rb_cPG_CompositeCoder, "needs_quotation?", pg_coder_needs_quotation_get, 0 );
550
+ rb_define_method( rb_cPG_CompositeCoder, "delimiter=", pg_coder_delimiter_set, 1 );
551
+ rb_define_method( rb_cPG_CompositeCoder, "delimiter", pg_coder_delimiter_get, 0 );
552
+
553
+ /* Document-class: PG::CompositeEncoder < PG::CompositeCoder */
554
+ rb_cPG_CompositeEncoder = rb_define_class_under( rb_mPG, "CompositeEncoder", rb_cPG_CompositeCoder );
555
+ rb_define_alloc_func( rb_cPG_CompositeEncoder, pg_composite_encoder_allocate );
556
+ /* Document-class: PG::CompositeDecoder < PG::CompositeCoder */
557
+ rb_cPG_CompositeDecoder = rb_define_class_under( rb_mPG, "CompositeDecoder", rb_cPG_CompositeCoder );
558
+ rb_define_alloc_func( rb_cPG_CompositeDecoder, pg_composite_decoder_allocate );
559
+
560
+ rb_mPG_BinaryFormatting = rb_define_module_under( rb_cPG_Coder, "BinaryFormatting");
561
+ }