pg 0.18.4 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) 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 +137 -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 +14 -0
  16. data/History.md +876 -0
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +4 -4
  19. data/README.ja.md +276 -0
  20. data/README.md +286 -0
  21. data/Rakefile +38 -141
  22. data/Rakefile.cross +69 -72
  23. data/certs/ged.pem +24 -0
  24. data/certs/larskanis-2022.pem +26 -0
  25. data/certs/larskanis-2023.pem +24 -0
  26. data/ext/errorcodes.def +113 -0
  27. data/ext/errorcodes.rb +1 -1
  28. data/ext/errorcodes.txt +36 -2
  29. data/ext/extconf.rb +119 -54
  30. data/ext/gvl_wrappers.c +8 -0
  31. data/ext/gvl_wrappers.h +44 -33
  32. data/ext/pg.c +226 -200
  33. data/ext/pg.h +99 -99
  34. data/ext/pg_binary_decoder.c +162 -16
  35. data/ext/pg_binary_encoder.c +245 -20
  36. data/ext/pg_coder.c +189 -44
  37. data/ext/pg_connection.c +1865 -1172
  38. data/ext/pg_copy_coder.c +398 -42
  39. data/ext/pg_errors.c +1 -1
  40. data/ext/pg_record_coder.c +522 -0
  41. data/ext/pg_result.c +727 -232
  42. data/ext/pg_text_decoder.c +627 -43
  43. data/ext/pg_text_encoder.c +266 -102
  44. data/ext/pg_tuple.c +572 -0
  45. data/ext/pg_type_map.c +58 -17
  46. data/ext/pg_type_map_all_strings.c +21 -7
  47. data/ext/pg_type_map_by_class.c +59 -27
  48. data/ext/pg_type_map_by_column.c +80 -37
  49. data/ext/pg_type_map_by_mri_type.c +49 -20
  50. data/ext/pg_type_map_by_oid.c +62 -29
  51. data/ext/pg_type_map_in_ruby.c +56 -22
  52. data/ext/{util.c → pg_util.c} +12 -12
  53. data/ext/{util.h → pg_util.h} +2 -2
  54. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  55. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  56. data/lib/pg/basic_type_map_for_results.rb +104 -0
  57. data/lib/pg/basic_type_registry.rb +299 -0
  58. data/lib/pg/binary_decoder/date.rb +9 -0
  59. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  60. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  61. data/lib/pg/coder.rb +36 -13
  62. data/lib/pg/connection.rb +797 -77
  63. data/lib/pg/exceptions.rb +16 -2
  64. data/lib/pg/result.rb +24 -7
  65. data/lib/pg/text_decoder/date.rb +18 -0
  66. data/lib/pg/text_decoder/inet.rb +9 -0
  67. data/lib/pg/text_decoder/json.rb +14 -0
  68. data/lib/pg/text_decoder/numeric.rb +9 -0
  69. data/lib/pg/text_decoder/timestamp.rb +30 -0
  70. data/lib/pg/text_encoder/date.rb +12 -0
  71. data/lib/pg/text_encoder/inet.rb +28 -0
  72. data/lib/pg/text_encoder/json.rb +14 -0
  73. data/lib/pg/text_encoder/numeric.rb +9 -0
  74. data/lib/pg/text_encoder/timestamp.rb +24 -0
  75. data/lib/pg/tuple.rb +30 -0
  76. data/lib/pg/type_map_by_column.rb +3 -2
  77. data/lib/pg/version.rb +4 -0
  78. data/lib/pg.rb +106 -41
  79. data/misc/openssl-pg-segfault.rb +31 -0
  80. data/misc/postgres/History.txt +9 -0
  81. data/misc/postgres/Manifest.txt +5 -0
  82. data/misc/postgres/README.txt +21 -0
  83. data/misc/postgres/Rakefile +21 -0
  84. data/misc/postgres/lib/postgres.rb +16 -0
  85. data/misc/ruby-pg/History.txt +9 -0
  86. data/misc/ruby-pg/Manifest.txt +5 -0
  87. data/misc/ruby-pg/README.txt +21 -0
  88. data/misc/ruby-pg/Rakefile +21 -0
  89. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  90. data/pg.gemspec +34 -0
  91. data/rakelib/task_extension.rb +46 -0
  92. data/sample/array_insert.rb +1 -1
  93. data/sample/async_api.rb +4 -8
  94. data/sample/async_copyto.rb +1 -1
  95. data/sample/async_mixed.rb +1 -1
  96. data/sample/check_conn.rb +1 -1
  97. data/sample/copydata.rb +71 -0
  98. data/sample/copyfrom.rb +1 -1
  99. data/sample/copyto.rb +1 -1
  100. data/sample/cursor.rb +1 -1
  101. data/sample/disk_usage_report.rb +6 -15
  102. data/sample/issue-119.rb +2 -2
  103. data/sample/losample.rb +1 -1
  104. data/sample/minimal-testcase.rb +2 -2
  105. data/sample/notify_wait.rb +1 -1
  106. data/sample/pg_statistics.rb +6 -15
  107. data/sample/replication_monitor.rb +9 -18
  108. data/sample/test_binary_values.rb +1 -1
  109. data/sample/wal_shipper.rb +2 -2
  110. data/sample/warehouse_partitions.rb +8 -17
  111. data/translation/.po4a-version +7 -0
  112. data/translation/po/all.pot +910 -0
  113. data/translation/po/ja.po +1047 -0
  114. data/translation/po4a.cfg +12 -0
  115. data.tar.gz.sig +0 -0
  116. metadata +136 -213
  117. metadata.gz.sig +0 -0
  118. data/ChangeLog +0 -5911
  119. data/History.rdoc +0 -338
  120. data/README.ja.rdoc +0 -14
  121. data/README.rdoc +0 -164
  122. data/lib/pg/basic_type_mapping.rb +0 -399
  123. data/lib/pg/constants.rb +0 -11
  124. data/lib/pg/text_decoder.rb +0 -44
  125. data/lib/pg/text_encoder.rb +0 -27
  126. data/spec/data/expected_trace.out +0 -26
  127. data/spec/data/random_binary_data +0 -0
  128. data/spec/helpers.rb +0 -355
  129. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  130. data/spec/pg/connection_spec.rb +0 -1544
  131. data/spec/pg/result_spec.rb +0 -449
  132. data/spec/pg/type_map_by_class_spec.rb +0 -138
  133. data/spec/pg/type_map_by_column_spec.rb +0 -222
  134. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  135. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  136. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  137. data/spec/pg/type_map_spec.rb +0 -22
  138. data/spec/pg/type_spec.rb +0 -697
  139. 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
+ }
data/ext/pg_type_map.c CHANGED
@@ -1,56 +1,97 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id: pg_type_map.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
7
7
  #include "pg.h"
8
8
 
9
+ void
10
+ pg_typemap_mark( void *_this )
11
+ {
12
+ t_typemap *this = (t_typemap *)_this;
13
+ rb_gc_mark_movable(this->default_typemap);
14
+ }
15
+
16
+ size_t
17
+ pg_typemap_memsize( const void *_this )
18
+ {
19
+ t_typemap *this = (t_typemap *)_this;
20
+ return sizeof(*this);
21
+ }
22
+
23
+ void
24
+ pg_typemap_compact( void *_this )
25
+ {
26
+ t_typemap *this = (t_typemap *)_this;
27
+ pg_gc_location(this->default_typemap);
28
+ }
29
+
30
+ const rb_data_type_t pg_typemap_type = {
31
+ "PG::TypeMap",
32
+ {
33
+ pg_typemap_mark,
34
+ RUBY_TYPED_DEFAULT_FREE,
35
+ pg_typemap_memsize,
36
+ pg_compact_callback(pg_typemap_compact),
37
+ },
38
+ 0,
39
+ 0,
40
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
41
+ };
42
+
9
43
  VALUE rb_cTypeMap;
10
44
  VALUE rb_mDefaultTypeMappable;
11
45
  static ID s_id_fit_to_query;
12
46
  static ID s_id_fit_to_result;
13
47
 
48
+ NORETURN( VALUE
49
+ pg_typemap_fit_to_result( VALUE self, VALUE result ));
50
+ NORETURN( VALUE
51
+ pg_typemap_fit_to_query( VALUE self, VALUE params ));
52
+ NORETURN( int
53
+ pg_typemap_fit_to_copy_get( VALUE self ));
54
+ NORETURN( VALUE
55
+ pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field ));
56
+ NORETURN( t_pg_coder *
57
+ pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field ));
58
+ NORETURN( VALUE
59
+ pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx ));
60
+
14
61
  VALUE
15
62
  pg_typemap_fit_to_result( VALUE self, VALUE result )
16
63
  {
17
64
  rb_raise( rb_eNotImpError, "type map %s is not suitable to map result values", rb_obj_classname(self) );
18
- return Qnil;
19
65
  }
20
66
 
21
67
  VALUE
22
68
  pg_typemap_fit_to_query( VALUE self, VALUE params )
23
69
  {
24
70
  rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
25
- return Qnil;
26
71
  }
27
72
 
28
73
  int
29
74
  pg_typemap_fit_to_copy_get( VALUE self )
30
75
  {
31
76
  rb_raise( rb_eNotImpError, "type map %s is not suitable to map get_copy_data results", rb_obj_classname(self) );
32
- return Qnil;
33
77
  }
34
78
 
35
79
  VALUE
36
80
  pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
37
81
  {
38
82
  rb_raise( rb_eNotImpError, "type map is not suitable to map result values" );
39
- return Qnil;
40
83
  }
41
84
 
42
85
  t_pg_coder *
43
86
  pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
44
87
  {
45
88
  rb_raise( rb_eNotImpError, "type map is not suitable to map query params" );
46
- return NULL;
47
89
  }
48
90
 
49
91
  VALUE
50
92
  pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
51
93
  {
52
94
  rb_raise( rb_eNotImpError, "type map is not suitable to map get_copy_data results" );
53
- return Qnil;
54
95
  }
55
96
 
56
97
  const struct pg_typemap_funcs pg_typemap_funcs = {
@@ -68,7 +109,7 @@ pg_typemap_s_allocate( VALUE klass )
68
109
  VALUE self;
69
110
  t_typemap *this;
70
111
 
71
- self = Data_Make_Struct( klass, t_typemap, NULL, -1, this );
112
+ self = TypedData_Make_Struct( klass, t_typemap, &pg_typemap_type, this );
72
113
  this->funcs = pg_typemap_funcs;
73
114
 
74
115
  return self;
@@ -87,14 +128,14 @@ pg_typemap_s_allocate( VALUE klass )
87
128
  static VALUE
88
129
  pg_typemap_default_type_map_set(VALUE self, VALUE typemap)
89
130
  {
90
- t_typemap *this = DATA_PTR( self );
131
+ t_typemap *this = RTYPEDDATA_DATA( self );
132
+ t_typemap *tm;
133
+ UNUSED(tm);
91
134
 
92
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
93
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
94
- rb_obj_classname( typemap ) );
95
- }
96
- Check_Type(typemap, T_DATA);
97
- this->default_typemap = typemap;
135
+ rb_check_frozen(self);
136
+ /* Check type of method param */
137
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, tm);
138
+ RB_OBJ_WRITE(self, &this->default_typemap, typemap);
98
139
 
99
140
  return typemap;
100
141
  }
@@ -112,7 +153,7 @@ pg_typemap_default_type_map_set(VALUE self, VALUE typemap)
112
153
  static VALUE
113
154
  pg_typemap_default_type_map_get(VALUE self)
114
155
  {
115
- t_typemap *this = DATA_PTR( self );
156
+ t_typemap *this = RTYPEDDATA_DATA( self );
116
157
 
117
158
  return this->default_typemap;
118
159
  }
@@ -136,7 +177,7 @@ pg_typemap_with_default_type_map(VALUE self, VALUE typemap)
136
177
  }
137
178
 
138
179
  void
139
- init_pg_type_map()
180
+ init_pg_type_map(void)
140
181
  {
141
182
  s_id_fit_to_query = rb_intern("fit_to_query");
142
183
  s_id_fit_to_result = rb_intern("fit_to_result");