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
@@ -0,0 +1,561 @@
1
+ /*
2
+ * pg_copycoder.c - PG::Coder class extension
3
+ *
4
+ */
5
+
6
+ #include "pg.h"
7
+
8
+ #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
9
+ #define OCTVALUE(c) ((c) - '0')
10
+
11
+ VALUE rb_cPG_CopyCoder;
12
+ VALUE rb_cPG_CopyEncoder;
13
+ VALUE rb_cPG_CopyDecoder;
14
+
15
+ typedef struct {
16
+ t_pg_coder comp;
17
+ VALUE typemap;
18
+ VALUE null_string;
19
+ char delimiter;
20
+ } t_pg_copycoder;
21
+
22
+
23
+ static void
24
+ pg_copycoder_mark( t_pg_copycoder *this )
25
+ {
26
+ rb_gc_mark(this->typemap);
27
+ rb_gc_mark(this->null_string);
28
+ }
29
+
30
+ static VALUE
31
+ pg_copycoder_encoder_allocate( VALUE klass )
32
+ {
33
+ t_pg_copycoder *this;
34
+ VALUE self = Data_Make_Struct( klass, t_pg_copycoder, pg_copycoder_mark, -1, this );
35
+ pg_coder_init_encoder( self );
36
+ this->typemap = pg_typemap_all_strings;
37
+ this->delimiter = '\t';
38
+ this->null_string = rb_str_new_cstr("\\N");
39
+ return self;
40
+ }
41
+
42
+ static VALUE
43
+ pg_copycoder_decoder_allocate( VALUE klass )
44
+ {
45
+ t_pg_copycoder *this;
46
+ VALUE self = Data_Make_Struct( klass, t_pg_copycoder, pg_copycoder_mark, -1, this );
47
+ pg_coder_init_decoder( self );
48
+ this->typemap = pg_typemap_all_strings;
49
+ this->delimiter = '\t';
50
+ this->null_string = rb_str_new_cstr("\\N");
51
+ return self;
52
+ }
53
+
54
+ /*
55
+ * call-seq:
56
+ * coder.delimiter = String
57
+ *
58
+ * Specifies the character that separates columns within each row (line) of the file.
59
+ * The default is a tab character in text format, a comma in CSV format.
60
+ * This must be a single one-byte character. This option is ignored when using binary format.
61
+ */
62
+ static VALUE
63
+ pg_copycoder_delimiter_set(VALUE self, VALUE delimiter)
64
+ {
65
+ t_pg_copycoder *this = DATA_PTR(self);
66
+ StringValue(delimiter);
67
+ if(RSTRING_LEN(delimiter) != 1)
68
+ rb_raise( rb_eArgError, "delimiter size must be one byte");
69
+ this->delimiter = *RSTRING_PTR(delimiter);
70
+ return delimiter;
71
+ }
72
+
73
+ /*
74
+ * call-seq:
75
+ * coder.delimiter -> String
76
+ *
77
+ * The character that separates columns within each row (line) of the file.
78
+ */
79
+ static VALUE
80
+ pg_copycoder_delimiter_get(VALUE self)
81
+ {
82
+ t_pg_copycoder *this = DATA_PTR(self);
83
+ return rb_str_new(&this->delimiter, 1);
84
+ }
85
+
86
+ /*
87
+ * Specifies the string that represents a null value. The default is \\N (backslash-N)
88
+ * in text format, and an unquoted empty string in CSV format. You might prefer an
89
+ * empty string even in text format for cases where you don't want to distinguish nulls
90
+ * from empty strings. This option is ignored when using binary format.
91
+ */
92
+ static VALUE
93
+ pg_copycoder_null_string_set(VALUE self, VALUE null_string)
94
+ {
95
+ t_pg_copycoder *this = DATA_PTR(self);
96
+ StringValue(null_string);
97
+ this->null_string = null_string;
98
+ return null_string;
99
+ }
100
+
101
+ /*
102
+ * The string that represents a null value.
103
+ */
104
+ static VALUE
105
+ pg_copycoder_null_string_get(VALUE self)
106
+ {
107
+ t_pg_copycoder *this = DATA_PTR(self);
108
+ return this->null_string;
109
+ }
110
+
111
+ /*
112
+ * call-seq:
113
+ * coder.type_map = map
114
+ *
115
+ * +map+ must be a kind of PG::TypeMap .
116
+ *
117
+ * Defaults to a PG::TypeMapAllStrings , so that PG::TextEncoder::String respectively
118
+ * PG::TextDecoder::String is used for encoding/decoding of all columns.
119
+ *
120
+ */
121
+ static VALUE
122
+ pg_copycoder_type_map_set(VALUE self, VALUE type_map)
123
+ {
124
+ t_pg_copycoder *this = DATA_PTR( self );
125
+
126
+ if ( !rb_obj_is_kind_of(type_map, rb_cTypeMap) ){
127
+ rb_raise( rb_eTypeError, "wrong elements type %s (expected some kind of PG::TypeMap)",
128
+ rb_obj_classname( type_map ) );
129
+ }
130
+ this->typemap = type_map;
131
+
132
+ return type_map;
133
+ }
134
+
135
+ /*
136
+ * call-seq:
137
+ * coder.type_map -> PG::TypeMap
138
+ *
139
+ */
140
+ static VALUE
141
+ pg_copycoder_type_map_get(VALUE self)
142
+ {
143
+ t_pg_copycoder *this = DATA_PTR( self );
144
+
145
+ return this->typemap;
146
+ }
147
+
148
+
149
+ /*
150
+ * Document-class: PG::TextEncoder::CopyRow < PG::CopyEncoder
151
+ *
152
+ * This class encodes one row of arbitrary columns for transmission as COPY data in text format.
153
+ * See the {COPY command}[http://www.postgresql.org/docs/current/static/sql-copy.html]
154
+ * for description of the format.
155
+ *
156
+ * It is intended to be used in conjunction with PG::Connection#put_copy_data .
157
+ *
158
+ * The columns are expected as Array of values. The single values are encoded as defined
159
+ * in the assigned #type_map. If no type_map was assigned, all values are converted to
160
+ * strings by PG::TextEncoder::String.
161
+ *
162
+ * Example with default type map ( TypeMapAllStrings ):
163
+ * conn.exec "create table my_table (a text,b int,c bool)"
164
+ * enco = PG::TextEncoder::CopyRow.new
165
+ * conn.copy_data "COPY my_table FROM STDIN", enco do
166
+ * conn.put_copy_data ["astring", 7, false]
167
+ * conn.put_copy_data ["string2", 42, true]
168
+ * end
169
+ * This creates +my_table+ and inserts two rows.
170
+ */
171
+ static int
172
+ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate)
173
+ {
174
+ t_pg_copycoder *this = (t_pg_copycoder *)conv;
175
+ t_pg_coder_enc_func enc_func;
176
+ static t_pg_coder *p_elem_coder;
177
+ int i;
178
+ t_typemap *p_typemap;
179
+ char *current_out;
180
+ char *end_capa_ptr;
181
+
182
+ p_typemap = DATA_PTR( this->typemap );
183
+ p_typemap->funcs.fit_to_query( this->typemap, value );
184
+
185
+ /* Allocate a new string with embedded capacity and realloc exponential when needed. */
186
+ PG_RB_STR_NEW( *intermediate, current_out, end_capa_ptr );
187
+
188
+ for( i=0; i<RARRAY_LEN(value); i++){
189
+ char *ptr1;
190
+ char *ptr2;
191
+ int strlen;
192
+ int backslashs;
193
+ VALUE subint;
194
+ VALUE entry;
195
+
196
+ entry = rb_ary_entry(value, i);
197
+
198
+ if( i > 0 ){
199
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
200
+ *current_out++ = this->delimiter;
201
+ }
202
+
203
+ switch(TYPE(entry)){
204
+ case T_NIL:
205
+ PG_RB_STR_ENSURE_CAPA( *intermediate, RSTRING_LEN(this->null_string), current_out, end_capa_ptr );
206
+ memcpy( current_out, RSTRING_PTR(this->null_string), RSTRING_LEN(this->null_string) );
207
+ current_out += RSTRING_LEN(this->null_string);
208
+ break;
209
+ default:
210
+ p_elem_coder = p_typemap->funcs.typecast_query_param(p_typemap, entry, i);
211
+ enc_func = pg_coder_enc_func(p_elem_coder);
212
+
213
+ /* 1st pass for retiving the required memory space */
214
+ strlen = enc_func(p_elem_coder, entry, NULL, &subint);
215
+
216
+ if( strlen == -1 ){
217
+ /* we can directly use String value in subint */
218
+ strlen = RSTRING_LEN(subint);
219
+
220
+ /* size of string assuming the worst case, that every character must be escaped. */
221
+ PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2, current_out, end_capa_ptr );
222
+
223
+ /* Copy string from subint with backslash escaping */
224
+ for(ptr1 = RSTRING_PTR(subint); ptr1 < RSTRING_PTR(subint) + strlen; ptr1++) {
225
+ /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
226
+ if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
227
+ *current_out++ = '\\';
228
+ }
229
+ *current_out++ = *ptr1;
230
+ }
231
+ } else {
232
+ /* 2nd pass for writing the data to prepared buffer */
233
+ /* size of string assuming the worst case, that every character must be escaped. */
234
+ PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2, current_out, end_capa_ptr );
235
+
236
+ /* Place the unescaped string at current output position. */
237
+ strlen = enc_func(p_elem_coder, entry, current_out, &subint);
238
+
239
+ ptr1 = current_out;
240
+ ptr2 = current_out + strlen;
241
+
242
+ /* count required backlashs */
243
+ for(backslashs = 0; ptr1 != ptr2; ptr1++) {
244
+ /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
245
+ if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
246
+ backslashs++;
247
+ }
248
+ }
249
+
250
+ ptr1 = current_out + strlen;
251
+ ptr2 = current_out + strlen + backslashs;
252
+ current_out = ptr2;
253
+
254
+ /* Then store the escaped string on the final position, walking
255
+ * right to left, until all backslashs are placed. */
256
+ while( ptr1 != ptr2 ) {
257
+ *--ptr2 = *--ptr1;
258
+ if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
259
+ *--ptr2 = '\\';
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
266
+ *current_out++ = '\n';
267
+
268
+ rb_str_set_len( *intermediate, current_out - RSTRING_PTR(*intermediate) );
269
+
270
+ return -1;
271
+ }
272
+
273
+
274
+ /*
275
+ * Return decimal value for a hexadecimal digit
276
+ */
277
+ static int
278
+ GetDecimalFromHex(char hex)
279
+ {
280
+ if (hex >= '0' && hex <= '9')
281
+ return hex - '0';
282
+ else if (hex >= 'a' && hex <= 'f')
283
+ return hex - 'a' + 10;
284
+ else if (hex >= 'A' && hex <= 'F')
285
+ return hex - 'A' + 10;
286
+ else
287
+ return -1;
288
+ }
289
+
290
+ /*
291
+ * Document-class: PG::TextDecoder::CopyRow < PG::CopyDecoder
292
+ *
293
+ * This class decodes one row of arbitrary columns received as COPY data in text format.
294
+ * See the {COPY command}[http://www.postgresql.org/docs/current/static/sql-copy.html]
295
+ * for description of the format.
296
+ *
297
+ * It is intended to be used in conjunction with PG::Connection#get_copy_data .
298
+ *
299
+ * The columns are retrieved as Array of values. The single values are decoded as defined
300
+ * in the assigned #type_map. If no type_map was assigned, all values are converted to
301
+ * strings by PG::TextDecoder::String.
302
+ *
303
+ * Example with default type map ( TypeMapAllStrings ):
304
+ * deco = PG::TextDecoder::CopyRow.new
305
+ * conn.copy_data "COPY my_table TO STDOUT", deco do
306
+ * while row=conn.get_copy_data
307
+ * p row
308
+ * end
309
+ * end
310
+ * This prints all rows of +my_table+ to stdout:
311
+ * ["astring", "7", "f"]
312
+ * ["string2", "42", "t"]
313
+ */
314
+ /*
315
+ * Parse the current line into separate attributes (fields),
316
+ * performing de-escaping as needed.
317
+ *
318
+ * All fields are gathered into a ruby Array. The de-escaped field data is written
319
+ * into to a ruby String. This object is reused for non string columns.
320
+ * For String columns the field value is directly used as return value and no
321
+ * reuse of the memory is done.
322
+ *
323
+ * The parser is thankfully borrowed from the PostgreSQL sources:
324
+ * src/backend/commands/copy.c
325
+ */
326
+ static VALUE
327
+ pg_text_dec_copy_row(t_pg_coder *conv, char *input_line, int len, int _tuple, int _field, int enc_idx)
328
+ {
329
+ t_pg_copycoder *this = (t_pg_copycoder *)conv;
330
+
331
+ /* Return value: array */
332
+ VALUE array;
333
+
334
+ /* Current field */
335
+ VALUE field_str;
336
+
337
+ char delimc = this->delimiter;
338
+ int fieldno;
339
+ int expected_fields;
340
+ char *output_ptr;
341
+ char *cur_ptr;
342
+ char *line_end_ptr;
343
+ char *end_capa_ptr;
344
+ t_typemap *p_typemap;
345
+
346
+ p_typemap = DATA_PTR( this->typemap );
347
+ expected_fields = p_typemap->funcs.fit_to_copy_get( this->typemap );
348
+
349
+ /* The received input string will probably have this->nfields fields. */
350
+ array = rb_ary_new2(expected_fields);
351
+
352
+ /* Allocate a new string with embedded capacity and realloc later with
353
+ * exponential growing size when needed. */
354
+ PG_RB_TAINTED_STR_NEW( field_str, output_ptr, end_capa_ptr );
355
+
356
+ /* set pointer variables for loop */
357
+ cur_ptr = input_line;
358
+ line_end_ptr = input_line + len;
359
+
360
+ /* Outer loop iterates over fields */
361
+ fieldno = 0;
362
+ for (;;)
363
+ {
364
+ int found_delim = 0;
365
+ char *start_ptr;
366
+ char *end_ptr;
367
+ int input_len;
368
+
369
+ /* Remember start of field on input side */
370
+ start_ptr = cur_ptr;
371
+
372
+ /*
373
+ * Scan data for field.
374
+ *
375
+ * Note that in this loop, we are scanning to locate the end of field
376
+ * and also speculatively performing de-escaping. Once we find the
377
+ * end-of-field, we can match the raw field contents against the null
378
+ * marker string. Only after that comparison fails do we know that
379
+ * de-escaping is actually the right thing to do; therefore we *must
380
+ * not* throw any syntax errors before we've done the null-marker
381
+ * check.
382
+ */
383
+ for (;;)
384
+ {
385
+ /* The current character in the input string. */
386
+ char c;
387
+
388
+ end_ptr = cur_ptr;
389
+ if (cur_ptr >= line_end_ptr)
390
+ break;
391
+ c = *cur_ptr++;
392
+ if (c == delimc){
393
+ found_delim = 1;
394
+ break;
395
+ }
396
+ if (c == '\n'){
397
+ break;
398
+ }
399
+ if (c == '\\'){
400
+ if (cur_ptr >= line_end_ptr)
401
+ break;
402
+
403
+ c = *cur_ptr++;
404
+ switch (c){
405
+ case '0':
406
+ case '1':
407
+ case '2':
408
+ case '3':
409
+ case '4':
410
+ case '5':
411
+ case '6':
412
+ case '7':
413
+ {
414
+ /* handle \013 */
415
+ int val;
416
+
417
+ val = OCTVALUE(c);
418
+ if (cur_ptr < line_end_ptr)
419
+ {
420
+ c = *cur_ptr;
421
+ if (ISOCTAL(c))
422
+ {
423
+ cur_ptr++;
424
+ val = (val << 3) + OCTVALUE(c);
425
+ if (cur_ptr < line_end_ptr)
426
+ {
427
+ c = *cur_ptr;
428
+ if (ISOCTAL(c))
429
+ {
430
+ cur_ptr++;
431
+ val = (val << 3) + OCTVALUE(c);
432
+ }
433
+ }
434
+ }
435
+ }
436
+ c = val & 0377;
437
+ }
438
+ break;
439
+ case 'x':
440
+ /* Handle \x3F */
441
+ if (cur_ptr < line_end_ptr)
442
+ {
443
+ char hexchar = *cur_ptr;
444
+ int val = GetDecimalFromHex(hexchar);;
445
+
446
+ if (val >= 0)
447
+ {
448
+ cur_ptr++;
449
+ if (cur_ptr < line_end_ptr)
450
+ {
451
+ int val2;
452
+ hexchar = *cur_ptr;
453
+ val2 = GetDecimalFromHex(hexchar);
454
+
455
+ if (val2 >= 0)
456
+ {
457
+ cur_ptr++;
458
+ val = (val << 4) + val2;
459
+ }
460
+ }
461
+ c = val & 0xff;
462
+ }
463
+ }
464
+ break;
465
+ case 'b':
466
+ c = '\b';
467
+ break;
468
+ case 'f':
469
+ c = '\f';
470
+ break;
471
+ case 'n':
472
+ c = '\n';
473
+ break;
474
+ case 'r':
475
+ c = '\r';
476
+ break;
477
+ case 't':
478
+ c = '\t';
479
+ break;
480
+ case 'v':
481
+ c = '\v';
482
+ break;
483
+
484
+ /*
485
+ * in all other cases, take the char after '\'
486
+ * literally
487
+ */
488
+ }
489
+ }
490
+
491
+ PG_RB_STR_ENSURE_CAPA( field_str, 1, output_ptr, end_capa_ptr );
492
+ /* Add c to output string */
493
+ *output_ptr++ = c;
494
+ }
495
+
496
+ if (!found_delim && cur_ptr < line_end_ptr)
497
+ rb_raise( rb_eArgError, "trailing data after linefeed at position: %ld", (long)(cur_ptr - input_line) + 1 );
498
+
499
+
500
+ /* Check whether raw input matched null marker */
501
+ input_len = end_ptr - start_ptr;
502
+ if (input_len == RSTRING_LEN(this->null_string) &&
503
+ strncmp(start_ptr, RSTRING_PTR(this->null_string), input_len) == 0) {
504
+ rb_ary_push(array, Qnil);
505
+ } else {
506
+ VALUE field_value;
507
+
508
+ rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
509
+ field_value = p_typemap->funcs.typecast_copy_get( p_typemap, field_str, fieldno, 0, enc_idx );
510
+
511
+ rb_ary_push(array, field_value);
512
+
513
+ if( field_value == field_str ){
514
+ /* Our output string will be send to the user, so we can not reuse
515
+ * it for the next field. */
516
+ PG_RB_TAINTED_STR_NEW( field_str, output_ptr, end_capa_ptr );
517
+ }
518
+ }
519
+ /* Reset the pointer to the start of the output/buffer string. */
520
+ output_ptr = RSTRING_PTR(field_str);
521
+
522
+ fieldno++;
523
+ /* Done if we hit EOL instead of a delim */
524
+ if (!found_delim)
525
+ break;
526
+ }
527
+
528
+ return array;
529
+ }
530
+
531
+
532
+ void
533
+ init_pg_copycoder()
534
+ {
535
+ /* Document-class: PG::CopyCoder < PG::Coder
536
+ *
537
+ * This is the base class for all type cast classes for COPY data,
538
+ */
539
+ rb_cPG_CopyCoder = rb_define_class_under( rb_mPG, "CopyCoder", rb_cPG_Coder );
540
+ rb_define_method( rb_cPG_CopyCoder, "type_map=", pg_copycoder_type_map_set, 1 );
541
+ rb_define_method( rb_cPG_CopyCoder, "type_map", pg_copycoder_type_map_get, 0 );
542
+ rb_define_method( rb_cPG_CopyCoder, "delimiter=", pg_copycoder_delimiter_set, 1 );
543
+ rb_define_method( rb_cPG_CopyCoder, "delimiter", pg_copycoder_delimiter_get, 0 );
544
+ rb_define_method( rb_cPG_CopyCoder, "null_string=", pg_copycoder_null_string_set, 1 );
545
+ rb_define_method( rb_cPG_CopyCoder, "null_string", pg_copycoder_null_string_get, 0 );
546
+
547
+ /* Document-class: PG::CopyEncoder < PG::CopyCoder */
548
+ rb_cPG_CopyEncoder = rb_define_class_under( rb_mPG, "CopyEncoder", rb_cPG_CopyCoder );
549
+ rb_define_alloc_func( rb_cPG_CopyEncoder, pg_copycoder_encoder_allocate );
550
+ /* Document-class: PG::CopyDecoder < PG::CopyCoder */
551
+ rb_cPG_CopyDecoder = rb_define_class_under( rb_mPG, "CopyDecoder", rb_cPG_CopyCoder );
552
+ rb_define_alloc_func( rb_cPG_CopyDecoder, pg_copycoder_decoder_allocate );
553
+
554
+ /* Make RDoc aware of the encoder classes... */
555
+ /* rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" ); */
556
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "CopyRow", rb_cPG_CopyEncoder ); */
557
+ pg_define_coder( "CopyRow", pg_text_enc_copy_row, rb_cPG_CopyEncoder, rb_mPG_TextEncoder );
558
+ /* rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" ); */
559
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "CopyRow", rb_cPG_CopyDecoder ); */
560
+ pg_define_coder( "CopyRow", pg_text_dec_copy_row, rb_cPG_CopyDecoder, rb_mPG_TextDecoder );
561
+ }
data/ext/pg_errors.c CHANGED
@@ -10,6 +10,9 @@ VALUE rb_ePGerror;
10
10
  VALUE rb_eServerError;
11
11
  VALUE rb_eUnableToSend;
12
12
  VALUE rb_eConnectionBad;
13
+ VALUE rb_eInvalidResultStatus;
14
+ VALUE rb_eNoResultError;
15
+ VALUE rb_eInvalidChangeOfResultFields;
13
16
 
14
17
  static VALUE
15
18
  define_error_class(const char *name, const char *baseclass_code)
@@ -84,6 +87,9 @@ init_pg_errors()
84
87
  rb_eServerError = rb_define_class_under( rb_mPG, "ServerError", rb_ePGerror );
85
88
  rb_eUnableToSend = rb_define_class_under( rb_mPG, "UnableToSend", rb_ePGerror );
86
89
  rb_eConnectionBad = rb_define_class_under( rb_mPG, "ConnectionBad", rb_ePGerror );
90
+ rb_eInvalidResultStatus = rb_define_class_under( rb_mPG, "InvalidResultStatus", rb_ePGerror );
91
+ rb_eNoResultError = rb_define_class_under( rb_mPG, "NoResultError", rb_ePGerror );
92
+ rb_eInvalidChangeOfResultFields = rb_define_class_under( rb_mPG, "InvalidChangeOfResultFields", rb_ePGerror );
87
93
 
88
94
  #include "errorcodes.def"
89
95
  }