pg 0.18.4 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  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/BSDL +2 -2
  15. data/Gemfile +14 -0
  16. data/History.rdoc +436 -4
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +4 -4
  19. data/README.ja.rdoc +1 -2
  20. data/README.rdoc +67 -17
  21. data/Rakefile +32 -144
  22. data/Rakefile.cross +70 -69
  23. data/certs/ged.pem +24 -0
  24. data/certs/larskanis-2022.pem +26 -0
  25. data/ext/errorcodes.def +109 -0
  26. data/ext/errorcodes.rb +1 -1
  27. data/ext/errorcodes.txt +35 -2
  28. data/ext/extconf.rb +119 -54
  29. data/ext/gvl_wrappers.c +8 -0
  30. data/ext/gvl_wrappers.h +44 -33
  31. data/ext/pg.c +213 -171
  32. data/ext/pg.h +92 -98
  33. data/ext/pg_binary_decoder.c +82 -15
  34. data/ext/pg_binary_encoder.c +20 -19
  35. data/ext/pg_coder.c +175 -39
  36. data/ext/pg_connection.c +1730 -1135
  37. data/ext/pg_copy_coder.c +94 -27
  38. data/ext/pg_record_coder.c +521 -0
  39. data/ext/pg_result.c +640 -221
  40. data/ext/pg_text_decoder.c +606 -40
  41. data/ext/pg_text_encoder.c +250 -99
  42. data/ext/pg_tuple.c +569 -0
  43. data/ext/pg_type_map.c +55 -15
  44. data/ext/pg_type_map_all_strings.c +19 -5
  45. data/ext/pg_type_map_by_class.c +54 -24
  46. data/ext/pg_type_map_by_column.c +73 -34
  47. data/ext/pg_type_map_by_mri_type.c +48 -19
  48. data/ext/pg_type_map_by_oid.c +55 -25
  49. data/ext/pg_type_map_in_ruby.c +51 -20
  50. data/ext/{util.c → pg_util.c} +12 -12
  51. data/ext/{util.h → pg_util.h} +2 -2
  52. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  53. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  54. data/lib/pg/basic_type_map_for_results.rb +81 -0
  55. data/lib/pg/basic_type_registry.rb +301 -0
  56. data/lib/pg/binary_decoder.rb +23 -0
  57. data/lib/pg/coder.rb +24 -3
  58. data/lib/pg/connection.rb +723 -65
  59. data/lib/pg/constants.rb +2 -1
  60. data/lib/pg/exceptions.rb +9 -2
  61. data/lib/pg/result.rb +24 -7
  62. data/lib/pg/text_decoder.rb +24 -22
  63. data/lib/pg/text_encoder.rb +40 -8
  64. data/lib/pg/tuple.rb +30 -0
  65. data/lib/pg/type_map_by_column.rb +3 -2
  66. data/lib/pg/version.rb +4 -0
  67. data/lib/pg.rb +61 -36
  68. data/misc/openssl-pg-segfault.rb +31 -0
  69. data/misc/postgres/History.txt +9 -0
  70. data/misc/postgres/Manifest.txt +5 -0
  71. data/misc/postgres/README.txt +21 -0
  72. data/misc/postgres/Rakefile +21 -0
  73. data/misc/postgres/lib/postgres.rb +16 -0
  74. data/misc/ruby-pg/History.txt +9 -0
  75. data/misc/ruby-pg/Manifest.txt +5 -0
  76. data/misc/ruby-pg/README.txt +21 -0
  77. data/misc/ruby-pg/Rakefile +21 -0
  78. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  79. data/pg.gemspec +32 -0
  80. data/rakelib/task_extension.rb +46 -0
  81. data/sample/array_insert.rb +1 -1
  82. data/sample/async_api.rb +4 -8
  83. data/sample/async_copyto.rb +1 -1
  84. data/sample/async_mixed.rb +1 -1
  85. data/sample/check_conn.rb +1 -1
  86. data/sample/copydata.rb +71 -0
  87. data/sample/copyfrom.rb +1 -1
  88. data/sample/copyto.rb +1 -1
  89. data/sample/cursor.rb +1 -1
  90. data/sample/disk_usage_report.rb +6 -15
  91. data/sample/issue-119.rb +2 -2
  92. data/sample/losample.rb +1 -1
  93. data/sample/minimal-testcase.rb +2 -2
  94. data/sample/notify_wait.rb +1 -1
  95. data/sample/pg_statistics.rb +6 -15
  96. data/sample/replication_monitor.rb +9 -18
  97. data/sample/test_binary_values.rb +1 -1
  98. data/sample/wal_shipper.rb +2 -2
  99. data/sample/warehouse_partitions.rb +8 -17
  100. data.tar.gz.sig +0 -0
  101. metadata +79 -225
  102. metadata.gz.sig +0 -0
  103. data/ChangeLog +0 -5911
  104. data/lib/pg/basic_type_mapping.rb +0 -399
  105. data/spec/data/expected_trace.out +0 -26
  106. data/spec/data/random_binary_data +0 -0
  107. data/spec/helpers.rb +0 -355
  108. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  109. data/spec/pg/connection_spec.rb +0 -1544
  110. data/spec/pg/result_spec.rb +0 -449
  111. data/spec/pg/type_map_by_class_spec.rb +0 -138
  112. data/spec/pg/type_map_by_column_spec.rb +0 -222
  113. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  114. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  115. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  116. data/spec/pg/type_map_spec.rb +0 -22
  117. data/spec/pg/type_spec.rb +0 -697
  118. data/spec/pg_spec.rb +0 -50
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_oid.c - PG::TypeMapByOid class extension
3
- * $Id: pg_type_map_by_oid.c,v c99d26015e3c 2014/12/12 20:58:25 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -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
  {
@@ -110,21 +110,21 @@ pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
110
110
  char * val = PQgetvalue( p_result->pgresult, tuple, field );
111
111
  int len = PQgetlength( p_result->pgresult, tuple, field );
112
112
  t_pg_coder_dec_func dec_func = pg_coder_dec_func( p_coder, format );
113
- return dec_func( p_coder, val, len, tuple, field, ENCODING_GET(result) );
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)",
@@ -351,5 +380,6 @@ init_pg_type_map_by_oid()
351
380
  rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
352
381
  rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
353
382
  rb_define_method( rb_cTypeMapByOid, "build_column_map", pg_tmbo_build_column_map, 1 );
383
+ /* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
354
384
  rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
355
385
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_in_ruby.c - PG::TypeMapInRuby class extension
3
- * $Id: pg_type_map_in_ruby.c,v 3d89d3aae4fd 2015/01/05 16:19:41 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -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;
@@ -1,11 +1,11 @@
1
1
  /*
2
- * util.c - Utils for ruby-pg
3
- * $Id: util.c,v 5fb9170f6a7d 2015/06/29 11:15:12 kanis $
2
+ * pg_util.c - Utils for ruby-pg
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
7
7
  #include "pg.h"
8
- #include "util.h"
8
+ #include "pg_util.h"
9
9
 
10
10
  static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
11
11
 
@@ -15,19 +15,19 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
15
15
  * in-place (with _out_ == _in_).
16
16
  */
17
17
  void
18
- base64_encode( char *out, char *in, int len)
18
+ base64_encode( char *out, const char *in, int len)
19
19
  {
20
- unsigned char *in_ptr = (unsigned char *)in + len;
20
+ const unsigned char *in_ptr = (const unsigned char *)in + len;
21
21
  char *out_ptr = out + BASE64_ENCODED_SIZE(len);
22
22
  int part_len = len % 3;
23
23
 
24
24
  if( part_len > 0 ){
25
- long byte2 = part_len > 2 ? *--in_ptr : 0;
25
+ long byte2 = 0;
26
26
  long byte1 = part_len > 1 ? *--in_ptr : 0;
27
27
  long byte0 = *--in_ptr;
28
28
  long triple = (byte0 << 16) + (byte1 << 8) + byte2;
29
29
 
30
- *--out_ptr = part_len > 2 ? base64_encode_table[(triple >> 0 * 6) & 0x3F] : '=';
30
+ *--out_ptr = '=';
31
31
  *--out_ptr = part_len > 1 ? base64_encode_table[(triple >> 1 * 6) & 0x3F] : '=';
32
32
  *--out_ptr = base64_encode_table[(triple >> 2 * 6) & 0x3F];
33
33
  *--out_ptr = base64_encode_table[(triple >> 3 * 6) & 0x3F];
@@ -72,12 +72,12 @@ static const unsigned char base64_decode_table[] =
72
72
  * It is possible to decode a string in-place (with _out_ == _in_).
73
73
  */
74
74
  int
75
- base64_decode( char *out, char *in, unsigned int len)
75
+ base64_decode( char *out, const char *in, unsigned int len)
76
76
  {
77
77
  unsigned char a, b, c, d;
78
- unsigned char *in_ptr = (unsigned char *)in;
78
+ const unsigned char *in_ptr = (const unsigned char *)in;
79
79
  unsigned char *out_ptr = (unsigned char *)out;
80
- unsigned char *iend_ptr = (unsigned char *)in + len;
80
+ const unsigned char *iend_ptr = (unsigned char *)in + len;
81
81
 
82
82
  for(;;){
83
83
  if( in_ptr+3 < iend_ptr &&
@@ -91,7 +91,7 @@ base64_decode( char *out, 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, 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
  /*
@@ -57,8 +57,8 @@
57
57
  #define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
58
58
  #define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
59
59
 
60
- void base64_encode( char *out, char *in, int len);
61
- int base64_decode( char *out, char *in, unsigned int len);
60
+ void base64_encode( char *out, const char *in, int len);
61
+ int base64_decode( char *out, const char *in, unsigned int len);
62
62
 
63
63
  int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
64
64
 
@@ -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
@@ -0,0 +1,193 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'pg' unless defined?( PG )
5
+
6
+ # Simple set of rules for type casting common Ruby types to PostgreSQL.
7
+ #
8
+ # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
9
+ # PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
10
+ #
11
+ # Query params are type casted based on the class of the given value.
12
+ #
13
+ # Higher level libraries will most likely not make use of this class, but use their
14
+ # own derivation of PG::TypeMapByClass or another set of rules to choose suitable
15
+ # encoders and decoders for the values to be sent.
16
+ #
17
+ # Example:
18
+ # conn = PG::Connection.new
19
+ # # Assign a default ruleset for type casts of input and output values.
20
+ # conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
21
+ # # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
22
+ # # The format of the parameter is set to 0 (text) and the OID of this parameter is set to 20 (int8).
23
+ # res = conn.exec_params( "SELECT $1", [5] )
24
+ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
25
+ # Helper class for submission of binary strings into bytea columns.
26
+ #
27
+ # Since PG::BasicTypeMapForQueries chooses the encoder to be used by the class of the submitted value,
28
+ # it's necessary to send binary strings as BinaryData.
29
+ # That way they're distinct from text strings.
30
+ # Please note however that PG::BasicTypeMapForResults delivers bytea columns as plain String
31
+ # with binary encoding.
32
+ #
33
+ # conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
34
+ # conn.exec("CREATE TEMP TABLE test (data bytea)")
35
+ # bd = PG::BasicTypeMapForQueries::BinaryData.new("ab\xff\0cd")
36
+ # conn.exec_params("INSERT INTO test (data) VALUES ($1)", [bd])
37
+ class BinaryData < String
38
+ end
39
+
40
+ class UndefinedEncoder < RuntimeError
41
+ end
42
+
43
+ include PG::BasicTypeRegistry::Checker
44
+
45
+ # Create a new type map for query submission
46
+ #
47
+ # Options:
48
+ # * +registry+: Custom type registry, nil for default global registry
49
+ # * +if_undefined+: Optional +Proc+ object which is called, if no type for an parameter class is not defined in the registry.
50
+ def initialize(connection_or_coder_maps, registry: nil, if_undefined: nil)
51
+ @coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
52
+ @array_encoders_by_klass = array_encoders_by_klass
53
+ @encode_array_as = :array
54
+ @if_undefined = if_undefined || proc { |oid_name, format|
55
+ raise UndefinedEncoder, "no encoder defined for type #{oid_name.inspect} format #{format}"
56
+ }
57
+ init_encoders
58
+ end
59
+
60
+ # Change the mechanism that is used to encode ruby array values
61
+ #
62
+ # Possible values:
63
+ # * +:array+ : Encode the ruby array as a PostgreSQL array.
64
+ # The array element type is inferred from the class of the first array element. This is the default.
65
+ # * +:json+ : Encode the ruby array as a JSON document.
66
+ # * +:record+ : Encode the ruby array as a composite type row.
67
+ # * <code>"_type"</code> : Encode the ruby array as a particular PostgreSQL type.
68
+ # All PostgreSQL array types are supported.
69
+ # If there's an encoder registered for the elements +type+, it will be used.
70
+ # Otherwise a string conversion (by +value.to_s+) is done.
71
+ def encode_array_as=(pg_type)
72
+ case pg_type
73
+ when :array
74
+ when :json
75
+ when :record
76
+ when /\A_/
77
+ else
78
+ raise ArgumentError, "invalid pg_type #{pg_type.inspect}"
79
+ end
80
+
81
+ @encode_array_as = pg_type
82
+
83
+ init_encoders
84
+ end
85
+
86
+ attr_reader :encode_array_as
87
+
88
+ private
89
+
90
+ def init_encoders
91
+ coders.each { |kl, c| self[kl] = nil } # Clear type map
92
+ populate_encoder_list
93
+ @textarray_encoder = coder_by_name(0, :encoder, '_text')
94
+ end
95
+
96
+ def coder_by_name(format, direction, name)
97
+ check_format_and_direction(format, direction)
98
+ @coder_maps.map_for(format, direction).coder_by_name(name)
99
+ end
100
+
101
+ def undefined(name, format)
102
+ @if_undefined.call(name, format)
103
+ end
104
+
105
+ def populate_encoder_list
106
+ DEFAULT_TYPE_MAP.each do |klass, selector|
107
+ if Array === selector
108
+ format, name, oid_name = selector
109
+ coder = coder_by_name(format, :encoder, name).dup
110
+ if coder
111
+ if oid_name
112
+ oid_coder = coder_by_name(format, :encoder, oid_name)
113
+ if oid_coder
114
+ coder.oid = oid_coder.oid
115
+ else
116
+ undefined(oid_name, format)
117
+ end
118
+ else
119
+ coder.oid = 0
120
+ end
121
+ self[klass] = coder
122
+ else
123
+ undefined(name, format)
124
+ end
125
+ else
126
+
127
+ case @encode_array_as
128
+ when :array
129
+ self[klass] = selector
130
+ when :json
131
+ self[klass] = PG::TextEncoder::JSON.new
132
+ when :record
133
+ self[klass] = PG::TextEncoder::Record.new type_map: self
134
+ when /\A_/
135
+ coder = coder_by_name(0, :encoder, @encode_array_as)
136
+ if coder
137
+ self[klass] = coder
138
+ else
139
+ undefined(@encode_array_as, format)
140
+ end
141
+ else
142
+ raise ArgumentError, "invalid pg_type #{@encode_array_as.inspect}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def array_encoders_by_klass
149
+ DEFAULT_ARRAY_TYPE_MAP.inject({}) do |h, (klass, (format, name))|
150
+ h[klass] = coder_by_name(format, :encoder, name)
151
+ h
152
+ end
153
+ end
154
+
155
+ def get_array_type(value)
156
+ elem = value
157
+ while elem.kind_of?(Array)
158
+ elem = elem.first
159
+ end
160
+ @array_encoders_by_klass[elem.class] ||
161
+ elem.class.ancestors.lazy.map{|ancestor| @array_encoders_by_klass[ancestor] }.find{|a| a } ||
162
+ @textarray_encoder
163
+ end
164
+
165
+ DEFAULT_TYPE_MAP = {
166
+ TrueClass => [1, 'bool', 'bool'],
167
+ FalseClass => [1, 'bool', 'bool'],
168
+ # We use text format and no type OID for numbers, because setting the OID can lead
169
+ # to unnecessary type conversions on server side.
170
+ Integer => [0, 'int8'],
171
+ Float => [0, 'float8'],
172
+ BigDecimal => [0, 'numeric'],
173
+ Time => [0, 'timestamptz'],
174
+ # We use text format and no type OID for IPAddr, because setting the OID can lead
175
+ # to unnecessary inet/cidr conversions on the server side.
176
+ IPAddr => [0, 'inet'],
177
+ Hash => [0, 'json'],
178
+ Array => :get_array_type,
179
+ BinaryData => [1, 'bytea'],
180
+ }
181
+
182
+ DEFAULT_ARRAY_TYPE_MAP = {
183
+ TrueClass => [0, '_bool'],
184
+ FalseClass => [0, '_bool'],
185
+ Integer => [0, '_int8'],
186
+ String => [0, '_text'],
187
+ Float => [0, '_float8'],
188
+ BigDecimal => [0, '_numeric'],
189
+ Time => [0, '_timestamptz'],
190
+ IPAddr => [0, '_inet'],
191
+ }
192
+
193
+ end