pg 1.6.0.rc1-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +4 -0
  3. data/BSDL +22 -0
  4. data/Contributors.rdoc +46 -0
  5. data/Gemfile +23 -0
  6. data/History.md +958 -0
  7. data/LICENSE +56 -0
  8. data/Manifest.txt +72 -0
  9. data/POSTGRES +23 -0
  10. data/README-OS_X.rdoc +68 -0
  11. data/README-Windows.rdoc +56 -0
  12. data/README.ja.md +300 -0
  13. data/README.md +286 -0
  14. data/Rakefile +161 -0
  15. data/certs/ged.pem +24 -0
  16. data/certs/kanis@comcard.de.pem +20 -0
  17. data/certs/larskanis-2022.pem +26 -0
  18. data/certs/larskanis-2023.pem +24 -0
  19. data/certs/larskanis-2024.pem +24 -0
  20. data/ext/errorcodes.def +1043 -0
  21. data/ext/errorcodes.rb +45 -0
  22. data/ext/errorcodes.txt +494 -0
  23. data/ext/extconf.rb +282 -0
  24. data/ext/gvl_wrappers.c +32 -0
  25. data/ext/gvl_wrappers.h +297 -0
  26. data/ext/pg.c +703 -0
  27. data/ext/pg.h +390 -0
  28. data/ext/pg_binary_decoder.c +460 -0
  29. data/ext/pg_binary_encoder.c +583 -0
  30. data/ext/pg_cancel_connection.c +360 -0
  31. data/ext/pg_coder.c +622 -0
  32. data/ext/pg_connection.c +4869 -0
  33. data/ext/pg_copy_coder.c +921 -0
  34. data/ext/pg_errors.c +95 -0
  35. data/ext/pg_record_coder.c +522 -0
  36. data/ext/pg_result.c +1764 -0
  37. data/ext/pg_text_decoder.c +1008 -0
  38. data/ext/pg_text_encoder.c +833 -0
  39. data/ext/pg_tuple.c +572 -0
  40. data/ext/pg_type_map.c +200 -0
  41. data/ext/pg_type_map_all_strings.c +130 -0
  42. data/ext/pg_type_map_by_class.c +271 -0
  43. data/ext/pg_type_map_by_column.c +355 -0
  44. data/ext/pg_type_map_by_mri_type.c +313 -0
  45. data/ext/pg_type_map_by_oid.c +388 -0
  46. data/ext/pg_type_map_in_ruby.c +333 -0
  47. data/ext/pg_util.c +149 -0
  48. data/ext/pg_util.h +65 -0
  49. data/ext/vc/pg.sln +26 -0
  50. data/ext/vc/pg_18/pg.vcproj +216 -0
  51. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  52. data/lib/2.7/pg_ext.so +0 -0
  53. data/lib/3.0/pg_ext.so +0 -0
  54. data/lib/3.1/pg_ext.so +0 -0
  55. data/lib/3.2/pg_ext.so +0 -0
  56. data/lib/3.3/pg_ext.so +0 -0
  57. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  58. data/lib/pg/basic_type_map_for_queries.rb +202 -0
  59. data/lib/pg/basic_type_map_for_results.rb +104 -0
  60. data/lib/pg/basic_type_registry.rb +311 -0
  61. data/lib/pg/binary_decoder/date.rb +9 -0
  62. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  63. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  64. data/lib/pg/cancel_connection.rb +30 -0
  65. data/lib/pg/coder.rb +106 -0
  66. data/lib/pg/connection.rb +1027 -0
  67. data/lib/pg/exceptions.rb +31 -0
  68. data/lib/pg/result.rb +43 -0
  69. data/lib/pg/text_decoder/date.rb +21 -0
  70. data/lib/pg/text_decoder/inet.rb +9 -0
  71. data/lib/pg/text_decoder/json.rb +17 -0
  72. data/lib/pg/text_decoder/numeric.rb +9 -0
  73. data/lib/pg/text_decoder/timestamp.rb +30 -0
  74. data/lib/pg/text_encoder/date.rb +13 -0
  75. data/lib/pg/text_encoder/inet.rb +31 -0
  76. data/lib/pg/text_encoder/json.rb +17 -0
  77. data/lib/pg/text_encoder/numeric.rb +9 -0
  78. data/lib/pg/text_encoder/timestamp.rb +24 -0
  79. data/lib/pg/tuple.rb +30 -0
  80. data/lib/pg/type_map_by_column.rb +16 -0
  81. data/lib/pg/version.rb +4 -0
  82. data/lib/pg.rb +144 -0
  83. data/misc/openssl-pg-segfault.rb +31 -0
  84. data/misc/postgres/History.txt +9 -0
  85. data/misc/postgres/Manifest.txt +5 -0
  86. data/misc/postgres/README.txt +21 -0
  87. data/misc/postgres/Rakefile +21 -0
  88. data/misc/postgres/lib/postgres.rb +16 -0
  89. data/misc/ruby-pg/History.txt +9 -0
  90. data/misc/ruby-pg/Manifest.txt +5 -0
  91. data/misc/ruby-pg/README.txt +21 -0
  92. data/misc/ruby-pg/Rakefile +21 -0
  93. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  94. data/pg.gemspec +36 -0
  95. data/ports/x86_64-linux/lib/libpq-ruby-pg.so.1 +0 -0
  96. data/rakelib/task_extension.rb +46 -0
  97. data/sample/array_insert.rb +20 -0
  98. data/sample/async_api.rb +102 -0
  99. data/sample/async_copyto.rb +39 -0
  100. data/sample/async_mixed.rb +56 -0
  101. data/sample/check_conn.rb +21 -0
  102. data/sample/copydata.rb +71 -0
  103. data/sample/copyfrom.rb +81 -0
  104. data/sample/copyto.rb +19 -0
  105. data/sample/cursor.rb +21 -0
  106. data/sample/disk_usage_report.rb +177 -0
  107. data/sample/issue-119.rb +94 -0
  108. data/sample/losample.rb +69 -0
  109. data/sample/minimal-testcase.rb +17 -0
  110. data/sample/notify_wait.rb +72 -0
  111. data/sample/pg_statistics.rb +285 -0
  112. data/sample/replication_monitor.rb +222 -0
  113. data/sample/test_binary_values.rb +33 -0
  114. data/sample/wal_shipper.rb +434 -0
  115. data/sample/warehouse_partitions.rb +311 -0
  116. data.tar.gz.sig +0 -0
  117. metadata +252 -0
  118. metadata.gz.sig +0 -0
@@ -0,0 +1,921 @@
1
+ /*
2
+ * pg_copycoder.c - PG::Coder class extension
3
+ *
4
+ */
5
+
6
+ #include "pg.h"
7
+ #include "pg_util.h"
8
+
9
+ #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
10
+ #define OCTVALUE(c) ((c) - '0')
11
+
12
+ VALUE rb_cPG_CopyCoder;
13
+ VALUE rb_cPG_CopyEncoder;
14
+ VALUE rb_cPG_CopyDecoder;
15
+
16
+ typedef struct {
17
+ t_pg_coder comp;
18
+ VALUE typemap;
19
+ VALUE null_string;
20
+ char delimiter;
21
+ } t_pg_copycoder;
22
+
23
+
24
+ static void
25
+ pg_copycoder_mark( void *_this )
26
+ {
27
+ t_pg_copycoder *this = (t_pg_copycoder *)_this;
28
+ rb_gc_mark_movable(this->typemap);
29
+ rb_gc_mark_movable(this->null_string);
30
+ }
31
+
32
+ static size_t
33
+ pg_copycoder_memsize( const void *_this )
34
+ {
35
+ const t_pg_copycoder *this = (const t_pg_copycoder *)_this;
36
+ return sizeof(*this);
37
+ }
38
+
39
+ static void
40
+ pg_copycoder_compact( void *_this )
41
+ {
42
+ t_pg_copycoder *this = (t_pg_copycoder *)_this;
43
+ pg_coder_compact(&this->comp);
44
+ pg_gc_location(this->typemap);
45
+ pg_gc_location(this->null_string);
46
+ }
47
+
48
+ static const rb_data_type_t pg_copycoder_type = {
49
+ "PG::CopyCoder",
50
+ {
51
+ pg_copycoder_mark,
52
+ RUBY_TYPED_DEFAULT_FREE,
53
+ pg_copycoder_memsize,
54
+ pg_copycoder_compact,
55
+ },
56
+ &pg_coder_type,
57
+ 0,
58
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
59
+ };
60
+
61
+ static VALUE
62
+ pg_copycoder_encoder_allocate( VALUE klass )
63
+ {
64
+ t_pg_copycoder *this;
65
+ VALUE self = TypedData_Make_Struct( klass, t_pg_copycoder, &pg_copycoder_type, this );
66
+ pg_coder_init_encoder( self );
67
+ RB_OBJ_WRITE(self, &this->typemap, pg_typemap_all_strings);
68
+ this->delimiter = '\t';
69
+ RB_OBJ_WRITE(self, &this->null_string, rb_str_new_cstr("\\N"));
70
+ return self;
71
+ }
72
+
73
+ static VALUE
74
+ pg_copycoder_decoder_allocate( VALUE klass )
75
+ {
76
+ t_pg_copycoder *this;
77
+ VALUE self = TypedData_Make_Struct( klass, t_pg_copycoder, &pg_copycoder_type, this );
78
+ pg_coder_init_decoder( self );
79
+ RB_OBJ_WRITE(self, &this->typemap, pg_typemap_all_strings);
80
+ this->delimiter = '\t';
81
+ RB_OBJ_WRITE(self, &this->null_string, rb_str_new_cstr("\\N"));
82
+ return self;
83
+ }
84
+
85
+ /*
86
+ * call-seq:
87
+ * coder.delimiter = String
88
+ *
89
+ * Specifies the character that separates columns within each row (line) of the file.
90
+ * The default is a tab character in text format.
91
+ * This must be a single one-byte character.
92
+ *
93
+ * This option is ignored when using binary format.
94
+ */
95
+ static VALUE
96
+ pg_copycoder_delimiter_set(VALUE self, VALUE delimiter)
97
+ {
98
+ t_pg_copycoder *this = RTYPEDDATA_DATA(self);
99
+ rb_check_frozen(self);
100
+ StringValue(delimiter);
101
+ if(RSTRING_LEN(delimiter) != 1)
102
+ rb_raise( rb_eArgError, "delimiter size must be one byte");
103
+ this->delimiter = *RSTRING_PTR(delimiter);
104
+ return delimiter;
105
+ }
106
+
107
+ /*
108
+ * call-seq:
109
+ * coder.delimiter -> String
110
+ *
111
+ * The character that separates columns within each row (line) of the file.
112
+ */
113
+ static VALUE
114
+ pg_copycoder_delimiter_get(VALUE self)
115
+ {
116
+ t_pg_copycoder *this = RTYPEDDATA_DATA(self);
117
+ return rb_str_new(&this->delimiter, 1);
118
+ }
119
+
120
+ /*
121
+ * Specifies the string that represents a null value.
122
+ * The default is \\N (backslash-N) in text format.
123
+ * You might prefer an empty string even in text format for cases where you don't want to distinguish nulls from empty strings.
124
+ *
125
+ * This option is ignored when using binary format.
126
+ */
127
+ static VALUE
128
+ pg_copycoder_null_string_set(VALUE self, VALUE null_string)
129
+ {
130
+ t_pg_copycoder *this = RTYPEDDATA_DATA(self);
131
+ rb_check_frozen(self);
132
+ StringValue(null_string);
133
+ RB_OBJ_WRITE(self, &this->null_string, null_string);
134
+ return null_string;
135
+ }
136
+
137
+ /*
138
+ * The string that represents a null value.
139
+ */
140
+ static VALUE
141
+ pg_copycoder_null_string_get(VALUE self)
142
+ {
143
+ t_pg_copycoder *this = RTYPEDDATA_DATA(self);
144
+ return this->null_string;
145
+ }
146
+
147
+ /*
148
+ * call-seq:
149
+ * coder.type_map = map
150
+ *
151
+ * Defines how single columns are encoded or decoded.
152
+ * +map+ must be a kind of PG::TypeMap .
153
+ *
154
+ * Defaults to a PG::TypeMapAllStrings , so that PG::TextEncoder::String respectively
155
+ * PG::TextDecoder::String is used for encoding/decoding of each column.
156
+ *
157
+ */
158
+ static VALUE
159
+ pg_copycoder_type_map_set(VALUE self, VALUE type_map)
160
+ {
161
+ t_pg_copycoder *this = RTYPEDDATA_DATA( self );
162
+
163
+ rb_check_frozen(self);
164
+ if ( !rb_obj_is_kind_of(type_map, rb_cTypeMap) ){
165
+ rb_raise( rb_eTypeError, "wrong elements type %s (expected some kind of PG::TypeMap)",
166
+ rb_obj_classname( type_map ) );
167
+ }
168
+ RB_OBJ_WRITE(self, &this->typemap, type_map);
169
+
170
+ return type_map;
171
+ }
172
+
173
+ /*
174
+ * call-seq:
175
+ * coder.type_map -> PG::TypeMap
176
+ *
177
+ * The PG::TypeMap that will be used for encoding and decoding of columns.
178
+ */
179
+ static VALUE
180
+ pg_copycoder_type_map_get(VALUE self)
181
+ {
182
+ t_pg_copycoder *this = RTYPEDDATA_DATA( self );
183
+
184
+ return this->typemap;
185
+ }
186
+
187
+
188
+ /*
189
+ * Document-class: PG::TextEncoder::CopyRow < PG::CopyEncoder
190
+ *
191
+ * This class encodes one row of arbitrary columns for transmission as COPY data in text format.
192
+ * See the {COPY command}[http://www.postgresql.org/docs/current/static/sql-copy.html]
193
+ * for description of the format.
194
+ *
195
+ * It is intended to be used in conjunction with PG::Connection#put_copy_data .
196
+ *
197
+ * The columns are expected as Array of values. The single values are encoded as defined
198
+ * in the assigned #type_map. If no type_map was assigned, all values are converted to
199
+ * strings by PG::TextEncoder::String.
200
+ *
201
+ * Example with default type map ( TypeMapAllStrings ):
202
+ * conn.exec "create table my_table (a text,b int,c bool)"
203
+ * enco = PG::TextEncoder::CopyRow.new
204
+ * conn.copy_data "COPY my_table FROM STDIN", enco do
205
+ * conn.put_copy_data ["astring", 7, false]
206
+ * conn.put_copy_data ["string2", 42, true]
207
+ * end
208
+ * This creates +my_table+ and inserts two rows.
209
+ *
210
+ * It is possible to manually assign a type encoder for each column per PG::TypeMapByColumn,
211
+ * or to make use of PG::BasicTypeMapBasedOnResult to assign them based on the table OIDs.
212
+ *
213
+ * See also PG::TextDecoder::CopyRow for the decoding direction with
214
+ * PG::Connection#get_copy_data .
215
+ * And see PG::BinaryEncoder::CopyRow for an encoder of the COPY binary format.
216
+ */
217
+ static int
218
+ pg_text_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
219
+ {
220
+ t_pg_copycoder *this = (t_pg_copycoder *)conv;
221
+ t_pg_coder_enc_func enc_func;
222
+ static t_pg_coder *p_elem_coder;
223
+ int i;
224
+ t_typemap *p_typemap;
225
+ char *current_out;
226
+ char *end_capa_ptr;
227
+
228
+ p_typemap = RTYPEDDATA_DATA( this->typemap );
229
+ p_typemap->funcs.fit_to_query( this->typemap, value );
230
+
231
+ /* Allocate a new string with embedded capacity and realloc exponential when needed. */
232
+ PG_RB_STR_NEW( *intermediate, current_out, end_capa_ptr );
233
+ PG_ENCODING_SET_NOCHECK(*intermediate, enc_idx);
234
+
235
+ for( i=0; i<RARRAY_LEN(value); i++){
236
+ char *ptr1;
237
+ char *ptr2;
238
+ int strlen;
239
+ int backslashes;
240
+ VALUE subint;
241
+ VALUE entry;
242
+
243
+ entry = rb_ary_entry(value, i);
244
+
245
+ if( i > 0 ){
246
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
247
+ *current_out++ = this->delimiter;
248
+ }
249
+
250
+ switch(TYPE(entry)){
251
+ case T_NIL:
252
+ PG_RB_STR_ENSURE_CAPA( *intermediate, RSTRING_LEN(this->null_string), current_out, end_capa_ptr );
253
+ memcpy( current_out, RSTRING_PTR(this->null_string), RSTRING_LEN(this->null_string) );
254
+ current_out += RSTRING_LEN(this->null_string);
255
+ break;
256
+ default:
257
+ p_elem_coder = p_typemap->funcs.typecast_query_param(p_typemap, entry, i);
258
+ enc_func = pg_coder_enc_func(p_elem_coder);
259
+
260
+ /* 1st pass for retiving the required memory space */
261
+ strlen = enc_func(p_elem_coder, entry, NULL, &subint, enc_idx);
262
+
263
+ if( strlen == -1 ){
264
+ /* we can directly use String value in subint */
265
+ strlen = RSTRING_LENINT(subint);
266
+
267
+ /* size of string assuming the worst case, that every character must be escaped. */
268
+ PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2, current_out, end_capa_ptr );
269
+
270
+ /* Copy string from subint with backslash escaping */
271
+ for(ptr1 = RSTRING_PTR(subint); ptr1 < RSTRING_PTR(subint) + strlen; ptr1++) {
272
+ /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
273
+ if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
274
+ *current_out++ = '\\';
275
+ }
276
+ *current_out++ = *ptr1;
277
+ }
278
+ } else {
279
+ /* 2nd pass for writing the data to prepared buffer */
280
+ /* size of string assuming the worst case, that every character must be escaped. */
281
+ PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2, current_out, end_capa_ptr );
282
+
283
+ /* Place the unescaped string at current output position. */
284
+ strlen = enc_func(p_elem_coder, entry, current_out, &subint, enc_idx);
285
+
286
+ ptr1 = current_out;
287
+ ptr2 = current_out + strlen;
288
+
289
+ /* count required backlashs */
290
+ for(backslashes = 0; ptr1 != ptr2; ptr1++) {
291
+ /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
292
+ if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
293
+ backslashes++;
294
+ }
295
+ }
296
+
297
+ ptr1 = current_out + strlen;
298
+ ptr2 = current_out + strlen + backslashes;
299
+ current_out = ptr2;
300
+
301
+ /* Then store the escaped string on the final position, walking
302
+ * right to left, until all backslashes are placed. */
303
+ while( ptr1 != ptr2 ) {
304
+ *--ptr2 = *--ptr1;
305
+ if(*ptr1 == '\\' || *ptr1 == '\n' || *ptr1 == '\r' || *ptr1 == this->delimiter){
306
+ *--ptr2 = '\\';
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
313
+ *current_out++ = '\n';
314
+
315
+ rb_str_set_len( *intermediate, current_out - RSTRING_PTR(*intermediate) );
316
+
317
+ return -1;
318
+ }
319
+
320
+
321
+ /*
322
+ * Document-class: PG::BinaryEncoder::CopyRow < PG::CopyEncoder
323
+ *
324
+ * This class encodes one row of arbitrary columns for transmission as COPY data in binary format.
325
+ * See the {COPY command}[http://www.postgresql.org/docs/current/static/sql-copy.html]
326
+ * for description of the format.
327
+ *
328
+ * It is intended to be used in conjunction with PG::Connection#put_copy_data .
329
+ *
330
+ * The columns are expected as Array of values. The single values are encoded as defined
331
+ * in the assigned #type_map. If no type_map was assigned, all values are converted to
332
+ * strings by PG::BinaryEncoder::String.
333
+ *
334
+ * Example with default type map ( TypeMapAllStrings ):
335
+ * conn.exec "create table my_table (a text,b int,c bool)"
336
+ * enco = PG::BinaryEncoder::CopyRow.new
337
+ * conn.copy_data "COPY my_table FROM STDIN WITH (FORMAT binary)", enco do
338
+ * conn.put_copy_data ["astring", "\x00\x00\x00\a", "\x00"]
339
+ * conn.put_copy_data ["string2", "\x00\x00\x00*", "\x01"]
340
+ * end
341
+ * This creates +my_table+ and inserts two rows with binary fields.
342
+ *
343
+ * The binary format is less portable and less readable than the text format.
344
+ * It is therefore recommended to either manually assign a type encoder for each column per PG::TypeMapByColumn,
345
+ * or to make use of PG::BasicTypeMapBasedOnResult to assign them based on the table OIDs.
346
+ *
347
+ * Manually assigning a type encoder works per type map like so:
348
+ *
349
+ * conn.exec "create table my_table (a text,b int,c bool)"
350
+ * tm = PG::TypeMapByColumn.new( [
351
+ * PG::BinaryEncoder::String.new,
352
+ * PG::BinaryEncoder::Int4.new,
353
+ * PG::BinaryEncoder::Boolean.new] )
354
+ * enco = PG::BinaryEncoder::CopyRow.new( type_map: tm )
355
+ * conn.copy_data "COPY my_table FROM STDIN WITH (FORMAT binary)", enco do
356
+ * conn.put_copy_data ["astring", 7, false]
357
+ * conn.put_copy_data ["string2", 42, true]
358
+ * end
359
+ *
360
+ * See also PG::BinaryDecoder::CopyRow for the decoding direction with
361
+ * PG::Connection#get_copy_data .
362
+ * And see PG::TextEncoder::CopyRow for an encoder of the COPY text format.
363
+ */
364
+ static int
365
+ pg_bin_enc_copy_row(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
366
+ {
367
+ t_pg_copycoder *this = (t_pg_copycoder *)conv;
368
+ int i;
369
+ t_typemap *p_typemap;
370
+ char *current_out;
371
+ char *end_capa_ptr;
372
+
373
+ p_typemap = RTYPEDDATA_DATA( this->typemap );
374
+ p_typemap->funcs.fit_to_query( this->typemap, value );
375
+
376
+ /* Allocate a new string with embedded capacity and realloc exponential when needed. */
377
+ PG_RB_STR_NEW( *intermediate, current_out, end_capa_ptr );
378
+ PG_ENCODING_SET_NOCHECK(*intermediate, enc_idx);
379
+
380
+ /* 2 bytes for number of fields */
381
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 2, current_out, end_capa_ptr );
382
+ write_nbo16(RARRAY_LEN(value), current_out);
383
+ current_out += 2;
384
+
385
+ for( i=0; i<RARRAY_LEN(value); i++){
386
+ int strlen;
387
+ VALUE subint;
388
+ VALUE entry;
389
+ t_pg_coder_enc_func enc_func;
390
+ static t_pg_coder *p_elem_coder;
391
+
392
+ entry = rb_ary_entry(value, i);
393
+
394
+ switch(TYPE(entry)){
395
+ case T_NIL:
396
+ /* 4 bytes for -1 indicating a NULL value */
397
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 4, current_out, end_capa_ptr );
398
+ write_nbo32(-1, current_out);
399
+ current_out += 4;
400
+ break;
401
+ default:
402
+ p_elem_coder = p_typemap->funcs.typecast_query_param(p_typemap, entry, i);
403
+ enc_func = pg_coder_enc_func(p_elem_coder);
404
+
405
+ /* 1st pass for retiving the required memory space */
406
+ strlen = enc_func(p_elem_coder, entry, NULL, &subint, enc_idx);
407
+
408
+ if( strlen == -1 ){
409
+ /* we can directly use String value in subint */
410
+ strlen = RSTRING_LENINT(subint);
411
+
412
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 4 + strlen, current_out, end_capa_ptr );
413
+ /* 4 bytes length */
414
+ write_nbo32(strlen, current_out);
415
+ current_out += 4;
416
+
417
+ memcpy( current_out, RSTRING_PTR(subint), strlen );
418
+ current_out += strlen;
419
+ } else {
420
+ /* 2nd pass for writing the data to prepared buffer */
421
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 4 + strlen, current_out, end_capa_ptr );
422
+ /* 4 bytes length */
423
+ write_nbo32(strlen, current_out);
424
+ current_out += 4;
425
+
426
+ /* Place the string at current output position. */
427
+ strlen = enc_func(p_elem_coder, entry, current_out, &subint, enc_idx);
428
+ current_out += strlen;
429
+ }
430
+ }
431
+ }
432
+
433
+ rb_str_set_len( *intermediate, current_out - RSTRING_PTR(*intermediate) );
434
+
435
+ return -1;
436
+ }
437
+
438
+
439
+ /*
440
+ * Return decimal value for a hexadecimal digit
441
+ */
442
+ static int
443
+ GetDecimalFromHex(char hex)
444
+ {
445
+ if (hex >= '0' && hex <= '9')
446
+ return hex - '0';
447
+ else if (hex >= 'a' && hex <= 'f')
448
+ return hex - 'a' + 10;
449
+ else if (hex >= 'A' && hex <= 'F')
450
+ return hex - 'A' + 10;
451
+ else
452
+ return -1;
453
+ }
454
+
455
+ /*
456
+ * Document-class: PG::TextDecoder::CopyRow < PG::CopyDecoder
457
+ *
458
+ * This class decodes one row of arbitrary columns received as COPY data in text format.
459
+ * See the {COPY command}[http://www.postgresql.org/docs/current/static/sql-copy.html]
460
+ * for description of the format.
461
+ *
462
+ * It is intended to be used in conjunction with PG::Connection#get_copy_data .
463
+ *
464
+ * The columns are retrieved as Array of values. The single values are decoded as defined
465
+ * in the assigned #type_map. If no type_map was assigned, all values are converted to
466
+ * strings by PG::TextDecoder::String.
467
+ *
468
+ * Example with default type map ( TypeMapAllStrings ):
469
+ * conn.exec("CREATE TABLE my_table AS VALUES('astring', 7, FALSE), ('string2', 42, TRUE) ")
470
+ *
471
+ * deco = PG::TextDecoder::CopyRow.new
472
+ * conn.copy_data "COPY my_table TO STDOUT", deco do
473
+ * while row=conn.get_copy_data
474
+ * p row
475
+ * end
476
+ * end
477
+ * This prints all rows of +my_table+ :
478
+ * ["astring", "7", "f"]
479
+ * ["string2", "42", "t"]
480
+ *
481
+ * Example with column based type map:
482
+ * tm = PG::TypeMapByColumn.new( [
483
+ * PG::TextDecoder::String.new,
484
+ * PG::TextDecoder::Integer.new,
485
+ * PG::TextDecoder::Boolean.new] )
486
+ * deco = PG::TextDecoder::CopyRow.new( type_map: tm )
487
+ * conn.copy_data "COPY my_table TO STDOUT", deco do
488
+ * while row=conn.get_copy_data
489
+ * p row
490
+ * end
491
+ * end
492
+ * This prints the rows with type casted columns:
493
+ * ["astring", 7, false]
494
+ * ["string2", 42, true]
495
+ *
496
+ * Instead of manually assigning a type decoder for each column, PG::BasicTypeMapForResults
497
+ * can be used to assign them based on the table OIDs.
498
+ *
499
+ * See also PG::TextEncoder::CopyRow for the encoding direction with
500
+ * PG::Connection#put_copy_data .
501
+ * And see PG::BinaryDecoder::CopyRow for a decoder of the COPY binary format.
502
+ */
503
+ /*
504
+ * Parse the current line into separate attributes (fields),
505
+ * performing de-escaping as needed.
506
+ *
507
+ * All fields are gathered into a ruby Array. The de-escaped field data is written
508
+ * into to a ruby String. This object is reused for non string columns.
509
+ * For String columns the field value is directly used as return value and no
510
+ * reuse of the memory is done.
511
+ *
512
+ * The parser is thankfully borrowed from the PostgreSQL sources:
513
+ * src/backend/commands/copy.c
514
+ */
515
+ static VALUE
516
+ pg_text_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tuple, int _field, int enc_idx)
517
+ {
518
+ t_pg_copycoder *this = (t_pg_copycoder *)conv;
519
+
520
+ /* Return value: array */
521
+ VALUE array;
522
+
523
+ /* Current field */
524
+ VALUE field_str;
525
+
526
+ char delimc = this->delimiter;
527
+ int fieldno;
528
+ int expected_fields;
529
+ char *output_ptr;
530
+ const char *cur_ptr;
531
+ const char *line_end_ptr;
532
+ char *end_capa_ptr;
533
+ t_typemap *p_typemap;
534
+
535
+ p_typemap = RTYPEDDATA_DATA( this->typemap );
536
+ expected_fields = p_typemap->funcs.fit_to_copy_get( this->typemap );
537
+
538
+ /* The received input string will probably have this->nfields fields. */
539
+ array = rb_ary_new2(expected_fields);
540
+
541
+ /* Allocate a new string with embedded capacity and realloc later with
542
+ * exponential growing size when needed. */
543
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
544
+
545
+ /* set pointer variables for loop */
546
+ cur_ptr = input_line;
547
+ line_end_ptr = input_line + len;
548
+
549
+ /* Outer loop iterates over fields */
550
+ fieldno = 0;
551
+ for (;;)
552
+ {
553
+ int found_delim = 0;
554
+ const char *start_ptr;
555
+ const char *end_ptr;
556
+ long input_len;
557
+
558
+ /* Remember start of field on input side */
559
+ start_ptr = cur_ptr;
560
+
561
+ /*
562
+ * Scan data for field.
563
+ *
564
+ * Note that in this loop, we are scanning to locate the end of field
565
+ * and also speculatively performing de-escaping. Once we find the
566
+ * end-of-field, we can match the raw field contents against the null
567
+ * marker string. Only after that comparison fails do we know that
568
+ * de-escaping is actually the right thing to do; therefore we *must
569
+ * not* throw any syntax errors before we've done the null-marker
570
+ * check.
571
+ */
572
+ for (;;)
573
+ {
574
+ /* The current character in the input string. */
575
+ char c;
576
+
577
+ end_ptr = cur_ptr;
578
+ if (cur_ptr >= line_end_ptr)
579
+ break;
580
+ c = *cur_ptr++;
581
+ if (c == delimc){
582
+ found_delim = 1;
583
+ break;
584
+ }
585
+ if (c == '\n'){
586
+ break;
587
+ }
588
+ if (c == '\\'){
589
+ if (cur_ptr >= line_end_ptr)
590
+ break;
591
+
592
+ c = *cur_ptr++;
593
+ switch (c){
594
+ case '0':
595
+ case '1':
596
+ case '2':
597
+ case '3':
598
+ case '4':
599
+ case '5':
600
+ case '6':
601
+ case '7':
602
+ {
603
+ /* handle \013 */
604
+ int val;
605
+
606
+ val = OCTVALUE(c);
607
+ if (cur_ptr < line_end_ptr)
608
+ {
609
+ c = *cur_ptr;
610
+ if (ISOCTAL(c))
611
+ {
612
+ cur_ptr++;
613
+ val = (val << 3) + OCTVALUE(c);
614
+ if (cur_ptr < line_end_ptr)
615
+ {
616
+ c = *cur_ptr;
617
+ if (ISOCTAL(c))
618
+ {
619
+ cur_ptr++;
620
+ val = (val << 3) + OCTVALUE(c);
621
+ }
622
+ }
623
+ }
624
+ }
625
+ c = val & 0377;
626
+ }
627
+ break;
628
+ case 'x':
629
+ /* Handle \x3F */
630
+ if (cur_ptr < line_end_ptr)
631
+ {
632
+ char hexchar = *cur_ptr;
633
+ int val = GetDecimalFromHex(hexchar);;
634
+
635
+ if (val >= 0)
636
+ {
637
+ cur_ptr++;
638
+ if (cur_ptr < line_end_ptr)
639
+ {
640
+ int val2;
641
+ hexchar = *cur_ptr;
642
+ val2 = GetDecimalFromHex(hexchar);
643
+
644
+ if (val2 >= 0)
645
+ {
646
+ cur_ptr++;
647
+ val = (val << 4) + val2;
648
+ }
649
+ }
650
+ c = val & 0xff;
651
+ }
652
+ }
653
+ break;
654
+ case 'b':
655
+ c = '\b';
656
+ break;
657
+ case 'f':
658
+ c = '\f';
659
+ break;
660
+ case 'n':
661
+ c = '\n';
662
+ break;
663
+ case 'r':
664
+ c = '\r';
665
+ break;
666
+ case 't':
667
+ c = '\t';
668
+ break;
669
+ case 'v':
670
+ c = '\v';
671
+ break;
672
+
673
+ /*
674
+ * in all other cases, take the char after '\'
675
+ * literally
676
+ */
677
+ }
678
+ }
679
+
680
+ PG_RB_STR_ENSURE_CAPA( field_str, 1, output_ptr, end_capa_ptr );
681
+ /* Add c to output string */
682
+ *output_ptr++ = c;
683
+ }
684
+
685
+ if (!found_delim && cur_ptr < line_end_ptr)
686
+ rb_raise( rb_eArgError, "trailing data after linefeed at position: %ld", (long)(cur_ptr - input_line) + 1 );
687
+
688
+
689
+ /* Check whether raw input matched null marker */
690
+ input_len = end_ptr - start_ptr;
691
+ if (input_len == RSTRING_LEN(this->null_string) &&
692
+ strncmp(start_ptr, RSTRING_PTR(this->null_string), input_len) == 0) {
693
+ rb_ary_push(array, Qnil);
694
+ } else {
695
+ VALUE field_value;
696
+
697
+ rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
698
+ field_value = p_typemap->funcs.typecast_copy_get( p_typemap, field_str, fieldno, 0, enc_idx );
699
+
700
+ rb_ary_push(array, field_value);
701
+
702
+ if( field_value == field_str ){
703
+ /* Our output string will be send to the user, so we can not reuse
704
+ * it for the next field. */
705
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
706
+ }
707
+ }
708
+ /* Reset the pointer to the start of the output/buffer string. */
709
+ output_ptr = RSTRING_PTR(field_str);
710
+
711
+ fieldno++;
712
+ /* Done if we hit EOL instead of a delim */
713
+ if (!found_delim)
714
+ break;
715
+ }
716
+
717
+ return array;
718
+ }
719
+
720
+
721
+ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
722
+
723
+ /*
724
+ * Document-class: PG::BinaryDecoder::CopyRow < PG::CopyDecoder
725
+ *
726
+ * This class decodes one row of arbitrary columns received as COPY data in binary format.
727
+ * See the {COPY command}[http://www.postgresql.org/docs/current/static/sql-copy.html]
728
+ * for description of the format.
729
+ *
730
+ * It is intended to be used in conjunction with PG::Connection#get_copy_data .
731
+ *
732
+ * The columns are retrieved as Array of values. The single values are decoded as defined
733
+ * in the assigned #type_map. If no type_map was assigned, all values are converted to
734
+ * strings by PG::BinaryDecoder::String.
735
+ *
736
+ * Example with default type map ( TypeMapAllStrings ):
737
+ * conn.exec("CREATE TABLE my_table AS VALUES('astring', 7, FALSE), ('string2', 42, TRUE) ")
738
+ *
739
+ * deco = PG::BinaryDecoder::CopyRow.new
740
+ * conn.copy_data "COPY my_table TO STDOUT WITH (FORMAT binary)", deco do
741
+ * while row=conn.get_copy_data
742
+ * p row
743
+ * end
744
+ * end
745
+ * This prints all rows of +my_table+ in binary format:
746
+ * ["astring", "\x00\x00\x00\a", "\x00"]
747
+ * ["string2", "\x00\x00\x00*", "\x01"]
748
+ *
749
+ * Example with column based type map:
750
+ * tm = PG::TypeMapByColumn.new( [
751
+ * PG::BinaryDecoder::String.new,
752
+ * PG::BinaryDecoder::Integer.new,
753
+ * PG::BinaryDecoder::Boolean.new] )
754
+ * deco = PG::BinaryDecoder::CopyRow.new( type_map: tm )
755
+ * conn.copy_data "COPY my_table TO STDOUT WITH (FORMAT binary)", deco do
756
+ * while row=conn.get_copy_data
757
+ * p row
758
+ * end
759
+ * end
760
+ * This prints the rows with type casted columns:
761
+ * ["astring", 7, false]
762
+ * ["string2", 42, true]
763
+ *
764
+ * Instead of manually assigning a type decoder for each column, PG::BasicTypeMapForResults
765
+ * can be used to assign them based on the table OIDs.
766
+ *
767
+ * See also PG::BinaryEncoder::CopyRow for the encoding direction with
768
+ * PG::Connection#put_copy_data .
769
+ * And see PG::TextDecoder::CopyRow for a decoder of the COPY text format.
770
+ */
771
+ static VALUE
772
+ pg_bin_dec_copy_row(t_pg_coder *conv, const char *input_line, int len, int _tuple, int _field, int enc_idx)
773
+ {
774
+ t_pg_copycoder *this = (t_pg_copycoder *)conv;
775
+
776
+ /* Return value: array */
777
+ VALUE array;
778
+
779
+ /* Current field */
780
+ VALUE field_str;
781
+
782
+ int nfields;
783
+ int expected_fields;
784
+ int fieldno;
785
+ char *output_ptr;
786
+ const char *cur_ptr;
787
+ const char *line_end_ptr;
788
+ char *end_capa_ptr;
789
+ t_typemap *p_typemap;
790
+
791
+ p_typemap = RTYPEDDATA_DATA( this->typemap );
792
+ expected_fields = p_typemap->funcs.fit_to_copy_get( this->typemap );
793
+
794
+ /* Allocate a new string with embedded capacity and realloc later with
795
+ * exponential growing size when needed. */
796
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
797
+
798
+ /* set pointer variables for loop */
799
+ cur_ptr = input_line;
800
+ line_end_ptr = input_line + len;
801
+
802
+ if (line_end_ptr - cur_ptr >= 11 && memcmp(cur_ptr, BinarySignature, 11) == 0){
803
+ /* binary COPY header signature detected -> just drop it */
804
+ int ext_bytes;
805
+ cur_ptr += 11;
806
+
807
+ /* read flags */
808
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
809
+ cur_ptr += 4;
810
+
811
+ /* read header extensions */
812
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
813
+ ext_bytes = read_nbo32(cur_ptr);
814
+ if (ext_bytes < 0) goto length_error;
815
+ cur_ptr += 4;
816
+ if (line_end_ptr - cur_ptr < ext_bytes ) goto length_error;
817
+ cur_ptr += ext_bytes;
818
+ }
819
+
820
+ /* read row header */
821
+ if (line_end_ptr - cur_ptr < 2 ) goto length_error;
822
+ nfields = read_nbo16(cur_ptr);
823
+ cur_ptr += 2;
824
+
825
+ /* COPY data trailer? */
826
+ if (nfields < 0) {
827
+ if (nfields != -1) goto length_error;
828
+ array = Qnil;
829
+ } else {
830
+ array = rb_ary_new2(expected_fields);
831
+
832
+ for( fieldno = 0; fieldno < nfields; fieldno++){
833
+ long input_len;
834
+
835
+ /* read field size */
836
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
837
+ input_len = read_nbo32(cur_ptr);
838
+ cur_ptr += 4;
839
+
840
+ if (input_len < 0) {
841
+ if (input_len != -1) goto length_error;
842
+ /* NULL indicator */
843
+ rb_ary_push(array, Qnil);
844
+ } else {
845
+ VALUE field_value;
846
+ if (line_end_ptr - cur_ptr < input_len ) goto length_error;
847
+
848
+ /* copy input data to field_str */
849
+ PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
850
+ memcpy(output_ptr, cur_ptr, input_len);
851
+ cur_ptr += input_len;
852
+ output_ptr += input_len;
853
+ /* convert field_str through the type map */
854
+ rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
855
+ field_value = p_typemap->funcs.typecast_copy_get( p_typemap, field_str, fieldno, 1, enc_idx );
856
+
857
+ rb_ary_push(array, field_value);
858
+
859
+ if( field_value == field_str ){
860
+ /* Our output string will be send to the user, so we can not reuse
861
+ * it for the next field. */
862
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
863
+ }
864
+ }
865
+
866
+ /* Reset the pointer to the start of the output/buffer string. */
867
+ output_ptr = RSTRING_PTR(field_str);
868
+ }
869
+ }
870
+
871
+ if (cur_ptr < line_end_ptr)
872
+ rb_raise( rb_eArgError, "trailing data after row data at position: %ld", (long)(cur_ptr - input_line) + 1 );
873
+
874
+ return array;
875
+
876
+ length_error:
877
+ rb_raise( rb_eArgError, "premature end of COPY data at position: %ld", (long)(cur_ptr - input_line) + 1 );
878
+ }
879
+
880
+ void
881
+ init_pg_copycoder(void)
882
+ {
883
+ VALUE coder;
884
+ /* Document-class: PG::CopyCoder < PG::Coder
885
+ *
886
+ * This is the base class for all type cast classes for COPY data,
887
+ */
888
+ rb_cPG_CopyCoder = rb_define_class_under( rb_mPG, "CopyCoder", rb_cPG_Coder );
889
+ rb_define_method( rb_cPG_CopyCoder, "type_map=", pg_copycoder_type_map_set, 1 );
890
+ rb_define_method( rb_cPG_CopyCoder, "type_map", pg_copycoder_type_map_get, 0 );
891
+ rb_define_method( rb_cPG_CopyCoder, "delimiter=", pg_copycoder_delimiter_set, 1 );
892
+ rb_define_method( rb_cPG_CopyCoder, "delimiter", pg_copycoder_delimiter_get, 0 );
893
+ rb_define_method( rb_cPG_CopyCoder, "null_string=", pg_copycoder_null_string_set, 1 );
894
+ rb_define_method( rb_cPG_CopyCoder, "null_string", pg_copycoder_null_string_get, 0 );
895
+
896
+ /* Document-class: PG::CopyEncoder < PG::CopyCoder */
897
+ rb_cPG_CopyEncoder = rb_define_class_under( rb_mPG, "CopyEncoder", rb_cPG_CopyCoder );
898
+ rb_define_alloc_func( rb_cPG_CopyEncoder, pg_copycoder_encoder_allocate );
899
+ /* Document-class: PG::CopyDecoder < PG::CopyCoder */
900
+ rb_cPG_CopyDecoder = rb_define_class_under( rb_mPG, "CopyDecoder", rb_cPG_CopyCoder );
901
+ rb_define_alloc_func( rb_cPG_CopyDecoder, pg_copycoder_decoder_allocate );
902
+
903
+ /* Make RDoc aware of the encoder classes... */
904
+ /* rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" ); */
905
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "CopyRow", rb_cPG_CopyEncoder ); */
906
+ coder = pg_define_coder( "CopyRow", pg_text_enc_copy_row, rb_cPG_CopyEncoder, rb_mPG_TextEncoder );
907
+ rb_include_module( coder, rb_mPG_BinaryFormatting );
908
+ /* rb_mPG_BinaryEncoder = rb_define_module_under( rb_mPG, "BinaryEncoder" ); */
909
+ /* dummy = rb_define_class_under( rb_mPG_BinaryEncoder, "CopyRow", rb_cPG_CopyEncoder ); */
910
+ pg_define_coder( "CopyRow", pg_bin_enc_copy_row, rb_cPG_CopyEncoder, rb_mPG_BinaryEncoder );
911
+
912
+ /* rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" ); */
913
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "CopyRow", rb_cPG_CopyDecoder ); */
914
+ coder = pg_define_coder( "CopyRow", pg_text_dec_copy_row, rb_cPG_CopyDecoder, rb_mPG_TextDecoder );
915
+ /* Although CopyRow is a text decoder, data can contain zero bytes and are not zero terminated.
916
+ * They are handled like binaries. So format is set to 1 (binary). */
917
+ rb_include_module( coder, rb_mPG_BinaryFormatting );
918
+ /* rb_mPG_BinaryDecoder = rb_define_module_under( rb_mPG, "BinaryDecoder" ); */
919
+ /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "CopyRow", rb_cPG_CopyDecoder ); */
920
+ pg_define_coder( "CopyRow", pg_bin_dec_copy_row, rb_cPG_CopyDecoder, rb_mPG_BinaryDecoder );
921
+ }