pg 1.3.0.rc2-x64-mingw-ucrt

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