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