pg 1.1.3 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.appveyor.yml +42 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +117 -0
- data/.github/workflows/source-gem.yml +141 -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/Gemfile +14 -0
- data/History.md +884 -0
- data/Manifest.txt +3 -3
- data/README-Windows.rdoc +4 -4
- data/README.ja.md +300 -0
- data/README.md +286 -0
- data/Rakefile +37 -137
- data/Rakefile.cross +62 -62
- data/certs/ged.pem +24 -0
- data/certs/larskanis-2022.pem +26 -0
- data/certs/larskanis-2023.pem +24 -0
- data/ext/errorcodes.def +80 -0
- data/ext/errorcodes.rb +0 -0
- data/ext/errorcodes.txt +22 -2
- data/ext/extconf.rb +105 -26
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +204 -152
- data/ext/pg.h +52 -21
- data/ext/pg_binary_decoder.c +100 -17
- data/ext/pg_binary_encoder.c +238 -13
- data/ext/pg_coder.c +109 -34
- data/ext/pg_connection.c +1383 -983
- data/ext/pg_copy_coder.c +356 -35
- data/ext/pg_errors.c +1 -1
- data/ext/pg_record_coder.c +522 -0
- data/ext/pg_result.c +439 -172
- data/ext/pg_text_decoder.c +42 -18
- data/ext/pg_text_encoder.c +201 -56
- data/ext/pg_tuple.c +97 -66
- data/ext/pg_type_map.c +45 -11
- data/ext/pg_type_map_all_strings.c +21 -7
- data/ext/pg_type_map_by_class.c +59 -27
- data/ext/pg_type_map_by_column.c +80 -37
- data/ext/pg_type_map_by_mri_type.c +49 -20
- data/ext/pg_type_map_by_oid.c +62 -29
- data/ext/pg_type_map_in_ruby.c +56 -22
- data/ext/{util.c → pg_util.c} +7 -7
- 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 +35 -12
- data/lib/pg/connection.rb +744 -84
- data/lib/pg/exceptions.rb +15 -1
- data/lib/pg/result.rb +13 -1
- 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/type_map_by_column.rb +2 -1
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +94 -39
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +34 -0
- data/rakelib/task_extension.rb +46 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +102 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data/translation/.po4a-version +7 -0
- data/translation/po/all.pot +936 -0
- data/translation/po/ja.po +1036 -0
- data/translation/po4a.cfg +12 -0
- data.tar.gz.sig +0 -0
- metadata +144 -222
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +0 -485
- data/README.ja.rdoc +0 -14
- data/README.rdoc +0 -178
- data/lib/pg/basic_type_mapping.rb +0 -459
- data/lib/pg/binary_decoder.rb +0 -22
- data/lib/pg/constants.rb +0 -11
- data/lib/pg/text_decoder.rb +0 -47
- data/lib/pg/text_encoder.rb +0 -69
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -381
- data/spec/pg/basic_type_mapping_spec.rb +0 -508
- data/spec/pg/connection_spec.rb +0 -1849
- data/spec/pg/connection_sync_spec.rb +0 -41
- data/spec/pg/result_spec.rb +0 -491
- data/spec/pg/tuple_spec.rb +0 -280
- 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 -949
- data/spec/pg_spec.rb +0 -50
- /data/ext/{util.h → pg_util.h} +0 -0
data/ext/pg_result.c
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
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
|
|
18
25
|
|
|
19
26
|
#define PGRESULT_DATA_BLOCKSIZE 2048
|
|
20
27
|
typedef struct pgresAttValue
|
|
@@ -44,7 +51,7 @@ count_leading_zero_bits(unsigned int x)
|
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
static ssize_t
|
|
47
|
-
pgresult_approx_size(PGresult *result)
|
|
54
|
+
pgresult_approx_size(const PGresult *result)
|
|
48
55
|
{
|
|
49
56
|
int num_fields = PQnfields(result);
|
|
50
57
|
ssize_t size = 0;
|
|
@@ -94,25 +101,97 @@ pgresult_approx_size(PGresult *result)
|
|
|
94
101
|
|
|
95
102
|
return size;
|
|
96
103
|
}
|
|
104
|
+
#endif
|
|
97
105
|
|
|
106
|
+
/*
|
|
107
|
+
* GC Mark function
|
|
108
|
+
*/
|
|
98
109
|
static void
|
|
99
|
-
|
|
110
|
+
pgresult_gc_mark( void *_this )
|
|
100
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;
|
|
101
148
|
if( this->pgresult && !this->autoclear ){
|
|
102
149
|
PQclear(this->pgresult);
|
|
103
150
|
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
104
151
|
rb_gc_adjust_memory_usage(-this->result_size);
|
|
105
152
|
#endif
|
|
106
153
|
}
|
|
154
|
+
this->result_size = 0;
|
|
155
|
+
this->nfields = -1;
|
|
107
156
|
this->pgresult = NULL;
|
|
108
157
|
}
|
|
109
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
|
+
|
|
110
167
|
static size_t
|
|
111
|
-
pgresult_memsize(
|
|
168
|
+
pgresult_memsize( const void *_this )
|
|
112
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
|
+
*/
|
|
113
174
|
return this->result_size;
|
|
114
175
|
}
|
|
115
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
|
+
}
|
|
194
|
+
|
|
116
195
|
/*
|
|
117
196
|
* Global functions
|
|
118
197
|
*/
|
|
@@ -124,31 +203,35 @@ static VALUE
|
|
|
124
203
|
pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
125
204
|
{
|
|
126
205
|
int nfields = result ? PQnfields(result) : 0;
|
|
127
|
-
VALUE self
|
|
206
|
+
VALUE self;
|
|
128
207
|
t_pg_result *this;
|
|
129
208
|
|
|
130
209
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
|
131
|
-
RTYPEDDATA_DATA(self) = this;
|
|
132
|
-
|
|
133
210
|
this->pgresult = result;
|
|
211
|
+
/* Initialize connection and typemap prior to any object allocations,
|
|
212
|
+
* to make sure valid objects are marked. */
|
|
134
213
|
this->connection = rb_pgconn;
|
|
135
214
|
this->typemap = pg_typemap_all_strings;
|
|
136
|
-
this->p_typemap =
|
|
215
|
+
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
137
216
|
this->nfields = -1;
|
|
138
217
|
this->tuple_hash = Qnil;
|
|
139
218
|
this->field_map = Qnil;
|
|
140
|
-
|
|
141
|
-
|
|
219
|
+
this->flags = 0;
|
|
220
|
+
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
|
142
221
|
|
|
143
222
|
if( result ){
|
|
144
223
|
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
|
145
224
|
VALUE typemap = p_conn->type_map_for_results;
|
|
146
|
-
|
|
147
225
|
/* Type check is done when assigned to PG::Connection. */
|
|
148
|
-
t_typemap *p_typemap =
|
|
149
|
-
|
|
150
|
-
this->
|
|
151
|
-
|
|
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();
|
|
152
235
|
}
|
|
153
236
|
|
|
154
237
|
return self;
|
|
@@ -159,22 +242,38 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
|
159
242
|
{
|
|
160
243
|
VALUE self = pg_new_result2(result, rb_pgconn);
|
|
161
244
|
t_pg_result *this = pgresult_get_this(self);
|
|
162
|
-
t_pg_connection *p_conn = pg_get_connection(rb_pgconn);
|
|
163
245
|
|
|
164
246
|
this->autoclear = 0;
|
|
165
247
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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);
|
|
169
255
|
|
|
170
256
|
#ifdef HAVE_RB_GC_ADJUST_MEMORY_USAGE
|
|
171
|
-
|
|
257
|
+
rb_gc_adjust_memory_usage(this->result_size);
|
|
172
258
|
#endif
|
|
173
|
-
}
|
|
174
259
|
|
|
175
260
|
return self;
|
|
176
261
|
}
|
|
177
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
|
+
|
|
178
277
|
VALUE
|
|
179
278
|
pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
180
279
|
{
|
|
@@ -193,7 +292,11 @@ pg_new_result_autoclear(PGresult *result, VALUE rb_pgconn)
|
|
|
193
292
|
* call-seq:
|
|
194
293
|
* res.check -> nil
|
|
195
294
|
*
|
|
196
|
-
* 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+
|
|
197
300
|
*/
|
|
198
301
|
VALUE
|
|
199
302
|
pg_result_check( VALUE self )
|
|
@@ -218,10 +321,16 @@ pg_result_check( VALUE self )
|
|
|
218
321
|
case PGRES_SINGLE_TUPLE:
|
|
219
322
|
case PGRES_EMPTY_QUERY:
|
|
220
323
|
case PGRES_COMMAND_OK:
|
|
324
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
325
|
+
case PGRES_PIPELINE_SYNC:
|
|
326
|
+
#endif
|
|
221
327
|
return self;
|
|
222
328
|
case PGRES_BAD_RESPONSE:
|
|
223
329
|
case PGRES_FATAL_ERROR:
|
|
224
330
|
case PGRES_NONFATAL_ERROR:
|
|
331
|
+
#ifdef HAVE_PQENTERPIPELINEMODE
|
|
332
|
+
case PGRES_PIPELINE_ABORTED:
|
|
333
|
+
#endif
|
|
225
334
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
|
226
335
|
break;
|
|
227
336
|
default:
|
|
@@ -229,7 +338,7 @@ pg_result_check( VALUE self )
|
|
|
229
338
|
}
|
|
230
339
|
}
|
|
231
340
|
|
|
232
|
-
PG_ENCODING_SET_NOCHECK( error,
|
|
341
|
+
PG_ENCODING_SET_NOCHECK( error, this->enc_idx );
|
|
233
342
|
|
|
234
343
|
sqlstate = PQresultErrorField( this->pgresult, PG_DIAG_SQLSTATE );
|
|
235
344
|
klass = lookup_error_class( sqlstate );
|
|
@@ -261,25 +370,44 @@ pg_result_check( VALUE self )
|
|
|
261
370
|
* Special care must be taken when PG::Tuple objects are used.
|
|
262
371
|
* In this case #clear must not be called unless all PG::Tuple objects of this result are fully materialized.
|
|
263
372
|
*
|
|
264
|
-
* If PG::Result#autoclear? is true then the result is marked as cleared
|
|
265
|
-
* 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.
|
|
266
374
|
*
|
|
267
375
|
*/
|
|
268
376
|
VALUE
|
|
269
377
|
pg_result_clear(VALUE self)
|
|
270
378
|
{
|
|
271
379
|
t_pg_result *this = pgresult_get_this(self);
|
|
380
|
+
rb_check_frozen(self);
|
|
272
381
|
pgresult_clear( this );
|
|
273
382
|
return Qnil;
|
|
274
383
|
}
|
|
275
384
|
|
|
385
|
+
/*
|
|
386
|
+
* call-seq:
|
|
387
|
+
* res.freeze
|
|
388
|
+
*
|
|
389
|
+
* Freeze the PG::Result object and unlink the result from the related PG::Connection.
|
|
390
|
+
*
|
|
391
|
+
* A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
|
|
392
|
+
* It also denies setting a type_map or field_name_type.
|
|
393
|
+
*
|
|
394
|
+
*/
|
|
395
|
+
static VALUE
|
|
396
|
+
pg_result_freeze(VALUE self)
|
|
397
|
+
{
|
|
398
|
+
t_pg_result *this = pgresult_get_this(self);
|
|
399
|
+
|
|
400
|
+
RB_OBJ_WRITE(self, &this->connection, Qnil);
|
|
401
|
+
return rb_call_super(0, NULL);
|
|
402
|
+
}
|
|
403
|
+
|
|
276
404
|
/*
|
|
277
405
|
* call-seq:
|
|
278
406
|
* res.cleared? -> boolean
|
|
279
407
|
*
|
|
280
|
-
* Returns +true+ if the backend result memory has been
|
|
408
|
+
* Returns +true+ if the backend result memory has been freed.
|
|
281
409
|
*/
|
|
282
|
-
VALUE
|
|
410
|
+
static VALUE
|
|
283
411
|
pgresult_cleared_p( VALUE self )
|
|
284
412
|
{
|
|
285
413
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -290,11 +418,13 @@ pgresult_cleared_p( VALUE self )
|
|
|
290
418
|
* call-seq:
|
|
291
419
|
* res.autoclear? -> boolean
|
|
292
420
|
*
|
|
293
|
-
* Returns +true+ if the underlying C struct will be cleared
|
|
294
|
-
*
|
|
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 .
|
|
295
425
|
*
|
|
296
426
|
*/
|
|
297
|
-
VALUE
|
|
427
|
+
static VALUE
|
|
298
428
|
pgresult_autoclear_p( VALUE self )
|
|
299
429
|
{
|
|
300
430
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -305,37 +435,6 @@ pgresult_autoclear_p( VALUE self )
|
|
|
305
435
|
* DATA pointer functions
|
|
306
436
|
*/
|
|
307
437
|
|
|
308
|
-
/*
|
|
309
|
-
* GC Mark function
|
|
310
|
-
*/
|
|
311
|
-
static void
|
|
312
|
-
pgresult_gc_mark( t_pg_result *this )
|
|
313
|
-
{
|
|
314
|
-
int i;
|
|
315
|
-
|
|
316
|
-
if( !this ) return;
|
|
317
|
-
rb_gc_mark( this->connection );
|
|
318
|
-
rb_gc_mark( this->typemap );
|
|
319
|
-
rb_gc_mark( this->tuple_hash );
|
|
320
|
-
rb_gc_mark( this->field_map );
|
|
321
|
-
|
|
322
|
-
for( i=0; i < this->nfields; i++ ){
|
|
323
|
-
rb_gc_mark( this->fnames[i] );
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/*
|
|
328
|
-
* GC Free function
|
|
329
|
-
*/
|
|
330
|
-
static void
|
|
331
|
-
pgresult_gc_free( t_pg_result *this )
|
|
332
|
-
{
|
|
333
|
-
if( !this ) return;
|
|
334
|
-
pgresult_clear( this );
|
|
335
|
-
|
|
336
|
-
xfree(this);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
438
|
/*
|
|
340
439
|
* Fetch the PG::Result object data pointer and check it's
|
|
341
440
|
* PGresult data pointer for sanity.
|
|
@@ -365,32 +464,30 @@ pgresult_get(VALUE self)
|
|
|
365
464
|
return this->pgresult;
|
|
366
465
|
}
|
|
367
466
|
|
|
368
|
-
|
|
369
|
-
static const rb_data_type_t pgresult_type = {
|
|
370
|
-
"pg",
|
|
371
|
-
{
|
|
372
|
-
(void (*)(void*))pgresult_gc_mark,
|
|
373
|
-
(void (*)(void*))pgresult_gc_free,
|
|
374
|
-
(size_t (*)(const void *))pgresult_memsize,
|
|
375
|
-
},
|
|
376
|
-
0, 0,
|
|
377
|
-
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
|
378
|
-
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
379
|
-
#endif
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
/*
|
|
383
|
-
* Document-method: allocate
|
|
384
|
-
*
|
|
385
|
-
* call-seq:
|
|
386
|
-
* PG::Result.allocate -> result
|
|
387
|
-
*/
|
|
388
|
-
static VALUE
|
|
389
|
-
pgresult_s_allocate( VALUE klass )
|
|
467
|
+
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
390
468
|
{
|
|
391
|
-
VALUE
|
|
392
|
-
|
|
393
|
-
|
|
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;
|
|
394
491
|
}
|
|
395
492
|
|
|
396
493
|
static void pgresult_init_fnames(VALUE self)
|
|
@@ -402,12 +499,10 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
402
499
|
int nfields = PQnfields(this->pgresult);
|
|
403
500
|
|
|
404
501
|
for( i=0; i<nfields; i++ ){
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
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);
|
|
408
505
|
this->nfields = i + 1;
|
|
409
|
-
|
|
410
|
-
RB_GC_GUARD(fname);
|
|
411
506
|
}
|
|
412
507
|
this->nfields = nfields;
|
|
413
508
|
}
|
|
@@ -419,6 +514,8 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
419
514
|
*
|
|
420
515
|
* The class to represent the query result tuples (rows).
|
|
421
516
|
* An instance of this class is created as the result of every query.
|
|
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 .
|
|
422
519
|
*
|
|
423
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.
|
|
424
521
|
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
@@ -451,6 +548,11 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
451
548
|
* * +PGRES_NONFATAL_ERROR+
|
|
452
549
|
* * +PGRES_FATAL_ERROR+
|
|
453
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.
|
|
454
556
|
*/
|
|
455
557
|
static VALUE
|
|
456
558
|
pgresult_result_status(VALUE self)
|
|
@@ -460,16 +562,39 @@ pgresult_result_status(VALUE self)
|
|
|
460
562
|
|
|
461
563
|
/*
|
|
462
564
|
* call-seq:
|
|
463
|
-
*
|
|
565
|
+
* PG::Result.res_status( status ) -> String
|
|
464
566
|
*
|
|
465
|
-
* Returns the string representation of
|
|
567
|
+
* Returns the string representation of +status+.
|
|
466
568
|
*
|
|
467
569
|
*/
|
|
468
570
|
static VALUE
|
|
469
|
-
|
|
571
|
+
pgresult_s_res_status(VALUE self, VALUE status)
|
|
470
572
|
{
|
|
471
|
-
|
|
472
|
-
|
|
573
|
+
return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* call-seq:
|
|
578
|
+
* res.res_status -> String
|
|
579
|
+
* res.res_status( status ) -> String
|
|
580
|
+
*
|
|
581
|
+
* Returns the string representation of the status of the result or of the provided +status+.
|
|
582
|
+
*
|
|
583
|
+
*/
|
|
584
|
+
static VALUE
|
|
585
|
+
pgresult_res_status(int argc, VALUE *argv, VALUE self)
|
|
586
|
+
{
|
|
587
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
588
|
+
VALUE ret;
|
|
589
|
+
|
|
590
|
+
if( argc == 0 ){
|
|
591
|
+
ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
|
|
592
|
+
}else if( argc == 1 ){
|
|
593
|
+
ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
|
|
594
|
+
}else{
|
|
595
|
+
rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
|
|
596
|
+
}
|
|
597
|
+
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
|
473
598
|
return ret;
|
|
474
599
|
}
|
|
475
600
|
|
|
@@ -482,11 +607,40 @@ pgresult_res_status(VALUE self, VALUE status)
|
|
|
482
607
|
static VALUE
|
|
483
608
|
pgresult_error_message(VALUE self)
|
|
484
609
|
{
|
|
485
|
-
|
|
486
|
-
|
|
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);
|
|
487
613
|
return ret;
|
|
488
614
|
}
|
|
489
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
|
+
|
|
490
644
|
/*
|
|
491
645
|
* call-seq:
|
|
492
646
|
* res.error_field(fieldcode) -> String
|
|
@@ -536,14 +690,14 @@ pgresult_error_message(VALUE self)
|
|
|
536
690
|
static VALUE
|
|
537
691
|
pgresult_error_field(VALUE self, VALUE field)
|
|
538
692
|
{
|
|
539
|
-
|
|
693
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
540
694
|
int fieldcode = NUM2INT( field );
|
|
541
|
-
char * fieldstr = PQresultErrorField(
|
|
695
|
+
char * fieldstr = PQresultErrorField( this->pgresult, fieldcode );
|
|
542
696
|
VALUE ret = Qnil;
|
|
543
697
|
|
|
544
698
|
if ( fieldstr ) {
|
|
545
|
-
ret =
|
|
546
|
-
PG_ENCODING_SET_NOCHECK( ret,
|
|
699
|
+
ret = rb_str_new2( fieldstr );
|
|
700
|
+
PG_ENCODING_SET_NOCHECK( ret, this->enc_idx );
|
|
547
701
|
}
|
|
548
702
|
|
|
549
703
|
return ret;
|
|
@@ -581,24 +735,40 @@ pgresult_nfields(VALUE self)
|
|
|
581
735
|
|
|
582
736
|
/*
|
|
583
737
|
* call-seq:
|
|
584
|
-
* 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
|
|
585
754
|
*
|
|
586
755
|
* Returns the name of the column corresponding to _index_.
|
|
756
|
+
* Depending on #field_name_type= it's a String or Symbol.
|
|
757
|
+
*
|
|
587
758
|
*/
|
|
588
759
|
static VALUE
|
|
589
760
|
pgresult_fname(VALUE self, VALUE index)
|
|
590
761
|
{
|
|
591
|
-
|
|
592
|
-
PGresult *result = pgresult_get(self);
|
|
762
|
+
t_pg_result *this = pgresult_get_this_safe(self);
|
|
593
763
|
int i = NUM2INT(index);
|
|
764
|
+
char *cfname;
|
|
594
765
|
|
|
595
|
-
if (i < 0 || i >= PQnfields(
|
|
766
|
+
if (i < 0 || i >= PQnfields(this->pgresult)) {
|
|
596
767
|
rb_raise(rb_eArgError,"invalid field number %d", i);
|
|
597
768
|
}
|
|
598
769
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
return rb_obj_freeze(fname);
|
|
770
|
+
cfname = PQfname(this->pgresult, i);
|
|
771
|
+
return pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
|
602
772
|
}
|
|
603
773
|
|
|
604
774
|
/*
|
|
@@ -897,8 +1067,9 @@ pgresult_paramtype(VALUE self, VALUE param_number)
|
|
|
897
1067
|
static VALUE
|
|
898
1068
|
pgresult_cmd_status(VALUE self)
|
|
899
1069
|
{
|
|
900
|
-
|
|
901
|
-
|
|
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);
|
|
902
1073
|
return ret;
|
|
903
1074
|
}
|
|
904
1075
|
|
|
@@ -927,7 +1098,7 @@ pgresult_cmd_tuples(VALUE self)
|
|
|
927
1098
|
{
|
|
928
1099
|
long n;
|
|
929
1100
|
n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
|
|
930
|
-
return
|
|
1101
|
+
return LONG2NUM(n);
|
|
931
1102
|
}
|
|
932
1103
|
|
|
933
1104
|
/*
|
|
@@ -979,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
979
1150
|
}
|
|
980
1151
|
/* Store a copy of the filled hash for use at the next row. */
|
|
981
1152
|
if( num_tuples > 10 )
|
|
982
|
-
this->tuple_hash
|
|
1153
|
+
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
|
983
1154
|
|
|
984
1155
|
return tuple;
|
|
985
1156
|
}
|
|
@@ -1098,8 +1269,12 @@ static VALUE
|
|
|
1098
1269
|
pgresult_field_values( VALUE self, VALUE field )
|
|
1099
1270
|
{
|
|
1100
1271
|
PGresult *result = pgresult_get( self );
|
|
1101
|
-
const char *fieldname
|
|
1102
|
-
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 );
|
|
1103
1278
|
|
|
1104
1279
|
if ( fnum < 0 )
|
|
1105
1280
|
rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
|
|
@@ -1142,6 +1317,25 @@ pgresult_tuple_values(VALUE self, VALUE index)
|
|
|
1142
1317
|
}
|
|
1143
1318
|
}
|
|
1144
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
|
+
|
|
1145
1339
|
/*
|
|
1146
1340
|
* call-seq:
|
|
1147
1341
|
* res.tuple( n ) -> PG::Tuple
|
|
@@ -1162,19 +1356,7 @@ pgresult_tuple(VALUE self, VALUE index)
|
|
|
1162
1356
|
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1163
1357
|
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1164
1358
|
|
|
1165
|
-
|
|
1166
|
-
int i;
|
|
1167
|
-
VALUE field_map = rb_hash_new();
|
|
1168
|
-
|
|
1169
|
-
if( this->nfields == -1 )
|
|
1170
|
-
pgresult_init_fnames( self );
|
|
1171
|
-
|
|
1172
|
-
for( i = 0; i < this->nfields; i++ ){
|
|
1173
|
-
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
|
1174
|
-
}
|
|
1175
|
-
rb_obj_freeze(field_map);
|
|
1176
|
-
this->field_map = field_map;
|
|
1177
|
-
}
|
|
1359
|
+
ensure_init_for_tuple(self);
|
|
1178
1360
|
|
|
1179
1361
|
return pg_tuple_new(self, tuple_num);
|
|
1180
1362
|
}
|
|
@@ -1206,7 +1388,7 @@ pgresult_each(VALUE self)
|
|
|
1206
1388
|
* call-seq:
|
|
1207
1389
|
* res.fields() -> Array
|
|
1208
1390
|
*
|
|
1209
|
-
*
|
|
1391
|
+
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
1210
1392
|
*/
|
|
1211
1393
|
static VALUE
|
|
1212
1394
|
pgresult_fields(VALUE self)
|
|
@@ -1238,14 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1238
1420
|
t_pg_result *this = pgresult_get_this(self);
|
|
1239
1421
|
t_typemap *p_typemap;
|
|
1240
1422
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
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);
|
|
1246
1426
|
|
|
1247
|
-
|
|
1248
|
-
|
|
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 );
|
|
1249
1430
|
|
|
1250
1431
|
return typemap;
|
|
1251
1432
|
}
|
|
@@ -1266,22 +1447,21 @@ pgresult_type_map_get(VALUE self)
|
|
|
1266
1447
|
}
|
|
1267
1448
|
|
|
1268
1449
|
|
|
1269
|
-
static
|
|
1270
|
-
yield_hash(VALUE self, int ntuples, int nfields)
|
|
1450
|
+
static int
|
|
1451
|
+
yield_hash(VALUE self, int ntuples, int nfields, void *data)
|
|
1271
1452
|
{
|
|
1272
1453
|
int tuple_num;
|
|
1273
|
-
t_pg_result *this = pgresult_get_this(self);
|
|
1274
1454
|
UNUSED(nfields);
|
|
1275
1455
|
|
|
1276
1456
|
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1277
1457
|
rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
|
|
1278
1458
|
}
|
|
1279
1459
|
|
|
1280
|
-
|
|
1460
|
+
return 1; /* clear the result */
|
|
1281
1461
|
}
|
|
1282
1462
|
|
|
1283
|
-
static
|
|
1284
|
-
yield_array(VALUE self, int ntuples, int nfields)
|
|
1463
|
+
static int
|
|
1464
|
+
yield_array(VALUE self, int ntuples, int nfields, void *data)
|
|
1285
1465
|
{
|
|
1286
1466
|
int row;
|
|
1287
1467
|
t_pg_result *this = pgresult_get_this(self);
|
|
@@ -1297,31 +1477,40 @@ yield_array(VALUE self, int ntuples, int nfields)
|
|
|
1297
1477
|
rb_yield( rb_ary_new4( nfields, row_values ));
|
|
1298
1478
|
}
|
|
1299
1479
|
|
|
1300
|
-
|
|
1480
|
+
return 1; /* clear the result */
|
|
1301
1481
|
}
|
|
1302
1482
|
|
|
1303
|
-
static
|
|
1304
|
-
yield_tuple(VALUE self, int ntuples, int nfields)
|
|
1483
|
+
static int
|
|
1484
|
+
yield_tuple(VALUE self, int ntuples, int nfields, void *data)
|
|
1305
1485
|
{
|
|
1306
1486
|
int tuple_num;
|
|
1307
1487
|
t_pg_result *this = pgresult_get_this(self);
|
|
1308
|
-
VALUE
|
|
1488
|
+
VALUE copy;
|
|
1309
1489
|
UNUSED(nfields);
|
|
1310
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
|
+
|
|
1311
1497
|
for(tuple_num = 0; tuple_num < ntuples; tuple_num++) {
|
|
1312
|
-
VALUE tuple = pgresult_tuple(
|
|
1498
|
+
VALUE tuple = pgresult_tuple(copy, INT2FIX(tuple_num));
|
|
1313
1499
|
rb_yield( tuple );
|
|
1314
1500
|
}
|
|
1501
|
+
return 0; /* don't clear the result */
|
|
1315
1502
|
}
|
|
1316
1503
|
|
|
1317
|
-
static
|
|
1318
|
-
|
|
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)
|
|
1319
1507
|
{
|
|
1320
1508
|
t_pg_result *this;
|
|
1321
|
-
int nfields;
|
|
1509
|
+
int nfields, nfields2;
|
|
1322
1510
|
PGconn *pgconn;
|
|
1323
1511
|
PGresult *pgresult;
|
|
1324
1512
|
|
|
1513
|
+
rb_check_frozen(self);
|
|
1325
1514
|
RETURN_ENUMERATOR(self, 0, NULL);
|
|
1326
1515
|
|
|
1327
1516
|
this = pgresult_get_this_safe(self);
|
|
@@ -1334,6 +1523,7 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
|
|
1334
1523
|
|
|
1335
1524
|
switch( PQresultStatus(pgresult) ){
|
|
1336
1525
|
case PGRES_TUPLES_OK:
|
|
1526
|
+
case PGRES_COMMAND_OK:
|
|
1337
1527
|
if( ntuples == 0 )
|
|
1338
1528
|
return self;
|
|
1339
1529
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
@@ -1343,14 +1533,24 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
|
|
1343
1533
|
pg_result_check( self );
|
|
1344
1534
|
}
|
|
1345
1535
|
|
|
1346
|
-
|
|
1536
|
+
nfields2 = PQnfields(pgresult);
|
|
1537
|
+
if( nfields != nfields2 ){
|
|
1538
|
+
pgresult_clear( this );
|
|
1539
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, nfields2);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
if( yielder( self, ntuples, nfields, data ) ){
|
|
1543
|
+
pgresult_clear( this );
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
if( gvl_PQisBusy(pgconn) ){
|
|
1547
|
+
/* wait for input (without blocking) before reading each result */
|
|
1548
|
+
pgconn_block( 0, NULL, this->connection );
|
|
1549
|
+
}
|
|
1347
1550
|
|
|
1348
1551
|
pgresult = gvl_PQgetResult(pgconn);
|
|
1349
1552
|
if( pgresult == NULL )
|
|
1350
|
-
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another
|
|
1351
|
-
|
|
1352
|
-
if( nfields != PQnfields(pgresult) )
|
|
1353
|
-
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");
|
|
1354
1554
|
|
|
1355
1555
|
this->pgresult = pgresult;
|
|
1356
1556
|
}
|
|
@@ -1390,13 +1590,11 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int))
|
|
|
1390
1590
|
* # do something with each received row of the second query
|
|
1391
1591
|
* end
|
|
1392
1592
|
* conn.get_result # => nil (no more results)
|
|
1393
|
-
*
|
|
1394
|
-
* Available since PostgreSQL-9.2
|
|
1395
1593
|
*/
|
|
1396
1594
|
static VALUE
|
|
1397
1595
|
pgresult_stream_each(VALUE self)
|
|
1398
1596
|
{
|
|
1399
|
-
return pgresult_stream_any(self, yield_hash);
|
|
1597
|
+
return pgresult_stream_any(self, yield_hash, NULL);
|
|
1400
1598
|
}
|
|
1401
1599
|
|
|
1402
1600
|
/*
|
|
@@ -1408,13 +1606,11 @@ pgresult_stream_each(VALUE self)
|
|
|
1408
1606
|
*
|
|
1409
1607
|
* This method works equally to #stream_each , but yields an Array of
|
|
1410
1608
|
* values.
|
|
1411
|
-
*
|
|
1412
|
-
* Available since PostgreSQL-9.2
|
|
1413
1609
|
*/
|
|
1414
1610
|
static VALUE
|
|
1415
1611
|
pgresult_stream_each_row(VALUE self)
|
|
1416
1612
|
{
|
|
1417
|
-
return pgresult_stream_any(self, yield_array);
|
|
1613
|
+
return pgresult_stream_any(self, yield_array, NULL);
|
|
1418
1614
|
}
|
|
1419
1615
|
|
|
1420
1616
|
/*
|
|
@@ -1424,38 +1620,108 @@ pgresult_stream_each_row(VALUE self)
|
|
|
1424
1620
|
* Yields each row of the result set in single row mode.
|
|
1425
1621
|
*
|
|
1426
1622
|
* This method works equally to #stream_each , but yields a PG::Tuple object.
|
|
1427
|
-
*
|
|
1428
|
-
* Available since PostgreSQL-9.2
|
|
1429
1623
|
*/
|
|
1430
1624
|
static VALUE
|
|
1431
1625
|
pgresult_stream_each_tuple(VALUE self)
|
|
1432
1626
|
{
|
|
1433
|
-
|
|
1627
|
+
/* allocate VALUEs that are shared between all streamed tuples */
|
|
1628
|
+
ensure_init_for_tuple(self);
|
|
1629
|
+
|
|
1630
|
+
return pgresult_stream_any(self, yield_tuple, NULL);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
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);
|
|
1658
|
+
|
|
1659
|
+
rb_check_frozen(self);
|
|
1660
|
+
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
|
1661
|
+
|
|
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);
|
|
1667
|
+
|
|
1668
|
+
return sym;
|
|
1434
1669
|
}
|
|
1435
1670
|
|
|
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;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1436
1691
|
|
|
1437
1692
|
void
|
|
1438
|
-
init_pg_result()
|
|
1693
|
+
init_pg_result(void)
|
|
1439
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
|
+
|
|
1440
1699
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
|
1441
|
-
|
|
1700
|
+
rb_undef_alloc_func(rb_cPGresult);
|
|
1442
1701
|
rb_include_module(rb_cPGresult, rb_mEnumerable);
|
|
1443
1702
|
rb_include_module(rb_cPGresult, rb_mPGconstants);
|
|
1444
1703
|
|
|
1445
1704
|
/****** PG::Result INSTANCE METHODS: libpq ******/
|
|
1446
1705
|
rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
|
|
1447
|
-
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);
|
|
1448
1708
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
|
1449
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
|
|
1450
1714
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
|
1451
1715
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
|
1452
1716
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
|
1717
|
+
rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
|
|
1453
1718
|
rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
|
|
1454
1719
|
rb_define_alias (rb_cPGresult, "check_result", "check");
|
|
1455
1720
|
rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
|
|
1456
1721
|
rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
|
|
1457
1722
|
rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
|
|
1458
1723
|
rb_define_alias(rb_cPGresult, "num_fields", "nfields");
|
|
1724
|
+
rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
|
|
1459
1725
|
rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
|
|
1460
1726
|
rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
|
|
1461
1727
|
rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
|
|
@@ -1494,6 +1760,7 @@ init_pg_result()
|
|
|
1494
1760
|
rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
|
|
1495
1761
|
rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
|
|
1496
1762
|
rb_define_method(rb_cPGresult, "stream_each_tuple", pgresult_stream_each_tuple, 0);
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
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
|
+
}
|