pg 1.0.0 → 1.5.9
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/Gemfile +20 -0
- data/History.md +932 -0
- data/Manifest.txt +8 -3
- data/README-Windows.rdoc +4 -4
- data/README.ja.md +300 -0
- data/README.md +286 -0
- data/Rakefile +41 -138
- data/Rakefile.cross +71 -66
- data/certs/ged.pem +24 -0
- data/certs/kanis@comcard.de.pem +20 -0
- data/certs/larskanis-2022.pem +26 -0
- data/certs/larskanis-2023.pem +24 -0
- data/certs/larskanis-2024.pem +24 -0
- data/ext/errorcodes.def +84 -5
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +23 -6
- data/ext/extconf.rb +109 -25
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +213 -155
- data/ext/pg.h +89 -23
- data/ext/pg_binary_decoder.c +164 -16
- data/ext/pg_binary_encoder.c +238 -13
- data/ext/pg_coder.c +159 -35
- data/ext/pg_connection.c +1584 -967
- data/ext/pg_copy_coder.c +373 -43
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +522 -0
- data/ext/pg_result.c +710 -217
- data/ext/pg_text_decoder.c +630 -43
- data/ext/pg_text_encoder.c +222 -72
- data/ext/pg_tuple.c +572 -0
- data/ext/pg_type_map.c +45 -11
- data/ext/pg_type_map_all_strings.c +21 -7
- data/ext/pg_type_map_by_class.c +59 -27
- data/ext/pg_type_map_by_column.c +80 -37
- data/ext/pg_type_map_by_mri_type.c +49 -20
- data/ext/pg_type_map_by_oid.c +62 -29
- data/ext/pg_type_map_in_ruby.c +56 -22
- data/ext/{util.c → pg_util.c} +12 -12
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg/basic_type_map_based_on_result.rb +67 -0
- data/lib/pg/basic_type_map_for_queries.rb +202 -0
- data/lib/pg/basic_type_map_for_results.rb +104 -0
- data/lib/pg/basic_type_registry.rb +311 -0
- data/lib/pg/binary_decoder/date.rb +9 -0
- data/lib/pg/binary_decoder/timestamp.rb +26 -0
- data/lib/pg/binary_encoder/timestamp.rb +20 -0
- data/lib/pg/coder.rb +36 -13
- data/lib/pg/connection.rb +769 -70
- data/lib/pg/exceptions.rb +22 -2
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder/date.rb +21 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +17 -0
- data/lib/pg/text_decoder/numeric.rb +9 -0
- data/lib/pg/text_decoder/timestamp.rb +30 -0
- data/lib/pg/text_encoder/date.rb +13 -0
- data/lib/pg/text_encoder/inet.rb +31 -0
- data/lib/pg/text_encoder/json.rb +17 -0
- data/lib/pg/text_encoder/numeric.rb +9 -0
- data/lib/pg/text_encoder/timestamp.rb +24 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +106 -39
- 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 +36 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +102 -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 +138 -223
- metadata.gz.sig +0 -0
- data/.gemtest +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +0 -422
- data/README.ja.rdoc +0 -14
- data/README.rdoc +0 -167
- data/lib/pg/basic_type_mapping.rb +0 -426
- data/lib/pg/constants.rb +0 -11
- data/lib/pg/text_decoder.rb +0 -51
- data/lib/pg/text_encoder.rb +0 -35
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -348
- data/spec/pg/basic_type_mapping_spec.rb +0 -305
- data/spec/pg/connection_spec.rb +0 -1719
- data/spec/pg/result_spec.rb +0 -456
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -222
- 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 -777
- data/spec/pg_spec.rb +0 -50
data/ext/pg_result.c
CHANGED
@@ -1,21 +1,196 @@
|
|
1
1
|
/*
|
2
2
|
* pg_result.c - PG::Result class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
7
7
|
#include "pg.h"
|
8
8
|
|
9
|
-
|
10
9
|
VALUE rb_cPGresult;
|
10
|
+
static VALUE sym_symbol, sym_string, sym_static_symbol;
|
11
11
|
|
12
|
-
static void pgresult_gc_free( t_pg_result * );
|
13
12
|
static VALUE pgresult_type_map_set( VALUE, VALUE );
|
14
|
-
static VALUE pgresult_s_allocate( VALUE );
|
15
13
|
static t_pg_result *pgresult_get_this( VALUE );
|
16
14
|
static t_pg_result *pgresult_get_this_safe( VALUE );
|
17
15
|
|
16
|
+
#if defined(HAVE_PQRESULTMEMORYSIZE)
|
17
|
+
|
18
|
+
static ssize_t
|
19
|
+
pgresult_approx_size(const PGresult *result)
|
20
|
+
{
|
21
|
+
return PQresultMemorySize(result);
|
22
|
+
}
|
23
|
+
|
24
|
+
#else
|
25
|
+
|
26
|
+
#define PGRESULT_DATA_BLOCKSIZE 2048
|
27
|
+
typedef struct pgresAttValue
|
28
|
+
{
|
29
|
+
int len; /* length in bytes of the value */
|
30
|
+
char *value; /* actual value, plus terminating zero byte */
|
31
|
+
} PGresAttValue;
|
32
|
+
|
18
33
|
|
34
|
+
static int
|
35
|
+
count_leading_zero_bits(unsigned int x)
|
36
|
+
{
|
37
|
+
#if defined(__GNUC__) || defined(__clang__)
|
38
|
+
return __builtin_clz(x);
|
39
|
+
#elif defined(_MSC_VER)
|
40
|
+
DWORD r = 0;
|
41
|
+
_BitScanForward(&r, x);
|
42
|
+
return (int)r;
|
43
|
+
#else
|
44
|
+
unsigned int a;
|
45
|
+
for(a=0; a < sizeof(unsigned int) * 8; a++){
|
46
|
+
if( x & (1 << (sizeof(unsigned int) * 8 - 1))) return a;
|
47
|
+
x <<= 1;
|
48
|
+
}
|
49
|
+
return a;
|
50
|
+
#endif
|
51
|
+
}
|
52
|
+
|
53
|
+
static ssize_t
|
54
|
+
pgresult_approx_size(const PGresult *result)
|
55
|
+
{
|
56
|
+
int num_fields = PQnfields(result);
|
57
|
+
ssize_t size = 0;
|
58
|
+
|
59
|
+
if( num_fields > 0 ){
|
60
|
+
int num_tuples = PQntuples(result);
|
61
|
+
|
62
|
+
if( num_tuples > 0 ){
|
63
|
+
int pos;
|
64
|
+
|
65
|
+
/* This is a simple heuristic to determine the number of sample fields and subsequently to approximate the memory size taken by all field values of the result set.
|
66
|
+
* Since scanning of all field values is would have a severe performance impact, only a small subset of fields is retrieved and the result is extrapolated to the whole result set.
|
67
|
+
* The given algorithm has no real scientific background, but is made for speed and typical table layouts.
|
68
|
+
*/
|
69
|
+
int num_samples =
|
70
|
+
(num_fields < 9 ? num_fields : 39 - count_leading_zero_bits(num_fields-8)) *
|
71
|
+
(num_tuples < 8 ? 1 : 30 - count_leading_zero_bits(num_tuples));
|
72
|
+
|
73
|
+
/* start with scanning very last fields, since they are most probably in the cache */
|
74
|
+
for( pos = 0; pos < (num_samples+1)/2; pos++ ){
|
75
|
+
size += PQgetlength(result, num_tuples - 1 - (pos / num_fields), num_fields - 1 - (pos % num_fields));
|
76
|
+
}
|
77
|
+
/* scan the very first fields */
|
78
|
+
for( pos = 0; pos < num_samples/2; pos++ ){
|
79
|
+
size += PQgetlength(result, pos / num_fields, pos % num_fields);
|
80
|
+
}
|
81
|
+
/* extrapolate sample size to whole result set */
|
82
|
+
size = size * num_tuples * num_fields / num_samples;
|
83
|
+
}
|
84
|
+
|
85
|
+
/* count metadata */
|
86
|
+
size += num_fields * (
|
87
|
+
sizeof(PGresAttDesc) + /* column description */
|
88
|
+
num_tuples * (
|
89
|
+
sizeof(PGresAttValue) + 1 /* ptr, len and zero termination of each value */
|
90
|
+
)
|
91
|
+
);
|
92
|
+
|
93
|
+
/* Account free space due to libpq's default block size */
|
94
|
+
size = (size + PGRESULT_DATA_BLOCKSIZE - 1) / PGRESULT_DATA_BLOCKSIZE * PGRESULT_DATA_BLOCKSIZE;
|
95
|
+
|
96
|
+
/* count tuple pointers */
|
97
|
+
size += sizeof(void*) * ((num_tuples + 128 - 1) / 128 * 128);
|
98
|
+
}
|
99
|
+
|
100
|
+
size += 216; /* add PGresult size */
|
101
|
+
|
102
|
+
return size;
|
103
|
+
}
|
104
|
+
#endif
|
105
|
+
|
106
|
+
/*
|
107
|
+
* GC Mark function
|
108
|
+
*/
|
109
|
+
static void
|
110
|
+
pgresult_gc_mark( void *_this )
|
111
|
+
{
|
112
|
+
t_pg_result *this = (t_pg_result *)_this;
|
113
|
+
int i;
|
114
|
+
|
115
|
+
rb_gc_mark_movable( this->connection );
|
116
|
+
rb_gc_mark_movable( this->typemap );
|
117
|
+
rb_gc_mark_movable( this->tuple_hash );
|
118
|
+
rb_gc_mark_movable( this->field_map );
|
119
|
+
|
120
|
+
for( i=0; i < this->nfields; i++ ){
|
121
|
+
rb_gc_mark_movable( this->fnames[i] );
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
static void
|
126
|
+
pgresult_gc_compact( void *_this )
|
127
|
+
{
|
128
|
+
t_pg_result *this = (t_pg_result *)_this;
|
129
|
+
int i;
|
130
|
+
|
131
|
+
pg_gc_location( this->connection );
|
132
|
+
pg_gc_location( this->typemap );
|
133
|
+
pg_gc_location( this->tuple_hash );
|
134
|
+
pg_gc_location( this->field_map );
|
135
|
+
|
136
|
+
for( i=0; i < this->nfields; i++ ){
|
137
|
+
pg_gc_location( this->fnames[i] );
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
/*
|
142
|
+
* GC Free function
|
143
|
+
*/
|
144
|
+
static void
|
145
|
+
pgresult_clear( void *_this )
|
146
|
+
{
|
147
|
+
t_pg_result *this = (t_pg_result *)_this;
|
148
|
+
if( this->pgresult && !this->autoclear ){
|
149
|
+
PQclear(this->pgresult);
|
150
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
151
|
+
rb_gc_adjust_memory_usage(-this->result_size);
|
152
|
+
#endif
|
153
|
+
}
|
154
|
+
this->result_size = 0;
|
155
|
+
this->nfields = -1;
|
156
|
+
this->pgresult = NULL;
|
157
|
+
}
|
158
|
+
|
159
|
+
static void
|
160
|
+
pgresult_gc_free( void *_this )
|
161
|
+
{
|
162
|
+
t_pg_result *this = (t_pg_result *)_this;
|
163
|
+
pgresult_clear( this );
|
164
|
+
xfree(this);
|
165
|
+
}
|
166
|
+
|
167
|
+
static size_t
|
168
|
+
pgresult_memsize( const void *_this )
|
169
|
+
{
|
170
|
+
const t_pg_result *this = (const t_pg_result *)_this;
|
171
|
+
/* Ideally the memory 'this' is pointing to should be taken into account as well.
|
172
|
+
* However we don't want to store two memory sizes in t_pg_result just for reporting by ObjectSpace.memsize_of.
|
173
|
+
*/
|
174
|
+
return this->result_size;
|
175
|
+
}
|
176
|
+
|
177
|
+
static const rb_data_type_t pgresult_type = {
|
178
|
+
"PG::Result",
|
179
|
+
{
|
180
|
+
pgresult_gc_mark,
|
181
|
+
pgresult_gc_free,
|
182
|
+
pgresult_memsize,
|
183
|
+
pg_compact_callback(pgresult_gc_compact),
|
184
|
+
},
|
185
|
+
0, 0,
|
186
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
187
|
+
};
|
188
|
+
|
189
|
+
/* Needed by sequel_pg gem, do not delete */
|
190
|
+
int pg_get_result_enc_idx(VALUE self)
|
191
|
+
{
|
192
|
+
return pgresult_get_this(self)->enc_idx;
|
193
|
+
}
|
19
194
|
|
20
195
|
/*
|
21
196
|
* Global functions
|
@@ -24,45 +199,91 @@ static t_pg_result *pgresult_get_this_safe( VALUE );
|
|
24
199
|
/*
|
25
200
|
* Result constructor
|
26
201
|
*/
|
27
|
-
VALUE
|
28
|
-
|
202
|
+
static VALUE
|
203
|
+
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
29
204
|
{
|
30
205
|
int nfields = result ? PQnfields(result) : 0;
|
31
|
-
VALUE self
|
206
|
+
VALUE self;
|
32
207
|
t_pg_result *this;
|
33
208
|
|
34
209
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
35
|
-
DATA_PTR(self) = this;
|
36
|
-
|
37
210
|
this->pgresult = result;
|
211
|
+
/* Initialize connection and typemap prior to any object allocations,
|
212
|
+
* to make sure valid objects are marked. */
|
38
213
|
this->connection = rb_pgconn;
|
39
214
|
this->typemap = pg_typemap_all_strings;
|
40
|
-
this->p_typemap =
|
41
|
-
this->autoclear = 0;
|
215
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
42
216
|
this->nfields = -1;
|
43
217
|
this->tuple_hash = Qnil;
|
44
|
-
|
45
|
-
|
218
|
+
this->field_map = Qnil;
|
219
|
+
this->flags = 0;
|
220
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
46
221
|
|
47
222
|
if( result ){
|
48
223
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
49
224
|
VALUE typemap = p_conn->type_map_for_results;
|
50
|
-
|
51
225
|
/* Type check is done when assigned to PG::Connection. */
|
52
|
-
t_typemap *p_typemap =
|
53
|
-
|
54
|
-
this->
|
55
|
-
|
226
|
+
t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
|
227
|
+
|
228
|
+
this->enc_idx = p_conn->enc_idx;
|
229
|
+
typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
230
|
+
RB_OBJ_WRITE(self, &this->typemap, typemap);
|
231
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
232
|
+
this->flags = p_conn->flags;
|
233
|
+
} else {
|
234
|
+
this->enc_idx = rb_locale_encindex();
|
56
235
|
}
|
57
236
|
|
58
237
|
return self;
|
59
238
|
}
|
60
239
|
|
240
|
+
VALUE
|
241
|
+
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
242
|
+
{
|
243
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
244
|
+
t_pg_result *this = pgresult_get_this(self);
|
245
|
+
|
246
|
+
this->autoclear = 0;
|
247
|
+
|
248
|
+
/* Estimate size of underlying pgresult memory storage and account to ruby GC.
|
249
|
+
* There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
|
250
|
+
*/
|
251
|
+
/* TODO: If someday most systems provide PQresultMemorySize(), it's questionable to store result_size in t_pg_result in addition to the value already stored in PGresult.
|
252
|
+
* For now the memory savings don't justify the ifdefs necessary to support both cases.
|
253
|
+
*/
|
254
|
+
this->result_size = pgresult_approx_size(result);
|
255
|
+
|
256
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
257
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
258
|
+
#endif
|
259
|
+
|
260
|
+
return self;
|
261
|
+
}
|
262
|
+
|
263
|
+
static VALUE
|
264
|
+
pg_copy_result(t_pg_result *this)
|
265
|
+
{
|
266
|
+
int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
|
267
|
+
size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
|
268
|
+
t_pg_result *copy;
|
269
|
+
|
270
|
+
copy = (t_pg_result *)xmalloc(len);
|
271
|
+
memcpy(copy, this, len);
|
272
|
+
this->result_size = 0;
|
273
|
+
|
274
|
+
return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
|
275
|
+
}
|
276
|
+
|
61
277
|
VALUE
|
62
278
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
63
279
|
{
|
64
|
-
VALUE self =
|
280
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
65
281
|
t_pg_result *this = pgresult_get_this(self);
|
282
|
+
|
283
|
+
/* Autocleared results are freed implicit instead of by PQclear().
|
284
|
+
* So it's not very useful to be accounted by ruby GC.
|
285
|
+
*/
|
286
|
+
this->result_size = 0;
|
66
287
|
this->autoclear = 1;
|
67
288
|
return self;
|
68
289
|
}
|
@@ -71,7 +292,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
71
292
|
* call-seq:
|
72
293
|
* res.check -> nil
|
73
294
|
*
|
74
|
-
* Raises appropriate exception if PG::Result is in a bad state
|
295
|
+
* Raises appropriate exception if PG::Result is in a bad state, which is:
|
296
|
+
* * +PGRES_BAD_RESPONSE+
|
297
|
+
* * +PGRES_FATAL_ERROR+
|
298
|
+
* * +PGRES_NONFATAL_ERROR+
|
299
|
+
* * +PGRES_PIPELINE_ABORTED+
|
75
300
|
*/
|
76
301
|
VALUE
|
77
302
|
pg_result_check( VALUE self )
|
@@ -96,10 +321,16 @@ pg_result_check( VALUE self )
|
|
96
321
|
case PGRES_SINGLE_TUPLE:
|
97
322
|
case PGRES_EMPTY_QUERY:
|
98
323
|
case PGRES_COMMAND_OK:
|
324
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
325
|
+
case PGRES_PIPELINE_SYNC:
|
326
|
+
#endif
|
99
327
|
return self;
|
100
328
|
case PGRES_BAD_RESPONSE:
|
101
329
|
case PGRES_FATAL_ERROR:
|
102
330
|
case PGRES_NONFATAL_ERROR:
|
331
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
332
|
+
case PGRES_PIPELINE_ABORTED:
|
333
|
+
#endif
|
103
334
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
104
335
|
break;
|
105
336
|
default:
|
@@ -107,7 +338,7 @@ pg_result_check( VALUE self )
|
|
107
338
|
}
|
108
339
|
}
|
109
340
|
|
110
|
-
PG_ENCODING_SET_NOCHECK( error,
|
341
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
111
342
|
|
112
343
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
113
344
|
klass = lookup_error_class( sqlstate );
|
@@ -131,29 +362,52 @@ pg_result_check( VALUE self )
|
|
131
362
|
* call-seq:
|
132
363
|
* res.clear() -> nil
|
133
364
|
*
|
134
|
-
* Clears the PG::Result object as the result of
|
365
|
+
* Clears the PG::Result object as the result of a query.
|
366
|
+
* This frees all underlying memory consumed by the result object.
|
367
|
+
* Afterwards access to result methods raises PG::Error "result has been cleared".
|
368
|
+
*
|
369
|
+
* Explicit calling #clear can lead to better memory performance, but is not generally necessary.
|
370
|
+
* Special care must be taken when PG::Tuple objects are used.
|
371
|
+
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
135
372
|
*
|
136
|
-
* If PG::Result#autoclear? is true then the result is marked as cleared
|
137
|
-
* and the underlying C struct will be cleared automatically by libpq.
|
373
|
+
* If PG::Result#autoclear? is +true+ then the result is only marked as cleared but clearing the underlying C struct will happen when the callback returns.
|
138
374
|
*
|
139
375
|
*/
|
140
376
|
VALUE
|
141
377
|
pg_result_clear(VALUE self)
|
142
378
|
{
|
143
379
|
t_pg_result *this = pgresult_get_this(self);
|
144
|
-
|
145
|
-
|
146
|
-
this->pgresult = NULL;
|
380
|
+
rb_check_frozen(self);
|
381
|
+
pgresult_clear( this );
|
147
382
|
return Qnil;
|
148
383
|
}
|
149
384
|
|
385
|
+
/*
|
386
|
+
* call-seq:
|
387
|
+
* res.freeze
|
388
|
+
*
|
389
|
+
* Freeze the PG::Result object and unlink the result from the related PG::Connection.
|
390
|
+
*
|
391
|
+
* A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
|
392
|
+
* It also denies setting a type_map or field_name_type.
|
393
|
+
*
|
394
|
+
*/
|
395
|
+
static VALUE
|
396
|
+
pg_result_freeze(VALUE self)
|
397
|
+
{
|
398
|
+
t_pg_result *this = pgresult_get_this(self);
|
399
|
+
|
400
|
+
RB_OBJ_WRITE(self, &this->connection, Qnil);
|
401
|
+
return rb_call_super(0, NULL);
|
402
|
+
}
|
403
|
+
|
150
404
|
/*
|
151
405
|
* call-seq:
|
152
406
|
* res.cleared? -> boolean
|
153
407
|
*
|
154
|
-
* Returns +true+ if the backend result memory has been
|
408
|
+
* Returns +true+ if the backend result memory has been freed.
|
155
409
|
*/
|
156
|
-
VALUE
|
410
|
+
static VALUE
|
157
411
|
pgresult_cleared_p( VALUE self )
|
158
412
|
{
|
159
413
|
t_pg_result *this = pgresult_get_this(self);
|
@@ -164,11 +418,13 @@ pgresult_cleared_p( VALUE self )
|
|
164
418
|
* call-seq:
|
165
419
|
* res.autoclear? -> boolean
|
166
420
|
*
|
167
|
-
* Returns +true+ if the underlying C struct will be cleared
|
168
|
-
*
|
421
|
+
* Returns +true+ if the underlying C struct will be cleared at the end of a callback.
|
422
|
+
* This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
|
423
|
+
*
|
424
|
+
* All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
|
169
425
|
*
|
170
426
|
*/
|
171
|
-
VALUE
|
427
|
+
static VALUE
|
172
428
|
pgresult_autoclear_p( VALUE self )
|
173
429
|
{
|
174
430
|
t_pg_result *this = pgresult_get_this(self);
|
@@ -179,37 +435,6 @@ pgresult_autoclear_p( VALUE self )
|
|
179
435
|
* DATA pointer functions
|
180
436
|
*/
|
181
437
|
|
182
|
-
/*
|
183
|
-
* GC Mark function
|
184
|
-
*/
|
185
|
-
static void
|
186
|
-
pgresult_gc_mark( t_pg_result *this )
|
187
|
-
{
|
188
|
-
int i;
|
189
|
-
|
190
|
-
if( !this ) return;
|
191
|
-
rb_gc_mark( this->connection );
|
192
|
-
rb_gc_mark( this->typemap );
|
193
|
-
rb_gc_mark( this->tuple_hash );
|
194
|
-
|
195
|
-
for( i=0; i < this->nfields; i++ ){
|
196
|
-
rb_gc_mark( this->fnames[i] );
|
197
|
-
}
|
198
|
-
}
|
199
|
-
|
200
|
-
/*
|
201
|
-
* GC Free function
|
202
|
-
*/
|
203
|
-
static void
|
204
|
-
pgresult_gc_free( t_pg_result *this )
|
205
|
-
{
|
206
|
-
if( !this ) return;
|
207
|
-
if(this->pgresult != NULL && !this->autoclear)
|
208
|
-
PQclear(this->pgresult);
|
209
|
-
|
210
|
-
xfree(this);
|
211
|
-
}
|
212
|
-
|
213
438
|
/*
|
214
439
|
* Fetch the PG::Result object data pointer and check it's
|
215
440
|
* PGresult data pointer for sanity.
|
@@ -239,18 +464,30 @@ pgresult_get(VALUE self)
|
|
239
464
|
return this->pgresult;
|
240
465
|
}
|
241
466
|
|
242
|
-
|
243
|
-
* Document-method: allocate
|
244
|
-
*
|
245
|
-
* call-seq:
|
246
|
-
* PG::Result.allocate -> result
|
247
|
-
*/
|
248
|
-
static VALUE
|
249
|
-
pgresult_s_allocate( VALUE klass )
|
467
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
250
468
|
{
|
251
|
-
VALUE
|
252
|
-
|
253
|
-
|
469
|
+
VALUE fname;
|
470
|
+
#ifdef TRUFFLERUBY
|
471
|
+
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
472
|
+
#else
|
473
|
+
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
474
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
475
|
+
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
476
|
+
if( fname == Qnil ){
|
477
|
+
fname = rb_str_new2(cstr);
|
478
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
479
|
+
fname = rb_str_intern(fname);
|
480
|
+
}
|
481
|
+
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
482
|
+
#endif
|
483
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
484
|
+
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
485
|
+
} else {
|
486
|
+
fname = rb_str_new2(cstr);
|
487
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
488
|
+
fname = rb_obj_freeze(fname);
|
489
|
+
}
|
490
|
+
return fname;
|
254
491
|
}
|
255
492
|
|
256
493
|
static void pgresult_init_fnames(VALUE self)
|
@@ -262,12 +499,10 @@ static void pgresult_init_fnames(VALUE self)
|
|
262
499
|
int nfields = PQnfields(this->pgresult);
|
263
500
|
|
264
501
|
for( i=0; i<nfields; i++ ){
|
265
|
-
|
266
|
-
|
267
|
-
this->fnames[i]
|
502
|
+
char *cfname = PQfname(this->pgresult, i);
|
503
|
+
VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
504
|
+
RB_OBJ_WRITE(self, &this->fnames[i], fname);
|
268
505
|
this->nfields = i + 1;
|
269
|
-
|
270
|
-
RB_GC_GUARD(fname);
|
271
506
|
}
|
272
507
|
this->nfields = nfields;
|
273
508
|
}
|
@@ -279,8 +514,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
279
514
|
*
|
280
515
|
* The class to represent the query result tuples (rows).
|
281
516
|
* An instance of this class is created as the result of every query.
|
282
|
-
*
|
283
|
-
*
|
517
|
+
* All result rows and columns are stored in a memory block attached to the PG::Result object.
|
518
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
519
|
+
*
|
520
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
521
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
284
522
|
*
|
285
523
|
* Example:
|
286
524
|
* require 'pg'
|
@@ -310,6 +548,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
310
548
|
* * +PGRES_NONFATAL_ERROR+
|
311
549
|
* * +PGRES_FATAL_ERROR+
|
312
550
|
* * +PGRES_COPY_BOTH+
|
551
|
+
* * +PGRES_SINGLE_TUPLE+
|
552
|
+
* * +PGRES_PIPELINE_SYNC+
|
553
|
+
* * +PGRES_PIPELINE_ABORTED+
|
554
|
+
*
|
555
|
+
* Use <tt>res.res_status</tt> to retrieve the string representation.
|
313
556
|
*/
|
314
557
|
static VALUE
|
315
558
|
pgresult_result_status(VALUE self)
|
@@ -319,16 +562,39 @@ pgresult_result_status(VALUE self)
|
|
319
562
|
|
320
563
|
/*
|
321
564
|
* call-seq:
|
322
|
-
*
|
565
|
+
* PG::Result.res_status( status ) -> String
|
323
566
|
*
|
324
|
-
* Returns the string representation of
|
567
|
+
* Returns the string representation of +status+.
|
325
568
|
*
|
326
569
|
*/
|
327
570
|
static VALUE
|
328
|
-
|
571
|
+
pgresult_s_res_status(VALUE self, VALUE status)
|
329
572
|
{
|
330
|
-
|
331
|
-
|
573
|
+
return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
|
574
|
+
}
|
575
|
+
|
576
|
+
/*
|
577
|
+
* call-seq:
|
578
|
+
* res.res_status -> String
|
579
|
+
* res.res_status( status ) -> String
|
580
|
+
*
|
581
|
+
* Returns the string representation of the status of the result or of the provided +status+.
|
582
|
+
*
|
583
|
+
*/
|
584
|
+
static VALUE
|
585
|
+
pgresult_res_status(int argc, VALUE *argv, VALUE self)
|
586
|
+
{
|
587
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
588
|
+
VALUE ret;
|
589
|
+
|
590
|
+
if( argc == 0 ){
|
591
|
+
ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
|
592
|
+
}else if( argc == 1 ){
|
593
|
+
ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
|
594
|
+
}else{
|
595
|
+
rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
|
596
|
+
}
|
597
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
332
598
|
return ret;
|
333
599
|
}
|
334
600
|
|
@@ -341,10 +607,39 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
341
607
|
static VALUE
|
342
608
|
pgresult_error_message(VALUE self)
|
343
609
|
{
|
344
|
-
|
345
|
-
|
610
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
611
|
+
VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
|
612
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
613
|
+
return ret;
|
614
|
+
}
|
615
|
+
|
616
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
617
|
+
/*
|
618
|
+
* call-seq:
|
619
|
+
* res.verbose_error_message( verbosity, show_context ) -> String
|
620
|
+
*
|
621
|
+
* Returns a reformatted version of the error message associated with a PGresult object.
|
622
|
+
*
|
623
|
+
* Available since PostgreSQL-9.6
|
624
|
+
*/
|
625
|
+
static VALUE
|
626
|
+
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
627
|
+
{
|
628
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
629
|
+
VALUE ret;
|
630
|
+
char *c_str;
|
631
|
+
|
632
|
+
c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
|
633
|
+
if(!c_str)
|
634
|
+
rb_raise(rb_eNoMemError, "insufficient memory to format error message");
|
635
|
+
|
636
|
+
ret = rb_str_new2(c_str);
|
637
|
+
PQfreemem(c_str);
|
638
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
639
|
+
|
346
640
|
return ret;
|
347
641
|
}
|
642
|
+
#endif
|
348
643
|
|
349
644
|
/*
|
350
645
|
* call-seq:
|
@@ -369,7 +664,7 @@ pgresult_error_message(VALUE self)
|
|
369
664
|
* An example:
|
370
665
|
*
|
371
666
|
* begin
|
372
|
-
* conn.exec( "SELECT * FROM
|
667
|
+
* conn.exec( "SELECT * FROM nonexistent_table" )
|
373
668
|
* rescue PG::Error => err
|
374
669
|
* p [
|
375
670
|
* err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
@@ -389,20 +684,20 @@ pgresult_error_message(VALUE self)
|
|
389
684
|
*
|
390
685
|
* Outputs:
|
391
686
|
*
|
392
|
-
* ["ERROR", "42P01", "relation \"
|
687
|
+
* ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
|
393
688
|
* "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
|
394
689
|
*/
|
395
690
|
static VALUE
|
396
691
|
pgresult_error_field(VALUE self, VALUE field)
|
397
692
|
{
|
398
|
-
|
693
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
399
694
|
int fieldcode = NUM2INT( field );
|
400
|
-
char * fieldstr = PQresultErrorField(
|
695
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
401
696
|
VALUE ret = Qnil;
|
402
697
|
|
403
698
|
if ( fieldstr ) {
|
404
|
-
ret =
|
405
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
699
|
+
ret = rb_str_new2( fieldstr );
|
700
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
406
701
|
}
|
407
702
|
|
408
703
|
return ret;
|
@@ -423,7 +718,7 @@ pgresult_ntuples(VALUE self)
|
|
423
718
|
static VALUE
|
424
719
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
425
720
|
{
|
426
|
-
|
721
|
+
return pgresult_ntuples(self);
|
427
722
|
}
|
428
723
|
|
429
724
|
/*
|
@@ -440,24 +735,40 @@ pgresult_nfields(VALUE self)
|
|
440
735
|
|
441
736
|
/*
|
442
737
|
* call-seq:
|
443
|
-
* res.
|
738
|
+
* res.binary_tuples() -> Integer
|
739
|
+
*
|
740
|
+
* Returns 1 if the PGresult contains binary data and 0 if it contains text data.
|
741
|
+
*
|
742
|
+
* This function is deprecated (except for its use in connection with COPY), because it is possible for a single PGresult to contain text data in some columns and binary data in others.
|
743
|
+
* Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
|
744
|
+
*/
|
745
|
+
static VALUE
|
746
|
+
pgresult_binary_tuples(VALUE self)
|
747
|
+
{
|
748
|
+
return INT2NUM(PQbinaryTuples(pgresult_get(self)));
|
749
|
+
}
|
750
|
+
|
751
|
+
/*
|
752
|
+
* call-seq:
|
753
|
+
* res.fname( index ) -> String or Symbol
|
444
754
|
*
|
445
755
|
* Returns the name of the column corresponding to _index_.
|
756
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
757
|
+
*
|
446
758
|
*/
|
447
759
|
static VALUE
|
448
760
|
pgresult_fname(VALUE self, VALUE index)
|
449
761
|
{
|
450
|
-
|
451
|
-
PGresult *result = pgresult_get(self);
|
762
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
452
763
|
int i = NUM2INT(index);
|
764
|
+
char *cfname;
|
453
765
|
|
454
|
-
if (i < 0 || i >= PQnfields(
|
766
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
455
767
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
456
768
|
}
|
457
769
|
|
458
|
-
|
459
|
-
|
460
|
-
return rb_obj_freeze(fname);
|
770
|
+
cfname = PQfname(this->pgresult, i);
|
771
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
461
772
|
}
|
462
773
|
|
463
774
|
/*
|
@@ -756,8 +1067,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
756
1067
|
static VALUE
|
757
1068
|
pgresult_cmd_status(VALUE self)
|
758
1069
|
{
|
759
|
-
|
760
|
-
|
1070
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1071
|
+
VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
|
1072
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
761
1073
|
return ret;
|
762
1074
|
}
|
763
1075
|
|
@@ -786,7 +1098,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
786
1098
|
{
|
787
1099
|
long n;
|
788
1100
|
n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
|
789
|
-
return
|
1101
|
+
return LONG2NUM(n);
|
790
1102
|
}
|
791
1103
|
|
792
1104
|
/*
|
@@ -838,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
|
|
838
1150
|
}
|
839
1151
|
/* Store a copy of the filled hash for use at the next row. */
|
840
1152
|
if( num_tuples > 10 )
|
841
|
-
this->tuple_hash
|
1153
|
+
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
842
1154
|
|
843
1155
|
return tuple;
|
844
1156
|
}
|
@@ -957,8 +1269,12 @@ static VALUE
|
|
957
1269
|
pgresult_field_values( VALUE self, VALUE field )
|
958
1270
|
{
|
959
1271
|
PGresult *result = pgresult_get( self );
|
960
|
-
const char *fieldname
|
961
|
-
int fnum
|
1272
|
+
const char *fieldname;
|
1273
|
+
int fnum;
|
1274
|
+
|
1275
|
+
if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
|
1276
|
+
fieldname = StringValueCStr( field );
|
1277
|
+
fnum = PQfnumber( result, fieldname );
|
962
1278
|
|
963
1279
|
if ( fnum < 0 )
|
964
1280
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
@@ -967,6 +1283,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
967
1283
|
}
|
968
1284
|
|
969
1285
|
|
1286
|
+
/*
|
1287
|
+
* call-seq:
|
1288
|
+
* res.tuple_values( n ) -> array
|
1289
|
+
*
|
1290
|
+
* Returns an Array of the field values from the nth row of the result.
|
1291
|
+
*
|
1292
|
+
*/
|
1293
|
+
static VALUE
|
1294
|
+
pgresult_tuple_values(VALUE self, VALUE index)
|
1295
|
+
{
|
1296
|
+
int tuple_num = NUM2INT( index );
|
1297
|
+
t_pg_result *this;
|
1298
|
+
int field;
|
1299
|
+
int num_tuples;
|
1300
|
+
int num_fields;
|
1301
|
+
|
1302
|
+
this = pgresult_get_this_safe(self);
|
1303
|
+
num_tuples = PQntuples(this->pgresult);
|
1304
|
+
num_fields = PQnfields(this->pgresult);
|
1305
|
+
|
1306
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1307
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1308
|
+
|
1309
|
+
{
|
1310
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
1311
|
+
|
1312
|
+
/* populate the row */
|
1313
|
+
for ( field = 0; field < num_fields; field++ ) {
|
1314
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
|
1315
|
+
}
|
1316
|
+
return rb_ary_new4( num_fields, row_values );
|
1317
|
+
}
|
1318
|
+
}
|
1319
|
+
|
1320
|
+
static void ensure_init_for_tuple(VALUE self)
|
1321
|
+
{
|
1322
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
1323
|
+
|
1324
|
+
if( this->field_map == Qnil ){
|
1325
|
+
int i;
|
1326
|
+
VALUE field_map = rb_hash_new();
|
1327
|
+
|
1328
|
+
if( this->nfields == -1 )
|
1329
|
+
pgresult_init_fnames( self );
|
1330
|
+
|
1331
|
+
for( i = 0; i < this->nfields; i++ ){
|
1332
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
1333
|
+
}
|
1334
|
+
rb_obj_freeze(field_map);
|
1335
|
+
RB_OBJ_WRITE(self, &this->field_map, field_map);
|
1336
|
+
}
|
1337
|
+
}
|
1338
|
+
|
1339
|
+
/*
|
1340
|
+
* call-seq:
|
1341
|
+
* res.tuple( n ) -> PG::Tuple
|
1342
|
+
*
|
1343
|
+
* Returns a PG::Tuple from the nth row of the result.
|
1344
|
+
*
|
1345
|
+
*/
|
1346
|
+
static VALUE
|
1347
|
+
pgresult_tuple(VALUE self, VALUE index)
|
1348
|
+
{
|
1349
|
+
int tuple_num = NUM2INT( index );
|
1350
|
+
t_pg_result *this;
|
1351
|
+
int num_tuples;
|
1352
|
+
|
1353
|
+
this = pgresult_get_this_safe(self);
|
1354
|
+
num_tuples = PQntuples(this->pgresult);
|
1355
|
+
|
1356
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
1357
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
1358
|
+
|
1359
|
+
ensure_init_for_tuple(self);
|
1360
|
+
|
1361
|
+
return pg_tuple_new(self, tuple_num);
|
1362
|
+
}
|
1363
|
+
|
1364
|
+
|
970
1365
|
/*
|
971
1366
|
* call-seq:
|
972
1367
|
* res.each{ |tuple| ... }
|
@@ -993,7 +1388,7 @@ pgresult_each(VALUE self)
|
|
993
1388
|
* call-seq:
|
994
1389
|
* res.fields() -> Array
|
995
1390
|
*
|
996
|
-
*
|
1391
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
997
1392
|
*/
|
998
1393
|
static VALUE
|
999
1394
|
pgresult_fields(VALUE self)
|
@@ -1025,14 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
1025
1420
|
t_pg_result *this = pgresult_get_this(self);
|
1026
1421
|
t_typemap *p_typemap;
|
1027
1422
|
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
}
|
1032
|
-
Data_Get_Struct(typemap, t_typemap, p_typemap);
|
1423
|
+
rb_check_frozen(self);
|
1424
|
+
/* Check type of method param */
|
1425
|
+
TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
|
1033
1426
|
|
1034
|
-
|
1035
|
-
|
1427
|
+
typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
1428
|
+
RB_OBJ_WRITE(self, &this->typemap, typemap);
|
1429
|
+
this->p_typemap = RTYPEDDATA_DATA( typemap );
|
1036
1430
|
|
1037
1431
|
return typemap;
|
1038
1432
|
}
|
@@ -1052,47 +1446,71 @@ pgresult_type_map_get(VALUE self)
|
|
1052
1446
|
return this->typemap;
|
1053
1447
|
}
|
1054
1448
|
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
*
|
1071
|
-
|
1072
|
-
|
1073
|
-
*
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1449
|
+
|
1450
|
+
static int
|
1451
|
+
yield_hash(VALUE self, int ntuples, int nfields, void *data)
|
1452
|
+
{
|
1453
|
+
int tuple_num;
|
1454
|
+
UNUSED(nfields);
|
1455
|
+
|
1456
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1457
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
1458
|
+
}
|
1459
|
+
|
1460
|
+
return 1; /* clear the result */
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
static int
|
1464
|
+
yield_array(VALUE self, int ntuples, int nfields, void *data)
|
1465
|
+
{
|
1466
|
+
int row;
|
1467
|
+
t_pg_result *this = pgresult_get_this(self);
|
1468
|
+
|
1469
|
+
for ( row = 0; row < ntuples; row++ ) {
|
1470
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
1471
|
+
int field;
|
1472
|
+
|
1473
|
+
/* populate the row */
|
1474
|
+
for ( field = 0; field < nfields; field++ ) {
|
1475
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
1476
|
+
}
|
1477
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
1478
|
+
}
|
1479
|
+
|
1480
|
+
return 1; /* clear the result */
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
static int
|
1484
|
+
yield_tuple(VALUE self, int ntuples, int nfields, void *data)
|
1485
|
+
{
|
1486
|
+
int tuple_num;
|
1487
|
+
t_pg_result *this = pgresult_get_this(self);
|
1488
|
+
VALUE copy;
|
1489
|
+
UNUSED(nfields);
|
1490
|
+
|
1491
|
+
/* make a copy of the base result, that is bound to the PG::Tuple */
|
1492
|
+
copy = pg_copy_result(this);
|
1493
|
+
/* The copy is now owner of the PGresult and is responsible to PQclear it.
|
1494
|
+
* We clear the pgresult here, so that it's not double freed on error within yield. */
|
1495
|
+
this->pgresult = NULL;
|
1496
|
+
|
1497
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
1498
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
1499
|
+
rb_yield( tuple );
|
1500
|
+
}
|
1501
|
+
return 0; /* don't clear the result */
|
1502
|
+
}
|
1503
|
+
|
1504
|
+
/* Non-static, and data pointer for use by sequel_pg */
|
1505
|
+
VALUE
|
1506
|
+
pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
|
1090
1507
|
{
|
1091
1508
|
t_pg_result *this;
|
1092
|
-
int nfields;
|
1509
|
+
int nfields, nfields2;
|
1093
1510
|
PGconn *pgconn;
|
1094
1511
|
PGresult *pgresult;
|
1095
1512
|
|
1513
|
+
rb_check_frozen(self);
|
1096
1514
|
RETURN_ENUMERATOR(self, 0, NULL);
|
1097
1515
|
|
1098
1516
|
this = pgresult_get_this_safe(self);
|
@@ -1101,11 +1519,11 @@ pgresult_stream_each(VALUE self)
|
|
1101
1519
|
nfields = PQnfields(pgresult);
|
1102
1520
|
|
1103
1521
|
for(;;){
|
1104
|
-
int tuple_num;
|
1105
1522
|
int ntuples = PQntuples(pgresult);
|
1106
1523
|
|
1107
1524
|
switch( PQresultStatus(pgresult) ){
|
1108
1525
|
case PGRES_TUPLES_OK:
|
1526
|
+
case PGRES_COMMAND_OK:
|
1109
1527
|
if( ntuples == 0 )
|
1110
1528
|
return self;
|
1111
1529
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
@@ -1115,21 +1533,24 @@ pgresult_stream_each(VALUE self)
|
|
1115
1533
|
pg_result_check( self );
|
1116
1534
|
}
|
1117
1535
|
|
1118
|
-
|
1119
|
-
|
1536
|
+
nfields2 = PQnfields(pgresult);
|
1537
|
+
if( nfields != nfields2 ){
|
1538
|
+
pgresult_clear( this );
|
1539
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, nfields2);
|
1540
|
+
}
|
1541
|
+
|
1542
|
+
if( yielder( self, ntuples, nfields, data ) ){
|
1543
|
+
pgresult_clear( this );
|
1120
1544
|
}
|
1121
1545
|
|
1122
|
-
if(
|
1123
|
-
|
1124
|
-
this->
|
1546
|
+
if( gvl_PQisBusy(pgconn) ){
|
1547
|
+
/* wait for input (without blocking) before reading each result */
|
1548
|
+
pgconn_block( 0, NULL, this->connection );
|
1125
1549
|
}
|
1126
1550
|
|
1127
1551
|
pgresult = gvl_PQgetResult(pgconn);
|
1128
1552
|
if( pgresult == NULL )
|
1129
|
-
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another
|
1130
|
-
|
1131
|
-
if( nfields != PQnfields(pgresult) )
|
1132
|
-
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
|
1553
|
+
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
|
1133
1554
|
|
1134
1555
|
this->pgresult = pgresult;
|
1135
1556
|
}
|
@@ -1138,6 +1559,44 @@ pgresult_stream_each(VALUE self)
|
|
1138
1559
|
return self;
|
1139
1560
|
}
|
1140
1561
|
|
1562
|
+
|
1563
|
+
/*
|
1564
|
+
* call-seq:
|
1565
|
+
* res.stream_each{ |tuple| ... }
|
1566
|
+
*
|
1567
|
+
* Invokes block for each tuple in the result set in single row mode.
|
1568
|
+
*
|
1569
|
+
* This is a convenience method for retrieving all result tuples
|
1570
|
+
* as they are transferred. It is an alternative to repeated calls of
|
1571
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
1572
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
1573
|
+
* the same speed as with ordinary results.
|
1574
|
+
*
|
1575
|
+
* The base result must be in status PGRES_SINGLE_TUPLE.
|
1576
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
1577
|
+
* A PG::Error is raised for any errors from the server.
|
1578
|
+
*
|
1579
|
+
* Row description data does not change while the iteration. All value retrieval
|
1580
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
1581
|
+
* the iteration and +0+ after all tuples were yielded.
|
1582
|
+
*
|
1583
|
+
* Example:
|
1584
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
1585
|
+
* conn.set_single_row_mode
|
1586
|
+
* conn.get_result.stream_each do |row|
|
1587
|
+
* # do something with each received row of the first query
|
1588
|
+
* end
|
1589
|
+
* conn.get_result.stream_each do |row|
|
1590
|
+
* # do something with each received row of the second query
|
1591
|
+
* end
|
1592
|
+
* conn.get_result # => nil (no more results)
|
1593
|
+
*/
|
1594
|
+
static VALUE
|
1595
|
+
pgresult_stream_each(VALUE self)
|
1596
|
+
{
|
1597
|
+
return pgresult_stream_any(self, yield_hash, NULL);
|
1598
|
+
}
|
1599
|
+
|
1141
1600
|
/*
|
1142
1601
|
* call-seq:
|
1143
1602
|
* res.stream_each_row { |row| ... }
|
@@ -1147,92 +1606,122 @@ pgresult_stream_each(VALUE self)
|
|
1147
1606
|
*
|
1148
1607
|
* This method works equally to #stream_each , but yields an Array of
|
1149
1608
|
* values.
|
1150
|
-
*
|
1151
|
-
* Available since PostgreSQL-9.2
|
1152
1609
|
*/
|
1153
1610
|
static VALUE
|
1154
1611
|
pgresult_stream_each_row(VALUE self)
|
1155
1612
|
{
|
1156
|
-
|
1157
|
-
|
1158
|
-
int nfields;
|
1159
|
-
PGconn *pgconn;
|
1160
|
-
PGresult *pgresult;
|
1161
|
-
|
1162
|
-
RETURN_ENUMERATOR(self, 0, NULL);
|
1163
|
-
|
1164
|
-
this = pgresult_get_this_safe(self);
|
1165
|
-
pgconn = pg_get_pgconn(this->connection);
|
1166
|
-
pgresult = this->pgresult;
|
1167
|
-
nfields = PQnfields(pgresult);
|
1168
|
-
|
1169
|
-
for(;;){
|
1170
|
-
int ntuples = PQntuples(pgresult);
|
1613
|
+
return pgresult_stream_any(self, yield_array, NULL);
|
1614
|
+
}
|
1171
1615
|
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1616
|
+
/*
|
1617
|
+
* call-seq:
|
1618
|
+
* res.stream_each_tuple { |tuple| ... }
|
1619
|
+
*
|
1620
|
+
* Yields each row of the result set in single row mode.
|
1621
|
+
*
|
1622
|
+
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
1623
|
+
*/
|
1624
|
+
static VALUE
|
1625
|
+
pgresult_stream_each_tuple(VALUE self)
|
1626
|
+
{
|
1627
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
1628
|
+
ensure_init_for_tuple(self);
|
1182
1629
|
|
1183
|
-
|
1184
|
-
|
1185
|
-
int field;
|
1630
|
+
return pgresult_stream_any(self, yield_tuple, NULL);
|
1631
|
+
}
|
1186
1632
|
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1633
|
+
/*
|
1634
|
+
* call-seq:
|
1635
|
+
* res.field_name_type = Symbol
|
1636
|
+
*
|
1637
|
+
* Set type of field names specific to this result.
|
1638
|
+
* It can be set to one of:
|
1639
|
+
* * +:string+ to use String based field names
|
1640
|
+
* * +:symbol+ to use Symbol based field names
|
1641
|
+
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
|
1642
|
+
*
|
1643
|
+
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
1644
|
+
*
|
1645
|
+
* This setting affects several result methods:
|
1646
|
+
* * keys of Hash returned by #[] , #each and #stream_each
|
1647
|
+
* * #fields
|
1648
|
+
* * #fname
|
1649
|
+
* * field names used by #tuple and #stream_each_tuple
|
1650
|
+
*
|
1651
|
+
* The type of field names can only be changed before any of the affected methods have been called.
|
1652
|
+
*
|
1653
|
+
*/
|
1654
|
+
static VALUE
|
1655
|
+
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
1656
|
+
{
|
1657
|
+
t_pg_result *this = pgresult_get_this(self);
|
1193
1658
|
|
1194
|
-
|
1195
|
-
|
1196
|
-
this->pgresult = NULL;
|
1197
|
-
}
|
1659
|
+
rb_check_frozen(self);
|
1660
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
1198
1661
|
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1662
|
+
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
1663
|
+
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
1664
|
+
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
1665
|
+
else if ( sym == sym_string );
|
1666
|
+
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
1202
1667
|
|
1203
|
-
|
1204
|
-
|
1668
|
+
return sym;
|
1669
|
+
}
|
1205
1670
|
|
1206
|
-
|
1671
|
+
/*
|
1672
|
+
* call-seq:
|
1673
|
+
* res.field_name_type -> Symbol
|
1674
|
+
*
|
1675
|
+
* Get type of field names.
|
1676
|
+
*
|
1677
|
+
* See description at #field_name_type=
|
1678
|
+
*/
|
1679
|
+
static VALUE
|
1680
|
+
pgresult_field_name_type_get(VALUE self)
|
1681
|
+
{
|
1682
|
+
t_pg_result *this = pgresult_get_this(self);
|
1683
|
+
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
1684
|
+
return sym_symbol;
|
1685
|
+
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
1686
|
+
return sym_static_symbol;
|
1687
|
+
} else {
|
1688
|
+
return sym_string;
|
1207
1689
|
}
|
1208
|
-
|
1209
|
-
/* never reached */
|
1210
|
-
return self;
|
1211
1690
|
}
|
1212
1691
|
|
1213
|
-
|
1214
1692
|
void
|
1215
|
-
init_pg_result()
|
1693
|
+
init_pg_result(void)
|
1216
1694
|
{
|
1695
|
+
sym_string = ID2SYM(rb_intern("string"));
|
1696
|
+
sym_symbol = ID2SYM(rb_intern("symbol"));
|
1697
|
+
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
1698
|
+
|
1217
1699
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
1218
|
-
|
1700
|
+
rb_undef_alloc_func(rb_cPGresult);
|
1219
1701
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
1220
1702
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
1221
1703
|
|
1222
1704
|
/****** PG::Result INSTANCE METHODS: libpq ******/
|
1223
1705
|
rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
|
1224
|
-
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
1706
|
+
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, -1);
|
1707
|
+
rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
|
1225
1708
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
1226
1709
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
1710
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
1711
|
+
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
1712
|
+
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
1713
|
+
#endif
|
1227
1714
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
1228
1715
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
1229
1716
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
1717
|
+
rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
|
1230
1718
|
rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
|
1231
1719
|
rb_define_alias (rb_cPGresult, "check_result", "check");
|
1232
1720
|
rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
|
1233
1721
|
rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
|
1234
1722
|
rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
|
1235
1723
|
rb_define_alias(rb_cPGresult, "num_fields", "nfields");
|
1724
|
+
rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
|
1236
1725
|
rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
|
1237
1726
|
rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
|
1238
1727
|
rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
|
@@ -1259,6 +1748,8 @@ init_pg_result()
|
|
1259
1748
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
1260
1749
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
1261
1750
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
1751
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
1752
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
1262
1753
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
1263
1754
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
1264
1755
|
|
@@ -1268,6 +1759,8 @@ init_pg_result()
|
|
1268
1759
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
1269
1760
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
1270
1761
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
1271
|
-
|
1272
|
-
|
1762
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
1273
1763
|
|
1764
|
+
rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
|
1765
|
+
rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
|
1766
|
+
}
|