pg 0.18.1 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +141 -0
  7. data/.gitignore +22 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/BSDL +2 -2
  15. data/Gemfile +17 -0
  16. data/History.md +901 -0
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.md +300 -0
  20. data/README.md +286 -0
  21. data/Rakefile +43 -131
  22. data/Rakefile.cross +89 -70
  23. data/certs/ged.pem +24 -0
  24. data/certs/kanis@comcard.de.pem +20 -0
  25. data/certs/larskanis-2022.pem +26 -0
  26. data/certs/larskanis-2023.pem +24 -0
  27. data/certs/larskanis-2024.pem +24 -0
  28. data/ext/errorcodes.def +113 -0
  29. data/ext/errorcodes.rb +1 -1
  30. data/ext/errorcodes.txt +36 -2
  31. data/ext/extconf.rb +128 -55
  32. data/ext/gvl_wrappers.c +8 -0
  33. data/ext/gvl_wrappers.h +44 -33
  34. data/ext/pg.c +228 -202
  35. data/ext/pg.h +108 -99
  36. data/ext/pg_binary_decoder.c +164 -16
  37. data/ext/pg_binary_encoder.c +249 -22
  38. data/ext/pg_coder.c +189 -44
  39. data/ext/pg_connection.c +1889 -1195
  40. data/ext/pg_copy_coder.c +398 -42
  41. data/ext/pg_errors.c +1 -1
  42. data/ext/pg_record_coder.c +522 -0
  43. data/ext/pg_result.c +729 -234
  44. data/ext/pg_text_decoder.c +635 -52
  45. data/ext/pg_text_encoder.c +294 -130
  46. data/ext/pg_tuple.c +572 -0
  47. data/ext/pg_type_map.c +64 -23
  48. data/ext/pg_type_map_all_strings.c +21 -7
  49. data/ext/pg_type_map_by_class.c +59 -27
  50. data/ext/pg_type_map_by_column.c +86 -43
  51. data/ext/pg_type_map_by_mri_type.c +50 -21
  52. data/ext/pg_type_map_by_oid.c +62 -29
  53. data/ext/pg_type_map_in_ruby.c +59 -28
  54. data/ext/{util.c → pg_util.c} +13 -13
  55. data/ext/{util.h → pg_util.h} +3 -3
  56. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  57. data/lib/pg/basic_type_map_for_queries.rb +202 -0
  58. data/lib/pg/basic_type_map_for_results.rb +104 -0
  59. data/lib/pg/basic_type_registry.rb +303 -0
  60. data/lib/pg/binary_decoder/date.rb +9 -0
  61. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  62. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  63. data/lib/pg/coder.rb +36 -13
  64. data/lib/pg/connection.rb +813 -74
  65. data/lib/pg/exceptions.rb +16 -2
  66. data/lib/pg/result.rb +24 -7
  67. data/lib/pg/text_decoder/date.rb +18 -0
  68. data/lib/pg/text_decoder/inet.rb +9 -0
  69. data/lib/pg/text_decoder/json.rb +14 -0
  70. data/lib/pg/text_decoder/numeric.rb +9 -0
  71. data/lib/pg/text_decoder/timestamp.rb +30 -0
  72. data/lib/pg/text_encoder/date.rb +12 -0
  73. data/lib/pg/text_encoder/inet.rb +28 -0
  74. data/lib/pg/text_encoder/json.rb +14 -0
  75. data/lib/pg/text_encoder/numeric.rb +9 -0
  76. data/lib/pg/text_encoder/timestamp.rb +24 -0
  77. data/lib/pg/tuple.rb +30 -0
  78. data/lib/pg/type_map_by_column.rb +3 -2
  79. data/lib/pg/version.rb +4 -0
  80. data/lib/pg.rb +106 -41
  81. data/misc/openssl-pg-segfault.rb +31 -0
  82. data/misc/postgres/History.txt +9 -0
  83. data/misc/postgres/Manifest.txt +5 -0
  84. data/misc/postgres/README.txt +21 -0
  85. data/misc/postgres/Rakefile +21 -0
  86. data/misc/postgres/lib/postgres.rb +16 -0
  87. data/misc/ruby-pg/History.txt +9 -0
  88. data/misc/ruby-pg/Manifest.txt +5 -0
  89. data/misc/ruby-pg/README.txt +21 -0
  90. data/misc/ruby-pg/Rakefile +21 -0
  91. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  92. data/pg.gemspec +34 -0
  93. data/rakelib/task_extension.rb +46 -0
  94. data/sample/array_insert.rb +1 -1
  95. data/sample/async_api.rb +4 -8
  96. data/sample/async_copyto.rb +1 -1
  97. data/sample/async_mixed.rb +1 -1
  98. data/sample/check_conn.rb +1 -1
  99. data/sample/copydata.rb +71 -0
  100. data/sample/copyfrom.rb +1 -1
  101. data/sample/copyto.rb +1 -1
  102. data/sample/cursor.rb +1 -1
  103. data/sample/disk_usage_report.rb +6 -15
  104. data/sample/issue-119.rb +2 -2
  105. data/sample/losample.rb +1 -1
  106. data/sample/minimal-testcase.rb +2 -2
  107. data/sample/notify_wait.rb +1 -1
  108. data/sample/pg_statistics.rb +6 -15
  109. data/sample/replication_monitor.rb +9 -18
  110. data/sample/test_binary_values.rb +1 -1
  111. data/sample/wal_shipper.rb +2 -2
  112. data/sample/warehouse_partitions.rb +8 -17
  113. data.tar.gz.sig +0 -0
  114. metadata +135 -207
  115. metadata.gz.sig +0 -0
  116. data/ChangeLog +0 -5378
  117. data/History.rdoc +0 -297
  118. data/README.ja.rdoc +0 -14
  119. data/README.rdoc +0 -161
  120. data/lib/pg/basic_type_mapping.rb +0 -399
  121. data/lib/pg/constants.rb +0 -11
  122. data/lib/pg/text_decoder.rb +0 -42
  123. data/lib/pg/text_encoder.rb +0 -27
  124. data/spec/data/expected_trace.out +0 -26
  125. data/spec/data/random_binary_data +0 -0
  126. data/spec/helpers.rb +0 -355
  127. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  128. data/spec/pg/connection_spec.rb +0 -1459
  129. data/spec/pg/result_spec.rb +0 -449
  130. data/spec/pg/type_map_by_class_spec.rb +0 -138
  131. data/spec/pg/type_map_by_column_spec.rb +0 -222
  132. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  133. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  134. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  135. data/spec/pg/type_map_spec.rb +0 -22
  136. data/spec/pg/type_spec.rb +0 -665
  137. data/spec/pg_spec.rb +0 -50
data/ext/pg_tuple.c ADDED
@@ -0,0 +1,572 @@
1
+ #include "pg.h"
2
+
3
+ /********************************************************************
4
+ *
5
+ * Document-class: PG::Tuple
6
+ *
7
+ * The class to represent one query result tuple (row).
8
+ * An instance of this class can be created by PG::Result#tuple .
9
+ *
10
+ * All field values of the tuple are retrieved on demand from the underlying PGresult object and converted to a Ruby object.
11
+ * Subsequent access to the same field returns the same object, since they are cached when materialized.
12
+ * Each PG::Tuple holds a reference to the related PG::Result object, but gets detached, when all fields are materialized.
13
+ *
14
+ * Example:
15
+ * require 'pg'
16
+ * conn = PG.connect(:dbname => 'test')
17
+ * res = conn.exec('VALUES(1,2), (3,4)')
18
+ * t0 = res.tuple(0) # => #<PG::Tuple column1: "1", column2: "2">
19
+ * t1 = res.tuple(1) # => #<PG::Tuple column1: "3", column2: "4">
20
+ * t1[0] # => "3"
21
+ * t1["column2"] # => "4"
22
+ */
23
+
24
+ static VALUE rb_cPG_Tuple;
25
+
26
+ typedef struct {
27
+ /* PG::Result object this tuple was retrieved from.
28
+ * Qnil when all fields are materialized.
29
+ */
30
+ VALUE result;
31
+
32
+ /* Store the typemap of the result.
33
+ * It's not enough to reference the PG::TypeMap object through the result,
34
+ * since it could be exchanged after the tuple has been created.
35
+ */
36
+ VALUE typemap;
37
+
38
+ /* Hash with maps field names to index into values[]
39
+ * Shared between all instances retrieved from one PG::Result.
40
+ */
41
+ VALUE field_map;
42
+
43
+ /* Row number within the result set. */
44
+ int row_num;
45
+
46
+ /* Number of fields in the result set. */
47
+ int num_fields;
48
+
49
+ /* Materialized values.
50
+ * And in case of dup column names, a field_names Array subsequently.
51
+ */
52
+ VALUE values[0];
53
+ } t_pg_tuple;
54
+
55
+ static inline VALUE *
56
+ pg_tuple_get_field_names_ptr( t_pg_tuple *this )
57
+ {
58
+ if( this->num_fields != (int)RHASH_SIZE(this->field_map) ){
59
+ return &this->values[this->num_fields];
60
+ } else {
61
+ static VALUE f = Qfalse;
62
+ return &f;
63
+ }
64
+ }
65
+
66
+ static inline VALUE
67
+ pg_tuple_get_field_names( t_pg_tuple *this )
68
+ {
69
+ return *pg_tuple_get_field_names_ptr(this);
70
+ }
71
+
72
+ static void
73
+ pg_tuple_gc_mark( void *_this )
74
+ {
75
+ t_pg_tuple *this = (t_pg_tuple *)_this;
76
+ int i;
77
+
78
+ if( !this ) return;
79
+ rb_gc_mark_movable( this->result );
80
+ rb_gc_mark_movable( this->typemap );
81
+ rb_gc_mark_movable( this->field_map );
82
+
83
+ for( i = 0; i < this->num_fields; i++ ){
84
+ rb_gc_mark_movable( this->values[i] );
85
+ }
86
+ rb_gc_mark_movable( pg_tuple_get_field_names(this) );
87
+ }
88
+
89
+ static void
90
+ pg_tuple_gc_compact( void *_this )
91
+ {
92
+ t_pg_tuple *this = (t_pg_tuple *)_this;
93
+ int i;
94
+
95
+ if( !this ) return;
96
+ pg_gc_location( this->result );
97
+ pg_gc_location( this->typemap );
98
+ pg_gc_location( this->field_map );
99
+
100
+ for( i = 0; i < this->num_fields; i++ ){
101
+ pg_gc_location( this->values[i] );
102
+ }
103
+ pg_gc_location( *pg_tuple_get_field_names_ptr(this) );
104
+ }
105
+
106
+ static void
107
+ pg_tuple_gc_free( void *_this )
108
+ {
109
+ t_pg_tuple *this = (t_pg_tuple *)_this;
110
+ if( !this ) return;
111
+ xfree(this);
112
+ }
113
+
114
+ static size_t
115
+ pg_tuple_memsize( const void *_this )
116
+ {
117
+ const t_pg_tuple *this = (const t_pg_tuple *)_this;
118
+ if( this==NULL ) return 0;
119
+ return sizeof(*this) + sizeof(*this->values) * this->num_fields;
120
+ }
121
+
122
+ static const rb_data_type_t pg_tuple_type = {
123
+ "PG::Tuple",
124
+ {
125
+ pg_tuple_gc_mark,
126
+ pg_tuple_gc_free,
127
+ pg_tuple_memsize,
128
+ pg_compact_callback(pg_tuple_gc_compact),
129
+ },
130
+ 0, 0,
131
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
132
+ };
133
+
134
+ /*
135
+ * Document-method: allocate
136
+ *
137
+ * call-seq:
138
+ * PG::VeryTuple.allocate -> obj
139
+ */
140
+ static VALUE
141
+ pg_tuple_s_allocate( VALUE klass )
142
+ {
143
+ return TypedData_Wrap_Struct( klass, &pg_tuple_type, NULL );
144
+ }
145
+
146
+ VALUE
147
+ pg_tuple_new(VALUE result, int row_num)
148
+ {
149
+ t_pg_tuple *this;
150
+ VALUE self = pg_tuple_s_allocate( rb_cPG_Tuple );
151
+ t_pg_result *p_result = pgresult_get_this(result);
152
+ int num_fields = p_result->nfields;
153
+ int i;
154
+ VALUE field_map = p_result->field_map;
155
+ int dup_names = num_fields != (int)RHASH_SIZE(field_map);
156
+
157
+ this = (t_pg_tuple *)xmalloc(
158
+ sizeof(*this) +
159
+ sizeof(*this->values) * num_fields +
160
+ sizeof(*this->values) * (dup_names ? 1 : 0));
161
+
162
+ RB_OBJ_WRITE(self, &this->result, result);
163
+ RB_OBJ_WRITE(self, &this->typemap, p_result->typemap);
164
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
165
+ this->row_num = row_num;
166
+ this->num_fields = num_fields;
167
+
168
+ for( i = 0; i < num_fields; i++ ){
169
+ this->values[i] = Qundef;
170
+ }
171
+
172
+ if( dup_names ){
173
+ /* Some of the column names are duplicated -> we need the keys as Array in addition.
174
+ * Store it behind the values to save the space in the common case of no dups.
175
+ */
176
+ VALUE keys_array = rb_obj_freeze(rb_ary_new4(num_fields, p_result->fnames));
177
+ RB_OBJ_WRITE(self, &this->values[num_fields], keys_array);
178
+ }
179
+
180
+ RTYPEDDATA_DATA(self) = this;
181
+
182
+ return self;
183
+ }
184
+
185
+ static inline t_pg_tuple *
186
+ pg_tuple_get_this( VALUE self )
187
+ {
188
+ t_pg_tuple *this;
189
+ TypedData_Get_Struct(self, t_pg_tuple, &pg_tuple_type, this);
190
+ if (this == NULL)
191
+ rb_raise(rb_eTypeError, "tuple is empty");
192
+
193
+ return this;
194
+ }
195
+
196
+ static VALUE
197
+ pg_tuple_materialize_field(VALUE self, int col)
198
+ {
199
+ t_pg_tuple *this = RTYPEDDATA_DATA( self );
200
+ VALUE value = this->values[col];
201
+
202
+ if( value == Qundef ){
203
+ t_typemap *p_typemap = RTYPEDDATA_DATA( this->typemap );
204
+
205
+ pgresult_get(this->result); /* make sure we have a valid PGresult object */
206
+ value = p_typemap->funcs.typecast_result_value(p_typemap, this->result, this->row_num, col);
207
+ RB_OBJ_WRITE(self, &this->values[col], value);
208
+ }
209
+
210
+ return value;
211
+ }
212
+
213
+ static void
214
+ pg_tuple_detach(VALUE self)
215
+ {
216
+ t_pg_tuple *this = RTYPEDDATA_DATA( self );
217
+ RB_OBJ_WRITE(self, &this->result, Qnil);
218
+ RB_OBJ_WRITE(self, &this->typemap, Qnil);
219
+ this->row_num = -1;
220
+ }
221
+
222
+ static void
223
+ pg_tuple_materialize(VALUE self)
224
+ {
225
+ t_pg_tuple *this = RTYPEDDATA_DATA( self );
226
+ int field_num;
227
+ for(field_num = 0; field_num < this->num_fields; field_num++) {
228
+ pg_tuple_materialize_field(self, field_num);
229
+ }
230
+
231
+ pg_tuple_detach(self);
232
+ }
233
+
234
+ /*
235
+ * call-seq:
236
+ * tup.fetch(key) → value
237
+ * tup.fetch(key, default) → value
238
+ * tup.fetch(key) { |key| block } → value
239
+ *
240
+ * Returns a field value by either column index or column name.
241
+ *
242
+ * An integer +key+ is interpreted as column index.
243
+ * Negative values of index count from the end of the array.
244
+ *
245
+ * Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
246
+ *
247
+ * If the key can't be found, there are several options:
248
+ * With no other arguments, it will raise a IndexError exception;
249
+ * if default is given, then that will be returned;
250
+ * if the optional code block is specified, then that will be run and its result returned.
251
+ */
252
+ static VALUE
253
+ pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
254
+ {
255
+ VALUE key;
256
+ long block_given;
257
+ VALUE index;
258
+ int field_num;
259
+ t_pg_tuple *this = pg_tuple_get_this(self);
260
+
261
+ rb_check_arity(argc, 1, 2);
262
+ key = argv[0];
263
+
264
+ block_given = rb_block_given_p();
265
+ if (block_given && argc == 2) {
266
+ rb_warn("block supersedes default value argument");
267
+ }
268
+
269
+ switch(rb_type(key)){
270
+ case T_FIXNUM:
271
+ case T_BIGNUM:
272
+ field_num = NUM2INT(key);
273
+ if ( field_num < 0 )
274
+ field_num = this->num_fields + field_num;
275
+ if ( field_num < 0 || field_num >= this->num_fields ){
276
+ if (block_given) return rb_yield(key);
277
+ if (argc == 1) rb_raise( rb_eIndexError, "Index %d is out of range", field_num );
278
+ return argv[1];
279
+ }
280
+ break;
281
+ default:
282
+ index = rb_hash_aref(this->field_map, key);
283
+
284
+ if (index == Qnil) {
285
+ if (block_given) return rb_yield(key);
286
+ if (argc == 1) rb_raise( rb_eKeyError, "column not found" );
287
+ return argv[1];
288
+ }
289
+
290
+ field_num = NUM2INT(index);
291
+ }
292
+
293
+ return pg_tuple_materialize_field(self, field_num);
294
+ }
295
+
296
+ /*
297
+ * call-seq:
298
+ * tup[ key ] -> value
299
+ *
300
+ * Returns a field value by either column index or column name.
301
+ *
302
+ * An integer +key+ is interpreted as column index.
303
+ * Negative values of index count from the end of the array.
304
+ *
305
+ * Depending on Result#field_name_type= a string or symbol +key+ is interpreted as column name.
306
+ *
307
+ * If the key can't be found, it returns +nil+ .
308
+ */
309
+ static VALUE
310
+ pg_tuple_aref(VALUE self, VALUE key)
311
+ {
312
+ VALUE index;
313
+ int field_num;
314
+ t_pg_tuple *this = pg_tuple_get_this(self);
315
+
316
+ switch(rb_type(key)){
317
+ case T_FIXNUM:
318
+ case T_BIGNUM:
319
+ field_num = NUM2INT(key);
320
+ if ( field_num < 0 )
321
+ field_num = this->num_fields + field_num;
322
+ if ( field_num < 0 || field_num >= this->num_fields )
323
+ return Qnil;
324
+ break;
325
+ default:
326
+ index = rb_hash_aref(this->field_map, key);
327
+ if( index == Qnil ) return Qnil;
328
+ field_num = NUM2INT(index);
329
+ }
330
+
331
+ return pg_tuple_materialize_field(self, field_num);
332
+ }
333
+
334
+ static VALUE
335
+ pg_tuple_num_fields_for_enum(VALUE self, VALUE args, VALUE eobj)
336
+ {
337
+ t_pg_tuple *this = pg_tuple_get_this(self);
338
+ return INT2NUM(this->num_fields);
339
+ }
340
+
341
+ static int
342
+ pg_tuple_yield_key_value(VALUE key, VALUE index, VALUE self)
343
+ {
344
+ VALUE value = pg_tuple_materialize_field(self, NUM2INT(index));
345
+ rb_yield_values(2, key, value);
346
+ return ST_CONTINUE;
347
+ }
348
+
349
+ /*
350
+ * call-seq:
351
+ * tup.each{ |key, value| ... }
352
+ *
353
+ * Invokes block for each field name and value in the tuple.
354
+ */
355
+ static VALUE
356
+ pg_tuple_each(VALUE self)
357
+ {
358
+ t_pg_tuple *this = pg_tuple_get_this(self);
359
+ VALUE field_names;
360
+
361
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
362
+
363
+ field_names = pg_tuple_get_field_names(this);
364
+
365
+ if( field_names == Qfalse ){
366
+ rb_hash_foreach(this->field_map, pg_tuple_yield_key_value, self);
367
+ } else {
368
+ int i;
369
+ for( i = 0; i < this->num_fields; i++ ){
370
+ VALUE value = pg_tuple_materialize_field(self, i);
371
+ rb_yield_values(2, RARRAY_AREF(field_names, i), value);
372
+ }
373
+ }
374
+
375
+ pg_tuple_detach(self);
376
+ return self;
377
+ }
378
+
379
+ /*
380
+ * call-seq:
381
+ * tup.each_value{ |value| ... }
382
+ *
383
+ * Invokes block for each field value in the tuple.
384
+ */
385
+ static VALUE
386
+ pg_tuple_each_value(VALUE self)
387
+ {
388
+ t_pg_tuple *this = pg_tuple_get_this(self);
389
+ int field_num;
390
+
391
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
392
+
393
+ for(field_num = 0; field_num < this->num_fields; field_num++) {
394
+ VALUE value = pg_tuple_materialize_field(self, field_num);
395
+ rb_yield(value);
396
+ }
397
+
398
+ pg_tuple_detach(self);
399
+ return self;
400
+ }
401
+
402
+
403
+ /*
404
+ * call-seq:
405
+ * tup.values -> Array
406
+ *
407
+ * Returns the values of this tuple as Array.
408
+ * +res.tuple(i).values+ is equal to +res.tuple_values(i)+ .
409
+ */
410
+ static VALUE
411
+ pg_tuple_values(VALUE self)
412
+ {
413
+ t_pg_tuple *this = pg_tuple_get_this(self);
414
+
415
+ pg_tuple_materialize(self);
416
+ return rb_ary_new4(this->num_fields, &this->values[0]);
417
+ }
418
+
419
+ static VALUE
420
+ pg_tuple_field_map(VALUE self)
421
+ {
422
+ t_pg_tuple *this = pg_tuple_get_this(self);
423
+ return this->field_map;
424
+ }
425
+
426
+ static VALUE
427
+ pg_tuple_field_names(VALUE self)
428
+ {
429
+ t_pg_tuple *this = pg_tuple_get_this(self);
430
+ return pg_tuple_get_field_names(this);
431
+ }
432
+
433
+ /*
434
+ * call-seq:
435
+ * tup.length → integer
436
+ *
437
+ * Returns number of fields of this tuple.
438
+ */
439
+ static VALUE
440
+ pg_tuple_length(VALUE self)
441
+ {
442
+ t_pg_tuple *this = pg_tuple_get_this(self);
443
+ return INT2NUM(this->num_fields);
444
+ }
445
+
446
+ /*
447
+ * call-seq:
448
+ * tup.index(key) → integer
449
+ *
450
+ * Returns the field number which matches the given column name.
451
+ */
452
+ static VALUE
453
+ pg_tuple_index(VALUE self, VALUE key)
454
+ {
455
+ t_pg_tuple *this = pg_tuple_get_this(self);
456
+ return rb_hash_aref(this->field_map, key);
457
+ }
458
+
459
+
460
+ static VALUE
461
+ pg_tuple_dump(VALUE self)
462
+ {
463
+ VALUE field_names;
464
+ VALUE values;
465
+ VALUE a;
466
+ t_pg_tuple *this = pg_tuple_get_this(self);
467
+
468
+ pg_tuple_materialize(self);
469
+
470
+ field_names = pg_tuple_get_field_names(this);
471
+ if( field_names == Qfalse )
472
+ field_names = rb_funcall(this->field_map, rb_intern("keys"), 0);
473
+
474
+ values = rb_ary_new4(this->num_fields, &this->values[0]);
475
+ a = rb_ary_new3(2, field_names, values);
476
+
477
+ rb_copy_generic_ivar(a, self);
478
+
479
+ return a;
480
+ }
481
+
482
+ static VALUE
483
+ pg_tuple_load(VALUE self, VALUE a)
484
+ {
485
+ int num_fields;
486
+ int i;
487
+ t_pg_tuple *this;
488
+ VALUE values;
489
+ VALUE field_names;
490
+ VALUE field_map;
491
+ int dup_names;
492
+
493
+ rb_check_frozen(self);
494
+
495
+ TypedData_Get_Struct(self, t_pg_tuple, &pg_tuple_type, this);
496
+ if (this)
497
+ rb_raise(rb_eTypeError, "tuple is not empty");
498
+
499
+ Check_Type(a, T_ARRAY);
500
+ if (RARRAY_LEN(a) != 2)
501
+ rb_raise(rb_eTypeError, "expected an array of 2 elements");
502
+
503
+ field_names = RARRAY_AREF(a, 0);
504
+ Check_Type(field_names, T_ARRAY);
505
+ rb_obj_freeze(field_names);
506
+ values = RARRAY_AREF(a, 1);
507
+ Check_Type(values, T_ARRAY);
508
+ num_fields = RARRAY_LENINT(values);
509
+
510
+ if (RARRAY_LENINT(field_names) != num_fields)
511
+ rb_raise(rb_eTypeError, "different number of fields and values");
512
+
513
+ field_map = rb_hash_new();
514
+ for( i = 0; i < num_fields; i++ ){
515
+ rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
516
+ }
517
+ rb_obj_freeze(field_map);
518
+
519
+ dup_names = num_fields != (int)RHASH_SIZE(field_map);
520
+
521
+ this = (t_pg_tuple *)xmalloc(
522
+ sizeof(*this) +
523
+ sizeof(*this->values) * num_fields +
524
+ sizeof(*this->values) * (dup_names ? 1 : 0));
525
+
526
+ RB_OBJ_WRITE(self, &this->result, Qnil);
527
+ RB_OBJ_WRITE(self, &this->typemap, Qnil);
528
+ this->row_num = -1;
529
+ this->num_fields = num_fields;
530
+ RB_OBJ_WRITE(self, &this->field_map, field_map);
531
+
532
+ for( i = 0; i < num_fields; i++ ){
533
+ VALUE v = RARRAY_AREF(values, i);
534
+ if( v == Qundef )
535
+ rb_raise(rb_eTypeError, "field %d is not materialized", i);
536
+ RB_OBJ_WRITE(self, &this->values[i], v);
537
+ }
538
+
539
+ if( dup_names ){
540
+ RB_OBJ_WRITE(self, &this->values[num_fields], field_names);
541
+ }
542
+
543
+ RTYPEDDATA_DATA(self) = this;
544
+
545
+ rb_copy_generic_ivar(self, a);
546
+
547
+ return self;
548
+ }
549
+
550
+ void
551
+ init_pg_tuple(void)
552
+ {
553
+ rb_cPG_Tuple = rb_define_class_under( rb_mPG, "Tuple", rb_cObject );
554
+ rb_define_alloc_func( rb_cPG_Tuple, pg_tuple_s_allocate );
555
+ rb_include_module(rb_cPG_Tuple, rb_mEnumerable);
556
+
557
+ rb_define_method(rb_cPG_Tuple, "fetch", pg_tuple_fetch, -1);
558
+ rb_define_method(rb_cPG_Tuple, "[]", pg_tuple_aref, 1);
559
+ rb_define_method(rb_cPG_Tuple, "each", pg_tuple_each, 0);
560
+ rb_define_method(rb_cPG_Tuple, "each_value", pg_tuple_each_value, 0);
561
+ rb_define_method(rb_cPG_Tuple, "values", pg_tuple_values, 0);
562
+ rb_define_method(rb_cPG_Tuple, "length", pg_tuple_length, 0);
563
+ rb_define_alias(rb_cPG_Tuple, "size", "length");
564
+ rb_define_method(rb_cPG_Tuple, "index", pg_tuple_index, 1);
565
+
566
+ rb_define_private_method(rb_cPG_Tuple, "field_map", pg_tuple_field_map, 0);
567
+ rb_define_private_method(rb_cPG_Tuple, "field_names", pg_tuple_field_names, 0);
568
+
569
+ /* methods for marshaling */
570
+ rb_define_private_method(rb_cPG_Tuple, "marshal_dump", pg_tuple_dump, 0);
571
+ rb_define_private_method(rb_cPG_Tuple, "marshal_load", pg_tuple_load, 1);
572
+ }