pg 0.18.2 → 1.5.3
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 +42 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +117 -0
- data/.github/workflows/source-gem.yml +137 -0
- data/.gitignore +22 -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/BSDL +2 -2
- data/Gemfile +14 -0
- data/History.md +876 -0
- data/Manifest.txt +8 -21
- data/README-Windows.rdoc +17 -28
- data/README.ja.md +276 -0
- data/README.md +286 -0
- data/Rakefile +40 -131
- data/Rakefile.cross +88 -70
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/certs/larskanis-2023.pem +24 -0
- data/ext/errorcodes.def +113 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +36 -2
- data/ext/extconf.rb +120 -54
- data/ext/gvl_wrappers.c +8 -0
- data/ext/gvl_wrappers.h +44 -33
- data/ext/pg.c +226 -200
- data/ext/pg.h +99 -99
- data/ext/pg_binary_decoder.c +164 -16
- data/ext/pg_binary_encoder.c +249 -22
- data/ext/pg_coder.c +189 -44
- data/ext/pg_connection.c +1866 -1173
- data/ext/pg_copy_coder.c +398 -42
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +522 -0
- data/ext/pg_result.c +727 -232
- data/ext/pg_text_decoder.c +629 -43
- data/ext/pg_text_encoder.c +269 -102
- data/ext/pg_tuple.c +572 -0
- data/ext/pg_type_map.c +64 -23
- 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 +86 -43
- 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 +198 -0
- data/lib/pg/basic_type_map_for_results.rb +104 -0
- data/lib/pg/basic_type_registry.rb +299 -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 +797 -77
- data/lib/pg/exceptions.rb +16 -2
- data/lib/pg/result.rb +24 -7
- data/lib/pg/text_decoder/date.rb +18 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +14 -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 +12 -0
- data/lib/pg/text_encoder/inet.rb +28 -0
- data/lib/pg/text_encoder/json.rb +14 -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 -41
- 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 +34 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +1 -1
- data/sample/async_api.rb +4 -8
- data/sample/async_copyto.rb +1 -1
- data/sample/async_mixed.rb +1 -1
- data/sample/check_conn.rb +1 -1
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +1 -1
- data/sample/copyto.rb +1 -1
- data/sample/cursor.rb +1 -1
- data/sample/disk_usage_report.rb +6 -15
- data/sample/issue-119.rb +2 -2
- data/sample/losample.rb +1 -1
- data/sample/minimal-testcase.rb +2 -2
- data/sample/notify_wait.rb +1 -1
- data/sample/pg_statistics.rb +6 -15
- data/sample/replication_monitor.rb +9 -18
- data/sample/test_binary_values.rb +1 -1
- data/sample/wal_shipper.rb +2 -2
- data/sample/warehouse_partitions.rb +8 -17
- data/translation/.po4a-version +7 -0
- data/translation/po/all.pot +910 -0
- data/translation/po/ja.po +1047 -0
- data/translation/po4a.cfg +12 -0
- data.tar.gz.sig +0 -0
- metadata +137 -204
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -5545
- data/History.rdoc +0 -313
- data/README.ja.rdoc +0 -14
- data/README.rdoc +0 -161
- data/lib/pg/basic_type_mapping.rb +0 -399
- data/lib/pg/constants.rb +0 -11
- data/lib/pg/text_decoder.rb +0 -42
- data/lib/pg/text_encoder.rb +0 -27
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -355
- data/spec/pg/basic_type_mapping_spec.rb +0 -251
- data/spec/pg/connection_spec.rb +0 -1535
- data/spec/pg/result_spec.rb +0 -449
- 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 -688
- 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 */
|
|
18
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 )
|
|
@@ -92,18 +317,20 @@ pg_result_check( VALUE self )
|
|
|
92
317
|
case PGRES_TUPLES_OK:
|
|
93
318
|
case PGRES_COPY_OUT:
|
|
94
319
|
case PGRES_COPY_IN:
|
|
95
|
-
#ifdef HAVE_CONST_PGRES_COPY_BOTH
|
|
96
320
|
case PGRES_COPY_BOTH:
|
|
97
|
-
#endif
|
|
98
|
-
#ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
|
|
99
321
|
case PGRES_SINGLE_TUPLE:
|
|
100
|
-
#endif
|
|
101
322
|
case PGRES_EMPTY_QUERY:
|
|
102
323
|
case PGRES_COMMAND_OK:
|
|
324
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
325
|
+
case PGRES_PIPELINE_SYNC:
|
|
326
|
+
#endif
|
|
103
327
|
return self;
|
|
104
328
|
case PGRES_BAD_RESPONSE:
|
|
105
329
|
case PGRES_FATAL_ERROR:
|
|
106
330
|
case PGRES_NONFATAL_ERROR:
|
|
331
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
332
|
+
case PGRES_PIPELINE_ABORTED:
|
|
333
|
+
#endif
|
|
107
334
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
|
108
335
|
break;
|
|
109
336
|
default:
|
|
@@ -111,7 +338,7 @@ pg_result_check( VALUE self )
|
|
|
111
338
|
}
|
|
112
339
|
}
|
|
113
340
|
|
|
114
|
-
PG_ENCODING_SET_NOCHECK( error,
|
|
341
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
|
115
342
|
|
|
116
343
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
|
117
344
|
klass = lookup_error_class( sqlstate );
|
|
@@ -135,29 +362,52 @@ pg_result_check( VALUE self )
|
|
|
135
362
|
* call-seq:
|
|
136
363
|
* res.clear() -> nil
|
|
137
364
|
*
|
|
138
|
-
* 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.
|
|
139
372
|
*
|
|
140
|
-
* If PG::Result#autoclear? is true then the result is marked as cleared
|
|
141
|
-
* 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.
|
|
142
374
|
*
|
|
143
375
|
*/
|
|
144
376
|
VALUE
|
|
145
377
|
pg_result_clear(VALUE self)
|
|
146
378
|
{
|
|
147
379
|
t_pg_result *this = pgresult_get_this(self);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this->pgresult = NULL;
|
|
380
|
+
rb_check_frozen(self);
|
|
381
|
+
pgresult_clear( this );
|
|
151
382
|
return Qnil;
|
|
152
383
|
}
|
|
153
384
|
|
|
154
385
|
/*
|
|
155
386
|
* call-seq:
|
|
156
|
-
* res.
|
|
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.
|
|
157
393
|
*
|
|
158
|
-
* Returns +true+ if the backend result memory has been free'd.
|
|
159
394
|
*/
|
|
160
395
|
VALUE
|
|
396
|
+
static 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
|
+
|
|
404
|
+
/*
|
|
405
|
+
* call-seq:
|
|
406
|
+
* res.cleared? -> boolean
|
|
407
|
+
*
|
|
408
|
+
* Returns +true+ if the backend result memory has been freed.
|
|
409
|
+
*/
|
|
410
|
+
static VALUE
|
|
161
411
|
pgresult_cleared_p( VALUE self )
|
|
162
412
|
{
|
|
163
413
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -168,11 +418,13 @@ pgresult_cleared_p( VALUE self )
|
|
|
168
418
|
* call-seq:
|
|
169
419
|
* res.autoclear? -> boolean
|
|
170
420
|
*
|
|
171
|
-
* Returns +true+ if the underlying C struct will be cleared
|
|
172
|
-
*
|
|
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 .
|
|
173
425
|
*
|
|
174
426
|
*/
|
|
175
|
-
VALUE
|
|
427
|
+
static VALUE
|
|
176
428
|
pgresult_autoclear_p( VALUE self )
|
|
177
429
|
{
|
|
178
430
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -183,37 +435,6 @@ pgresult_autoclear_p( VALUE self )
|
|
|
183
435
|
* DATA pointer functions
|
|
184
436
|
*/
|
|
185
437
|
|
|
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
438
|
/*
|
|
218
439
|
* Fetch the PG::Result object data pointer and check it's
|
|
219
440
|
* PGresult data pointer for sanity.
|
|
@@ -243,18 +464,30 @@ pgresult_get(VALUE self)
|
|
|
243
464
|
return this->pgresult;
|
|
244
465
|
}
|
|
245
466
|
|
|
246
|
-
|
|
247
|
-
* Document-method: allocate
|
|
248
|
-
*
|
|
249
|
-
* call-seq:
|
|
250
|
-
* PG::Result.allocate -> result
|
|
251
|
-
*/
|
|
252
|
-
static VALUE
|
|
253
|
-
pgresult_s_allocate( VALUE klass )
|
|
467
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
254
468
|
{
|
|
255
|
-
VALUE
|
|
256
|
-
|
|
257
|
-
|
|
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;
|
|
258
491
|
}
|
|
259
492
|
|
|
260
493
|
static void pgresult_init_fnames(VALUE self)
|
|
@@ -266,12 +499,10 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
266
499
|
int nfields = PQnfields(this->pgresult);
|
|
267
500
|
|
|
268
501
|
for( i=0; i<nfields; i++ ){
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
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);
|
|
272
505
|
this->nfields = i + 1;
|
|
273
|
-
|
|
274
|
-
RB_GC_GUARD(fname);
|
|
275
506
|
}
|
|
276
507
|
this->nfields = nfields;
|
|
277
508
|
}
|
|
@@ -283,12 +514,15 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
283
514
|
*
|
|
284
515
|
* The class to represent the query result tuples (rows).
|
|
285
516
|
* An instance of this class is created as the result of every query.
|
|
286
|
-
*
|
|
287
|
-
*
|
|
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.
|
|
288
522
|
*
|
|
289
523
|
* Example:
|
|
290
524
|
* require 'pg'
|
|
291
|
-
* conn =
|
|
525
|
+
* conn = PG.connect(:dbname => 'test')
|
|
292
526
|
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
293
527
|
* res.getvalue(0,0) # '1'
|
|
294
528
|
* res[0]['b'] # '2'
|
|
@@ -302,7 +536,7 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
302
536
|
|
|
303
537
|
/*
|
|
304
538
|
* call-seq:
|
|
305
|
-
* res.result_status() ->
|
|
539
|
+
* res.result_status() -> Integer
|
|
306
540
|
*
|
|
307
541
|
* Returns the status of the query. The status value is one of:
|
|
308
542
|
* * +PGRES_EMPTY_QUERY+
|
|
@@ -314,6 +548,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
314
548
|
* * +PGRES_NONFATAL_ERROR+
|
|
315
549
|
* * +PGRES_FATAL_ERROR+
|
|
316
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.
|
|
317
556
|
*/
|
|
318
557
|
static VALUE
|
|
319
558
|
pgresult_result_status(VALUE self)
|
|
@@ -323,16 +562,39 @@ pgresult_result_status(VALUE self)
|
|
|
323
562
|
|
|
324
563
|
/*
|
|
325
564
|
* call-seq:
|
|
326
|
-
*
|
|
565
|
+
* PG::Result.res_status( status ) -> String
|
|
327
566
|
*
|
|
328
|
-
* Returns the string representation of
|
|
567
|
+
* Returns the string representation of +status+.
|
|
329
568
|
*
|
|
330
569
|
*/
|
|
331
570
|
static VALUE
|
|
332
|
-
|
|
571
|
+
pgresult_s_res_status(VALUE self, VALUE status)
|
|
572
|
+
{
|
|
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)
|
|
333
586
|
{
|
|
334
|
-
|
|
335
|
-
|
|
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);
|
|
336
598
|
return ret;
|
|
337
599
|
}
|
|
338
600
|
|
|
@@ -345,11 +607,40 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
|
345
607
|
static VALUE
|
|
346
608
|
pgresult_error_message(VALUE self)
|
|
347
609
|
{
|
|
348
|
-
|
|
349
|
-
|
|
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);
|
|
350
613
|
return ret;
|
|
351
614
|
}
|
|
352
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
|
+
|
|
640
|
+
return ret;
|
|
641
|
+
}
|
|
642
|
+
#endif
|
|
643
|
+
|
|
353
644
|
/*
|
|
354
645
|
* call-seq:
|
|
355
646
|
* res.error_field(fieldcode) -> String
|
|
@@ -399,14 +690,14 @@ pgresult_error_message(VALUE self)
|
|
|
399
690
|
static VALUE
|
|
400
691
|
pgresult_error_field(VALUE self, VALUE field)
|
|
401
692
|
{
|
|
402
|
-
|
|
693
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
403
694
|
int fieldcode = NUM2INT( field );
|
|
404
|
-
char * fieldstr = PQresultErrorField(
|
|
695
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
|
405
696
|
VALUE ret = Qnil;
|
|
406
697
|
|
|
407
698
|
if ( fieldstr ) {
|
|
408
|
-
ret =
|
|
409
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
|
699
|
+
ret = rb_str_new2( fieldstr );
|
|
700
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
|
410
701
|
}
|
|
411
702
|
|
|
412
703
|
return ret;
|
|
@@ -414,7 +705,7 @@ pgresult_error_field(VALUE self, VALUE field)
|
|
|
414
705
|
|
|
415
706
|
/*
|
|
416
707
|
* call-seq:
|
|
417
|
-
* res.ntuples() ->
|
|
708
|
+
* res.ntuples() -> Integer
|
|
418
709
|
*
|
|
419
710
|
* Returns the number of tuples in the query result.
|
|
420
711
|
*/
|
|
@@ -427,7 +718,7 @@ pgresult_ntuples(VALUE self)
|
|
|
427
718
|
static VALUE
|
|
428
719
|
pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
|
429
720
|
{
|
|
430
|
-
|
|
721
|
+
return pgresult_ntuples(self);
|
|
431
722
|
}
|
|
432
723
|
|
|
433
724
|
/*
|
|
@@ -444,29 +735,45 @@ pgresult_nfields(VALUE self)
|
|
|
444
735
|
|
|
445
736
|
/*
|
|
446
737
|
* call-seq:
|
|
447
|
-
* 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
|
|
448
754
|
*
|
|
449
755
|
* Returns the name of the column corresponding to _index_.
|
|
756
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
|
757
|
+
*
|
|
450
758
|
*/
|
|
451
759
|
static VALUE
|
|
452
760
|
pgresult_fname(VALUE self, VALUE index)
|
|
453
761
|
{
|
|
454
|
-
|
|
455
|
-
PGresult *result = pgresult_get(self);
|
|
762
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
456
763
|
int i = NUM2INT(index);
|
|
764
|
+
char *cfname;
|
|
457
765
|
|
|
458
|
-
if (i < 0 || i >= PQnfields(
|
|
766
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
|
459
767
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
|
460
768
|
}
|
|
461
769
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return rb_obj_freeze(fname);
|
|
770
|
+
cfname = PQfname(this->pgresult, i);
|
|
771
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
465
772
|
}
|
|
466
773
|
|
|
467
774
|
/*
|
|
468
775
|
* call-seq:
|
|
469
|
-
* res.fnumber( name ) ->
|
|
776
|
+
* res.fnumber( name ) -> Integer
|
|
470
777
|
*
|
|
471
778
|
* Returns the index of the field specified by the string +name+.
|
|
472
779
|
* The given +name+ is treated like an identifier in an SQL command, that is,
|
|
@@ -527,7 +834,7 @@ pgresult_ftable(VALUE self, VALUE column_number)
|
|
|
527
834
|
|
|
528
835
|
/*
|
|
529
836
|
* call-seq:
|
|
530
|
-
* res.ftablecol( column_number ) ->
|
|
837
|
+
* res.ftablecol( column_number ) -> Integer
|
|
531
838
|
*
|
|
532
839
|
* Returns the column number (within its table) of the table from
|
|
533
840
|
* which the column _column_number_ is made up.
|
|
@@ -552,7 +859,7 @@ pgresult_ftablecol(VALUE self, VALUE column_number)
|
|
|
552
859
|
|
|
553
860
|
/*
|
|
554
861
|
* call-seq:
|
|
555
|
-
* res.fformat( column_number ) ->
|
|
862
|
+
* res.fformat( column_number ) -> Integer
|
|
556
863
|
*
|
|
557
864
|
* Returns the format (0 for text, 1 for binary) of column
|
|
558
865
|
* _column_number_.
|
|
@@ -696,7 +1003,7 @@ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
|
|
|
696
1003
|
|
|
697
1004
|
/*
|
|
698
1005
|
* call-seq:
|
|
699
|
-
* res.getlength( tup_num, field_num ) ->
|
|
1006
|
+
* res.getlength( tup_num, field_num ) -> Integer
|
|
700
1007
|
*
|
|
701
1008
|
* Returns the (String) length of the field in bytes.
|
|
702
1009
|
*
|
|
@@ -721,7 +1028,7 @@ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
|
|
|
721
1028
|
|
|
722
1029
|
/*
|
|
723
1030
|
* call-seq:
|
|
724
|
-
* res.nparams() ->
|
|
1031
|
+
* res.nparams() -> Integer
|
|
725
1032
|
*
|
|
726
1033
|
* Returns the number of parameters of a prepared statement.
|
|
727
1034
|
* Only useful for the result returned by conn.describePrepared
|
|
@@ -760,8 +1067,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
|
760
1067
|
static VALUE
|
|
761
1068
|
pgresult_cmd_status(VALUE self)
|
|
762
1069
|
{
|
|
763
|
-
|
|
764
|
-
|
|
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);
|
|
765
1073
|
return ret;
|
|
766
1074
|
}
|
|
767
1075
|
|
|
@@ -772,11 +1080,17 @@ pgresult_cmd_status(VALUE self)
|
|
|
772
1080
|
* Returns the number of tuples (rows) affected by the SQL command.
|
|
773
1081
|
*
|
|
774
1082
|
* If the SQL command that generated the PG::Result was not one of:
|
|
775
|
-
*
|
|
776
|
-
* *
|
|
777
|
-
* *
|
|
778
|
-
* *
|
|
779
|
-
* *
|
|
1083
|
+
*
|
|
1084
|
+
* * <tt>SELECT</tt>
|
|
1085
|
+
* * <tt>CREATE TABLE AS</tt>
|
|
1086
|
+
* * <tt>INSERT</tt>
|
|
1087
|
+
* * <tt>UPDATE</tt>
|
|
1088
|
+
* * <tt>DELETE</tt>
|
|
1089
|
+
* * <tt>MOVE</tt>
|
|
1090
|
+
* * <tt>FETCH</tt>
|
|
1091
|
+
* * <tt>COPY</tt>
|
|
1092
|
+
* * an +EXECUTE+ of a prepared query that contains an +INSERT+, +UPDATE+, or +DELETE+ statement
|
|
1093
|
+
*
|
|
780
1094
|
* or if no tuples were affected, <tt>0</tt> is returned.
|
|
781
1095
|
*/
|
|
782
1096
|
static VALUE
|
|
@@ -784,7 +1098,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
|
784
1098
|
{
|
|
785
1099
|
long n;
|
|
786
1100
|
n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
|
|
787
|
-
return
|
|
1101
|
+
return LONG2NUM(n);
|
|
788
1102
|
}
|
|
789
1103
|
|
|
790
1104
|
/*
|
|
@@ -836,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
836
1150
|
}
|
|
837
1151
|
/* Store a copy of the filled hash for use at the next row. */
|
|
838
1152
|
if( num_tuples > 10 )
|
|
839
|
-
this->tuple_hash
|
|
1153
|
+
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
|
840
1154
|
|
|
841
1155
|
return tuple;
|
|
842
1156
|
}
|
|
@@ -955,8 +1269,12 @@ static VALUE
|
|
|
955
1269
|
pgresult_field_values( VALUE self, VALUE field )
|
|
956
1270
|
{
|
|
957
1271
|
PGresult *result = pgresult_get( self );
|
|
958
|
-
const char *fieldname
|
|
959
|
-
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 );
|
|
960
1278
|
|
|
961
1279
|
if ( fnum < 0 )
|
|
962
1280
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
|
@@ -965,6 +1283,85 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
|
965
1283
|
}
|
|
966
1284
|
|
|
967
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
|
+
|
|
968
1365
|
/*
|
|
969
1366
|
* call-seq:
|
|
970
1367
|
* res.each{ |tuple| ... }
|
|
@@ -991,7 +1388,7 @@ pgresult_each(VALUE self)
|
|
|
991
1388
|
* call-seq:
|
|
992
1389
|
* res.fields() -> Array
|
|
993
1390
|
*
|
|
994
|
-
*
|
|
1391
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
995
1392
|
*/
|
|
996
1393
|
static VALUE
|
|
997
1394
|
pgresult_fields(VALUE self)
|
|
@@ -1023,14 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1023
1420
|
t_pg_result *this = pgresult_get_this(self);
|
|
1024
1421
|
t_typemap *p_typemap;
|
|
1025
1422
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
}
|
|
1030
|
-
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);
|
|
1031
1426
|
|
|
1032
|
-
|
|
1033
|
-
|
|
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 );
|
|
1034
1430
|
|
|
1035
1431
|
return typemap;
|
|
1036
1432
|
}
|
|
@@ -1050,46 +1446,71 @@ pgresult_type_map_get(VALUE self)
|
|
|
1050
1446
|
return this->typemap;
|
|
1051
1447
|
}
|
|
1052
1448
|
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
-
|
|
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)
|
|
1087
1507
|
{
|
|
1088
1508
|
t_pg_result *this;
|
|
1089
|
-
int nfields;
|
|
1509
|
+
int nfields, nfields2;
|
|
1090
1510
|
PGconn *pgconn;
|
|
1091
1511
|
PGresult *pgresult;
|
|
1092
1512
|
|
|
1513
|
+
rb_check_frozen(self);
|
|
1093
1514
|
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1094
1515
|
|
|
1095
1516
|
this = pgresult_get_this_safe(self);
|
|
@@ -1098,11 +1519,11 @@ pgresult_stream_each(VALUE self)
|
|
|
1098
1519
|
nfields = PQnfields(pgresult);
|
|
1099
1520
|
|
|
1100
1521
|
for(;;){
|
|
1101
|
-
int tuple_num;
|
|
1102
1522
|
int ntuples = PQntuples(pgresult);
|
|
1103
1523
|
|
|
1104
1524
|
switch( PQresultStatus(pgresult) ){
|
|
1105
1525
|
case PGRES_TUPLES_OK:
|
|
1526
|
+
case PGRES_COMMAND_OK:
|
|
1106
1527
|
if( ntuples == 0 )
|
|
1107
1528
|
return self;
|
|
1108
1529
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
@@ -1112,21 +1533,24 @@ pgresult_stream_each(VALUE self)
|
|
|
1112
1533
|
pg_result_check( self );
|
|
1113
1534
|
}
|
|
1114
1535
|
|
|
1115
|
-
|
|
1116
|
-
|
|
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);
|
|
1117
1540
|
}
|
|
1118
1541
|
|
|
1119
|
-
if(
|
|
1120
|
-
|
|
1121
|
-
|
|
1542
|
+
if( yielder( self, ntuples, nfields, data ) ){
|
|
1543
|
+
pgresult_clear( this );
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
if( gvl_PQisBusy(pgconn) ){
|
|
1547
|
+
/* wait for input (without blocking) before reading each result */
|
|
1548
|
+
pgconn_block( 0, NULL, this->connection );
|
|
1122
1549
|
}
|
|
1123
1550
|
|
|
1124
1551
|
pgresult = gvl_PQgetResult(pgconn);
|
|
1125
1552
|
if( pgresult == NULL )
|
|
1126
|
-
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another
|
|
1127
|
-
|
|
1128
|
-
if( nfields != PQnfields(pgresult) )
|
|
1129
|
-
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");
|
|
1130
1554
|
|
|
1131
1555
|
this->pgresult = pgresult;
|
|
1132
1556
|
}
|
|
@@ -1135,6 +1559,44 @@ pgresult_stream_each(VALUE self)
|
|
|
1135
1559
|
return self;
|
|
1136
1560
|
}
|
|
1137
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
|
+
|
|
1138
1600
|
/*
|
|
1139
1601
|
* call-seq:
|
|
1140
1602
|
* res.stream_each_row { |row| ... }
|
|
@@ -1148,87 +1610,118 @@ pgresult_stream_each(VALUE self)
|
|
|
1148
1610
|
static VALUE
|
|
1149
1611
|
pgresult_stream_each_row(VALUE self)
|
|
1150
1612
|
{
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
int nfields;
|
|
1154
|
-
PGconn *pgconn;
|
|
1155
|
-
PGresult *pgresult;
|
|
1156
|
-
|
|
1157
|
-
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1158
|
-
|
|
1159
|
-
this = pgresult_get_this_safe(self);
|
|
1160
|
-
pgconn = pg_get_pgconn(this->connection);
|
|
1161
|
-
pgresult = this->pgresult;
|
|
1162
|
-
nfields = PQnfields(pgresult);
|
|
1163
|
-
|
|
1164
|
-
for(;;){
|
|
1165
|
-
int ntuples = PQntuples(pgresult);
|
|
1613
|
+
return pgresult_stream_any(self, yield_array, NULL);
|
|
1614
|
+
}
|
|
1166
1615
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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);
|
|
1177
1629
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
int field;
|
|
1630
|
+
return pgresult_stream_any(self, yield_tuple, NULL);
|
|
1631
|
+
}
|
|
1181
1632
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
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);
|
|
1188
1658
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
this->pgresult = NULL;
|
|
1192
|
-
}
|
|
1659
|
+
rb_check_frozen(self);
|
|
1660
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
|
1193
1661
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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);
|
|
1197
1667
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1668
|
+
return sym;
|
|
1669
|
+
}
|
|
1200
1670
|
|
|
1201
|
-
|
|
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;
|
|
1202
1689
|
}
|
|
1203
|
-
|
|
1204
|
-
/* never reached */
|
|
1205
|
-
return self;
|
|
1206
1690
|
}
|
|
1207
|
-
#endif
|
|
1208
|
-
|
|
1209
1691
|
|
|
1210
1692
|
void
|
|
1211
|
-
init_pg_result()
|
|
1693
|
+
init_pg_result(void)
|
|
1212
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
|
+
|
|
1213
1699
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
|
1214
|
-
|
|
1700
|
+
rb_undef_alloc_func(rb_cPGresult);
|
|
1215
1701
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
|
1216
1702
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
|
1217
1703
|
|
|
1218
1704
|
/****** PG::Result INSTANCE METHODS: libpq ******/
|
|
1219
1705
|
rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
|
|
1220
|
-
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);
|
|
1221
1708
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
|
1222
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
|
|
1223
1714
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
|
1224
1715
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
|
1225
1716
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
|
1717
|
+
rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
|
|
1226
1718
|
rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
|
|
1227
1719
|
rb_define_alias (rb_cPGresult, "check_result", "check");
|
|
1228
1720
|
rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
|
|
1229
1721
|
rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
|
|
1230
1722
|
rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
|
|
1231
1723
|
rb_define_alias(rb_cPGresult, "num_fields", "nfields");
|
|
1724
|
+
rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
|
|
1232
1725
|
rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
|
|
1233
1726
|
rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
|
|
1234
1727
|
rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
|
|
@@ -1255,17 +1748,19 @@ init_pg_result()
|
|
|
1255
1748
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
|
1256
1749
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
|
1257
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);
|
|
1258
1753
|
rb_define_method(rb_cPGresult, "cleared?", pgresult_cleared_p, 0);
|
|
1259
1754
|
rb_define_method(rb_cPGresult, "autoclear?", pgresult_autoclear_p, 0);
|
|
1260
1755
|
|
|
1261
1756
|
rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
|
|
1262
1757
|
rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
|
|
1263
1758
|
|
|
1264
|
-
#ifdef HAVE_PQSETSINGLEROWMODE
|
|
1265
1759
|
/****** PG::Result INSTANCE METHODS: streaming ******/
|
|
1266
1760
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
|
1267
1761
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
|
1268
|
-
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1762
|
+
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
|
1271
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
|
+
}
|