pgsql 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +35 -0
- data/README +47 -0
- data/lib/Rakefile +31 -0
- data/lib/conn.c +902 -0
- data/lib/conn.h +42 -0
- data/lib/conn_exec.c +869 -0
- data/lib/conn_exec.h +14 -0
- data/lib/conn_quote.c +669 -0
- data/lib/conn_quote.h +27 -0
- data/lib/mkrf_conf +38 -0
- data/lib/module.c +47 -0
- data/lib/module.h +39 -0
- data/lib/result.c +891 -0
- data/lib/result.h +32 -0
- data/lib/undef.h +11 -0
- metadata +90 -0
data/lib/conn.h
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
/*
|
2
|
+
* conn.h -- PostgreSQL connection
|
3
|
+
*/
|
4
|
+
|
5
|
+
#ifndef __CONN_H
|
6
|
+
#define __CONN_H
|
7
|
+
|
8
|
+
#include "module.h"
|
9
|
+
|
10
|
+
|
11
|
+
#ifdef HAVE_FUNC_RB_LOCALE_ENCODING
|
12
|
+
#define RUBY_ENCODING
|
13
|
+
#endif
|
14
|
+
|
15
|
+
|
16
|
+
struct pgconn_data {
|
17
|
+
PGconn *conn;
|
18
|
+
#ifdef RUBY_ENCODING
|
19
|
+
VALUE external;
|
20
|
+
VALUE internal;
|
21
|
+
#endif
|
22
|
+
VALUE notice;
|
23
|
+
};
|
24
|
+
|
25
|
+
|
26
|
+
extern VALUE rb_cPgConn;
|
27
|
+
|
28
|
+
|
29
|
+
extern void pg_check_conninvalid( struct pgconn_data *c);
|
30
|
+
|
31
|
+
|
32
|
+
extern struct pgconn_data *get_pgconn( VALUE obj);
|
33
|
+
|
34
|
+
extern const char *pgconn_destring( struct pgconn_data *ptr, VALUE str, int *len);
|
35
|
+
extern VALUE pgconn_mkstring( struct pgconn_data *ptr, const char *str);
|
36
|
+
extern VALUE pgconn_mkstringn( struct pgconn_data *ptr, const char *str, int len);
|
37
|
+
|
38
|
+
extern void Init_pgsql_conn( void);
|
39
|
+
|
40
|
+
|
41
|
+
#endif
|
42
|
+
|
data/lib/conn_exec.c
ADDED
@@ -0,0 +1,869 @@
|
|
1
|
+
/*
|
2
|
+
* conn_exec.c -- PostgreSQL connection, command execution
|
3
|
+
*/
|
4
|
+
|
5
|
+
|
6
|
+
#include "conn_exec.h"
|
7
|
+
|
8
|
+
#include "conn_quote.h"
|
9
|
+
#include "result.h"
|
10
|
+
|
11
|
+
|
12
|
+
#ifdef HAVE_FUNC_RB_ERRINFO
|
13
|
+
#define RB_ERRINFO (rb_errinfo())
|
14
|
+
#else
|
15
|
+
#define RB_ERRINFO ruby_errinfo
|
16
|
+
#endif
|
17
|
+
|
18
|
+
|
19
|
+
static void pg_raise_connexec( struct pgconn_data *c);
|
20
|
+
|
21
|
+
static VALUE pg_statement_exec( VALUE conn, VALUE cmd, VALUE par);
|
22
|
+
static void pg_statement_send( VALUE conn, VALUE cmd, VALUE par);
|
23
|
+
static char **params_to_strings( VALUE conn, VALUE params, int *len);
|
24
|
+
static void free_strings( char **strs, int len);
|
25
|
+
static void pg_parse_parameters( int argc, VALUE *argv, VALUE *cmd, VALUE *par);
|
26
|
+
|
27
|
+
static VALUE pgconn_exec( int argc, VALUE *argv, VALUE obj);
|
28
|
+
static VALUE pgconn_send( int argc, VALUE *argv, VALUE obj);
|
29
|
+
static VALUE pgconn_fetch( VALUE obj);
|
30
|
+
static VALUE yield_or_return_result( VALUE res);
|
31
|
+
static VALUE clear_resultqueue( VALUE self);
|
32
|
+
|
33
|
+
static VALUE pgconn_query( int argc, VALUE *argv, VALUE self);
|
34
|
+
static VALUE pgconn_select_one( int argc, VALUE *argv, VALUE self);
|
35
|
+
static VALUE pgconn_select_value( int argc, VALUE *argv, VALUE self);
|
36
|
+
static VALUE pgconn_select_values( int argc, VALUE *argv, VALUE self);
|
37
|
+
static VALUE pgconn_get_notify( VALUE self);
|
38
|
+
|
39
|
+
static VALUE pgconn_transaction( int argc, VALUE *argv, VALUE self);
|
40
|
+
static VALUE rescue_transaction( VALUE self);
|
41
|
+
static VALUE yield_transaction( VALUE self);
|
42
|
+
static VALUE pgconn_subtransaction( int argc, VALUE *argv, VALUE self);
|
43
|
+
static VALUE rescue_subtransaction( VALUE ary);
|
44
|
+
static VALUE yield_subtransaction( VALUE ary);
|
45
|
+
static VALUE pgconn_transaction_status( VALUE self);
|
46
|
+
|
47
|
+
|
48
|
+
static VALUE pgconn_copy_stdin( int argc, VALUE *argv, VALUE self);
|
49
|
+
static VALUE put_end( VALUE conn);
|
50
|
+
static VALUE pgconn_putline( VALUE self, VALUE str);
|
51
|
+
static VALUE pgconn_copy_stdout( int argc, VALUE *argv, VALUE self);
|
52
|
+
static VALUE get_end( VALUE conn);
|
53
|
+
static VALUE pgconn_getline( int argc, VALUE *argv, VALUE self);
|
54
|
+
static VALUE pgconn_each_line( VALUE self);
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
static VALUE rb_ePgConnExec;
|
59
|
+
static VALUE rb_ePgConnTrans;
|
60
|
+
static VALUE rb_ePgConnCopy;
|
61
|
+
|
62
|
+
static ID id_to_a;
|
63
|
+
|
64
|
+
|
65
|
+
void
|
66
|
+
pg_raise_connexec( struct pgconn_data *c)
|
67
|
+
{
|
68
|
+
rb_raise( rb_ePgConnExec, PQerrorMessage( c->conn));
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
VALUE
|
73
|
+
pg_statement_exec( VALUE conn, VALUE cmd, VALUE par)
|
74
|
+
{
|
75
|
+
struct pgconn_data *c;
|
76
|
+
PGresult *result;
|
77
|
+
|
78
|
+
Data_Get_Struct( conn, struct pgconn_data, c);
|
79
|
+
pg_check_conninvalid( c);
|
80
|
+
if (NIL_P( par))
|
81
|
+
result = PQexec( c->conn, pgconn_destring( c, cmd, NULL));
|
82
|
+
else {
|
83
|
+
char **v;
|
84
|
+
int len;
|
85
|
+
|
86
|
+
v = params_to_strings( conn, par, &len);
|
87
|
+
result = PQexecParams( c->conn, pgconn_destring( c, cmd, NULL), len,
|
88
|
+
NULL, (const char **) v, NULL, NULL, 0);
|
89
|
+
free_strings( v, len);
|
90
|
+
}
|
91
|
+
if (result == NULL)
|
92
|
+
pg_raise_connexec( c);
|
93
|
+
return pgresult_new( result, c, cmd, par);
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
void
|
98
|
+
pg_statement_send( VALUE conn, VALUE cmd, VALUE par)
|
99
|
+
{
|
100
|
+
struct pgconn_data *c;
|
101
|
+
int res;
|
102
|
+
|
103
|
+
Data_Get_Struct( conn, struct pgconn_data, c);
|
104
|
+
pg_check_conninvalid( c);
|
105
|
+
if (NIL_P( par))
|
106
|
+
res = PQsendQuery( c->conn, pgconn_destring( c, cmd, NULL));
|
107
|
+
else {
|
108
|
+
char **v;
|
109
|
+
int len;
|
110
|
+
|
111
|
+
v = params_to_strings( conn, par, &len);
|
112
|
+
res = PQsendQueryParams( c->conn, pgconn_destring( c, cmd, NULL), len,
|
113
|
+
NULL, (const char **) v, NULL, NULL, 0);
|
114
|
+
free_strings( v, len);
|
115
|
+
}
|
116
|
+
if (res <= 0)
|
117
|
+
pg_raise_connexec( c);
|
118
|
+
}
|
119
|
+
|
120
|
+
char **
|
121
|
+
params_to_strings( VALUE conn, VALUE params, int *len)
|
122
|
+
{
|
123
|
+
struct pgconn_data *c;
|
124
|
+
VALUE *ptr;
|
125
|
+
int l;
|
126
|
+
char **values, **v;
|
127
|
+
char *a;
|
128
|
+
|
129
|
+
Data_Get_Struct( conn, struct pgconn_data, c);
|
130
|
+
ptr = RARRAY_PTR( params);
|
131
|
+
*len = l = RARRAY_LEN( params);
|
132
|
+
values = ALLOC_N( char *, l);
|
133
|
+
for (v = values; l; v++, ptr++, l--)
|
134
|
+
if (NIL_P( *ptr))
|
135
|
+
*v = NULL;
|
136
|
+
else {
|
137
|
+
const char *q;
|
138
|
+
char *p;
|
139
|
+
int n;
|
140
|
+
|
141
|
+
q = pgconn_destring( c, pgconn_stringize( conn, *ptr), &n);
|
142
|
+
a = ALLOC_N( char, n + 1);
|
143
|
+
for (p = a; *p = n ? *q : '\0'; ++p, ++q, --n)
|
144
|
+
;
|
145
|
+
*v = a;
|
146
|
+
}
|
147
|
+
return values;
|
148
|
+
}
|
149
|
+
|
150
|
+
void
|
151
|
+
free_strings( char **strs, int len)
|
152
|
+
{
|
153
|
+
char **p;
|
154
|
+
int l;
|
155
|
+
|
156
|
+
for (p = strs, l = len; l; --l, ++p)
|
157
|
+
xfree( *p);
|
158
|
+
xfree( strs);
|
159
|
+
}
|
160
|
+
|
161
|
+
|
162
|
+
void
|
163
|
+
pg_parse_parameters( int argc, VALUE *argv, VALUE *cmd, VALUE *par)
|
164
|
+
{
|
165
|
+
int len;
|
166
|
+
|
167
|
+
rb_scan_args( argc, argv, "1*", cmd, par);
|
168
|
+
StringValue( *cmd);
|
169
|
+
if (RARRAY_LEN( *par) <= 0)
|
170
|
+
*par = Qnil;
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
/*
|
175
|
+
* call-seq:
|
176
|
+
* conn.exec( sql, *bind_values) -> result
|
177
|
+
*
|
178
|
+
* Sends SQL query request specified by +sql+ to the PostgreSQL.
|
179
|
+
* Returns a Pg::Result instance.
|
180
|
+
*
|
181
|
+
* +bind_values+ represents values for the PostgreSQL bind parameters found in
|
182
|
+
* the +sql+. PostgreSQL bind parameters are presented as $1, $1, $2, etc.
|
183
|
+
*/
|
184
|
+
VALUE
|
185
|
+
pgconn_exec( int argc, VALUE *argv, VALUE self)
|
186
|
+
{
|
187
|
+
VALUE cmd, par;
|
188
|
+
VALUE res;
|
189
|
+
|
190
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
191
|
+
res = pg_statement_exec( self, cmd, par);
|
192
|
+
return yield_or_return_result( res);
|
193
|
+
}
|
194
|
+
|
195
|
+
|
196
|
+
/*
|
197
|
+
* call-seq:
|
198
|
+
* conn.send( sql, *bind_values) { |conn| ... } -> nil
|
199
|
+
*
|
200
|
+
* Sends an asynchronous SQL query request specified by +sql+ to the
|
201
|
+
* PostgreSQL server.
|
202
|
+
*
|
203
|
+
* Use Pg::Conn#fetch to fetch the results after you waited for data.
|
204
|
+
*
|
205
|
+
* Pg::Conn.connect do |conn|
|
206
|
+
* conn.send "select pg_sleep(3), * from t;" do
|
207
|
+
* ins = [ conn.socket]
|
208
|
+
* loop do
|
209
|
+
* r = IO.select ins, nil, nil, 0.5
|
210
|
+
* break if r
|
211
|
+
* puts Time.now
|
212
|
+
* end
|
213
|
+
* res = conn.fetch
|
214
|
+
* res.each { |w| puts w.inspect }
|
215
|
+
* end
|
216
|
+
* end
|
217
|
+
*/
|
218
|
+
VALUE
|
219
|
+
pgconn_send( int argc, VALUE *argv, VALUE self)
|
220
|
+
{
|
221
|
+
VALUE cmd, par;
|
222
|
+
|
223
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
224
|
+
pg_statement_send( self, cmd, par);
|
225
|
+
return rb_ensure( rb_yield, self, clear_resultqueue, self);
|
226
|
+
}
|
227
|
+
|
228
|
+
/*
|
229
|
+
* call-seq:
|
230
|
+
* conn.fetch() -> result or nil
|
231
|
+
* conn.fetch() { |result| ... } -> obj
|
232
|
+
*
|
233
|
+
* Fetches the results of the previous Pg::Conn#send call.
|
234
|
+
* See there for an example.
|
235
|
+
*
|
236
|
+
* The result will be +nil+ if there are no more results.
|
237
|
+
*/
|
238
|
+
VALUE
|
239
|
+
pgconn_fetch( VALUE self)
|
240
|
+
{
|
241
|
+
struct pgconn_data *c;
|
242
|
+
PGresult *result;
|
243
|
+
VALUE res;
|
244
|
+
|
245
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
246
|
+
pg_check_conninvalid( c);
|
247
|
+
if (PQconsumeInput( c->conn) == 0)
|
248
|
+
pg_raise_connexec( c);
|
249
|
+
if (PQisBusy( c->conn) > 0)
|
250
|
+
return Qnil;
|
251
|
+
result = PQgetResult( c->conn);
|
252
|
+
return result == NULL ? Qnil :
|
253
|
+
yield_or_return_result( pgresult_new( result, c, Qnil, Qnil));
|
254
|
+
}
|
255
|
+
|
256
|
+
VALUE
|
257
|
+
yield_or_return_result( VALUE result)
|
258
|
+
{
|
259
|
+
struct pgresult_data *r;
|
260
|
+
|
261
|
+
Data_Get_Struct( result, struct pgresult_data, r);
|
262
|
+
return rb_block_given_p() ?
|
263
|
+
rb_ensure( rb_yield, result, pgresult_clear, result) : result;
|
264
|
+
}
|
265
|
+
|
266
|
+
VALUE
|
267
|
+
clear_resultqueue( VALUE self)
|
268
|
+
{
|
269
|
+
struct pgconn_data *c;
|
270
|
+
PGresult *result;
|
271
|
+
|
272
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
273
|
+
while ((result = PQgetResult( c->conn)) != NULL)
|
274
|
+
PQclear( result);
|
275
|
+
return Qnil;
|
276
|
+
}
|
277
|
+
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
/*
|
282
|
+
* call-seq:
|
283
|
+
* conn.query( sql, *bind_values) -> rows
|
284
|
+
* conn.query( sql, *bind_values) { |row| ... } -> int or nil
|
285
|
+
*
|
286
|
+
* This is almost the same as Pg::Conn#exec except that it will yield or return
|
287
|
+
* rows skipping the result object.
|
288
|
+
*
|
289
|
+
* If given a block, the nonzero number of rows will be returned or nil
|
290
|
+
* otherwise.
|
291
|
+
*/
|
292
|
+
VALUE
|
293
|
+
pgconn_query( int argc, VALUE *argv, VALUE self)
|
294
|
+
{
|
295
|
+
VALUE cmd, par;
|
296
|
+
VALUE res;
|
297
|
+
|
298
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
299
|
+
res = pg_statement_exec( self, cmd, par);
|
300
|
+
if (rb_block_given_p())
|
301
|
+
return rb_ensure( pgresult_each, res, pgresult_clear, res);
|
302
|
+
else {
|
303
|
+
VALUE ret;
|
304
|
+
|
305
|
+
if (!id_to_a)
|
306
|
+
id_to_a = rb_intern( "to_a");
|
307
|
+
ret = rb_funcall( res, id_to_a, 0);
|
308
|
+
pgresult_clear( res);
|
309
|
+
return ret;
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
/*
|
314
|
+
* call-seq:
|
315
|
+
* conn.select_one( query, *bind_values)
|
316
|
+
*
|
317
|
+
* Return the first row of the query results.
|
318
|
+
* Equivalent to <code>conn.query( query, *bind_values).first</code>.
|
319
|
+
*/
|
320
|
+
VALUE
|
321
|
+
pgconn_select_one( int argc, VALUE *argv, VALUE self)
|
322
|
+
{
|
323
|
+
VALUE cmd, par;
|
324
|
+
VALUE res;
|
325
|
+
struct pgresult_data *r;
|
326
|
+
|
327
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
328
|
+
res = pg_statement_exec( self, cmd, par);
|
329
|
+
|
330
|
+
Data_Get_Struct( res, struct pgresult_data, r);
|
331
|
+
return pg_fetchrow( r, 0);
|
332
|
+
}
|
333
|
+
|
334
|
+
/*
|
335
|
+
* call-seq:
|
336
|
+
* conn.select_value( query, *bind_values)
|
337
|
+
*
|
338
|
+
* Return the first value of the first row of the query results.
|
339
|
+
* Equivalent to conn.query( query, *bind_values).first.first
|
340
|
+
*/
|
341
|
+
VALUE
|
342
|
+
pgconn_select_value( int argc, VALUE *argv, VALUE self)
|
343
|
+
{
|
344
|
+
VALUE cmd, par;
|
345
|
+
VALUE res;
|
346
|
+
struct pgresult_data *r;
|
347
|
+
|
348
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
349
|
+
res = pg_statement_exec( self, cmd, par);
|
350
|
+
|
351
|
+
Data_Get_Struct( res, struct pgresult_data, r);
|
352
|
+
return PQntuples( r->res) > 0 && PQnfields( r->res) > 0 ?
|
353
|
+
pg_fetchresult( r, 0, 0) : Qnil;
|
354
|
+
}
|
355
|
+
|
356
|
+
/*
|
357
|
+
* call-seq:
|
358
|
+
* conn.select_values( query, *bind_values)
|
359
|
+
*
|
360
|
+
* Equivalent to conn.query( query, *bind_values).flatten
|
361
|
+
*/
|
362
|
+
VALUE
|
363
|
+
pgconn_select_values( int argc, VALUE *argv, VALUE self)
|
364
|
+
{
|
365
|
+
VALUE cmd, par;
|
366
|
+
VALUE res;
|
367
|
+
struct pgresult_data *r;
|
368
|
+
int n, m, n_, l;
|
369
|
+
int i, j, k;
|
370
|
+
VALUE ret;
|
371
|
+
|
372
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
373
|
+
res = pg_statement_exec( self, cmd, par);
|
374
|
+
|
375
|
+
Data_Get_Struct( res, struct pgresult_data, r);
|
376
|
+
m = PQntuples( r->res), n = PQnfields( r->res);
|
377
|
+
l = m * n;
|
378
|
+
if (l == 0)
|
379
|
+
return Qnil;
|
380
|
+
ret = rb_ary_new2( l);
|
381
|
+
n_ = n;
|
382
|
+
for (k = 0, j = 0; m; ++j, --m) {
|
383
|
+
for (i = 0; n; ++i, --n, ++k)
|
384
|
+
rb_ary_store( ret, k, pg_fetchresult( r, j, i));
|
385
|
+
n = n_;
|
386
|
+
}
|
387
|
+
return ret;
|
388
|
+
}
|
389
|
+
|
390
|
+
/*
|
391
|
+
* call-seq:
|
392
|
+
* conn.get_notify() -> ary or nil
|
393
|
+
* conn.get_notify() { |rel,pid,msg| .... } -> obj
|
394
|
+
*
|
395
|
+
* Returns a notifier. If there is no unprocessed notifier, it returns +nil+.
|
396
|
+
*/
|
397
|
+
VALUE
|
398
|
+
pgconn_get_notify( VALUE self)
|
399
|
+
{
|
400
|
+
struct pgconn_data *c;
|
401
|
+
PGnotify *notify;
|
402
|
+
VALUE rel, pid, ext;
|
403
|
+
VALUE ret;
|
404
|
+
|
405
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
406
|
+
if (PQconsumeInput( c->conn) == 0)
|
407
|
+
pg_raise_connexec( c);
|
408
|
+
notify = PQnotifies( c->conn);
|
409
|
+
if (notify == NULL)
|
410
|
+
return Qnil;
|
411
|
+
rel = pgconn_mkstring( c, notify->relname);
|
412
|
+
pid = INT2FIX( notify->be_pid),
|
413
|
+
ext = pgconn_mkstring( c, notify->extra);
|
414
|
+
ret = rb_ary_new3( 3, rel, pid, ext);
|
415
|
+
PQfreemem( notify);
|
416
|
+
return rb_block_given_p() ? rb_yield( ret) : ret;
|
417
|
+
}
|
418
|
+
|
419
|
+
|
420
|
+
|
421
|
+
|
422
|
+
/*
|
423
|
+
* call-seq:
|
424
|
+
* conn.transaction( ser = nil, ro = nil) { |conn| ... }
|
425
|
+
*
|
426
|
+
* Open and close a transaction block. The isolation level will be
|
427
|
+
* 'serializable' if +ser+ is true, else 'repeatable read'.
|
428
|
+
* +ro+ means 'read only'.
|
429
|
+
*
|
430
|
+
* (In C++ terms, +ro+ is const, and +ser+ is not volatile.)
|
431
|
+
*
|
432
|
+
*/
|
433
|
+
VALUE
|
434
|
+
pgconn_transaction( int argc, VALUE *argv, VALUE self)
|
435
|
+
{
|
436
|
+
struct pgconn_data *c;
|
437
|
+
VALUE ser, ro;
|
438
|
+
VALUE cmd;
|
439
|
+
int p;
|
440
|
+
|
441
|
+
rb_scan_args( argc, argv, "02", &ser, &ro);
|
442
|
+
cmd = rb_str_buf_new2( "begin");
|
443
|
+
p = 0;
|
444
|
+
if (!NIL_P( ser)) {
|
445
|
+
rb_str_buf_cat2( cmd, " isolation level ");
|
446
|
+
rb_str_buf_cat2( cmd, RTEST(ser) ? "serializable" : "read committed");
|
447
|
+
p++;
|
448
|
+
}
|
449
|
+
if (!NIL_P( ro)) {
|
450
|
+
if (p) rb_str_buf_cat2( cmd, ",");
|
451
|
+
rb_str_buf_cat2( cmd, " read ");
|
452
|
+
rb_str_buf_cat2( cmd, (RTEST(ro) ? "only" : "write"));
|
453
|
+
}
|
454
|
+
rb_str_buf_cat2( cmd, ";");
|
455
|
+
|
456
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
457
|
+
if (PQtransactionStatus( c->conn) > PQTRANS_IDLE)
|
458
|
+
rb_raise( rb_ePgConnTrans,
|
459
|
+
"Nested transaction block. Use Conn#subtransaction.");
|
460
|
+
pgresult_clear( pg_statement_exec( self, cmd, Qnil));
|
461
|
+
return rb_rescue( yield_transaction, self, rescue_transaction, self);
|
462
|
+
}
|
463
|
+
|
464
|
+
VALUE
|
465
|
+
rescue_transaction( VALUE self)
|
466
|
+
{
|
467
|
+
pgresult_clear( pg_statement_exec( self, rb_str_new2( "rollback;"), Qnil));
|
468
|
+
rb_exc_raise( RB_ERRINFO);
|
469
|
+
return Qnil;
|
470
|
+
}
|
471
|
+
|
472
|
+
VALUE
|
473
|
+
yield_transaction( VALUE self)
|
474
|
+
{
|
475
|
+
VALUE r;
|
476
|
+
|
477
|
+
r = rb_yield( self);
|
478
|
+
pgresult_clear( pg_statement_exec( self, rb_str_new2( "commit;"), Qnil));
|
479
|
+
return r;
|
480
|
+
}
|
481
|
+
|
482
|
+
|
483
|
+
/*
|
484
|
+
* call-seq:
|
485
|
+
* conn.subtransaction( name, *args) { |conn,sp| ... }
|
486
|
+
*
|
487
|
+
* Open and close a transaction savepoint. The savepoints name +nam+ may
|
488
|
+
* contain % directives that will be expanded by +args+.
|
489
|
+
*/
|
490
|
+
VALUE
|
491
|
+
pgconn_subtransaction( int argc, VALUE *argv, VALUE self)
|
492
|
+
{
|
493
|
+
struct pgconn_data *c;
|
494
|
+
int a;
|
495
|
+
VALUE sp, par, cmd, ya;
|
496
|
+
const char *q;
|
497
|
+
char *p;
|
498
|
+
int n;
|
499
|
+
|
500
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
501
|
+
a = rb_scan_args( argc, argv, "1*", &sp, &par);
|
502
|
+
StringValue( sp);
|
503
|
+
if (a > 1)
|
504
|
+
sp = rb_str_format(RARRAY_LEN(par), RARRAY_PTR(par), sp);
|
505
|
+
|
506
|
+
cmd = rb_str_buf_new2( "savepoint ");
|
507
|
+
q = pgconn_destring( c, sp, &n);
|
508
|
+
p = PQescapeIdentifier( c->conn, q, n);
|
509
|
+
rb_str_buf_cat2( cmd, p);
|
510
|
+
ya = rb_ary_new3( 2, self, rb_str_new2( p));
|
511
|
+
PQfreemem( p);
|
512
|
+
rb_str_buf_cat2( cmd, ";");
|
513
|
+
|
514
|
+
pgresult_clear( pg_statement_exec( self, cmd, Qnil));
|
515
|
+
return rb_rescue( yield_subtransaction, ya, rescue_subtransaction, ya);
|
516
|
+
}
|
517
|
+
|
518
|
+
VALUE
|
519
|
+
rescue_subtransaction( VALUE ary)
|
520
|
+
{
|
521
|
+
VALUE cmd;
|
522
|
+
|
523
|
+
cmd = rb_str_buf_new2( "rollback to savepoint ");
|
524
|
+
rb_str_buf_append( cmd, rb_ary_entry( ary, 1));
|
525
|
+
rb_str_buf_cat2( cmd, ";");
|
526
|
+
pgresult_clear( pg_statement_exec( rb_ary_entry( ary, 0), cmd, Qnil));
|
527
|
+
rb_exc_raise( RB_ERRINFO);
|
528
|
+
return Qnil;
|
529
|
+
}
|
530
|
+
|
531
|
+
VALUE
|
532
|
+
yield_subtransaction( VALUE ary)
|
533
|
+
{
|
534
|
+
VALUE r, cmd;
|
535
|
+
|
536
|
+
r = rb_yield( ary);
|
537
|
+
cmd = rb_str_buf_new2( "release savepoint ");
|
538
|
+
rb_str_buf_append( cmd, rb_ary_entry( ary, 1));
|
539
|
+
rb_str_buf_cat2( cmd, ";");
|
540
|
+
pgresult_clear( pg_statement_exec( rb_ary_entry( ary, 0), cmd, Qnil));
|
541
|
+
return r;
|
542
|
+
}
|
543
|
+
|
544
|
+
|
545
|
+
|
546
|
+
|
547
|
+
/*
|
548
|
+
* call-seq:
|
549
|
+
* conn.transaction_status() -> int
|
550
|
+
*
|
551
|
+
* returns one of the following statuses:
|
552
|
+
*
|
553
|
+
* PQTRANS_IDLE = 0 (connection idle)
|
554
|
+
* PQTRANS_ACTIVE = 1 (command in progress)
|
555
|
+
* PQTRANS_INTRANS = 2 (idle, within transaction block)
|
556
|
+
* PQTRANS_INERROR = 3 (idle, within failed transaction)
|
557
|
+
* PQTRANS_UNKNOWN = 4 (cannot determine status)
|
558
|
+
*/
|
559
|
+
VALUE
|
560
|
+
pgconn_transaction_status( VALUE self)
|
561
|
+
{
|
562
|
+
struct pgconn_data *c;
|
563
|
+
|
564
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
565
|
+
return INT2FIX( PQtransactionStatus( c->conn));
|
566
|
+
}
|
567
|
+
|
568
|
+
|
569
|
+
|
570
|
+
/*
|
571
|
+
* call-seq:
|
572
|
+
* conn.copy_stdin( sql, *bind_values) { |result| ... } -> nil
|
573
|
+
*
|
574
|
+
* Write lines into a +COPY+ command. See +stringize_line+ for how to build
|
575
|
+
* standard lines.
|
576
|
+
*
|
577
|
+
* conn.copy_stdin "copy t from stdin;" do
|
578
|
+
* ary = ...
|
579
|
+
* l = stringize_line ary
|
580
|
+
* conn.put l
|
581
|
+
* end
|
582
|
+
*
|
583
|
+
* You may write a "\\." yourself if you like it.
|
584
|
+
*/
|
585
|
+
VALUE
|
586
|
+
pgconn_copy_stdin( int argc, VALUE *argv, VALUE self)
|
587
|
+
{
|
588
|
+
VALUE cmd, par;
|
589
|
+
VALUE res;
|
590
|
+
|
591
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
592
|
+
res = pg_statement_exec( self, cmd, par);
|
593
|
+
return rb_ensure( rb_yield, res, put_end, self);
|
594
|
+
}
|
595
|
+
|
596
|
+
|
597
|
+
VALUE
|
598
|
+
put_end( VALUE self)
|
599
|
+
{
|
600
|
+
struct pgconn_data *c;
|
601
|
+
int r;
|
602
|
+
PGresult *res;
|
603
|
+
|
604
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
605
|
+
/*
|
606
|
+
* I would like to hand over something like
|
607
|
+
* RSTRING_PTR( rb_obj_as_string( RB_ERRINFO))
|
608
|
+
* here but when execution is inside a rescue block
|
609
|
+
* the error info will be non-null even though the
|
610
|
+
* exception just has been caught.
|
611
|
+
*/
|
612
|
+
while ((r = PQputCopyEnd( c->conn, NULL)) == 0)
|
613
|
+
;
|
614
|
+
if (r < 0)
|
615
|
+
rb_raise( rb_ePgConnCopy, "Copy from stdin failed to finish.");
|
616
|
+
while ((res = PQgetResult( c->conn)) != NULL)
|
617
|
+
pgresult_new( res, c, Qnil, Qnil);
|
618
|
+
return Qnil;
|
619
|
+
}
|
620
|
+
|
621
|
+
/*
|
622
|
+
* call-seq:
|
623
|
+
* conn.putline( str) -> nil
|
624
|
+
* conn.putline( ary) -> nil
|
625
|
+
* conn.putline( str) { ... } -> nil
|
626
|
+
*
|
627
|
+
* Sends the string to the backend server.
|
628
|
+
* You have to open the stream with a +COPY+ command using +copy_stdin+.
|
629
|
+
*
|
630
|
+
* If +str+ doesn't end in a newline, one is appended. If the argument
|
631
|
+
* is +ary+, a line will be built using +stringize_line+.
|
632
|
+
*
|
633
|
+
* If the connection is in nonblocking mode and no data could be sent
|
634
|
+
* the closure will be called and its value will be returned.
|
635
|
+
*/
|
636
|
+
VALUE
|
637
|
+
pgconn_putline( VALUE self, VALUE arg)
|
638
|
+
{
|
639
|
+
struct pgconn_data *c;
|
640
|
+
VALUE str;
|
641
|
+
const char *p;
|
642
|
+
int l;
|
643
|
+
int r;
|
644
|
+
|
645
|
+
switch (TYPE( arg)) {
|
646
|
+
case T_STRING:
|
647
|
+
str = arg;
|
648
|
+
break;
|
649
|
+
case T_ARRAY:
|
650
|
+
str = pgconn_stringize_line( self, arg);
|
651
|
+
break;
|
652
|
+
default:
|
653
|
+
str = rb_obj_as_string( arg);
|
654
|
+
break;
|
655
|
+
}
|
656
|
+
if (RSTRING_PTR( str)[ RSTRING_LEN( str) - 1] != '\n') {
|
657
|
+
VALUE t;
|
658
|
+
|
659
|
+
t = rb_str_dup( str);
|
660
|
+
rb_str_buf_cat( t, "\n", 1);
|
661
|
+
str = t;
|
662
|
+
}
|
663
|
+
|
664
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
665
|
+
p = pgconn_destring( c, str, &l);
|
666
|
+
r = PQputCopyData( c->conn, p, l);
|
667
|
+
if (r < 0)
|
668
|
+
rb_raise( rb_ePgConnCopy, "Copy from stdin failed.");
|
669
|
+
else if (r == 0)
|
670
|
+
return rb_yield( Qnil);
|
671
|
+
return Qnil;
|
672
|
+
}
|
673
|
+
|
674
|
+
|
675
|
+
/*
|
676
|
+
* call-seq:
|
677
|
+
* conn.copy_stdout( sql, *bind_values) { ... } -> nil
|
678
|
+
*
|
679
|
+
* Read lines from a +COPY+ command. The form of the lines depends
|
680
|
+
* on the statement's parameters.
|
681
|
+
*
|
682
|
+
* conn.copy_stdout "copy t to stdout;" do
|
683
|
+
* l = conn.getline
|
684
|
+
* ary = l.split /\t/
|
685
|
+
* ary.map! { |x|
|
686
|
+
* unless x == "\\N" then
|
687
|
+
* x.gsub! /\\(.)/ do
|
688
|
+
* case $1
|
689
|
+
* when "t" then "\t"
|
690
|
+
* when "n" then "\n"
|
691
|
+
* when "\\" then "\\"
|
692
|
+
* end
|
693
|
+
* end
|
694
|
+
* end
|
695
|
+
* }
|
696
|
+
* ...
|
697
|
+
* end
|
698
|
+
*/
|
699
|
+
VALUE
|
700
|
+
pgconn_copy_stdout( int argc, VALUE *argv, VALUE self)
|
701
|
+
{
|
702
|
+
VALUE cmd, par;
|
703
|
+
VALUE res;
|
704
|
+
|
705
|
+
pg_parse_parameters( argc, argv, &cmd, &par);
|
706
|
+
res = pg_statement_exec( self, cmd, par);
|
707
|
+
return rb_ensure( rb_yield, res, get_end, self);
|
708
|
+
}
|
709
|
+
|
710
|
+
VALUE
|
711
|
+
get_end( VALUE self)
|
712
|
+
{
|
713
|
+
struct pgconn_data *c;
|
714
|
+
PGresult *res;
|
715
|
+
|
716
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
717
|
+
if ((res = PQgetResult( c->conn)) != NULL)
|
718
|
+
pgresult_new( res, c, Qnil, Qnil);
|
719
|
+
return Qnil;
|
720
|
+
}
|
721
|
+
|
722
|
+
/*
|
723
|
+
* call-seq:
|
724
|
+
* conn.getline( async = nil) -> str
|
725
|
+
* conn.getline( async = nil) { ... } -> str
|
726
|
+
*
|
727
|
+
* Reads a line from the backend server after a +COPY+ command.
|
728
|
+
* Returns +nil+ for EOF.
|
729
|
+
*
|
730
|
+
* If async is +true+ and no data is available then the block will be called
|
731
|
+
* and its value will be returned.
|
732
|
+
*
|
733
|
+
* Call this method inside a block passed to +copy_stdout+. See
|
734
|
+
* there for an example.
|
735
|
+
*/
|
736
|
+
VALUE
|
737
|
+
pgconn_getline( int argc, VALUE *argv, VALUE self)
|
738
|
+
{
|
739
|
+
struct pgconn_data *c;
|
740
|
+
VALUE as;
|
741
|
+
int async;
|
742
|
+
char *b;
|
743
|
+
int r;
|
744
|
+
|
745
|
+
async = rb_scan_args( argc, argv, "01", &as) > 0 && !NIL_P( as) ? 1 : 0;
|
746
|
+
|
747
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
748
|
+
r = PQgetCopyData( c->conn, &b, async);
|
749
|
+
if (r > 0) {
|
750
|
+
VALUE ret;
|
751
|
+
|
752
|
+
ret = pgconn_mkstringn( c, b, r);
|
753
|
+
PQfreemem( b);
|
754
|
+
rb_lastline_set( ret);
|
755
|
+
return ret;
|
756
|
+
} else if (r == 0)
|
757
|
+
return rb_yield( Qnil);
|
758
|
+
else {
|
759
|
+
/* PQgetResult() will be called in the ensure block. */
|
760
|
+
}
|
761
|
+
return Qnil;
|
762
|
+
}
|
763
|
+
|
764
|
+
/*
|
765
|
+
* call-seq:
|
766
|
+
* conn.each_line() { |line| ... } -> nil
|
767
|
+
*
|
768
|
+
* Reads line after line from a +COPY+ command.
|
769
|
+
*
|
770
|
+
* Call this method inside a block passed to +copy_stdout+. See
|
771
|
+
* there for an example.
|
772
|
+
*/
|
773
|
+
VALUE
|
774
|
+
pgconn_each_line( VALUE self)
|
775
|
+
{
|
776
|
+
struct pgconn_data *c;
|
777
|
+
char *b;
|
778
|
+
int r;
|
779
|
+
VALUE s;
|
780
|
+
|
781
|
+
Data_Get_Struct( self, struct pgconn_data, c);
|
782
|
+
for (; (r = PQgetCopyData( c->conn, &b, 0)) > 0;) {
|
783
|
+
s = pgconn_mkstringn( c, b, r);
|
784
|
+
PQfreemem( b);
|
785
|
+
rb_yield( s);
|
786
|
+
}
|
787
|
+
return Qnil;
|
788
|
+
}
|
789
|
+
|
790
|
+
|
791
|
+
|
792
|
+
|
793
|
+
|
794
|
+
|
795
|
+
|
796
|
+
|
797
|
+
/********************************************************************
|
798
|
+
*
|
799
|
+
* Document-class: Pg::Conn::ExecError
|
800
|
+
*
|
801
|
+
* Error while querying from a PostgreSQL connection.
|
802
|
+
*/
|
803
|
+
|
804
|
+
|
805
|
+
/********************************************************************
|
806
|
+
*
|
807
|
+
* Document-class: Pg::Conn::TransactionError
|
808
|
+
*
|
809
|
+
* Nested transaction blocks. Use savepoints.
|
810
|
+
*/
|
811
|
+
|
812
|
+
|
813
|
+
/********************************************************************
|
814
|
+
*
|
815
|
+
* Document-class: Pg::Conn::CopyError
|
816
|
+
*
|
817
|
+
* Nested transaction blocks. Use savepoints.
|
818
|
+
*/
|
819
|
+
|
820
|
+
|
821
|
+
void
|
822
|
+
Init_pgsql_conn_exec( void)
|
823
|
+
{
|
824
|
+
|
825
|
+
#ifdef RDOC_NEEDS_THIS
|
826
|
+
rb_cPgConn = rb_define_class_under( rb_mPg, "Conn", rb_cObject);
|
827
|
+
#endif
|
828
|
+
|
829
|
+
rb_ePgConnExec = rb_define_class_under( rb_cPgConn, "ExecError", rb_ePgError);
|
830
|
+
rb_ePgConnTrans = rb_define_class_under( rb_cPgConn, "TransactionError", rb_ePgError);
|
831
|
+
rb_ePgConnCopy = rb_define_class_under( rb_cPgConn, "CopyError", rb_ePgError);
|
832
|
+
|
833
|
+
rb_define_method( rb_cPgConn, "exec", &pgconn_exec, -1);
|
834
|
+
rb_define_method( rb_cPgConn, "send", &pgconn_send, -1);
|
835
|
+
rb_define_method( rb_cPgConn, "fetch", &pgconn_fetch, 0);
|
836
|
+
|
837
|
+
rb_define_method( rb_cPgConn, "query", &pgconn_query, -1);
|
838
|
+
rb_define_method( rb_cPgConn, "select_one", &pgconn_select_one, -1);
|
839
|
+
rb_define_method( rb_cPgConn, "select_value", &pgconn_select_value, -1);
|
840
|
+
rb_define_method( rb_cPgConn, "select_values", &pgconn_select_values, -1);
|
841
|
+
rb_define_method( rb_cPgConn, "get_notify", &pgconn_get_notify, 0);
|
842
|
+
|
843
|
+
|
844
|
+
|
845
|
+
#define TRANS_DEF( c) rb_define_const( rb_cPgConn, "T_" #c, INT2FIX( PQTRANS_ ## c))
|
846
|
+
TRANS_DEF( IDLE);
|
847
|
+
TRANS_DEF( ACTIVE);
|
848
|
+
TRANS_DEF( INTRANS);
|
849
|
+
TRANS_DEF( INERROR);
|
850
|
+
TRANS_DEF( UNKNOWN);
|
851
|
+
#undef TRANS_DEF
|
852
|
+
|
853
|
+
rb_define_method( rb_cPgConn, "transaction", &pgconn_transaction, -1);
|
854
|
+
rb_define_method( rb_cPgConn, "subtransaction", &pgconn_subtransaction, -1);
|
855
|
+
rb_define_alias( rb_cPgConn, "savepoint", "subtransaction");
|
856
|
+
rb_define_method( rb_cPgConn, "transaction_status", &pgconn_transaction_status, 0);
|
857
|
+
|
858
|
+
|
859
|
+
rb_define_method( rb_cPgConn, "copy_stdin", &pgconn_copy_stdin, -1);
|
860
|
+
rb_define_method( rb_cPgConn, "putline", &pgconn_putline, 1);
|
861
|
+
rb_define_alias( rb_cPgConn, "put", "putline");
|
862
|
+
rb_define_method( rb_cPgConn, "copy_stdout", &pgconn_copy_stdout, -1);
|
863
|
+
rb_define_method( rb_cPgConn, "getline", &pgconn_getline, -1);
|
864
|
+
rb_define_alias( rb_cPgConn, "get", "getline");
|
865
|
+
rb_define_method( rb_cPgConn, "each_line", &pgconn_each_line, 0);
|
866
|
+
|
867
|
+
ID id_to_a = 0;
|
868
|
+
}
|
869
|
+
|