pg 0.21.0 → 1.3.2
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/.appveyor.yml +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +86 -0
- data/.github/workflows/source-gem.yml +130 -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 +291 -4
- data/Manifest.txt +8 -4
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +62 -16
- data/Rakefile +32 -142
- data/Rakefile.cross +69 -71
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/ext/errorcodes.def +84 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +23 -2
- data/ext/extconf.rb +109 -52
- data/ext/gvl_wrappers.c +8 -0
- data/ext/gvl_wrappers.h +40 -33
- data/ext/pg.c +211 -146
- data/ext/pg.h +85 -95
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +145 -30
- data/ext/pg_connection.c +1349 -929
- data/ext/pg_copy_coder.c +60 -23
- data/ext/pg_record_coder.c +519 -0
- data/ext/pg_result.c +613 -207
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +190 -59
- data/ext/pg_tuple.c +575 -0
- data/ext/pg_type_map.c +42 -9
- data/ext/pg_type_map_all_strings.c +19 -5
- data/ext/pg_type_map_by_class.c +54 -24
- data/ext/pg_type_map_by_column.c +73 -34
- data/ext/pg_type_map_by_mri_type.c +48 -19
- data/ext/pg_type_map_by_oid.c +55 -25
- data/ext/pg_type_map_in_ruby.c +51 -20
- 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 +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/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +24 -3
- data/lib/pg/connection.rb +600 -46
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder.rb +21 -26
- data/lib/pg/text_encoder.rb +32 -8
- 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 +51 -38
- 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 +96 -229
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/lib/pg/basic_type_mapping.rb +0 -426
- data/lib/pg/deprecated_constants.rb +0 -21
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -352
- data/spec/pg/basic_type_mapping_spec.rb +0 -305
- data/spec/pg/connection_spec.rb +0 -1676
- 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
|
+
|
|
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
|
+
}
|
|
18
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,
|
|
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,88 @@ 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;
|
|
38
211
|
this->connection = rb_pgconn;
|
|
39
212
|
this->typemap = pg_typemap_all_strings;
|
|
40
|
-
this->p_typemap =
|
|
41
|
-
this->autoclear = 0;
|
|
213
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
42
214
|
this->nfields = -1;
|
|
43
215
|
this->tuple_hash = Qnil;
|
|
44
|
-
|
|
45
|
-
|
|
216
|
+
this->field_map = Qnil;
|
|
217
|
+
this->flags = 0;
|
|
218
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
|
46
219
|
|
|
47
220
|
if( result ){
|
|
48
221
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
|
49
222
|
VALUE typemap = p_conn->type_map_for_results;
|
|
50
|
-
|
|
51
223
|
/* Type check is done when assigned to PG::Connection. */
|
|
52
|
-
t_typemap *p_typemap =
|
|
224
|
+
t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
|
|
53
225
|
|
|
226
|
+
this->enc_idx = p_conn->enc_idx;
|
|
54
227
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
|
55
|
-
this->p_typemap =
|
|
228
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
229
|
+
this->flags = p_conn->flags;
|
|
230
|
+
} else {
|
|
231
|
+
this->enc_idx = rb_locale_encindex();
|
|
56
232
|
}
|
|
57
233
|
|
|
58
234
|
return self;
|
|
59
235
|
}
|
|
60
236
|
|
|
237
|
+
VALUE
|
|
238
|
+
pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
239
|
+
{
|
|
240
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
241
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
242
|
+
|
|
243
|
+
this->autoclear = 0;
|
|
244
|
+
|
|
245
|
+
/* Estimate size of underlying pgresult memory storage and account to ruby GC.
|
|
246
|
+
* There's no need to adjust the GC for xmalloc'ed memory, but libpq is using libc malloc() ruby doesn't know about.
|
|
247
|
+
*/
|
|
248
|
+
/* 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.
|
|
249
|
+
* For now the memory savings don't justify the ifdefs necessary to support both cases.
|
|
250
|
+
*/
|
|
251
|
+
this->result_size = pgresult_approx_size(result);
|
|
252
|
+
|
|
253
|
+
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
254
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
|
255
|
+
#endif
|
|
256
|
+
|
|
257
|
+
return self;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static VALUE
|
|
261
|
+
pg_copy_result(t_pg_result *this)
|
|
262
|
+
{
|
|
263
|
+
int nfields = this->nfields == -1 ? (this->pgresult ? PQnfields(this->pgresult) : 0) : this->nfields;
|
|
264
|
+
size_t len = sizeof(*this) + sizeof(*this->fnames) * nfields;
|
|
265
|
+
t_pg_result *copy;
|
|
266
|
+
|
|
267
|
+
copy = (t_pg_result *)xmalloc(len);
|
|
268
|
+
memcpy(copy, this, len);
|
|
269
|
+
this->result_size = 0;
|
|
270
|
+
|
|
271
|
+
return TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, copy);
|
|
272
|
+
}
|
|
273
|
+
|
|
61
274
|
VALUE
|
|
62
275
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
63
276
|
{
|
|
64
|
-
VALUE self =
|
|
277
|
+
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
65
278
|
t_pg_result *this = pgresult_get_this(self);
|
|
279
|
+
|
|
280
|
+
/* Autocleared results are freed implicit instead of by PQclear().
|
|
281
|
+
* So it's not very useful to be accounted by ruby GC.
|
|
282
|
+
*/
|
|
283
|
+
this->result_size = 0;
|
|
66
284
|
this->autoclear = 1;
|
|
67
285
|
return self;
|
|
68
286
|
}
|
|
@@ -71,7 +289,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
|
71
289
|
* call-seq:
|
|
72
290
|
* res.check -> nil
|
|
73
291
|
*
|
|
74
|
-
* Raises appropriate exception if PG::Result is in a bad state
|
|
292
|
+
* Raises appropriate exception if PG::Result is in a bad state, which is:
|
|
293
|
+
* * +PGRES_BAD_RESPONSE+
|
|
294
|
+
* * +PGRES_FATAL_ERROR+
|
|
295
|
+
* * +PGRES_NONFATAL_ERROR+
|
|
296
|
+
* * +PGRES_PIPELINE_ABORTED+
|
|
75
297
|
*/
|
|
76
298
|
VALUE
|
|
77
299
|
pg_result_check( VALUE self )
|
|
@@ -92,18 +314,20 @@ pg_result_check( VALUE self )
|
|
|
92
314
|
case PGRES_TUPLES_OK:
|
|
93
315
|
case PGRES_COPY_OUT:
|
|
94
316
|
case PGRES_COPY_IN:
|
|
95
|
-
#ifdef HAVE_CONST_PGRES_COPY_BOTH
|
|
96
317
|
case PGRES_COPY_BOTH:
|
|
97
|
-
#endif
|
|
98
|
-
#ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
|
|
99
318
|
case PGRES_SINGLE_TUPLE:
|
|
100
|
-
#endif
|
|
101
319
|
case PGRES_EMPTY_QUERY:
|
|
102
320
|
case PGRES_COMMAND_OK:
|
|
321
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
322
|
+
case PGRES_PIPELINE_SYNC:
|
|
323
|
+
#endif
|
|
103
324
|
return self;
|
|
104
325
|
case PGRES_BAD_RESPONSE:
|
|
105
326
|
case PGRES_FATAL_ERROR:
|
|
106
327
|
case PGRES_NONFATAL_ERROR:
|
|
328
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
329
|
+
case PGRES_PIPELINE_ABORTED:
|
|
330
|
+
#endif
|
|
107
331
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
|
108
332
|
break;
|
|
109
333
|
default:
|
|
@@ -111,7 +335,7 @@ pg_result_check( VALUE self )
|
|
|
111
335
|
}
|
|
112
336
|
}
|
|
113
337
|
|
|
114
|
-
PG_ENCODING_SET_NOCHECK( error,
|
|
338
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
|
115
339
|
|
|
116
340
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
|
117
341
|
klass = lookup_error_class( sqlstate );
|
|
@@ -135,19 +359,22 @@ pg_result_check( VALUE self )
|
|
|
135
359
|
* call-seq:
|
|
136
360
|
* res.clear() -> nil
|
|
137
361
|
*
|
|
138
|
-
* Clears the PG::Result object as the result of
|
|
362
|
+
* Clears the PG::Result object as the result of a query.
|
|
363
|
+
* This frees all underlying memory consumed by the result object.
|
|
364
|
+
* Afterwards access to result methods raises PG::Error "result has been cleared".
|
|
139
365
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
366
|
+
* Explicit calling #clear can lead to better memory performance, but is not generally necessary.
|
|
367
|
+
* Special care must be taken when PG::Tuple objects are used.
|
|
368
|
+
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
|
369
|
+
*
|
|
370
|
+
* 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.
|
|
142
371
|
*
|
|
143
372
|
*/
|
|
144
373
|
VALUE
|
|
145
374
|
pg_result_clear(VALUE self)
|
|
146
375
|
{
|
|
147
376
|
t_pg_result *this = pgresult_get_this(self);
|
|
148
|
-
|
|
149
|
-
PQclear(pgresult_get(self));
|
|
150
|
-
this->pgresult = NULL;
|
|
377
|
+
pgresult_clear( this );
|
|
151
378
|
return Qnil;
|
|
152
379
|
}
|
|
153
380
|
|
|
@@ -155,7 +382,7 @@ pg_result_clear(VALUE self)
|
|
|
155
382
|
* call-seq:
|
|
156
383
|
* res.cleared? -> boolean
|
|
157
384
|
*
|
|
158
|
-
* Returns +true+ if the backend result memory has been
|
|
385
|
+
* Returns +true+ if the backend result memory has been freed.
|
|
159
386
|
*/
|
|
160
387
|
VALUE
|
|
161
388
|
pgresult_cleared_p( VALUE self )
|
|
@@ -168,8 +395,10 @@ pgresult_cleared_p( VALUE self )
|
|
|
168
395
|
* call-seq:
|
|
169
396
|
* res.autoclear? -> boolean
|
|
170
397
|
*
|
|
171
|
-
* Returns +true+ if the underlying C struct will be cleared
|
|
172
|
-
*
|
|
398
|
+
* Returns +true+ if the underlying C struct will be cleared at the end of a callback.
|
|
399
|
+
* This applies only to Result objects received by the block to PG::Connection#set_notice_receiver .
|
|
400
|
+
*
|
|
401
|
+
* All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
|
|
173
402
|
*
|
|
174
403
|
*/
|
|
175
404
|
VALUE
|
|
@@ -183,37 +412,6 @@ pgresult_autoclear_p( VALUE self )
|
|
|
183
412
|
* DATA pointer functions
|
|
184
413
|
*/
|
|
185
414
|
|
|
186
|
-
/*
|
|
187
|
-
* GC Mark function
|
|
188
|
-
*/
|
|
189
|
-
static void
|
|
190
|
-
pgresult_gc_mark( t_pg_result *this )
|
|
191
|
-
{
|
|
192
|
-
int i;
|
|
193
|
-
|
|
194
|
-
if( !this ) return;
|
|
195
|
-
rb_gc_mark( this->connection );
|
|
196
|
-
rb_gc_mark( this->typemap );
|
|
197
|
-
rb_gc_mark( this->tuple_hash );
|
|
198
|
-
|
|
199
|
-
for( i=0; i < this->nfields; i++ ){
|
|
200
|
-
rb_gc_mark( this->fnames[i] );
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/*
|
|
205
|
-
* GC Free function
|
|
206
|
-
*/
|
|
207
|
-
static void
|
|
208
|
-
pgresult_gc_free( t_pg_result *this )
|
|
209
|
-
{
|
|
210
|
-
if( !this ) return;
|
|
211
|
-
if(this->pgresult != NULL && !this->autoclear)
|
|
212
|
-
PQclear(this->pgresult);
|
|
213
|
-
|
|
214
|
-
xfree(this);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
415
|
/*
|
|
218
416
|
* Fetch the PG::Result object data pointer and check it's
|
|
219
417
|
* PGresult data pointer for sanity.
|
|
@@ -243,18 +441,30 @@ pgresult_get(VALUE self)
|
|
|
243
441
|
return this->pgresult;
|
|
244
442
|
}
|
|
245
443
|
|
|
246
|
-
|
|
247
|
-
* Document-method: allocate
|
|
248
|
-
*
|
|
249
|
-
* call-seq:
|
|
250
|
-
* PG::Result.allocate -> result
|
|
251
|
-
*/
|
|
252
|
-
static VALUE
|
|
253
|
-
pgresult_s_allocate( VALUE klass )
|
|
444
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
254
445
|
{
|
|
255
|
-
VALUE
|
|
256
|
-
|
|
257
|
-
|
|
446
|
+
VALUE fname;
|
|
447
|
+
#ifdef TRUFFLERUBY
|
|
448
|
+
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
|
449
|
+
#else
|
|
450
|
+
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
451
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
452
|
+
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
|
453
|
+
if( fname == Qnil ){
|
|
454
|
+
fname = rb_str_new2(cstr);
|
|
455
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
456
|
+
fname = rb_str_intern(fname);
|
|
457
|
+
}
|
|
458
|
+
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
459
|
+
#endif
|
|
460
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
461
|
+
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
|
462
|
+
} else {
|
|
463
|
+
fname = rb_str_new2(cstr);
|
|
464
|
+
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
465
|
+
fname = rb_obj_freeze(fname);
|
|
466
|
+
}
|
|
467
|
+
return fname;
|
|
258
468
|
}
|
|
259
469
|
|
|
260
470
|
static void pgresult_init_fnames(VALUE self)
|
|
@@ -266,12 +476,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
266
476
|
int nfields = PQnfields(this->pgresult);
|
|
267
477
|
|
|
268
478
|
for( i=0; i<nfields; i++ ){
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this->fnames[i] = rb_obj_freeze(fname);
|
|
479
|
+
char *cfname = PQfname(this->pgresult, i);
|
|
480
|
+
this->fnames[i] = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
272
481
|
this->nfields = i + 1;
|
|
273
|
-
|
|
274
|
-
RB_GC_GUARD(fname);
|
|
275
482
|
}
|
|
276
483
|
this->nfields = nfields;
|
|
277
484
|
}
|
|
@@ -283,8 +490,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
283
490
|
*
|
|
284
491
|
* The class to represent the query result tuples (rows).
|
|
285
492
|
* An instance of this class is created as the result of every query.
|
|
286
|
-
*
|
|
287
|
-
*
|
|
493
|
+
* All result rows and columns are stored in a memory block attached to the PG::Result object.
|
|
494
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
|
495
|
+
*
|
|
496
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
|
497
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
288
498
|
*
|
|
289
499
|
* Example:
|
|
290
500
|
* require 'pg'
|
|
@@ -314,6 +524,9 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
314
524
|
* * +PGRES_NONFATAL_ERROR+
|
|
315
525
|
* * +PGRES_FATAL_ERROR+
|
|
316
526
|
* * +PGRES_COPY_BOTH+
|
|
527
|
+
* * +PGRES_SINGLE_TUPLE+
|
|
528
|
+
* * +PGRES_PIPELINE_SYNC+
|
|
529
|
+
* * +PGRES_PIPELINE_ABORTED+
|
|
317
530
|
*/
|
|
318
531
|
static VALUE
|
|
319
532
|
pgresult_result_status(VALUE self)
|
|
@@ -325,14 +538,15 @@ pgresult_result_status(VALUE self)
|
|
|
325
538
|
* call-seq:
|
|
326
539
|
* res.res_status( status ) -> String
|
|
327
540
|
*
|
|
328
|
-
* Returns the string representation of
|
|
541
|
+
* Returns the string representation of +status+.
|
|
329
542
|
*
|
|
330
543
|
*/
|
|
331
544
|
static VALUE
|
|
332
545
|
pgresult_res_status(VALUE self, VALUE status)
|
|
333
546
|
{
|
|
334
|
-
|
|
335
|
-
|
|
547
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
548
|
+
VALUE ret = rb_str_new2(PQresStatus(NUM2INT(status)));
|
|
549
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
336
550
|
return ret;
|
|
337
551
|
}
|
|
338
552
|
|
|
@@ -345,10 +559,39 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
|
345
559
|
static VALUE
|
|
346
560
|
pgresult_error_message(VALUE self)
|
|
347
561
|
{
|
|
348
|
-
|
|
349
|
-
|
|
562
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
563
|
+
VALUE ret = rb_str_new2(PQresultErrorMessage(this->pgresult));
|
|
564
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
565
|
+
return ret;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
569
|
+
/*
|
|
570
|
+
* call-seq:
|
|
571
|
+
* res.verbose_error_message( verbosity, show_context ) -> String
|
|
572
|
+
*
|
|
573
|
+
* Returns a reformatted version of the error message associated with a PGresult object.
|
|
574
|
+
*
|
|
575
|
+
* Available since PostgreSQL-9.6
|
|
576
|
+
*/
|
|
577
|
+
static VALUE
|
|
578
|
+
pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
579
|
+
{
|
|
580
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
581
|
+
VALUE ret;
|
|
582
|
+
char *c_str;
|
|
583
|
+
|
|
584
|
+
c_str = PQresultVerboseErrorMessage(this->pgresult, NUM2INT(verbosity), NUM2INT(show_context));
|
|
585
|
+
if(!c_str)
|
|
586
|
+
rb_raise(rb_eNoMemError, "insufficient memory to format error message");
|
|
587
|
+
|
|
588
|
+
ret = rb_str_new2(c_str);
|
|
589
|
+
PQfreemem(c_str);
|
|
590
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
591
|
+
|
|
350
592
|
return ret;
|
|
351
593
|
}
|
|
594
|
+
#endif
|
|
352
595
|
|
|
353
596
|
/*
|
|
354
597
|
* call-seq:
|
|
@@ -399,14 +642,14 @@ pgresult_error_message(VALUE self)
|
|
|
399
642
|
static VALUE
|
|
400
643
|
pgresult_error_field(VALUE self, VALUE field)
|
|
401
644
|
{
|
|
402
|
-
|
|
645
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
403
646
|
int fieldcode = NUM2INT( field );
|
|
404
|
-
char * fieldstr = PQresultErrorField(
|
|
647
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
|
405
648
|
VALUE ret = Qnil;
|
|
406
649
|
|
|
407
650
|
if ( fieldstr ) {
|
|
408
|
-
ret =
|
|
409
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
|
651
|
+
ret = rb_str_new2( fieldstr );
|
|
652
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
|
410
653
|
}
|
|
411
654
|
|
|
412
655
|
return ret;
|
|
@@ -427,7 +670,7 @@ pgresult_ntuples(VALUE self)
|
|
|
427
670
|
static VALUE
|
|
428
671
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
|
429
672
|
{
|
|
430
|
-
|
|
673
|
+
return pgresult_ntuples(self);
|
|
431
674
|
}
|
|
432
675
|
|
|
433
676
|
/*
|
|
@@ -444,24 +687,25 @@ pgresult_nfields(VALUE self)
|
|
|
444
687
|
|
|
445
688
|
/*
|
|
446
689
|
* call-seq:
|
|
447
|
-
* res.fname( index ) -> String
|
|
690
|
+
* res.fname( index ) -> String or Symbol
|
|
448
691
|
*
|
|
449
692
|
* Returns the name of the column corresponding to _index_.
|
|
693
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
|
694
|
+
*
|
|
450
695
|
*/
|
|
451
696
|
static VALUE
|
|
452
697
|
pgresult_fname(VALUE self, VALUE index)
|
|
453
698
|
{
|
|
454
|
-
|
|
455
|
-
PGresult *result = pgresult_get(self);
|
|
699
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
456
700
|
int i = NUM2INT(index);
|
|
701
|
+
char *cfname;
|
|
457
702
|
|
|
458
|
-
if (i < 0 || i >= PQnfields(
|
|
703
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
|
459
704
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
|
460
705
|
}
|
|
461
706
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return rb_obj_freeze(fname);
|
|
707
|
+
cfname = PQfname(this->pgresult, i);
|
|
708
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
465
709
|
}
|
|
466
710
|
|
|
467
711
|
/*
|
|
@@ -760,8 +1004,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
|
760
1004
|
static VALUE
|
|
761
1005
|
pgresult_cmd_status(VALUE self)
|
|
762
1006
|
{
|
|
763
|
-
|
|
764
|
-
|
|
1007
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
1008
|
+
VALUE ret = rb_str_new2(PQcmdStatus(this->pgresult));
|
|
1009
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
765
1010
|
return ret;
|
|
766
1011
|
}
|
|
767
1012
|
|
|
@@ -790,7 +1035,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
|
790
1035
|
{
|
|
791
1036
|
long n;
|
|
792
1037
|
n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
|
|
793
|
-
return
|
|
1038
|
+
return LONG2NUM(n);
|
|
794
1039
|
}
|
|
795
1040
|
|
|
796
1041
|
/*
|
|
@@ -961,8 +1206,12 @@ static VALUE
|
|
|
961
1206
|
pgresult_field_values( VALUE self, VALUE field )
|
|
962
1207
|
{
|
|
963
1208
|
PGresult *result = pgresult_get( self );
|
|
964
|
-
const char *fieldname
|
|
965
|
-
int fnum
|
|
1209
|
+
const char *fieldname;
|
|
1210
|
+
int fnum;
|
|
1211
|
+
|
|
1212
|
+
if( RB_TYPE_P(field, T_SYMBOL) ) field = rb_sym_to_s( field );
|
|
1213
|
+
fieldname = StringValueCStr( field );
|
|
1214
|
+
fnum = PQfnumber( result, fieldname );
|
|
966
1215
|
|
|
967
1216
|
if ( fnum < 0 )
|
|
968
1217
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
|
@@ -971,6 +1220,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
|
971
1220
|
}
|
|
972
1221
|
|
|
973
1222
|
|
|
1223
|
+
/*
|
|
1224
|
+
* call-seq:
|
|
1225
|
+
* res.tuple_values( n ) -> array
|
|
1226
|
+
*
|
|
1227
|
+
* Returns an Array of the field values from the nth row of the result.
|
|
1228
|
+
*
|
|
1229
|
+
*/
|
|
1230
|
+
static VALUE
|
|
1231
|
+
pgresult_tuple_values(VALUE self, VALUE index)
|
|
1232
|
+
{
|
|
1233
|
+
int tuple_num = NUM2INT( index );
|
|
1234
|
+
t_pg_result *this;
|
|
1235
|
+
int field;
|
|
1236
|
+
int num_tuples;
|
|
1237
|
+
int num_fields;
|
|
1238
|
+
|
|
1239
|
+
this = pgresult_get_this_safe(self);
|
|
1240
|
+
num_tuples = PQntuples(this->pgresult);
|
|
1241
|
+
num_fields = PQnfields(this->pgresult);
|
|
1242
|
+
|
|
1243
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1244
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1245
|
+
|
|
1246
|
+
{
|
|
1247
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, num_fields, PG_MAX_COLUMNS)
|
|
1248
|
+
|
|
1249
|
+
/* populate the row */
|
|
1250
|
+
for ( field = 0; field < num_fields; field++ ) {
|
|
1251
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field);
|
|
1252
|
+
}
|
|
1253
|
+
return rb_ary_new4( num_fields, row_values );
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
static void ensure_init_for_tuple(VALUE self)
|
|
1258
|
+
{
|
|
1259
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
1260
|
+
|
|
1261
|
+
if( this->field_map == Qnil ){
|
|
1262
|
+
int i;
|
|
1263
|
+
VALUE field_map = rb_hash_new();
|
|
1264
|
+
|
|
1265
|
+
if( this->nfields == -1 )
|
|
1266
|
+
pgresult_init_fnames( self );
|
|
1267
|
+
|
|
1268
|
+
for( i = 0; i < this->nfields; i++ ){
|
|
1269
|
+
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
|
1270
|
+
}
|
|
1271
|
+
rb_obj_freeze(field_map);
|
|
1272
|
+
this->field_map = field_map;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/*
|
|
1277
|
+
* call-seq:
|
|
1278
|
+
* res.tuple( n ) -> PG::Tuple
|
|
1279
|
+
*
|
|
1280
|
+
* Returns a PG::Tuple from the nth row of the result.
|
|
1281
|
+
*
|
|
1282
|
+
*/
|
|
1283
|
+
static VALUE
|
|
1284
|
+
pgresult_tuple(VALUE self, VALUE index)
|
|
1285
|
+
{
|
|
1286
|
+
int tuple_num = NUM2INT( index );
|
|
1287
|
+
t_pg_result *this;
|
|
1288
|
+
int num_tuples;
|
|
1289
|
+
|
|
1290
|
+
this = pgresult_get_this_safe(self);
|
|
1291
|
+
num_tuples = PQntuples(this->pgresult);
|
|
1292
|
+
|
|
1293
|
+
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1294
|
+
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1295
|
+
|
|
1296
|
+
ensure_init_for_tuple(self);
|
|
1297
|
+
|
|
1298
|
+
return pg_tuple_new(self, tuple_num);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
|
|
974
1302
|
/*
|
|
975
1303
|
* call-seq:
|
|
976
1304
|
* res.each{ |tuple| ... }
|
|
@@ -997,7 +1325,7 @@ pgresult_each(VALUE self)
|
|
|
997
1325
|
* call-seq:
|
|
998
1326
|
* res.fields() -> Array
|
|
999
1327
|
*
|
|
1000
|
-
*
|
|
1328
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
1001
1329
|
*/
|
|
1002
1330
|
static VALUE
|
|
1003
1331
|
pgresult_fields(VALUE self)
|
|
@@ -1029,14 +1357,11 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1029
1357
|
t_pg_result *this = pgresult_get_this(self);
|
|
1030
1358
|
t_typemap *p_typemap;
|
|
1031
1359
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
rb_obj_classname( typemap ) );
|
|
1035
|
-
}
|
|
1036
|
-
Data_Get_Struct(typemap, t_typemap, p_typemap);
|
|
1360
|
+
/* Check type of method param */
|
|
1361
|
+
TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
|
|
1037
1362
|
|
|
1038
1363
|
this->typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
|
1039
|
-
this->p_typemap =
|
|
1364
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
1040
1365
|
|
|
1041
1366
|
return typemap;
|
|
1042
1367
|
}
|
|
@@ -1056,40 +1381,63 @@ pgresult_type_map_get(VALUE self)
|
|
|
1056
1381
|
return this->typemap;
|
|
1057
1382
|
}
|
|
1058
1383
|
|
|
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
|
-
|
|
1090
|
-
|
|
1384
|
+
|
|
1385
|
+
static void
|
|
1386
|
+
yield_hash(VALUE self, int ntuples, int nfields)
|
|
1387
|
+
{
|
|
1388
|
+
int tuple_num;
|
|
1389
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1390
|
+
UNUSED(nfields);
|
|
1391
|
+
|
|
1392
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1393
|
+
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
pgresult_clear( this );
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
static void
|
|
1400
|
+
yield_array(VALUE self, int ntuples, int nfields)
|
|
1401
|
+
{
|
|
1402
|
+
int row;
|
|
1403
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1404
|
+
|
|
1405
|
+
for ( row = 0; row < ntuples; row++ ) {
|
|
1406
|
+
PG_VARIABLE_LENGTH_ARRAY(VALUE, row_values, nfields, PG_MAX_COLUMNS)
|
|
1407
|
+
int field;
|
|
1408
|
+
|
|
1409
|
+
/* populate the row */
|
|
1410
|
+
for ( field = 0; field < nfields; field++ ) {
|
|
1411
|
+
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
|
1412
|
+
}
|
|
1413
|
+
rb_yield( rb_ary_new4( nfields, row_values ));
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
pgresult_clear( this );
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
static void
|
|
1420
|
+
yield_tuple(VALUE self, int ntuples, int nfields)
|
|
1421
|
+
{
|
|
1422
|
+
int tuple_num;
|
|
1423
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1424
|
+
VALUE copy;
|
|
1425
|
+
UNUSED(nfields);
|
|
1426
|
+
|
|
1427
|
+
/* make a copy of the base result, that is bound to the PG::Tuple */
|
|
1428
|
+
copy = pg_copy_result(this);
|
|
1429
|
+
/* The copy is now owner of the PGresult and is responsible to PQclear it.
|
|
1430
|
+
* We clear the pgresult here, so that it's not double freed on error within yield. */
|
|
1431
|
+
this->pgresult = NULL;
|
|
1432
|
+
|
|
1433
|
+
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1434
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
|
1435
|
+
rb_yield( tuple );
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1091
1439
|
static VALUE
|
|
1092
|
-
|
|
1440
|
+
pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
|
1093
1441
|
{
|
|
1094
1442
|
t_pg_result *this;
|
|
1095
1443
|
int nfields;
|
|
@@ -1104,7 +1452,6 @@ pgresult_stream_each(VALUE self)
|
|
|
1104
1452
|
nfields = PQnfields(pgresult);
|
|
1105
1453
|
|
|
1106
1454
|
for(;;){
|
|
1107
|
-
int tuple_num;
|
|
1108
1455
|
int ntuples = PQntuples(pgresult);
|
|
1109
1456
|
|
|
1110
1457
|
switch( PQresultStatus(pgresult) ){
|
|
@@ -1118,14 +1465,7 @@ pgresult_stream_each(VALUE self)
|
|
|
1118
1465
|
pg_result_check( self );
|
|
1119
1466
|
}
|
|
1120
1467
|
|
|
1121
|
-
|
|
1122
|
-
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
if( !this->autoclear ){
|
|
1126
|
-
PQclear( pgresult );
|
|
1127
|
-
this->pgresult = NULL;
|
|
1128
|
-
}
|
|
1468
|
+
yielder( self, ntuples, nfields );
|
|
1129
1469
|
|
|
1130
1470
|
pgresult = gvl_PQgetResult(pgconn);
|
|
1131
1471
|
if( pgresult == NULL )
|
|
@@ -1141,6 +1481,44 @@ pgresult_stream_each(VALUE self)
|
|
|
1141
1481
|
return self;
|
|
1142
1482
|
}
|
|
1143
1483
|
|
|
1484
|
+
|
|
1485
|
+
/*
|
|
1486
|
+
* call-seq:
|
|
1487
|
+
* res.stream_each{ |tuple| ... }
|
|
1488
|
+
*
|
|
1489
|
+
* Invokes block for each tuple in the result set in single row mode.
|
|
1490
|
+
*
|
|
1491
|
+
* This is a convenience method for retrieving all result tuples
|
|
1492
|
+
* as they are transferred. It is an alternative to repeated calls of
|
|
1493
|
+
* PG::Connection#get_result , but given that it avoids the overhead of
|
|
1494
|
+
* wrapping each row into a dedicated result object, it delivers data in nearly
|
|
1495
|
+
* the same speed as with ordinary results.
|
|
1496
|
+
*
|
|
1497
|
+
* The base result must be in status PGRES_SINGLE_TUPLE.
|
|
1498
|
+
* It iterates over all tuples until the status changes to PGRES_TUPLES_OK.
|
|
1499
|
+
* A PG::Error is raised for any errors from the server.
|
|
1500
|
+
*
|
|
1501
|
+
* Row description data does not change while the iteration. All value retrieval
|
|
1502
|
+
* methods refer to only the current row. Result#ntuples returns +1+ while
|
|
1503
|
+
* the iteration and +0+ after all tuples were yielded.
|
|
1504
|
+
*
|
|
1505
|
+
* Example:
|
|
1506
|
+
* conn.send_query( "first SQL query; second SQL query" )
|
|
1507
|
+
* conn.set_single_row_mode
|
|
1508
|
+
* conn.get_result.stream_each do |row|
|
|
1509
|
+
* # do something with each received row of the first query
|
|
1510
|
+
* end
|
|
1511
|
+
* conn.get_result.stream_each do |row|
|
|
1512
|
+
* # do something with each received row of the second query
|
|
1513
|
+
* end
|
|
1514
|
+
* conn.get_result # => nil (no more results)
|
|
1515
|
+
*/
|
|
1516
|
+
static VALUE
|
|
1517
|
+
pgresult_stream_each(VALUE self)
|
|
1518
|
+
{
|
|
1519
|
+
return pgresult_stream_any(self, yield_hash);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1144
1522
|
/*
|
|
1145
1523
|
* call-seq:
|
|
1146
1524
|
* res.stream_each_row { |row| ... }
|
|
@@ -1154,70 +1532,92 @@ pgresult_stream_each(VALUE self)
|
|
|
1154
1532
|
static VALUE
|
|
1155
1533
|
pgresult_stream_each_row(VALUE self)
|
|
1156
1534
|
{
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
int nfields;
|
|
1160
|
-
PGconn *pgconn;
|
|
1161
|
-
PGresult *pgresult;
|
|
1162
|
-
|
|
1163
|
-
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1164
|
-
|
|
1165
|
-
this = pgresult_get_this_safe(self);
|
|
1166
|
-
pgconn = pg_get_pgconn(this->connection);
|
|
1167
|
-
pgresult = this->pgresult;
|
|
1168
|
-
nfields = PQnfields(pgresult);
|
|
1169
|
-
|
|
1170
|
-
for(;;){
|
|
1171
|
-
int ntuples = PQntuples(pgresult);
|
|
1172
|
-
|
|
1173
|
-
switch( PQresultStatus(pgresult) ){
|
|
1174
|
-
case PGRES_TUPLES_OK:
|
|
1175
|
-
if( ntuples == 0 )
|
|
1176
|
-
return self;
|
|
1177
|
-
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
1178
|
-
case PGRES_SINGLE_TUPLE:
|
|
1179
|
-
break;
|
|
1180
|
-
default:
|
|
1181
|
-
pg_result_check( self );
|
|
1182
|
-
}
|
|
1535
|
+
return pgresult_stream_any(self, yield_array);
|
|
1536
|
+
}
|
|
1183
1537
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1538
|
+
/*
|
|
1539
|
+
* call-seq:
|
|
1540
|
+
* res.stream_each_tuple { |tuple| ... }
|
|
1541
|
+
*
|
|
1542
|
+
* Yields each row of the result set in single row mode.
|
|
1543
|
+
*
|
|
1544
|
+
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
|
1545
|
+
*/
|
|
1546
|
+
static VALUE
|
|
1547
|
+
pgresult_stream_each_tuple(VALUE self)
|
|
1548
|
+
{
|
|
1549
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
|
1550
|
+
ensure_init_for_tuple(self);
|
|
1187
1551
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
row_values[field] = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, row, field);
|
|
1191
|
-
}
|
|
1192
|
-
rb_yield( rb_ary_new4( nfields, row_values ));
|
|
1193
|
-
}
|
|
1552
|
+
return pgresult_stream_any(self, yield_tuple);
|
|
1553
|
+
}
|
|
1194
1554
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1555
|
+
/*
|
|
1556
|
+
* call-seq:
|
|
1557
|
+
* res.field_name_type = Symbol
|
|
1558
|
+
*
|
|
1559
|
+
* Set type of field names specific to this result.
|
|
1560
|
+
* It can be set to one of:
|
|
1561
|
+
* * +:string+ to use String based field names
|
|
1562
|
+
* * +:symbol+ to use Symbol based field names
|
|
1563
|
+
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
|
|
1564
|
+
*
|
|
1565
|
+
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
|
1566
|
+
*
|
|
1567
|
+
* This setting affects several result methods:
|
|
1568
|
+
* * keys of Hash returned by #[] , #each and #stream_each
|
|
1569
|
+
* * #fields
|
|
1570
|
+
* * #fname
|
|
1571
|
+
* * field names used by #tuple and #stream_each_tuple
|
|
1572
|
+
*
|
|
1573
|
+
* The type of field names can only be changed before any of the affected methods have been called.
|
|
1574
|
+
*
|
|
1575
|
+
*/
|
|
1576
|
+
static VALUE
|
|
1577
|
+
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
|
1578
|
+
{
|
|
1579
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1580
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
|
1199
1581
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1582
|
+
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
|
1583
|
+
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
|
1584
|
+
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
|
1585
|
+
else if ( sym == sym_string );
|
|
1586
|
+
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
|
1203
1587
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1588
|
+
return sym;
|
|
1589
|
+
}
|
|
1206
1590
|
|
|
1207
|
-
|
|
1591
|
+
/*
|
|
1592
|
+
* call-seq:
|
|
1593
|
+
* res.field_name_type -> Symbol
|
|
1594
|
+
*
|
|
1595
|
+
* Get type of field names.
|
|
1596
|
+
*
|
|
1597
|
+
* See description at #field_name_type=
|
|
1598
|
+
*/
|
|
1599
|
+
static VALUE
|
|
1600
|
+
pgresult_field_name_type_get(VALUE self)
|
|
1601
|
+
{
|
|
1602
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
1603
|
+
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
1604
|
+
return sym_symbol;
|
|
1605
|
+
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
1606
|
+
return sym_static_symbol;
|
|
1607
|
+
} else {
|
|
1608
|
+
return sym_string;
|
|
1208
1609
|
}
|
|
1209
|
-
|
|
1210
|
-
/* never reached */
|
|
1211
|
-
return self;
|
|
1212
1610
|
}
|
|
1213
|
-
#endif
|
|
1214
|
-
|
|
1215
1611
|
|
|
1216
1612
|
void
|
|
1217
1613
|
init_pg_result()
|
|
1218
1614
|
{
|
|
1615
|
+
sym_string = ID2SYM(rb_intern("string"));
|
|
1616
|
+
sym_symbol = ID2SYM(rb_intern("symbol"));
|
|
1617
|
+
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
|
1618
|
+
|
|
1219
1619
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
|
1220
|
-
|
|
1620
|
+
rb_undef_alloc_func(rb_cPGresult);
|
|
1221
1621
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
|
1222
1622
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
|
1223
1623
|
|
|
@@ -1226,6 +1626,10 @@ init_pg_result()
|
|
|
1226
1626
|
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
|
1227
1627
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
|
1228
1628
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
|
1629
|
+
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
|
1630
|
+
rb_define_method(rb_cPGresult, "verbose_error_message", pgresult_verbose_error_message, 2);
|
|
1631
|
+
rb_define_alias( rb_cPGresult, "result_verbose_error_message", "verbose_error_message");
|
|
1632
|
+
#endif
|
|
1229
1633
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
|
1230
1634
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
|
1231
1635
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
|
@@ -1261,17 +1665,19 @@ init_pg_result()
|
|
|
1261
1665
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
|
1262
1666
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
|
1263
1667
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
|
1668
|
+
rb_define_method(rb_cPGresult, "tuple_values", pgresult_tuple_values, 1);
|
|
1669
|
+
rb_define_method(rb_cPGresult, "tuple", pgresult_tuple, 1);
|
|
1264
1670
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
|
1265
1671
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
|
1266
1672
|
|
|
1267
1673
|
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
|
1268
1674
|
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
|
1269
1675
|
|
|
1270
|
-
#ifdef HAVE_PQSETSINGLEROWMODE
|
|
1271
1676
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
|
1272
1677
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
|
1273
1678
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
|
1274
|
-
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1679
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
|
1277
1680
|
|
|
1681
|
+
rb_define_method(rb_cPGresult, "field_name_type=", pgresult_field_name_type_set, 1 );
|
|
1682
|
+
rb_define_method(rb_cPGresult, "field_name_type", pgresult_field_name_type_get, 0 );
|
|
1683
|
+
}
|