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,519 @@
1
+ /*
2
+ * pg_record_coder.c - PG::Coder class extension
3
+ *
4
+ */
5
+
6
+ #include "pg.h"
7
+
8
+ VALUE rb_cPG_RecordCoder;
9
+ VALUE rb_cPG_RecordEncoder;
10
+ VALUE rb_cPG_RecordDecoder;
11
+
12
+ typedef struct {
13
+ t_pg_coder comp;
14
+ VALUE typemap;
15
+ } t_pg_recordcoder;
16
+
17
+
18
+ static void
19
+ pg_recordcoder_mark( void *_this )
20
+ {
21
+ t_pg_recordcoder *this = (t_pg_recordcoder *)_this;
22
+ rb_gc_mark_movable(this->typemap);
23
+ }
24
+
25
+ static size_t
26
+ pg_recordcoder_memsize( const void *_this )
27
+ {
28
+ const t_pg_recordcoder *this = (const t_pg_recordcoder *)_this;
29
+ return sizeof(*this);
30
+ }
31
+
32
+ static void
33
+ pg_recordcoder_compact( void *_this )
34
+ {
35
+ t_pg_recordcoder *this = (t_pg_recordcoder *)_this;
36
+ pg_coder_compact(&this->comp);
37
+ pg_gc_location(this->typemap);
38
+ }
39
+
40
+ static const rb_data_type_t pg_recordcoder_type = {
41
+ "PG::RecordCoder",
42
+ {
43
+ pg_recordcoder_mark,
44
+ RUBY_TYPED_DEFAULT_FREE,
45
+ pg_recordcoder_memsize,
46
+ pg_compact_callback(pg_recordcoder_compact),
47
+ },
48
+ &pg_coder_type,
49
+ 0,
50
+ RUBY_TYPED_FREE_IMMEDIATELY,
51
+ };
52
+
53
+ static VALUE
54
+ pg_recordcoder_encoder_allocate( VALUE klass )
55
+ {
56
+ t_pg_recordcoder *this;
57
+ VALUE self = TypedData_Make_Struct( klass, t_pg_recordcoder, &pg_recordcoder_type, this );
58
+ pg_coder_init_encoder( self );
59
+ this->typemap = pg_typemap_all_strings;
60
+ return self;
61
+ }
62
+
63
+ static VALUE
64
+ pg_recordcoder_decoder_allocate( VALUE klass )
65
+ {
66
+ t_pg_recordcoder *this;
67
+ VALUE self = TypedData_Make_Struct( klass, t_pg_recordcoder, &pg_recordcoder_type, this );
68
+ pg_coder_init_decoder( self );
69
+ this->typemap = pg_typemap_all_strings;
70
+ return self;
71
+ }
72
+
73
+ /*
74
+ * call-seq:
75
+ * coder.type_map = map
76
+ *
77
+ * Defines how single columns are encoded or decoded.
78
+ * +map+ must be a kind of PG::TypeMap .
79
+ *
80
+ * Defaults to a PG::TypeMapAllStrings , so that PG::TextEncoder::String respectively
81
+ * PG::TextDecoder::String is used for encoding/decoding of each column.
82
+ *
83
+ */
84
+ static VALUE
85
+ pg_recordcoder_type_map_set(VALUE self, VALUE type_map)
86
+ {
87
+ t_pg_recordcoder *this = RTYPEDDATA_DATA( self );
88
+
89
+ if ( !rb_obj_is_kind_of(type_map, rb_cTypeMap) ){
90
+ rb_raise( rb_eTypeError, "wrong elements type %s (expected some kind of PG::TypeMap)",
91
+ rb_obj_classname( type_map ) );
92
+ }
93
+ this->typemap = type_map;
94
+
95
+ return type_map;
96
+ }
97
+
98
+ /*
99
+ * call-seq:
100
+ * coder.type_map -> PG::TypeMap
101
+ *
102
+ * The PG::TypeMap that will be used for encoding and decoding of columns.
103
+ */
104
+ static VALUE
105
+ pg_recordcoder_type_map_get(VALUE self)
106
+ {
107
+ t_pg_recordcoder *this = RTYPEDDATA_DATA( self );
108
+
109
+ return this->typemap;
110
+ }
111
+
112
+
113
+ /*
114
+ * Document-class: PG::TextEncoder::Record < PG::RecordEncoder
115
+ *
116
+ * This class encodes one record of columns for transmission as query parameter in text format.
117
+ * See PostgreSQL {Composite Types}[https://www.postgresql.org/docs/current/rowtypes.html] for a description of the format and how it can be used.
118
+ *
119
+ * PostgreSQL allows composite types to be used in many of the same ways that simple types can be used.
120
+ * For example, a column of a table can be declared to be of a composite type.
121
+ *
122
+ * The encoder expects the record columns as array of values.
123
+ * The single values are encoded as defined in the assigned #type_map.
124
+ * If no type_map was assigned, all values are converted to strings by PG::TextEncoder::String.
125
+ *
126
+ * It is possible to manually assign a type encoder for each column per PG::TypeMapByColumn,
127
+ * or to make use of PG::BasicTypeMapBasedOnResult to assign them based on the table OIDs.
128
+ *
129
+ * Encode a record from an <code>Array<String></code> to a +String+ in PostgreSQL Composite Type format (uses default type map TypeMapAllStrings):
130
+ * PG::TextEncoder::Record.new.encode([1, 2]) # => "(\"1\",\"2\")"
131
+ *
132
+ * Encode a record from <code>Array<Float></code> to +String+ :
133
+ * # Build a type map for two Floats
134
+ * tm = PG::TypeMapByColumn.new([PG::TextEncoder::Float.new]*2)
135
+ * # Use this type map to encode the record:
136
+ * PG::TextEncoder::Record.new(type_map: tm).encode([1,2])
137
+ * # => "(\"1.0000000000000000E+00\",\"2.0000000000000000E+00\")"
138
+ *
139
+ * Records can also be encoded and decoded directly to and from the database.
140
+ * This avoids intermediate string allocations and is very fast.
141
+ * Take the following type and table definitions:
142
+ * conn.exec("CREATE TYPE complex AS (r float, i float) ")
143
+ * conn.exec("CREATE TABLE my_table (v1 complex, v2 complex) ")
144
+ *
145
+ * A record can be encoded by adding a type map to Connection#exec_params and siblings:
146
+ * # Build a type map for the two floats "r" and "i" as in our "complex" type
147
+ * tm = PG::TypeMapByColumn.new([PG::TextEncoder::Float.new]*2)
148
+ * # Build a record encoder to encode this type as a record:
149
+ * enco = PG::TextEncoder::Record.new(type_map: tm)
150
+ * # Insert table data and use the encoder to cast the complex value "v1" from ruby array:
151
+ * conn.exec_params("INSERT INTO my_table VALUES ($1) RETURNING v1", [[1,2]], 0, PG::TypeMapByColumn.new([enco])).to_a
152
+ * # => [{"v1"=>"(1,2)"}]
153
+ *
154
+ * Alternatively the typemap can be build based on database OIDs rather than manually assigning encoders.
155
+ * # Fetch a NULL record of our type to retrieve the OIDs of the two fields "r" and "i"
156
+ * oids = conn.exec( "SELECT (NULL::complex).*" )
157
+ * # Build a type map (PG::TypeMapByColumn) for encoding the "complex" type
158
+ * etm = PG::BasicTypeMapBasedOnResult.new(conn).build_column_map( oids )
159
+ *
160
+ * It's also possible to use the BasicTypeMapForQueries to send records to the database server.
161
+ * In contrast to ORM libraries, PG doesn't have information regarding the type of data the server is expecting.
162
+ * So BasicTypeMapForQueries works based on the class of the values to be sent and it has to be instructed that a ruby array shall be casted to a record.
163
+ * # Retrieve OIDs of all basic types from the database
164
+ * etm = PG::BasicTypeMapForQueries.new(conn)
165
+ * etm.encode_array_as = :record
166
+ * # Apply the basic type registry to all values sent to the server
167
+ * conn.type_map_for_queries = etm
168
+ * # Send a complex number as an array of two integers
169
+ * conn.exec_params("INSERT INTO my_table VALUES ($1) RETURNING v1", [[1,2]]).to_a
170
+ * # => [{"v1"=>"(1,2)"}]
171
+ *
172
+ * Records can also be nested or further wrapped into other encoders like PG::TextEncoder::CopyRow.
173
+ *
174
+ * See also PG::TextDecoder::Record for the decoding direction.
175
+ */
176
+ static int
177
+ pg_text_enc_record(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate, int enc_idx)
178
+ {
179
+ t_pg_recordcoder *this = (t_pg_recordcoder *)conv;
180
+ t_pg_coder_enc_func enc_func;
181
+ static t_pg_coder *p_elem_coder;
182
+ int i;
183
+ t_typemap *p_typemap;
184
+ char *current_out;
185
+ char *end_capa_ptr;
186
+
187
+ p_typemap = RTYPEDDATA_DATA( this->typemap );
188
+ p_typemap->funcs.fit_to_query( this->typemap, value );
189
+
190
+ /* Allocate a new string with embedded capacity and realloc exponential when needed. */
191
+ PG_RB_STR_NEW( *intermediate, current_out, end_capa_ptr );
192
+ PG_ENCODING_SET_NOCHECK(*intermediate, enc_idx);
193
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
194
+ *current_out++ = '(';
195
+
196
+ for( i=0; i<RARRAY_LEN(value); i++){
197
+ char *ptr1;
198
+ char *ptr2;
199
+ long strlen;
200
+ int backslashs;
201
+ VALUE subint;
202
+ VALUE entry;
203
+
204
+ entry = rb_ary_entry(value, i);
205
+
206
+ if( i > 0 ){
207
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
208
+ *current_out++ = ',';
209
+ }
210
+
211
+ switch(TYPE(entry)){
212
+ case T_NIL:
213
+ /* emit nothing... */
214
+ break;
215
+ default:
216
+ p_elem_coder = p_typemap->funcs.typecast_query_param(p_typemap, entry, i);
217
+ enc_func = pg_coder_enc_func(p_elem_coder);
218
+
219
+ /* 1st pass for retiving the required memory space */
220
+ strlen = enc_func(p_elem_coder, entry, NULL, &subint, enc_idx);
221
+
222
+ if( strlen == -1 ){
223
+ /* we can directly use String value in subint */
224
+ strlen = RSTRING_LEN(subint);
225
+
226
+ /* size of string assuming the worst case, that every character must be escaped. */
227
+ PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2 + 2, current_out, end_capa_ptr );
228
+
229
+ *current_out++ = '"';
230
+ /* Record string from subint with backslash escaping */
231
+ for(ptr1 = RSTRING_PTR(subint); ptr1 < RSTRING_PTR(subint) + strlen; ptr1++) {
232
+ if (*ptr1 == '"' || *ptr1 == '\\') {
233
+ *current_out++ = *ptr1;
234
+ }
235
+ *current_out++ = *ptr1;
236
+ }
237
+ *current_out++ = '"';
238
+ } else {
239
+ /* 2nd pass for writing the data to prepared buffer */
240
+ /* size of string assuming the worst case, that every character must be escaped. */
241
+ PG_RB_STR_ENSURE_CAPA( *intermediate, strlen * 2 + 2, current_out, end_capa_ptr );
242
+
243
+ *current_out++ = '"';
244
+ /* Place the unescaped string at current output position. */
245
+ strlen = enc_func(p_elem_coder, entry, current_out, &subint, enc_idx);
246
+
247
+ ptr1 = current_out;
248
+ ptr2 = current_out + strlen;
249
+
250
+ /* count required backlashs */
251
+ for(backslashs = 0; ptr1 != ptr2; ptr1++) {
252
+ /* Escape backslash itself, newline, carriage return, and the current delimiter character. */
253
+ if(*ptr1 == '"' || *ptr1 == '\\'){
254
+ backslashs++;
255
+ }
256
+ }
257
+
258
+ ptr1 = current_out + strlen;
259
+ ptr2 = current_out + strlen + backslashs;
260
+ current_out = ptr2;
261
+
262
+ /* Then store the escaped string on the final position, walking
263
+ * right to left, until all backslashs are placed. */
264
+ while( ptr1 != ptr2 ) {
265
+ *--ptr2 = *--ptr1;
266
+ if(*ptr1 == '"' || *ptr1 == '\\'){
267
+ *--ptr2 = *ptr1;
268
+ }
269
+ }
270
+ *current_out++ = '"';
271
+ }
272
+ }
273
+ }
274
+ PG_RB_STR_ENSURE_CAPA( *intermediate, 1, current_out, end_capa_ptr );
275
+ *current_out++ = ')';
276
+
277
+ rb_str_set_len( *intermediate, current_out - RSTRING_PTR(*intermediate) );
278
+
279
+ return -1;
280
+ }
281
+
282
+ /*
283
+ * record_isspace() --- a non-locale-dependent isspace()
284
+ *
285
+ * We used to use isspace() for parsing array values, but that has
286
+ * undesirable results: an array value might be silently interpreted
287
+ * differently depending on the locale setting. Now we just hard-wire
288
+ * the traditional ASCII definition of isspace().
289
+ */
290
+ static int
291
+ record_isspace(char ch)
292
+ {
293
+ if (ch == ' ' ||
294
+ ch == '\t' ||
295
+ ch == '\n' ||
296
+ ch == '\r' ||
297
+ ch == '\v' ||
298
+ ch == '\f')
299
+ return 1;
300
+ return 0;
301
+ }
302
+
303
+ /*
304
+ * Document-class: PG::TextDecoder::Record < PG::RecordDecoder
305
+ *
306
+ * This class decodes one record of values received from a composite type column in text format.
307
+ * See PostgreSQL {Composite Types}[https://www.postgresql.org/docs/current/rowtypes.html] for a description of the format and how it can be used.
308
+ *
309
+ * PostgreSQL allows composite types to be used in many of the same ways that simple types can be used.
310
+ * For example, a column of a table can be declared to be of a composite type.
311
+ *
312
+ * The columns are returned from the decoder as array of values.
313
+ * The single values are decoded as defined in the assigned #type_map.
314
+ * If no type_map was assigned, all values are converted to strings by PG::TextDecoder::String.
315
+ *
316
+ * Decode a record in Composite Type format from +String+ to <code>Array<String></code> (uses default type map TypeMapAllStrings):
317
+ * PG::TextDecoder::Record.new.decode("(1,2)") # => ["1", "2"]
318
+ *
319
+ * Decode a record from +String+ to <code>Array<Float></code> :
320
+ * # Build a type map for two Floats
321
+ * tm = PG::TypeMapByColumn.new([PG::TextDecoder::Float.new]*2)
322
+ * # Use this type map to decode the record:
323
+ * PG::TextDecoder::Record.new(type_map: tm).decode("(1,2)")
324
+ * # => [1.0, 2.0]
325
+ *
326
+ * Records can also be encoded and decoded directly to and from the database.
327
+ * This avoids intermediate String allocations and is very fast.
328
+ * Take the following type and table definitions:
329
+ * conn.exec("CREATE TYPE complex AS (r float, i float) ")
330
+ * conn.exec("CREATE TABLE my_table (v1 complex, v2 complex) ")
331
+ * conn.exec("INSERT INTO my_table VALUES((2,3), (4,5)), ((6,7), (8,9)) ")
332
+ *
333
+ * The record can be decoded by applying a type map to the PG::Result object:
334
+ * # Build a type map for two floats "r" and "i"
335
+ * tm = PG::TypeMapByColumn.new([PG::TextDecoder::Float.new]*2)
336
+ * # Build a record decoder to decode this two-value type:
337
+ * deco = PG::TextDecoder::Record.new(type_map: tm)
338
+ * # Fetch table data and use the decoder to cast the two complex values "v1" and "v2":
339
+ * conn.exec("SELECT * FROM my_table").map_types!(PG::TypeMapByColumn.new([deco]*2)).to_a
340
+ * # => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]
341
+ *
342
+ * It's more very convenient to use the PG::BasicTypeRegistry, which is based on database OIDs.
343
+ * # Fetch a NULL record of our type to retrieve the OIDs of the two fields "r" and "i"
344
+ * oids = conn.exec( "SELECT (NULL::complex).*" )
345
+ * # Build a type map (PG::TypeMapByColumn) for decoding the "complex" type
346
+ * dtm = PG::BasicTypeMapForResults.new(conn).build_column_map( oids )
347
+ * # Register a record decoder for decoding our type "complex"
348
+ * PG::BasicTypeRegistry.register_coder(PG::TextDecoder::Record.new(type_map: dtm, name: "complex"))
349
+ * # Apply the basic type registry to all results retrieved from the server
350
+ * conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
351
+ * # Now queries decode the "complex" type (and many basic types) automatically
352
+ * conn.exec("SELECT * FROM my_table").to_a
353
+ * # => [{"v1"=>[2.0, 3.0], "v2"=>[4.0, 5.0]}, {"v1"=>[6.0, 7.0], "v2"=>[8.0, 9.0]}]
354
+ *
355
+ * Records can also be nested or further wrapped into other decoders like PG::TextDecoder::CopyRow.
356
+ *
357
+ * See also PG::TextEncoder::Record for the encoding direction (data sent to the server).
358
+ */
359
+ /*
360
+ * Parse the current line into separate attributes (fields),
361
+ * performing de-escaping as needed.
362
+ *
363
+ * All fields are gathered into a ruby Array. The de-escaped field data is written
364
+ * into to a ruby String. This object is reused for non string columns.
365
+ * For String columns the field value is directly used as return value and no
366
+ * reuse of the memory is done.
367
+ *
368
+ * The parser is thankfully borrowed from the PostgreSQL sources:
369
+ * src/backend/utils/adt/rowtypes.c
370
+ */
371
+ static VALUE
372
+ pg_text_dec_record(t_pg_coder *conv, char *input_line, int len, int _tuple, int _field, int enc_idx)
373
+ {
374
+ t_pg_recordcoder *this = (t_pg_recordcoder *)conv;
375
+
376
+ /* Return value: array */
377
+ VALUE array;
378
+
379
+ /* Current field */
380
+ VALUE field_str;
381
+
382
+ int fieldno;
383
+ int expected_fields;
384
+ char *output_ptr;
385
+ char *cur_ptr;
386
+ char *end_capa_ptr;
387
+ t_typemap *p_typemap;
388
+
389
+ p_typemap = RTYPEDDATA_DATA( this->typemap );
390
+ expected_fields = p_typemap->funcs.fit_to_copy_get( this->typemap );
391
+
392
+ /* The received input string will probably have this->nfields fields. */
393
+ array = rb_ary_new2(expected_fields);
394
+
395
+ /* Allocate a new string with embedded capacity and realloc later with
396
+ * exponential growing size when needed. */
397
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
398
+
399
+ /* set pointer variables for loop */
400
+ cur_ptr = input_line;
401
+
402
+ /*
403
+ * Scan the string. We use "buf" to accumulate the de-quoted data for
404
+ * each column, which is then fed to the appropriate input converter.
405
+ */
406
+ /* Allow leading whitespace */
407
+ while (*cur_ptr && record_isspace(*cur_ptr))
408
+ cur_ptr++;
409
+ if (*cur_ptr++ != '(')
410
+ rb_raise( rb_eArgError, "malformed record literal: \"%s\" - Missing left parenthesis.", input_line );
411
+
412
+ for (fieldno = 0; ; fieldno++)
413
+ {
414
+ /* Check for null: completely empty input means null */
415
+ if (*cur_ptr == ',' || *cur_ptr == ')')
416
+ {
417
+ rb_ary_push(array, Qnil);
418
+ }
419
+ else
420
+ {
421
+ /* Extract string for this column */
422
+ int inquote = 0;
423
+ VALUE field_value;
424
+
425
+ while (inquote || !(*cur_ptr == ',' || *cur_ptr == ')'))
426
+ {
427
+ char ch = *cur_ptr++;
428
+
429
+ if (ch == '\0')
430
+ rb_raise( rb_eArgError, "malformed record literal: \"%s\" - Unexpected end of input.", input_line );
431
+ if (ch == '\\')
432
+ {
433
+ if (*cur_ptr == '\0')
434
+ rb_raise( rb_eArgError, "malformed record literal: \"%s\" - Unexpected end of input.", input_line );
435
+ PG_RB_STR_ENSURE_CAPA( field_str, 1, output_ptr, end_capa_ptr );
436
+ *output_ptr++ = *cur_ptr++;
437
+ }
438
+ else if (ch == '"')
439
+ {
440
+ if (!inquote)
441
+ inquote = 1;
442
+ else if (*cur_ptr == '"')
443
+ {
444
+ /* doubled quote within quote sequence */
445
+ PG_RB_STR_ENSURE_CAPA( field_str, 1, output_ptr, end_capa_ptr );
446
+ *output_ptr++ = *cur_ptr++;
447
+ }
448
+ else
449
+ inquote = 0;
450
+ } else {
451
+ PG_RB_STR_ENSURE_CAPA( field_str, 1, output_ptr, end_capa_ptr );
452
+ /* Add ch to output string */
453
+ *output_ptr++ = ch;
454
+ }
455
+ }
456
+
457
+ /* Convert the column value */
458
+ rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
459
+ field_value = p_typemap->funcs.typecast_copy_get( p_typemap, field_str, fieldno, 0, enc_idx );
460
+
461
+ rb_ary_push(array, field_value);
462
+
463
+ if( field_value == field_str ){
464
+ /* Our output string will be send to the user, so we can not reuse
465
+ * it for the next field. */
466
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
467
+ }
468
+ /* Reset the pointer to the start of the output/buffer string. */
469
+ output_ptr = RSTRING_PTR(field_str);
470
+ }
471
+
472
+ /* Skip comma that separates prior field from this one */
473
+ if (*cur_ptr == ',') {
474
+ cur_ptr++;
475
+ } else if (*cur_ptr == ')') {
476
+ cur_ptr++;
477
+ /* Done if we hit closing parenthesis */
478
+ break;
479
+ } else {
480
+ rb_raise( rb_eArgError, "malformed record literal: \"%s\" - Too few columns.", input_line );
481
+ }
482
+ }
483
+
484
+ /* Allow trailing whitespace */
485
+ while (*cur_ptr && record_isspace(*cur_ptr))
486
+ cur_ptr++;
487
+ if (*cur_ptr)
488
+ rb_raise( rb_eArgError, "malformed record literal: \"%s\" - Junk after right parenthesis.", input_line );
489
+
490
+ return array;
491
+ }
492
+
493
+
494
+ void
495
+ init_pg_recordcoder()
496
+ {
497
+ /* Document-class: PG::RecordCoder < PG::Coder
498
+ *
499
+ * This is the base class for all type cast classes for COPY data,
500
+ */
501
+ rb_cPG_RecordCoder = rb_define_class_under( rb_mPG, "RecordCoder", rb_cPG_Coder );
502
+ rb_define_method( rb_cPG_RecordCoder, "type_map=", pg_recordcoder_type_map_set, 1 );
503
+ rb_define_method( rb_cPG_RecordCoder, "type_map", pg_recordcoder_type_map_get, 0 );
504
+
505
+ /* Document-class: PG::RecordEncoder < PG::RecordCoder */
506
+ rb_cPG_RecordEncoder = rb_define_class_under( rb_mPG, "RecordEncoder", rb_cPG_RecordCoder );
507
+ rb_define_alloc_func( rb_cPG_RecordEncoder, pg_recordcoder_encoder_allocate );
508
+ /* Document-class: PG::RecordDecoder < PG::RecordCoder */
509
+ rb_cPG_RecordDecoder = rb_define_class_under( rb_mPG, "RecordDecoder", rb_cPG_RecordCoder );
510
+ rb_define_alloc_func( rb_cPG_RecordDecoder, pg_recordcoder_decoder_allocate );
511
+
512
+ /* Make RDoc aware of the encoder classes... */
513
+ /* rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" ); */
514
+ /* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Record", rb_cPG_RecordEncoder ); */
515
+ pg_define_coder( "Record", pg_text_enc_record, rb_cPG_RecordEncoder, rb_mPG_TextEncoder );
516
+ /* rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" ); */
517
+ /* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Record", rb_cPG_RecordDecoder ); */
518
+ pg_define_coder( "Record", pg_text_dec_record, rb_cPG_RecordDecoder, rb_mPG_TextDecoder );
519
+ }