pg 1.2.3-x86-mingw32 → 1.3.0.rc1-x86-mingw32
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.appveyor.yml +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +80 -0
- data/.github/workflows/source-gem.yml +129 -0
- data/.gitignore +13 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/Gemfile +14 -0
- data/History.rdoc +75 -7
- data/Manifest.txt +0 -1
- data/README.rdoc +7 -6
- data/Rakefile +27 -138
- data/Rakefile.cross +5 -5
- data/certs/ged.pem +24 -0
- data/ext/errorcodes.def +8 -0
- data/ext/errorcodes.txt +3 -1
- data/ext/extconf.rb +90 -19
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +35 -1
- data/ext/pg.h +18 -1
- data/ext/pg_coder.c +82 -28
- data/ext/pg_connection.c +538 -279
- data/ext/pg_copy_coder.c +45 -16
- data/ext/pg_record_coder.c +38 -10
- data/ext/pg_result.c +61 -31
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +6 -6
- data/ext/pg_tuple.c +47 -21
- data/ext/pg_type_map.c +41 -8
- data/ext/pg_type_map_all_strings.c +14 -1
- data/ext/pg_type_map_by_class.c +49 -24
- data/ext/pg_type_map_by_column.c +64 -28
- data/ext/pg_type_map_by_mri_type.c +47 -18
- data/ext/pg_type_map_by_oid.c +52 -23
- data/ext/pg_type_map_in_ruby.c +50 -19
- data/ext/pg_util.c +2 -2
- data/lib/2.5/pg_ext.so +0 -0
- data/lib/2.6/pg_ext.so +0 -0
- data/lib/2.7/pg_ext.so +0 -0
- data/lib/3.0/pg_ext.so +0 -0
- data/lib/pg/basic_type_map_based_on_result.rb +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +296 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +369 -56
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +38 -25
- data/lib/x86-mingw32/libpq.dll +0 -0
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +32 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +106 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data.tar.gz.sig +0 -0
- metadata +81 -230
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -0
- data/lib/2.2/pg_ext.so +0 -0
- data/lib/2.3/pg_ext.so +0 -0
- data/lib/2.4/pg_ext.so +0 -0
- data/lib/pg/basic_type_mapping.rb +0 -522
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -380
- data/spec/pg/basic_type_mapping_spec.rb +0 -630
- data/spec/pg/connection_spec.rb +0 -1949
- data/spec/pg/connection_sync_spec.rb +0 -41
- data/spec/pg/result_spec.rb +0 -681
- data/spec/pg/tuple_spec.rb +0 -333
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -226
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -1123
- data/spec/pg_spec.rb +0 -50
data/ext/pg_type_map_by_oid.c
CHANGED
@@ -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 :
|
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
|
-
|
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 =
|
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 =
|
123
|
+
t_tmbo *this = RTYPEDDATA_DATA( self );
|
124
124
|
PGresult *pgresult = pgresult_get( result );
|
125
125
|
|
126
|
-
/* Ensure that the default type map fits
|
127
|
-
t_typemap *default_tm =
|
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 =
|
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 =
|
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(
|
157
|
+
pg_tmbo_mark( void *_this )
|
158
158
|
{
|
159
|
+
t_tmbo *this = (t_tmbo *)_this;
|
159
160
|
int i;
|
160
161
|
|
161
|
-
|
162
|
+
pg_typemap_mark(&this->typemap);
|
162
163
|
for( i=0; i<2; i++){
|
163
|
-
|
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 =
|
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 =
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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)",
|
data/ext/pg_type_map_in_ruby.c
CHANGED
@@ -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 =
|
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
|
-
|
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
|
56
|
-
default_tm =
|
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 =
|
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 =
|
99
|
-
t_typemap *default_tm =
|
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 =
|
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
|
124
|
-
default_tm =
|
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
|
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 =
|
165
|
-
t_typemap *default_tm =
|
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 =
|
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
|
202
|
-
default_tm =
|
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 =
|
243
|
-
t_typemap *default_tm =
|
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 =
|
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
|
-
|
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
|
/*
|
data/lib/2.5/pg_ext.so
CHANGED
Binary file
|
data/lib/2.6/pg_ext.so
CHANGED
Binary file
|
data/lib/2.7/pg_ext.so
CHANGED
Binary file
|
data/lib/3.0/pg_ext.so
ADDED
Binary file
|
@@ -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
|
@@ -0,0 +1,81 @@
|
|
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 to Ruby.
|
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::BasicTypeMapForResults.new .
|
10
|
+
#
|
11
|
+
# Result values are type casted based on the type OID of the given result column.
|
12
|
+
#
|
13
|
+
# Higher level libraries will most likely not make use of this class, but use their
|
14
|
+
# own set of rules to choose suitable encoders and decoders.
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
# conn = PG::Connection.new
|
18
|
+
# # Assign a default ruleset for type casts of output values.
|
19
|
+
# conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
|
20
|
+
# # Execute a query.
|
21
|
+
# res = conn.exec_params( "SELECT $1::INT", ['5'] )
|
22
|
+
# # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
|
23
|
+
# # is done by PG::TextDecoder::Integer internally for all value retrieval methods.
|
24
|
+
# res.values # => [[5]]
|
25
|
+
#
|
26
|
+
# PG::TypeMapByOid#build_column_map(result) can be used to generate
|
27
|
+
# a result independent PG::TypeMapByColumn type map, which can subsequently be used
|
28
|
+
# to cast #get_copy_data fields:
|
29
|
+
#
|
30
|
+
# For the following table:
|
31
|
+
# conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '{5,4,3}'::INT[])" )
|
32
|
+
#
|
33
|
+
# # Retrieve table OIDs per empty result set.
|
34
|
+
# res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
35
|
+
# # Build a type map for common database to ruby type decoders.
|
36
|
+
# btm = PG::BasicTypeMapForResults.new(conn)
|
37
|
+
# # Build a PG::TypeMapByColumn with decoders suitable for copytable.
|
38
|
+
# tm = btm.build_column_map( res )
|
39
|
+
# row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
|
40
|
+
#
|
41
|
+
# conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
|
42
|
+
# while row=conn.get_copy_data
|
43
|
+
# p row
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
# This prints the rows with type casted columns:
|
47
|
+
# ["a", 123, [5, 4, 3]]
|
48
|
+
#
|
49
|
+
# See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
|
50
|
+
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
51
|
+
include PG::BasicTypeRegistry::Checker
|
52
|
+
|
53
|
+
class WarningTypeMap < PG::TypeMapInRuby
|
54
|
+
def initialize(typenames)
|
55
|
+
@already_warned = Hash.new{|h, k| h[k] = {} }
|
56
|
+
@typenames_by_oid = typenames
|
57
|
+
end
|
58
|
+
|
59
|
+
def typecast_result_value(result, _tuple, field)
|
60
|
+
format = result.fformat(field)
|
61
|
+
oid = result.ftype(field)
|
62
|
+
unless @already_warned[format][oid]
|
63
|
+
warn "Warning: no type cast defined for type #{@typenames_by_oid[oid].inspect} format #{format} with oid #{oid}. Please cast this type explicitly to TEXT to be safe for future changes."
|
64
|
+
@already_warned[format][oid] = true
|
65
|
+
end
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(connection_or_coder_maps, registry: nil)
|
71
|
+
@coder_maps = build_coder_maps(connection_or_coder_maps, registry: registry)
|
72
|
+
|
73
|
+
# Populate TypeMapByOid hash with decoders
|
74
|
+
@coder_maps.each_format(:decoder).flat_map{|f| f.coders }.each do |coder|
|
75
|
+
add_coder(coder)
|
76
|
+
end
|
77
|
+
|
78
|
+
typenames = @coder_maps.typenames_by_oid
|
79
|
+
self.default_type_map = WarningTypeMap.new(typenames)
|
80
|
+
end
|
81
|
+
end
|