pg 1.2.3 → 1.3.5

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 (106) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +86 -0
  6. data/.github/workflows/source-gem.yml +129 -0
  7. data/.gitignore +13 -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/Gemfile +14 -0
  15. data/History.rdoc +153 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +8 -5
  20. data/certs/ged.pem +24 -0
  21. data/certs/larskanis-2022.pem +26 -0
  22. data/ext/errorcodes.def +8 -0
  23. data/ext/errorcodes.rb +0 -0
  24. data/ext/errorcodes.txt +3 -1
  25. data/ext/extconf.rb +131 -25
  26. data/ext/gvl_wrappers.c +4 -0
  27. data/ext/gvl_wrappers.h +23 -0
  28. data/ext/pg.c +59 -4
  29. data/ext/pg.h +19 -1
  30. data/ext/pg_coder.c +82 -28
  31. data/ext/pg_connection.c +680 -508
  32. data/ext/pg_copy_coder.c +45 -16
  33. data/ext/pg_record_coder.c +45 -15
  34. data/ext/pg_result.c +77 -40
  35. data/ext/pg_text_decoder.c +1 -1
  36. data/ext/pg_text_encoder.c +6 -6
  37. data/ext/pg_tuple.c +49 -29
  38. data/ext/pg_type_map.c +41 -8
  39. data/ext/pg_type_map_all_strings.c +15 -1
  40. data/ext/pg_type_map_by_class.c +49 -24
  41. data/ext/pg_type_map_by_column.c +66 -28
  42. data/ext/pg_type_map_by_mri_type.c +47 -18
  43. data/ext/pg_type_map_by_oid.c +52 -23
  44. data/ext/pg_type_map_in_ruby.c +50 -19
  45. data/ext/pg_util.c +2 -2
  46. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  47. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  48. data/lib/pg/basic_type_map_for_results.rb +81 -0
  49. data/lib/pg/basic_type_registry.rb +301 -0
  50. data/lib/pg/coder.rb +1 -1
  51. data/lib/pg/connection.rb +589 -58
  52. data/lib/pg/version.rb +4 -0
  53. data/lib/pg.rb +47 -32
  54. data/misc/openssl-pg-segfault.rb +31 -0
  55. data/misc/postgres/History.txt +9 -0
  56. data/misc/postgres/Manifest.txt +5 -0
  57. data/misc/postgres/README.txt +21 -0
  58. data/misc/postgres/Rakefile +21 -0
  59. data/misc/postgres/lib/postgres.rb +16 -0
  60. data/misc/ruby-pg/History.txt +9 -0
  61. data/misc/ruby-pg/Manifest.txt +5 -0
  62. data/misc/ruby-pg/README.txt +21 -0
  63. data/misc/ruby-pg/Rakefile +21 -0
  64. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  65. data/pg.gemspec +32 -0
  66. data/rakelib/task_extension.rb +46 -0
  67. data/sample/array_insert.rb +20 -0
  68. data/sample/async_api.rb +102 -0
  69. data/sample/async_copyto.rb +39 -0
  70. data/sample/async_mixed.rb +56 -0
  71. data/sample/check_conn.rb +21 -0
  72. data/sample/copydata.rb +71 -0
  73. data/sample/copyfrom.rb +81 -0
  74. data/sample/copyto.rb +19 -0
  75. data/sample/cursor.rb +21 -0
  76. data/sample/disk_usage_report.rb +177 -0
  77. data/sample/issue-119.rb +94 -0
  78. data/sample/losample.rb +69 -0
  79. data/sample/minimal-testcase.rb +17 -0
  80. data/sample/notify_wait.rb +72 -0
  81. data/sample/pg_statistics.rb +285 -0
  82. data/sample/replication_monitor.rb +222 -0
  83. data/sample/test_binary_values.rb +33 -0
  84. data/sample/wal_shipper.rb +434 -0
  85. data/sample/warehouse_partitions.rb +311 -0
  86. data.tar.gz.sig +0 -0
  87. metadata +87 -224
  88. metadata.gz.sig +0 -0
  89. data/ChangeLog +0 -0
  90. data/lib/pg/basic_type_mapping.rb +0 -522
  91. data/spec/data/expected_trace.out +0 -26
  92. data/spec/data/random_binary_data +0 -0
  93. data/spec/helpers.rb +0 -380
  94. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  95. data/spec/pg/connection_spec.rb +0 -1949
  96. data/spec/pg/connection_sync_spec.rb +0 -41
  97. data/spec/pg/result_spec.rb +0 -681
  98. data/spec/pg/tuple_spec.rb +0 -333
  99. data/spec/pg/type_map_by_class_spec.rb +0 -138
  100. data/spec/pg/type_map_by_column_spec.rb +0 -226
  101. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  102. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  103. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  104. data/spec/pg/type_map_spec.rb +0 -22
  105. data/spec/pg/type_spec.rb +0 -1123
  106. data/spec/pg_spec.rb +0 -50
@@ -71,16 +71,12 @@ pg_tmbmt_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int fiel
71
71
 
72
72
  obj = rb_funcall(ask_for_coder, rb_intern("call"), 1, param_value);
73
73
 
74
- if( rb_obj_is_kind_of(obj, rb_cPG_Coder) ){
75
- Data_Get_Struct(obj, t_pg_coder, p_coder);
76
- }else{
77
- rb_raise(rb_eTypeError, "argument %d has invalid type %s (should be nil or some kind of PG::Coder)",
78
- field+1, rb_obj_classname( obj ));
79
- }
74
+ /* Check argument type and store the coder pointer */
75
+ TypedData_Get_Struct(obj, t_pg_coder, &pg_coder_type, p_coder);
80
76
  }
81
77
 
82
78
  if( !p_coder ){
83
- t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
79
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
84
80
  return default_tm->funcs.typecast_query_param( default_tm, param_value, field );
85
81
  }
86
82
 
@@ -90,24 +86,57 @@ pg_tmbmt_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int fiel
90
86
  static VALUE
91
87
  pg_tmbmt_fit_to_query( VALUE self, VALUE params )
92
88
  {
93
- t_tmbmt *this = (t_tmbmt *)DATA_PTR(self);
89
+ t_tmbmt *this = (t_tmbmt *)RTYPEDDATA_DATA(self);
94
90
  /* Nothing to check at this typemap, but ensure that the default type map fits. */
95
- t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
91
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
96
92
  default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
97
93
  return self;
98
94
  }
99
95
 
100
96
  #define GC_MARK_AS_USED(type) \
101
- rb_gc_mark( this->coders.ask_##type ); \
102
- rb_gc_mark( this->coders.coder_obj_##type );
97
+ rb_gc_mark_movable( this->coders.ask_##type ); \
98
+ rb_gc_mark_movable( this->coders.coder_obj_##type );
103
99
 
104
100
  static void
105
- pg_tmbmt_mark( t_tmbmt *this )
101
+ pg_tmbmt_mark( void *_this )
106
102
  {
107
- rb_gc_mark(this->typemap.default_typemap);
103
+ t_tmbmt *this = (t_tmbmt *)_this;
104
+ pg_typemap_mark(&this->typemap);
108
105
  FOR_EACH_MRI_TYPE( GC_MARK_AS_USED );
109
106
  }
110
107
 
108
+ static size_t
109
+ pg_tmbmt_memsize( const void *_this )
110
+ {
111
+ const t_tmbmt *this = (const t_tmbmt *)_this;
112
+ return sizeof(*this);
113
+ }
114
+
115
+ #define GC_COMPACT(type) \
116
+ pg_gc_location( this->coders.ask_##type ); \
117
+ pg_gc_location( this->coders.coder_obj_##type );
118
+
119
+ static void
120
+ pg_tmbmt_compact( void *_this )
121
+ {
122
+ t_tmbmt *this = (t_tmbmt *)_this;
123
+ pg_typemap_compact(&this->typemap);
124
+ FOR_EACH_MRI_TYPE( GC_COMPACT );
125
+ }
126
+
127
+ static const rb_data_type_t pg_tmbmt_type = {
128
+ "PG::TypeMapByMriType",
129
+ {
130
+ pg_tmbmt_mark,
131
+ RUBY_TYPED_DEFAULT_FREE,
132
+ pg_tmbmt_memsize,
133
+ pg_compact_callback(pg_tmbmt_compact),
134
+ },
135
+ &pg_typemap_type,
136
+ 0,
137
+ RUBY_TYPED_FREE_IMMEDIATELY,
138
+ };
139
+
111
140
  #define INIT_VARIABLES(type) \
112
141
  this->coders.coder_##type = NULL; \
113
142
  this->coders.ask_##type = Qnil; \
@@ -119,7 +148,7 @@ pg_tmbmt_s_allocate( VALUE klass )
119
148
  t_tmbmt *this;
120
149
  VALUE self;
121
150
 
122
- self = Data_Make_Struct( klass, t_tmbmt, pg_tmbmt_mark, -1, this );
151
+ self = TypedData_Make_Struct( klass, t_tmbmt, &pg_tmbmt_type, this );
123
152
  this->typemap.funcs.fit_to_result = pg_typemap_fit_to_result;
124
153
  this->typemap.funcs.fit_to_query = pg_tmbmt_fit_to_query;
125
154
  this->typemap.funcs.fit_to_copy_get = pg_typemap_fit_to_copy_get;
@@ -140,7 +169,7 @@ pg_tmbmt_s_allocate( VALUE klass )
140
169
  this->coders.coder_##type = NULL; \
141
170
  this->coders.ask_##type = Qnil; \
142
171
  }else if(rb_obj_is_kind_of(coder, rb_cPG_Coder)){ \
143
- Data_Get_Struct(coder, t_pg_coder, this->coders.coder_##type); \
172
+ TypedData_Get_Struct(coder, t_pg_coder, &pg_coder_type, this->coders.coder_##type); \
144
173
  this->coders.ask_##type = Qnil; \
145
174
  }else if(RB_TYPE_P(coder, T_SYMBOL)){ \
146
175
  this->coders.coder_##type = NULL; \
@@ -188,7 +217,7 @@ pg_tmbmt_s_allocate( VALUE klass )
188
217
  static VALUE
189
218
  pg_tmbmt_aset( VALUE self, VALUE mri_type, VALUE coder )
190
219
  {
191
- t_tmbmt *this = DATA_PTR( self );
220
+ t_tmbmt *this = RTYPEDDATA_DATA( self );
192
221
  char *p_mri_type;
193
222
 
194
223
  p_mri_type = StringValueCStr(mri_type);
@@ -220,7 +249,7 @@ static VALUE
220
249
  pg_tmbmt_aref( VALUE self, VALUE mri_type )
221
250
  {
222
251
  VALUE coder;
223
- t_tmbmt *this = DATA_PTR( self );
252
+ t_tmbmt *this = RTYPEDDATA_DATA( self );
224
253
  char *p_mri_type;
225
254
 
226
255
  p_mri_type = StringValueCStr(mri_type);
@@ -248,7 +277,7 @@ pg_tmbmt_aref( VALUE self, VALUE mri_type )
248
277
  static VALUE
249
278
  pg_tmbmt_coders( VALUE self )
250
279
  {
251
- t_tmbmt *this = DATA_PTR( self );
280
+ t_tmbmt *this = RTYPEDDATA_DATA( self );
252
281
  VALUE hash_coders = rb_hash_new();
253
282
 
254
283
  FOR_EACH_MRI_TYPE( ADD_TO_HASH );
@@ -46,7 +46,7 @@ pg_tmbo_lookup_oid(t_tmbo *this, int format, Oid oid)
46
46
  } else {
47
47
  VALUE obj = rb_hash_lookup( this->format[format].oid_to_coder, UINT2NUM( oid ));
48
48
  /* obj must be nil or some kind of PG::Coder, this is checked at insertion */
49
- conv = NIL_P(obj) ? NULL : DATA_PTR(obj);
49
+ conv = NIL_P(obj) ? NULL : RTYPEDDATA_DATA(obj);
50
50
  /* Write the retrieved coder to the cache */
51
51
  p_ce->oid = oid;
52
52
  p_ce->p_coder = conv;
@@ -70,7 +70,7 @@ pg_tmbo_build_type_map_for_result2( t_tmbo *this, PGresult *pgresult )
70
70
  p_colmap->typemap.default_typemap = pg_typemap_all_strings;
71
71
 
72
72
  colmap = pg_tmbc_allocate();
73
- DATA_PTR(colmap) = p_colmap;
73
+ RTYPEDDATA_DATA(colmap) = p_colmap;
74
74
 
75
75
  for(i=0; i<nfields; i++)
76
76
  {
@@ -113,18 +113,18 @@ pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
113
113
  return dec_func( p_coder, val, len, tuple, field, p_result->enc_idx );
114
114
  }
115
115
 
116
- default_tm = DATA_PTR( this->typemap.default_typemap );
116
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
117
117
  return default_tm->funcs.typecast_result_value( default_tm, result, tuple, field );
118
118
  }
119
119
 
120
120
  static VALUE
121
121
  pg_tmbo_fit_to_result( VALUE self, VALUE result )
122
122
  {
123
- t_tmbo *this = DATA_PTR( self );
123
+ t_tmbo *this = RTYPEDDATA_DATA( self );
124
124
  PGresult *pgresult = pgresult_get( result );
125
125
 
126
- /* Ensure that the default type map fits equaly. */
127
- t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
126
+ /* Ensure that the default type map fits equally. */
127
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
128
128
  VALUE sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
129
129
 
130
130
  if( PQntuples( pgresult ) <= this->max_rows_for_online_lookup ){
@@ -137,7 +137,7 @@ pg_tmbo_fit_to_result( VALUE self, VALUE result )
137
137
  /* The default type map built a new object, so we need to propagate it
138
138
  * and build a copy of this type map. */
139
139
  VALUE new_typemap = pg_tmbo_s_allocate( rb_cTypeMapByOid );
140
- t_tmbo *p_new_typemap = DATA_PTR(new_typemap);
140
+ t_tmbo *p_new_typemap = RTYPEDDATA_DATA(new_typemap);
141
141
  *p_new_typemap = *this;
142
142
  p_new_typemap->typemap.default_typemap = sub_typemap;
143
143
  return new_typemap;
@@ -147,23 +147,56 @@ pg_tmbo_fit_to_result( VALUE self, VALUE result )
147
147
  * uses a fast array lookup.
148
148
  */
149
149
  VALUE new_typemap = pg_tmbo_build_type_map_for_result2( this, pgresult );
150
- t_tmbo *p_new_typemap = DATA_PTR(new_typemap);
150
+ t_tmbo *p_new_typemap = RTYPEDDATA_DATA(new_typemap);
151
151
  p_new_typemap->typemap.default_typemap = sub_typemap;
152
152
  return new_typemap;
153
153
  }
154
154
  }
155
155
 
156
156
  static void
157
- pg_tmbo_mark( t_tmbo *this )
157
+ pg_tmbo_mark( void *_this )
158
158
  {
159
+ t_tmbo *this = (t_tmbo *)_this;
159
160
  int i;
160
161
 
161
- rb_gc_mark(this->typemap.default_typemap);
162
+ pg_typemap_mark(&this->typemap);
162
163
  for( i=0; i<2; i++){
163
- rb_gc_mark(this->format[i].oid_to_coder);
164
+ rb_gc_mark_movable(this->format[i].oid_to_coder);
164
165
  }
165
166
  }
166
167
 
168
+ static size_t
169
+ pg_tmbo_memsize( const void *_this )
170
+ {
171
+ const t_tmbo *this = (const t_tmbo *)_this;
172
+ return sizeof(*this);
173
+ }
174
+
175
+ static void
176
+ pg_tmbo_compact( void *_this )
177
+ {
178
+ t_tmbo *this = (t_tmbo *)_this;
179
+ int i;
180
+
181
+ pg_typemap_compact(&this->typemap);
182
+ for( i=0; i<2; i++){
183
+ pg_gc_location(this->format[i].oid_to_coder);
184
+ }
185
+ }
186
+
187
+ static const rb_data_type_t pg_tmbo_type = {
188
+ "PG::TypeMapByOid",
189
+ {
190
+ pg_tmbo_mark,
191
+ RUBY_TYPED_DEFAULT_FREE,
192
+ pg_tmbo_memsize,
193
+ pg_compact_callback(pg_tmbo_compact),
194
+ },
195
+ &pg_typemap_type,
196
+ 0,
197
+ RUBY_TYPED_FREE_IMMEDIATELY,
198
+ };
199
+
167
200
  static VALUE
168
201
  pg_tmbo_s_allocate( VALUE klass )
169
202
  {
@@ -171,7 +204,7 @@ pg_tmbo_s_allocate( VALUE klass )
171
204
  VALUE self;
172
205
  int i;
173
206
 
174
- self = Data_Make_Struct( klass, t_tmbo, pg_tmbo_mark, -1, this );
207
+ self = TypedData_Make_Struct( klass, t_tmbo, &pg_tmbo_type, this );
175
208
 
176
209
  this->typemap.funcs.fit_to_result = pg_tmbo_fit_to_result;
177
210
  this->typemap.funcs.fit_to_query = pg_typemap_fit_to_query;
@@ -205,15 +238,11 @@ static VALUE
205
238
  pg_tmbo_add_coder( VALUE self, VALUE coder )
206
239
  {
207
240
  VALUE hash;
208
- t_tmbo *this = DATA_PTR( self );
241
+ t_tmbo *this = RTYPEDDATA_DATA( self );
209
242
  t_pg_coder *p_coder;
210
243
  struct pg_tmbo_oid_cache_entry *p_ce;
211
244
 
212
- if( !rb_obj_is_kind_of(coder, rb_cPG_Coder) )
213
- rb_raise(rb_eArgError, "invalid type %s (should be some kind of PG::Coder)",
214
- rb_obj_classname( coder ));
215
-
216
- Data_Get_Struct(coder, t_pg_coder, p_coder);
245
+ TypedData_Get_Struct(coder, t_pg_coder, &pg_coder_type, p_coder);
217
246
 
218
247
  if( p_coder->format < 0 || p_coder->format > 1 )
219
248
  rb_raise(rb_eArgError, "invalid format code %d", p_coder->format);
@@ -243,7 +272,7 @@ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
243
272
  {
244
273
  VALUE hash;
245
274
  VALUE coder;
246
- t_tmbo *this = DATA_PTR( self );
275
+ t_tmbo *this = RTYPEDDATA_DATA( self );
247
276
  int i_format = NUM2INT(format);
248
277
  struct pg_tmbo_oid_cache_entry *p_ce;
249
278
 
@@ -269,7 +298,7 @@ pg_tmbo_rm_coder( VALUE self, VALUE format, VALUE oid )
269
298
  static VALUE
270
299
  pg_tmbo_coders( VALUE self )
271
300
  {
272
- t_tmbo *this = DATA_PTR( self );
301
+ t_tmbo *this = RTYPEDDATA_DATA( self );
273
302
 
274
303
  return rb_ary_concat(
275
304
  rb_funcall(this->format[0].oid_to_coder, rb_intern("values"), 0),
@@ -288,7 +317,7 @@ pg_tmbo_coders( VALUE self )
288
317
  static VALUE
289
318
  pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
290
319
  {
291
- t_tmbo *this = DATA_PTR( self );
320
+ t_tmbo *this = RTYPEDDATA_DATA( self );
292
321
  this->max_rows_for_online_lookup = NUM2INT(value);
293
322
  return value;
294
323
  }
@@ -300,7 +329,7 @@ pg_tmbo_max_rows_for_online_lookup_set( VALUE self, VALUE value )
300
329
  static VALUE
301
330
  pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
302
331
  {
303
- t_tmbo *this = DATA_PTR( self );
332
+ t_tmbo *this = RTYPEDDATA_DATA( self );
304
333
  return INT2NUM(this->max_rows_for_online_lookup);
305
334
  }
306
335
 
@@ -315,7 +344,7 @@ pg_tmbo_max_rows_for_online_lookup_get( VALUE self )
315
344
  static VALUE
316
345
  pg_tmbo_build_column_map( VALUE self, VALUE result )
317
346
  {
318
- t_tmbo *this = DATA_PTR( self );
347
+ t_tmbo *this = RTYPEDDATA_DATA( self );
319
348
 
320
349
  if ( !rb_obj_is_kind_of(result, rb_cPGresult) ) {
321
350
  rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Result)",
@@ -19,6 +19,33 @@ typedef struct {
19
19
  VALUE self;
20
20
  } t_tmir;
21
21
 
22
+ static size_t
23
+ pg_tmir_memsize( const void *_this )
24
+ {
25
+ const t_tmir *this = (const t_tmir *)_this;
26
+ return sizeof(*this);
27
+ }
28
+
29
+ static void
30
+ pg_tmir_compact( void *_this )
31
+ {
32
+ t_tmir *this = (t_tmir *)_this;
33
+ pg_typemap_compact(&this->typemap);
34
+ pg_gc_location(this->self);
35
+ }
36
+
37
+ static const rb_data_type_t pg_tmir_type = {
38
+ "PG::TypeMapInRuby",
39
+ {
40
+ pg_typemap_mark,
41
+ RUBY_TYPED_DEFAULT_FREE,
42
+ pg_tmir_memsize,
43
+ pg_compact_callback(pg_tmir_compact),
44
+ },
45
+ &pg_typemap_type,
46
+ 0,
47
+ RUBY_TYPED_FREE_IMMEDIATELY,
48
+ };
22
49
 
23
50
  /*
24
51
  * call-seq:
@@ -34,33 +61,37 @@ typedef struct {
34
61
  static VALUE
35
62
  pg_tmir_fit_to_result( VALUE self, VALUE result )
36
63
  {
37
- t_tmir *this = DATA_PTR( self );
64
+ t_tmir *this = RTYPEDDATA_DATA( self );
38
65
  t_typemap *default_tm;
39
66
  t_typemap *p_new_typemap;
40
67
  VALUE sub_typemap;
41
68
  VALUE new_typemap;
42
69
 
43
70
  if( rb_respond_to(self, s_id_fit_to_result) ){
71
+ t_typemap *tm;
72
+ UNUSED(tm);
44
73
  new_typemap = rb_funcall( self, s_id_fit_to_result, 1, result );
45
74
 
46
75
  if ( !rb_obj_is_kind_of(new_typemap, rb_cTypeMap) ) {
76
+ /* TypedData_Get_Struct() raises "wrong argument type", which is misleading,
77
+ * so we better raise our own message */
47
78
  rb_raise( rb_eTypeError, "wrong return type from fit_to_result: %s expected kind of PG::TypeMap",
48
79
  rb_obj_classname( new_typemap ) );
49
80
  }
50
- Check_Type( new_typemap, T_DATA );
81
+ TypedData_Get_Struct(new_typemap, t_typemap, &pg_typemap_type, tm);
51
82
  } else {
52
83
  new_typemap = self;
53
84
  }
54
85
 
55
- /* Ensure that the default type map fits equaly. */
56
- default_tm = DATA_PTR( this->typemap.default_typemap );
86
+ /* Ensure that the default type map fits equally. */
87
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
57
88
  sub_typemap = default_tm->funcs.fit_to_result( this->typemap.default_typemap, result );
58
89
 
59
90
  if( sub_typemap != this->typemap.default_typemap ){
60
91
  new_typemap = rb_obj_dup( new_typemap );
61
92
  }
62
93
 
63
- p_new_typemap = DATA_PTR(new_typemap);
94
+ p_new_typemap = RTYPEDDATA_DATA(new_typemap);
64
95
  p_new_typemap->default_typemap = sub_typemap;
65
96
  return new_typemap;
66
97
  }
@@ -95,8 +126,8 @@ pg_tmir_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
95
126
  static VALUE
96
127
  pg_tmir_typecast_result_value( VALUE self, VALUE result, VALUE tuple, VALUE field )
97
128
  {
98
- t_tmir *this = DATA_PTR( self );
99
- t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
129
+ t_tmir *this = RTYPEDDATA_DATA( self );
130
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
100
131
  return default_tm->funcs.typecast_result_value( default_tm, result, NUM2INT(tuple), NUM2INT(field) );
101
132
  }
102
133
 
@@ -113,15 +144,15 @@ pg_tmir_typecast_result_value( VALUE self, VALUE result, VALUE tuple, VALUE fiel
113
144
  static VALUE
114
145
  pg_tmir_fit_to_query( VALUE self, VALUE params )
115
146
  {
116
- t_tmir *this = DATA_PTR( self );
147
+ t_tmir *this = RTYPEDDATA_DATA( self );
117
148
  t_typemap *default_tm;
118
149
 
119
150
  if( rb_respond_to(self, s_id_fit_to_query) ){
120
151
  rb_funcall( self, s_id_fit_to_query, 1, params );
121
152
  }
122
153
 
123
- /* Ensure that the default type map fits equaly. */
124
- default_tm = DATA_PTR( this->typemap.default_typemap );
154
+ /* Ensure that the default type map fits equally. */
155
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
125
156
  default_tm->funcs.fit_to_query( this->typemap.default_typemap, params );
126
157
 
127
158
  return self;
@@ -137,7 +168,7 @@ pg_tmir_query_param( t_typemap *p_typemap, VALUE param_value, int field )
137
168
  if ( NIL_P(coder) ){
138
169
  return NULL;
139
170
  } else if( rb_obj_is_kind_of(coder, rb_cPG_Coder) ) {
140
- return DATA_PTR(coder);
171
+ return RTYPEDDATA_DATA(coder);
141
172
  } else {
142
173
  rb_raise( rb_eTypeError, "wrong return type from typecast_query_param: %s expected nil or kind of PG::Coder",
143
174
  rb_obj_classname( coder ) );
@@ -161,8 +192,8 @@ pg_tmir_query_param( t_typemap *p_typemap, VALUE param_value, int field )
161
192
  static VALUE
162
193
  pg_tmir_typecast_query_param( VALUE self, VALUE param_value, VALUE field )
163
194
  {
164
- t_tmir *this = DATA_PTR( self );
165
- t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
195
+ t_tmir *this = RTYPEDDATA_DATA( self );
196
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
166
197
  t_pg_coder *p_coder = default_tm->funcs.typecast_query_param( default_tm, param_value, NUM2INT(field) );
167
198
 
168
199
  return p_coder ? p_coder->coder_obj : Qnil;
@@ -186,7 +217,7 @@ static VALUE pg_tmir_fit_to_copy_get_dummy( VALUE self ){}
186
217
  static int
187
218
  pg_tmir_fit_to_copy_get( VALUE self )
188
219
  {
189
- t_tmir *this = DATA_PTR( self );
220
+ t_tmir *this = RTYPEDDATA_DATA( self );
190
221
  t_typemap *default_tm;
191
222
  VALUE num_columns = INT2NUM(0);
192
223
 
@@ -198,8 +229,8 @@ pg_tmir_fit_to_copy_get( VALUE self )
198
229
  rb_raise( rb_eTypeError, "wrong return type from fit_to_copy_get: %s expected kind of Integer",
199
230
  rb_obj_classname( num_columns ) );
200
231
  }
201
- /* Ensure that the default type map fits equaly. */
202
- default_tm = DATA_PTR( this->typemap.default_typemap );
232
+ /* Ensure that the default type map fits equally. */
233
+ default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
203
234
  default_tm->funcs.fit_to_copy_get( this->typemap.default_typemap );
204
235
 
205
236
  return NUM2INT(num_columns);;
@@ -239,8 +270,8 @@ pg_tmir_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format
239
270
  static VALUE
240
271
  pg_tmir_typecast_copy_get( VALUE self, VALUE field_str, VALUE fieldno, VALUE format, VALUE enc )
241
272
  {
242
- t_tmir *this = DATA_PTR( self );
243
- t_typemap *default_tm = DATA_PTR( this->typemap.default_typemap );
273
+ t_tmir *this = RTYPEDDATA_DATA( self );
274
+ t_typemap *default_tm = RTYPEDDATA_DATA( this->typemap.default_typemap );
244
275
  int enc_idx = rb_to_encoding_index( enc );
245
276
 
246
277
  return default_tm->funcs.typecast_copy_get( default_tm, field_str, NUM2INT(fieldno), NUM2INT(format), enc_idx );
@@ -252,7 +283,7 @@ pg_tmir_s_allocate( VALUE klass )
252
283
  t_tmir *this;
253
284
  VALUE self;
254
285
 
255
- self = Data_Make_Struct( klass, t_tmir, NULL, -1, this );
286
+ self = TypedData_Make_Struct( klass, t_tmir, &pg_tmir_type, this );
256
287
 
257
288
  this->typemap.funcs.fit_to_result = pg_tmir_fit_to_result;
258
289
  this->typemap.funcs.fit_to_query = pg_tmir_fit_to_query;
data/ext/pg_util.c CHANGED
@@ -91,7 +91,7 @@ base64_decode( char *out, const char *in, unsigned int len)
91
91
  *out_ptr++ = (b << 4) | (c >> 2);
92
92
  *out_ptr++ = (c << 6) | d;
93
93
  } else if (in_ptr < iend_ptr){
94
- a = b = c = d = 0xff;
94
+ b = c = d = 0xff;
95
95
  while ((a = base64_decode_table[*in_ptr++]) == 0xff && in_ptr < iend_ptr) {}
96
96
  if (in_ptr < iend_ptr){
97
97
  while ((b = base64_decode_table[*in_ptr++]) == 0xff && in_ptr < iend_ptr) {}
@@ -116,7 +116,7 @@ base64_decode( char *out, const char *in, unsigned int len)
116
116
  }
117
117
 
118
118
 
119
- return (char*)out_ptr - out;
119
+ return (int)((char*)out_ptr - out);
120
120
  }
121
121
 
122
122
  /*
@@ -0,0 +1,47 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'pg' unless defined?( PG )
5
+
6
+ # Simple set of rules for type casting common PostgreSQL types from Ruby
7
+ # to PostgreSQL.
8
+ #
9
+ # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
10
+ # PostgreSQL's +pg_type+ table in PG::BasicTypeMapBasedOnResult.new .
11
+ #
12
+ # This class works equal to PG::BasicTypeMapForResults, but does not define decoders for
13
+ # the given result OIDs, but encoders. So it can be used to type cast field values based on
14
+ # the type OID retrieved by a separate SQL query.
15
+ #
16
+ # PG::TypeMapByOid#build_column_map(result) can be used to generate a result independent
17
+ # PG::TypeMapByColumn type map, which can subsequently be used to cast query bind parameters
18
+ # or #put_copy_data fields.
19
+ #
20
+ # Example:
21
+ # conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
22
+ #
23
+ # # Retrieve table OIDs per empty result set.
24
+ # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
25
+ # # Build a type map for common ruby to database type encoders.
26
+ # btm = PG::BasicTypeMapBasedOnResult.new(conn)
27
+ # # Build a PG::TypeMapByColumn with encoders suitable for copytable.
28
+ # tm = btm.build_column_map( res )
29
+ # row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
30
+ #
31
+ # conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
32
+ # conn.put_copy_data ['a', 123, [5,4,3]]
33
+ # end
34
+ # This inserts a single row into copytable with type casts from ruby to
35
+ # database types.
36
+ class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
37
+ include PG::BasicTypeRegistry::Checker
38
+
39
+ def initialize(connection_or_coder_maps, registry: nil)
40
+ @coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
41
+
42
+ # Populate TypeMapByOid hash with encoders
43
+ @coder_maps.each_format(:encoder).flat_map{|f| f.coders }.each do |coder|
44
+ add_coder(coder)
45
+ end
46
+ end
47
+ end