pg 0.19.0 → 1.1.0
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/ChangeLog +218 -1
- data/History.rdoc +106 -0
- data/Manifest.txt +5 -18
- data/README.rdoc +15 -5
- data/Rakefile +8 -9
- data/Rakefile.cross +19 -22
- data/ext/errorcodes.def +17 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +11 -1
- data/ext/extconf.rb +14 -32
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +19 -48
- data/ext/pg.h +46 -81
- data/ext/pg_binary_decoder.c +69 -6
- data/ext/pg_coder.c +53 -4
- data/ext/pg_connection.c +401 -253
- data/ext/pg_copy_coder.c +10 -5
- data/ext/pg_result.c +359 -131
- data/ext/pg_text_decoder.c +597 -37
- data/ext/pg_text_encoder.c +6 -7
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +14 -7
- data/ext/util.c +6 -6
- data/ext/util.h +2 -2
- data/lib/pg/basic_type_mapping.rb +40 -7
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +27 -7
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +6 -5
- data/lib/pg/text_decoder.rb +19 -23
- data/lib/pg/text_encoder.rb +36 -2
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +1 -1
- data/lib/pg.rb +21 -11
- data/spec/helpers.rb +47 -19
- data/spec/pg/basic_type_mapping_spec.rb +230 -27
- data/spec/pg/connection_spec.rb +402 -275
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +59 -17
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +1 -1
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +184 -12
- data/spec/pg_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +47 -53
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
data/ext/pg_text_encoder.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_text_encoder.c - PG::TextEncoder module
|
3
|
-
* $Id: pg_text_encoder.c,v
|
3
|
+
* $Id: pg_text_encoder.c,v e57f6b452eb3 2018/08/18 10:58:52 lars $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -468,20 +468,19 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
468
468
|
static char *
|
469
469
|
quote_identifier( VALUE value, VALUE out_string, char *current_out ){
|
470
470
|
char *p_in = RSTRING_PTR(value);
|
471
|
-
char *ptr1;
|
472
471
|
size_t strlen = RSTRING_LEN(value);
|
472
|
+
char *p_inend = p_in + strlen;
|
473
473
|
char *end_capa = current_out;
|
474
474
|
|
475
475
|
PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
|
476
476
|
*current_out++ = '"';
|
477
|
-
for(
|
478
|
-
char c = *
|
477
|
+
for(; p_in != p_inend; p_in++) {
|
478
|
+
char c = *p_in;
|
479
479
|
if (c == '"'){
|
480
|
-
|
481
|
-
PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
|
480
|
+
PG_RB_STR_ENSURE_CAPA( out_string, p_inend - p_in + 2, current_out, end_capa );
|
482
481
|
*current_out++ = '"';
|
483
482
|
} else if (c == 0){
|
484
|
-
|
483
|
+
rb_raise(rb_eArgError, "string contains null byte");
|
485
484
|
}
|
486
485
|
*current_out++ = c;
|
487
486
|
}
|
data/ext/pg_tuple.c
ADDED
@@ -0,0 +1,541 @@
|
|
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( 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
|
+
return Qfalse;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
static void
|
66
|
+
pg_tuple_gc_mark( t_pg_tuple *this )
|
67
|
+
{
|
68
|
+
int i;
|
69
|
+
|
70
|
+
if( !this ) return;
|
71
|
+
rb_gc_mark( this->result );
|
72
|
+
rb_gc_mark( this->typemap );
|
73
|
+
rb_gc_mark( this->field_map );
|
74
|
+
|
75
|
+
for( i = 0; i < this->num_fields; i++ ){
|
76
|
+
rb_gc_mark( this->values[i] );
|
77
|
+
}
|
78
|
+
rb_gc_mark( pg_tuple_get_field_names(this) );
|
79
|
+
}
|
80
|
+
|
81
|
+
static void
|
82
|
+
pg_tuple_gc_free( t_pg_tuple *this )
|
83
|
+
{
|
84
|
+
if( !this ) return;
|
85
|
+
xfree(this);
|
86
|
+
}
|
87
|
+
|
88
|
+
static size_t
|
89
|
+
pg_tuple_memsize( t_pg_tuple *this )
|
90
|
+
{
|
91
|
+
if( this==NULL ) return 0;
|
92
|
+
return sizeof(*this) + sizeof(*this->values) * this->num_fields;
|
93
|
+
}
|
94
|
+
|
95
|
+
static const rb_data_type_t pg_tuple_type = {
|
96
|
+
"pg",
|
97
|
+
{
|
98
|
+
(void (*)(void*))pg_tuple_gc_mark,
|
99
|
+
(void (*)(void*))pg_tuple_gc_free,
|
100
|
+
(size_t (*)(const void *))pg_tuple_memsize,
|
101
|
+
},
|
102
|
+
0, 0,
|
103
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
104
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
105
|
+
#endif
|
106
|
+
};
|
107
|
+
|
108
|
+
/*
|
109
|
+
* Document-method: allocate
|
110
|
+
*
|
111
|
+
* call-seq:
|
112
|
+
* PG::VeryTuple.allocate -> obj
|
113
|
+
*/
|
114
|
+
static VALUE
|
115
|
+
pg_tuple_s_allocate( VALUE klass )
|
116
|
+
{
|
117
|
+
return TypedData_Wrap_Struct( klass, &pg_tuple_type, NULL );
|
118
|
+
}
|
119
|
+
|
120
|
+
VALUE
|
121
|
+
pg_tuple_new(VALUE result, int row_num)
|
122
|
+
{
|
123
|
+
t_pg_tuple *this;
|
124
|
+
VALUE self = pg_tuple_s_allocate( rb_cPG_Tuple );
|
125
|
+
t_pg_result *p_result = pgresult_get_this(result);
|
126
|
+
int num_fields = p_result->nfields;
|
127
|
+
int i;
|
128
|
+
VALUE field_map = p_result->field_map;
|
129
|
+
int dup_names = num_fields != (int)RHASH_SIZE(field_map);
|
130
|
+
|
131
|
+
this = (t_pg_tuple *)xmalloc(
|
132
|
+
sizeof(*this) +
|
133
|
+
sizeof(*this->values) * num_fields +
|
134
|
+
sizeof(*this->values) * (dup_names ? 1 : 0));
|
135
|
+
RTYPEDDATA_DATA(self) = this;
|
136
|
+
|
137
|
+
this->result = result;
|
138
|
+
this->typemap = p_result->typemap;
|
139
|
+
this->field_map = field_map;
|
140
|
+
this->row_num = row_num;
|
141
|
+
this->num_fields = num_fields;
|
142
|
+
|
143
|
+
for( i = 0; i < num_fields; i++ ){
|
144
|
+
this->values[i] = Qundef;
|
145
|
+
}
|
146
|
+
|
147
|
+
if( dup_names ){
|
148
|
+
/* Some of the column names are duplicated -> we need the keys as Array in addition.
|
149
|
+
* Store it behind the values to save the space in the common case of no dups.
|
150
|
+
*/
|
151
|
+
this->values[num_fields] = rb_obj_freeze(rb_ary_new4(num_fields, p_result->fnames));
|
152
|
+
}
|
153
|
+
|
154
|
+
return self;
|
155
|
+
}
|
156
|
+
|
157
|
+
static inline t_pg_tuple *
|
158
|
+
pg_tuple_get_this( VALUE self )
|
159
|
+
{
|
160
|
+
t_pg_tuple *this;
|
161
|
+
TypedData_Get_Struct(self, t_pg_tuple, &pg_tuple_type, this);
|
162
|
+
if (this == NULL)
|
163
|
+
rb_raise(rb_eTypeError, "tuple is empty");
|
164
|
+
|
165
|
+
return this;
|
166
|
+
}
|
167
|
+
|
168
|
+
static VALUE
|
169
|
+
pg_tuple_materialize_field(t_pg_tuple *this, int col)
|
170
|
+
{
|
171
|
+
VALUE value = this->values[col];
|
172
|
+
|
173
|
+
if( value == Qundef ){
|
174
|
+
t_typemap *p_typemap = DATA_PTR( this->typemap );
|
175
|
+
|
176
|
+
pgresult_get(this->result); /* make sure we have a valid PGresult object */
|
177
|
+
value = p_typemap->funcs.typecast_result_value(p_typemap, this->result, this->row_num, col);
|
178
|
+
this->values[col] = value;
|
179
|
+
}
|
180
|
+
|
181
|
+
return value;
|
182
|
+
}
|
183
|
+
|
184
|
+
static void
|
185
|
+
pg_tuple_detach(t_pg_tuple *this)
|
186
|
+
{
|
187
|
+
this->result = Qnil;
|
188
|
+
this->typemap = Qnil;
|
189
|
+
this->row_num = -1;
|
190
|
+
}
|
191
|
+
|
192
|
+
static void
|
193
|
+
pg_tuple_materialize(t_pg_tuple *this)
|
194
|
+
{
|
195
|
+
int field_num;
|
196
|
+
for(field_num = 0; field_num < this->num_fields; field_num++) {
|
197
|
+
pg_tuple_materialize_field(this, field_num);
|
198
|
+
}
|
199
|
+
|
200
|
+
pg_tuple_detach(this);
|
201
|
+
}
|
202
|
+
|
203
|
+
/*
|
204
|
+
* call-seq:
|
205
|
+
* tup.fetch(key) → value
|
206
|
+
* tup.fetch(key, default) → value
|
207
|
+
* tup.fetch(key) { |key| block } → value
|
208
|
+
*
|
209
|
+
* Returns a field value by either column index or column name.
|
210
|
+
*
|
211
|
+
* An integer +key+ is interpreted as column index.
|
212
|
+
* Negative values of index count from the end of the array.
|
213
|
+
*
|
214
|
+
* A string +key+ is interpreted as column name.
|
215
|
+
*
|
216
|
+
* If the key can't be found, there are several options:
|
217
|
+
* With no other arguments, it will raise a IndexError exception;
|
218
|
+
* if default is given, then that will be returned;
|
219
|
+
* if the optional code block is specified, then that will be run and its result returned.
|
220
|
+
*/
|
221
|
+
static VALUE
|
222
|
+
pg_tuple_fetch(int argc, VALUE *argv, VALUE self)
|
223
|
+
{
|
224
|
+
VALUE key;
|
225
|
+
long block_given;
|
226
|
+
VALUE index;
|
227
|
+
int field_num;
|
228
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
229
|
+
|
230
|
+
rb_check_arity(argc, 1, 2);
|
231
|
+
key = argv[0];
|
232
|
+
|
233
|
+
block_given = rb_block_given_p();
|
234
|
+
if (block_given && argc == 2) {
|
235
|
+
rb_warn("block supersedes default value argument");
|
236
|
+
}
|
237
|
+
|
238
|
+
switch(rb_type(key)){
|
239
|
+
case T_FIXNUM:
|
240
|
+
case T_BIGNUM:
|
241
|
+
field_num = NUM2INT(key);
|
242
|
+
if ( field_num < 0 )
|
243
|
+
field_num = this->num_fields + field_num;
|
244
|
+
if ( field_num < 0 || field_num >= this->num_fields ){
|
245
|
+
if (block_given) return rb_yield(key);
|
246
|
+
if (argc == 1) rb_raise( rb_eIndexError, "Index %d is out of range", field_num );
|
247
|
+
return argv[1];
|
248
|
+
}
|
249
|
+
break;
|
250
|
+
default:
|
251
|
+
index = rb_hash_aref(this->field_map, key);
|
252
|
+
|
253
|
+
if (index == Qnil) {
|
254
|
+
if (block_given) return rb_yield(key);
|
255
|
+
if (argc == 1) rb_raise( rb_eKeyError, "column not found" );
|
256
|
+
return argv[1];
|
257
|
+
}
|
258
|
+
|
259
|
+
field_num = NUM2INT(index);
|
260
|
+
}
|
261
|
+
|
262
|
+
return pg_tuple_materialize_field(this, field_num);
|
263
|
+
}
|
264
|
+
|
265
|
+
/*
|
266
|
+
* call-seq:
|
267
|
+
* res[ name ] -> value
|
268
|
+
*
|
269
|
+
* Returns field _name_.
|
270
|
+
*/
|
271
|
+
static VALUE
|
272
|
+
pg_tuple_aref(VALUE self, VALUE key)
|
273
|
+
{
|
274
|
+
VALUE index;
|
275
|
+
int field_num;
|
276
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
277
|
+
|
278
|
+
switch(rb_type(key)){
|
279
|
+
case T_FIXNUM:
|
280
|
+
case T_BIGNUM:
|
281
|
+
field_num = NUM2INT(key);
|
282
|
+
if ( field_num < 0 )
|
283
|
+
field_num = this->num_fields + field_num;
|
284
|
+
if ( field_num < 0 || field_num >= this->num_fields )
|
285
|
+
return Qnil;
|
286
|
+
break;
|
287
|
+
default:
|
288
|
+
index = rb_hash_aref(this->field_map, key);
|
289
|
+
if( index == Qnil ) return Qnil;
|
290
|
+
field_num = NUM2INT(index);
|
291
|
+
}
|
292
|
+
|
293
|
+
return pg_tuple_materialize_field(this, field_num);
|
294
|
+
}
|
295
|
+
|
296
|
+
static VALUE
|
297
|
+
pg_tuple_num_fields_for_enum(VALUE self, VALUE args, VALUE eobj)
|
298
|
+
{
|
299
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
300
|
+
return INT2NUM(this->num_fields);
|
301
|
+
}
|
302
|
+
|
303
|
+
static int
|
304
|
+
pg_tuple_yield_key_value(VALUE key, VALUE index, VALUE _this)
|
305
|
+
{
|
306
|
+
t_pg_tuple *this = (t_pg_tuple *)_this;
|
307
|
+
VALUE value = pg_tuple_materialize_field(this, NUM2INT(index));
|
308
|
+
rb_yield_values(2, key, value);
|
309
|
+
return ST_CONTINUE;
|
310
|
+
}
|
311
|
+
|
312
|
+
/*
|
313
|
+
* call-seq:
|
314
|
+
* tup.each{ |key, value| ... }
|
315
|
+
*
|
316
|
+
* Invokes block for each field name and value in the tuple.
|
317
|
+
*/
|
318
|
+
static VALUE
|
319
|
+
pg_tuple_each(VALUE self)
|
320
|
+
{
|
321
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
322
|
+
VALUE field_names;
|
323
|
+
|
324
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
|
325
|
+
|
326
|
+
field_names = pg_tuple_get_field_names(this);
|
327
|
+
|
328
|
+
if( field_names == Qfalse ){
|
329
|
+
rb_hash_foreach(this->field_map, pg_tuple_yield_key_value, (VALUE)this);
|
330
|
+
} else {
|
331
|
+
int i;
|
332
|
+
for( i = 0; i < this->num_fields; i++ ){
|
333
|
+
VALUE value = pg_tuple_materialize_field(this, i);
|
334
|
+
rb_yield_values(2, RARRAY_AREF(field_names, i), value);
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
338
|
+
pg_tuple_detach(this);
|
339
|
+
return self;
|
340
|
+
}
|
341
|
+
|
342
|
+
/*
|
343
|
+
* call-seq:
|
344
|
+
* tup.each_value{ |value| ... }
|
345
|
+
*
|
346
|
+
* Invokes block for each field value in the tuple.
|
347
|
+
*/
|
348
|
+
static VALUE
|
349
|
+
pg_tuple_each_value(VALUE self)
|
350
|
+
{
|
351
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
352
|
+
int field_num;
|
353
|
+
|
354
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
|
355
|
+
|
356
|
+
for(field_num = 0; field_num < this->num_fields; field_num++) {
|
357
|
+
VALUE value = pg_tuple_materialize_field(this, field_num);
|
358
|
+
rb_yield(value);
|
359
|
+
}
|
360
|
+
|
361
|
+
pg_tuple_detach(this);
|
362
|
+
return self;
|
363
|
+
}
|
364
|
+
|
365
|
+
|
366
|
+
/*
|
367
|
+
* call-seq:
|
368
|
+
* tup.values -> Array
|
369
|
+
*
|
370
|
+
* Returns the values of this tuple as Array.
|
371
|
+
* +res.tuple(i).values+ is equal to +res.tuple_values(i)+ .
|
372
|
+
*/
|
373
|
+
static VALUE
|
374
|
+
pg_tuple_values(VALUE self)
|
375
|
+
{
|
376
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
377
|
+
|
378
|
+
pg_tuple_materialize(this);
|
379
|
+
return rb_ary_new4(this->num_fields, &this->values[0]);
|
380
|
+
}
|
381
|
+
|
382
|
+
static VALUE
|
383
|
+
pg_tuple_field_map(VALUE self)
|
384
|
+
{
|
385
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
386
|
+
return this->field_map;
|
387
|
+
}
|
388
|
+
|
389
|
+
static VALUE
|
390
|
+
pg_tuple_field_names(VALUE self)
|
391
|
+
{
|
392
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
393
|
+
return pg_tuple_get_field_names(this);
|
394
|
+
}
|
395
|
+
|
396
|
+
/*
|
397
|
+
* call-seq:
|
398
|
+
* tup.length → integer
|
399
|
+
*
|
400
|
+
* Returns number of fields of this tuple.
|
401
|
+
*/
|
402
|
+
static VALUE
|
403
|
+
pg_tuple_length(VALUE self)
|
404
|
+
{
|
405
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
406
|
+
return INT2NUM(this->num_fields);
|
407
|
+
}
|
408
|
+
|
409
|
+
/*
|
410
|
+
* call-seq:
|
411
|
+
* tup.index(key) → integer
|
412
|
+
*
|
413
|
+
* Returns the field number which matches the given column name.
|
414
|
+
*/
|
415
|
+
static VALUE
|
416
|
+
pg_tuple_index(VALUE self, VALUE key)
|
417
|
+
{
|
418
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
419
|
+
return rb_hash_aref(this->field_map, key);
|
420
|
+
}
|
421
|
+
|
422
|
+
|
423
|
+
static VALUE
|
424
|
+
pg_tuple_dump(VALUE self)
|
425
|
+
{
|
426
|
+
VALUE field_names;
|
427
|
+
VALUE values;
|
428
|
+
VALUE a;
|
429
|
+
t_pg_tuple *this = pg_tuple_get_this(self);
|
430
|
+
|
431
|
+
pg_tuple_materialize(this);
|
432
|
+
|
433
|
+
field_names = pg_tuple_get_field_names(this);
|
434
|
+
if( field_names == Qfalse )
|
435
|
+
field_names = rb_funcall(this->field_map, rb_intern("keys"), 0);
|
436
|
+
|
437
|
+
values = rb_ary_new4(this->num_fields, &this->values[0]);
|
438
|
+
a = rb_ary_new3(2, field_names, values);
|
439
|
+
|
440
|
+
if (FL_TEST(self, FL_EXIVAR)) {
|
441
|
+
rb_copy_generic_ivar(a, self);
|
442
|
+
FL_SET(a, FL_EXIVAR);
|
443
|
+
}
|
444
|
+
|
445
|
+
return a;
|
446
|
+
}
|
447
|
+
|
448
|
+
static VALUE
|
449
|
+
pg_tuple_load(VALUE self, VALUE a)
|
450
|
+
{
|
451
|
+
int num_fields;
|
452
|
+
int i;
|
453
|
+
t_pg_tuple *this;
|
454
|
+
VALUE values;
|
455
|
+
VALUE field_names;
|
456
|
+
VALUE field_map;
|
457
|
+
int dup_names;
|
458
|
+
|
459
|
+
rb_check_frozen(self);
|
460
|
+
rb_check_trusted(self);
|
461
|
+
|
462
|
+
TypedData_Get_Struct(self, t_pg_tuple, &pg_tuple_type, this);
|
463
|
+
if (this)
|
464
|
+
rb_raise(rb_eTypeError, "tuple is not empty");
|
465
|
+
|
466
|
+
Check_Type(a, T_ARRAY);
|
467
|
+
if (RARRAY_LEN(a) != 2)
|
468
|
+
rb_raise(rb_eTypeError, "expected an array of 2 elements");
|
469
|
+
|
470
|
+
field_names = RARRAY_AREF(a, 0);
|
471
|
+
Check_Type(field_names, T_ARRAY);
|
472
|
+
rb_obj_freeze(field_names);
|
473
|
+
values = RARRAY_AREF(a, 1);
|
474
|
+
Check_Type(values, T_ARRAY);
|
475
|
+
num_fields = RARRAY_LEN(values);
|
476
|
+
|
477
|
+
if (RARRAY_LEN(field_names) != num_fields)
|
478
|
+
rb_raise(rb_eTypeError, "different number of fields and values");
|
479
|
+
|
480
|
+
field_map = rb_hash_new();
|
481
|
+
for( i = 0; i < num_fields; i++ ){
|
482
|
+
rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
|
483
|
+
}
|
484
|
+
rb_obj_freeze(field_map);
|
485
|
+
|
486
|
+
dup_names = num_fields != (int)RHASH_SIZE(field_map);
|
487
|
+
|
488
|
+
this = (t_pg_tuple *)xmalloc(
|
489
|
+
sizeof(*this) +
|
490
|
+
sizeof(*this->values) * num_fields +
|
491
|
+
sizeof(*this->values) * (dup_names ? 1 : 0));
|
492
|
+
RTYPEDDATA_DATA(self) = this;
|
493
|
+
|
494
|
+
this->result = Qnil;
|
495
|
+
this->typemap = Qnil;
|
496
|
+
this->row_num = -1;
|
497
|
+
this->num_fields = num_fields;
|
498
|
+
this->field_map = field_map;
|
499
|
+
|
500
|
+
for( i = 0; i < num_fields; i++ ){
|
501
|
+
VALUE v = RARRAY_AREF(values, i);
|
502
|
+
if( v == Qundef )
|
503
|
+
rb_raise(rb_eTypeError, "field %d is not materialized", i);
|
504
|
+
this->values[i] = v;
|
505
|
+
}
|
506
|
+
|
507
|
+
if( dup_names ){
|
508
|
+
this->values[num_fields] = field_names;
|
509
|
+
}
|
510
|
+
|
511
|
+
if (FL_TEST(a, FL_EXIVAR)) {
|
512
|
+
rb_copy_generic_ivar(self, a);
|
513
|
+
FL_SET(self, FL_EXIVAR);
|
514
|
+
}
|
515
|
+
|
516
|
+
return self;
|
517
|
+
}
|
518
|
+
|
519
|
+
void
|
520
|
+
init_pg_tuple()
|
521
|
+
{
|
522
|
+
rb_cPG_Tuple = rb_define_class_under( rb_mPG, "Tuple", rb_cObject );
|
523
|
+
rb_define_alloc_func( rb_cPG_Tuple, pg_tuple_s_allocate );
|
524
|
+
rb_include_module(rb_cPG_Tuple, rb_mEnumerable);
|
525
|
+
|
526
|
+
rb_define_method(rb_cPG_Tuple, "fetch", pg_tuple_fetch, -1);
|
527
|
+
rb_define_method(rb_cPG_Tuple, "[]", pg_tuple_aref, 1);
|
528
|
+
rb_define_method(rb_cPG_Tuple, "each", pg_tuple_each, 0);
|
529
|
+
rb_define_method(rb_cPG_Tuple, "each_value", pg_tuple_each_value, 0);
|
530
|
+
rb_define_method(rb_cPG_Tuple, "values", pg_tuple_values, 0);
|
531
|
+
rb_define_method(rb_cPG_Tuple, "length", pg_tuple_length, 0);
|
532
|
+
rb_define_alias(rb_cPG_Tuple, "size", "length");
|
533
|
+
rb_define_method(rb_cPG_Tuple, "index", pg_tuple_index, 1);
|
534
|
+
|
535
|
+
rb_define_private_method(rb_cPG_Tuple, "field_map", pg_tuple_field_map, 0);
|
536
|
+
rb_define_private_method(rb_cPG_Tuple, "field_names", pg_tuple_field_names, 0);
|
537
|
+
|
538
|
+
/* methods for marshaling */
|
539
|
+
rb_define_private_method(rb_cPG_Tuple, "marshal_dump", pg_tuple_dump, 0);
|
540
|
+
rb_define_private_method(rb_cPG_Tuple, "marshal_load", pg_tuple_load, 1);
|
541
|
+
}
|
data/ext/pg_type_map.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_column_map.c - PG::ColumnMap class extension
|
3
|
-
* $Id: pg_type_map.c,v
|
3
|
+
* $Id: pg_type_map.c,v 2af122820861 2017/01/14 19:56:36 lars $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -11,46 +11,53 @@ VALUE rb_mDefaultTypeMappable;
|
|
11
11
|
static ID s_id_fit_to_query;
|
12
12
|
static ID s_id_fit_to_result;
|
13
13
|
|
14
|
+
NORETURN( VALUE
|
15
|
+
pg_typemap_fit_to_result( VALUE self, VALUE result ));
|
16
|
+
NORETURN( VALUE
|
17
|
+
pg_typemap_fit_to_query( VALUE self, VALUE params ));
|
18
|
+
NORETURN( int
|
19
|
+
pg_typemap_fit_to_copy_get( VALUE self ));
|
20
|
+
NORETURN( VALUE
|
21
|
+
pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field ));
|
22
|
+
NORETURN( t_pg_coder *
|
23
|
+
pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field ));
|
24
|
+
NORETURN( VALUE
|
25
|
+
pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx ));
|
26
|
+
|
14
27
|
VALUE
|
15
28
|
pg_typemap_fit_to_result( VALUE self, VALUE result )
|
16
29
|
{
|
17
30
|
rb_raise( rb_eNotImpError, "type map %s is not suitable to map result values", rb_obj_classname(self) );
|
18
|
-
return Qnil;
|
19
31
|
}
|
20
32
|
|
21
33
|
VALUE
|
22
34
|
pg_typemap_fit_to_query( VALUE self, VALUE params )
|
23
35
|
{
|
24
36
|
rb_raise( rb_eNotImpError, "type map %s is not suitable to map query params", rb_obj_classname(self) );
|
25
|
-
return Qnil;
|
26
37
|
}
|
27
38
|
|
28
39
|
int
|
29
40
|
pg_typemap_fit_to_copy_get( VALUE self )
|
30
41
|
{
|
31
42
|
rb_raise( rb_eNotImpError, "type map %s is not suitable to map get_copy_data results", rb_obj_classname(self) );
|
32
|
-
return Qnil;
|
33
43
|
}
|
34
44
|
|
35
45
|
VALUE
|
36
46
|
pg_typemap_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
|
37
47
|
{
|
38
48
|
rb_raise( rb_eNotImpError, "type map is not suitable to map result values" );
|
39
|
-
return Qnil;
|
40
49
|
}
|
41
50
|
|
42
51
|
t_pg_coder *
|
43
52
|
pg_typemap_typecast_query_param( t_typemap *p_typemap, VALUE param_value, int field )
|
44
53
|
{
|
45
54
|
rb_raise( rb_eNotImpError, "type map is not suitable to map query params" );
|
46
|
-
return NULL;
|
47
55
|
}
|
48
56
|
|
49
57
|
VALUE
|
50
58
|
pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format, int enc_idx )
|
51
59
|
{
|
52
60
|
rb_raise( rb_eNotImpError, "type map is not suitable to map get_copy_data results" );
|
53
|
-
return Qnil;
|
54
61
|
}
|
55
62
|
|
56
63
|
const struct pg_typemap_funcs pg_typemap_funcs = {
|
data/ext/util.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* util.c - Utils for ruby-pg
|
3
|
-
* $Id: util.c,v
|
3
|
+
* $Id: util.c,v fc1c4deb1398 2018/06/25 12:02:09 kanis $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -15,9 +15,9 @@ 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
|
|
@@ -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 &&
|
data/ext/util.h
CHANGED
@@ -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
|
|