globegit-postgresql-plruby 0.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.
- data/Changes +121 -0
- data/README.markdown +155 -0
- data/Rakefile +48 -0
- data/docs/plruby.rb +1931 -0
- data/ex_trans.sql +33 -0
- data/extconf.rb +267 -0
- data/plruby.html +1454 -0
- data/plruby.rd +1571 -0
- data/postgresql-plruby.gemspec +56 -0
- data/src/conversions.h +5 -0
- data/src/conversions/basic/conversions.h +25 -0
- data/src/conversions/basic/extconf.rb +8 -0
- data/src/conversions/basic/plruby_basic.c +357 -0
- data/src/conversions/bitstring/bitstring.sql +75 -0
- data/src/conversions/bitstring/conversions.h +15 -0
- data/src/conversions/bitstring/extconf.rb +8 -0
- data/src/conversions/bitstring/plruby_bitstring.c +579 -0
- data/src/conversions/convcommon.h +129 -0
- data/src/conversions/datetime/conversions.h +13 -0
- data/src/conversions/datetime/extconf.rb +8 -0
- data/src/conversions/datetime/plruby_datetime.c +269 -0
- data/src/conversions/geometry/conversions.h +37 -0
- data/src/conversions/geometry/extconf.rb +8 -0
- data/src/conversions/geometry/geometry.sql +196 -0
- data/src/conversions/geometry/plruby_geometry.c +2494 -0
- data/src/conversions/network/conversions.h +21 -0
- data/src/conversions/network/extconf.rb +8 -0
- data/src/conversions/network/network.sql +63 -0
- data/src/conversions/network/plruby_network.c +537 -0
- data/src/package.h +20 -0
- data/src/plpl.c +1708 -0
- data/src/plplan.c +893 -0
- data/src/plruby.c +1676 -0
- data/src/plruby.h +324 -0
- data/src/pltrans.c +388 -0
- data/test/conv_bitstring/b.rb +45 -0
- data/test/conv_bitstring/runtest +26 -0
- data/test/conv_bitstring/test.expected.73 +148 -0
- data/test/conv_bitstring/test.expected.74 +148 -0
- data/test/conv_bitstring/test.expected.80 +148 -0
- data/test/conv_bitstring/test.expected.81 +148 -0
- data/test/conv_bitstring/test.expected.82 +148 -0
- data/test/conv_bitstring/test.expected.83 +148 -0
- data/test/conv_bitstring/test.expected.84 +148 -0
- data/test/conv_bitstring/test.out +148 -0
- data/test/conv_bitstring/test_mklang.sql +8 -0
- data/test/conv_bitstring/test_queries.sql +63 -0
- data/test/conv_bitstring/test_queries.sql.in +63 -0
- data/test/conv_geometry/b.rb +45 -0
- data/test/conv_geometry/runtest +26 -0
- data/test/conv_geometry/test.expected.73 +265 -0
- data/test/conv_geometry/test.expected.74 +265 -0
- data/test/conv_geometry/test.expected.80 +265 -0
- data/test/conv_geometry/test.expected.81 +265 -0
- data/test/conv_geometry/test.expected.82 +265 -0
- data/test/conv_geometry/test.expected.83 +265 -0
- data/test/conv_geometry/test.expected.84 +265 -0
- data/test/conv_geometry/test.out +265 -0
- data/test/conv_geometry/test_mklang.sql +8 -0
- data/test/conv_geometry/test_queries.sql +194 -0
- data/test/conv_geometry/test_queries.sql.in +194 -0
- data/test/conv_network/b.rb +45 -0
- data/test/conv_network/runtest +26 -0
- data/test/conv_network/test.expected.73 +213 -0
- data/test/conv_network/test.expected.74 +237 -0
- data/test/conv_network/test.expected.80 +237 -0
- data/test/conv_network/test.expected.81 +237 -0
- data/test/conv_network/test.expected.82 +237 -0
- data/test/conv_network/test.expected.83 +237 -0
- data/test/conv_network/test.expected.84 +237 -0
- data/test/conv_network/test.out +237 -0
- data/test/conv_network/test_mklang.sql +8 -0
- data/test/conv_network/test_queries.sql +60 -0
- data/test/conv_network/test_queries.sql.in +60 -0
- data/test/plp/b.rb +34 -0
- data/test/plp/runtest +29 -0
- data/test/plp/test.expected.73 +472 -0
- data/test/plp/test.expected.74 +472 -0
- data/test/plp/test.expected.75 +472 -0
- data/test/plp/test.expected.80 +472 -0
- data/test/plp/test.expected.81 +472 -0
- data/test/plp/test.expected.82 +472 -0
- data/test/plp/test.expected.83 +472 -0
- data/test/plp/test.expected.84 +472 -0
- data/test/plp/test.out +472 -0
- data/test/plp/test_mklang.sql +8 -0
- data/test/plp/test_queries.sql +273 -0
- data/test/plp/test_setup.sql +931 -0
- data/test/plp/test_setup.sql.in +931 -0
- data/test/plt/b.rb +34 -0
- data/test/plt/runtest +29 -0
- data/test/plt/test.expected.73 +178 -0
- data/test/plt/test.expected.74 +178 -0
- data/test/plt/test.expected.75 +178 -0
- data/test/plt/test.expected.80 +178 -0
- data/test/plt/test.expected.81 +178 -0
- data/test/plt/test.expected.82 +178 -0
- data/test/plt/test.expected.83 +164 -0
- data/test/plt/test.expected.84 +168 -0
- data/test/plt/test.out +168 -0
- data/test/plt/test_mklang.sql +8 -0
- data/test/plt/test_queries.sql +72 -0
- data/test/plt/test_setup.sql +252 -0
- data/test/plt/test_setup.sql.in +252 -0
- data/test/range/b.rb +45 -0
- data/test/range/runtest +26 -0
- data/test/range/test.expected.73 +396 -0
- data/test/range/test.expected.73.in +396 -0
- data/test/range/test.expected.74 +396 -0
- data/test/range/test.expected.74.in +396 -0
- data/test/range/test.expected.75 +396 -0
- data/test/range/test.expected.75.in +396 -0
- data/test/range/test.expected.80 +396 -0
- data/test/range/test.expected.81 +397 -0
- data/test/range/test.expected.82 +397 -0
- data/test/range/test.expected.83 +397 -0
- data/test/range/test.expected.84 +399 -0
- data/test/range/test.out +399 -0
- data/test/range/test_mklang.sql +8 -0
- data/test/range/test_queries.sql +249 -0
- data/test/range/test_queries.sql.in +249 -0
- metadata +207 -0
data/src/plruby.h
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#include <unistd.h>
|
|
2
|
+
#include <fcntl.h>
|
|
3
|
+
#include <setjmp.h>
|
|
4
|
+
|
|
5
|
+
#include "postgres.h"
|
|
6
|
+
#include "executor/spi.h"
|
|
7
|
+
#include "executor/executor.h"
|
|
8
|
+
#include "commands/trigger.h"
|
|
9
|
+
#include "utils/elog.h"
|
|
10
|
+
#include "utils/builtins.h"
|
|
11
|
+
#include "fmgr.h"
|
|
12
|
+
#include "access/heapam.h"
|
|
13
|
+
|
|
14
|
+
#include "tcop/tcopprot.h"
|
|
15
|
+
#include "utils/syscache.h"
|
|
16
|
+
#include "catalog/pg_proc.h"
|
|
17
|
+
#include "catalog/pg_language.h"
|
|
18
|
+
#include "catalog/pg_type.h"
|
|
19
|
+
|
|
20
|
+
#include "nodes/makefuncs.h"
|
|
21
|
+
#include "nodes/nodes.h"
|
|
22
|
+
#include "parser/parse_type.h"
|
|
23
|
+
#include "utils/lsyscache.h"
|
|
24
|
+
#include "funcapi.h"
|
|
25
|
+
|
|
26
|
+
#include "utils/array.h"
|
|
27
|
+
|
|
28
|
+
#if PG_PL_VERSION >= 75
|
|
29
|
+
#include "nodes/pg_list.h"
|
|
30
|
+
#include "utils/typcache.h"
|
|
31
|
+
#include "access/xact.h"
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
#if PG_PL_VERSION >= 81
|
|
35
|
+
#include "utils/memutils.h"
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
#include "package.h"
|
|
39
|
+
|
|
40
|
+
#include <ruby.h>
|
|
41
|
+
#if HAVE_RUBY_ST_H
|
|
42
|
+
#include <ruby/st.h>
|
|
43
|
+
#elif HAVE_ST_H
|
|
44
|
+
#include <st.h>
|
|
45
|
+
#endif
|
|
46
|
+
|
|
47
|
+
#ifndef StringValuePtr
|
|
48
|
+
#define StringValuePtr(x) STR2CSTR(x)
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
#ifndef RSTRING_PTR
|
|
52
|
+
# define RSTRING_PTR(x_) RSTRING(x_)->ptr
|
|
53
|
+
# define RSTRING_LEN(x_) RSTRING(x_)->len
|
|
54
|
+
#endif
|
|
55
|
+
|
|
56
|
+
#ifndef RARRAY_PTR
|
|
57
|
+
# define RARRAY_PTR(x_) RARRAY(x_)->ptr
|
|
58
|
+
# define RARRAY_LEN(x_) RARRAY(x_)->len
|
|
59
|
+
#endif
|
|
60
|
+
|
|
61
|
+
#ifndef RHASH_TBL
|
|
62
|
+
#define RHASH_TBL(x_) (RHASH(x_)->tbl)
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
#ifndef RFLOAT_VALUE
|
|
66
|
+
#define RFLOAT_VALUE(x_) (RFLOAT(x_)->value)
|
|
67
|
+
#endif
|
|
68
|
+
|
|
69
|
+
extern VALUE rb_thread_list();
|
|
70
|
+
|
|
71
|
+
#ifndef SAFE_LEVEL
|
|
72
|
+
#define SAFE_LEVEL 12
|
|
73
|
+
#endif
|
|
74
|
+
|
|
75
|
+
#ifndef MAIN_SAFE_LEVEL
|
|
76
|
+
#ifdef PLRUBY_TIMEOUT
|
|
77
|
+
#define MAIN_SAFE_LEVEL 3
|
|
78
|
+
#else
|
|
79
|
+
#define MAIN_SAFE_LEVEL SAFE_LEVEL
|
|
80
|
+
#endif
|
|
81
|
+
#endif
|
|
82
|
+
|
|
83
|
+
#if SAFE_LEVEL <= MAIN_SAFE_LEVEL
|
|
84
|
+
#define MAIN_SAFE_LEVEL SAFE_LEVEL
|
|
85
|
+
#endif
|
|
86
|
+
|
|
87
|
+
#ifdef PLRUBY_TIMEOUT
|
|
88
|
+
|
|
89
|
+
extern int plruby_in_progress;
|
|
90
|
+
extern int plruby_interrupted;
|
|
91
|
+
|
|
92
|
+
#define PLRUBY_BEGIN(lvl_) do { \
|
|
93
|
+
int in_progress = plruby_in_progress; \
|
|
94
|
+
if (plruby_interrupted) { \
|
|
95
|
+
rb_raise(pl_ePLruby, "timeout"); \
|
|
96
|
+
} \
|
|
97
|
+
plruby_in_progress = lvl_;
|
|
98
|
+
|
|
99
|
+
#define PLRUBY_END \
|
|
100
|
+
plruby_in_progress = in_progress; \
|
|
101
|
+
if (plruby_interrupted) { \
|
|
102
|
+
rb_raise(pl_ePLruby, "timeout"); \
|
|
103
|
+
} \
|
|
104
|
+
} while (0)
|
|
105
|
+
|
|
106
|
+
#ifdef PG_PL_TRYCATCH
|
|
107
|
+
|
|
108
|
+
#define PLRUBY_BEGIN_PROTECT(lvl_) do { \
|
|
109
|
+
int in_progress = plruby_in_progress; \
|
|
110
|
+
if (plruby_interrupted) { \
|
|
111
|
+
rb_raise(pl_ePLruby, "timeout"); \
|
|
112
|
+
} \
|
|
113
|
+
plruby_in_progress = lvl_; \
|
|
114
|
+
PG_TRY(); \
|
|
115
|
+
{
|
|
116
|
+
|
|
117
|
+
extern int errorcode;
|
|
118
|
+
|
|
119
|
+
#define PLRUBY_END_PROTECT \
|
|
120
|
+
} \
|
|
121
|
+
PG_CATCH(); \
|
|
122
|
+
{ \
|
|
123
|
+
plruby_in_progress = in_progress; \
|
|
124
|
+
rb_raise(pl_eCatch, "propagate"); \
|
|
125
|
+
} \
|
|
126
|
+
PG_END_TRY(); \
|
|
127
|
+
plruby_in_progress = in_progress; \
|
|
128
|
+
if (plruby_interrupted) { \
|
|
129
|
+
rb_raise(pl_ePLruby, "timeout"); \
|
|
130
|
+
} \
|
|
131
|
+
} while (0)
|
|
132
|
+
|
|
133
|
+
#else
|
|
134
|
+
|
|
135
|
+
#define PLRUBY_BEGIN_PROTECT(lvl_) do { \
|
|
136
|
+
sigjmp_buf save_restart; \
|
|
137
|
+
int in_progress = plruby_in_progress; \
|
|
138
|
+
if (plruby_interrupted) { \
|
|
139
|
+
rb_raise(pl_ePLruby, "timeout"); \
|
|
140
|
+
} \
|
|
141
|
+
memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); \
|
|
142
|
+
if (sigsetjmp(Warn_restart, 1) != 0) { \
|
|
143
|
+
plruby_in_progress = in_progress; \
|
|
144
|
+
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \
|
|
145
|
+
rb_raise(pl_eCatch, "propagate"); \
|
|
146
|
+
} \
|
|
147
|
+
plruby_in_progress = lvl_;
|
|
148
|
+
|
|
149
|
+
#define PLRUBY_END_PROTECT \
|
|
150
|
+
plruby_in_progress = in_progress; \
|
|
151
|
+
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \
|
|
152
|
+
if (plruby_interrupted) { \
|
|
153
|
+
rb_raise(pl_ePLruby, "timeout"); \
|
|
154
|
+
} \
|
|
155
|
+
} while (0)
|
|
156
|
+
|
|
157
|
+
#endif
|
|
158
|
+
|
|
159
|
+
#else
|
|
160
|
+
|
|
161
|
+
#define PLRUBY_BEGIN(lvl_)
|
|
162
|
+
#define PLRUBY_END
|
|
163
|
+
|
|
164
|
+
#ifdef PG_PL_TRYCATCH
|
|
165
|
+
|
|
166
|
+
#define PLRUBY_BEGIN_PROTECT(lvl_) do { \
|
|
167
|
+
PG_TRY(); \
|
|
168
|
+
{
|
|
169
|
+
|
|
170
|
+
#define PLRUBY_END_PROTECT \
|
|
171
|
+
} \
|
|
172
|
+
PG_CATCH(); \
|
|
173
|
+
{ \
|
|
174
|
+
rb_raise(pl_eCatch, "propagate"); \
|
|
175
|
+
} \
|
|
176
|
+
PG_END_TRY(); \
|
|
177
|
+
} while (0)
|
|
178
|
+
|
|
179
|
+
#else
|
|
180
|
+
|
|
181
|
+
#define PLRUBY_BEGIN_PROTECT(lvl_) do { \
|
|
182
|
+
sigjmp_buf save_restart; \
|
|
183
|
+
memcpy(&save_restart, &Warn_restart, sizeof(save_restart)); \
|
|
184
|
+
if (sigsetjmp(Warn_restart, 1) != 0) { \
|
|
185
|
+
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \
|
|
186
|
+
rb_raise(pl_eCatch, "propagate"); \
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#define PLRUBY_END_PROTECT \
|
|
190
|
+
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); \
|
|
191
|
+
} while (0)
|
|
192
|
+
|
|
193
|
+
#endif
|
|
194
|
+
|
|
195
|
+
#endif
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
enum { TG_OK, TG_SKIP };
|
|
200
|
+
enum { TG_BEFORE, TG_AFTER, TG_ROW, TG_STATEMENT, TG_INSERT,
|
|
201
|
+
TG_DELETE, TG_UPDATE, TG_UNKNOWN };
|
|
202
|
+
|
|
203
|
+
struct pl_thread_st {
|
|
204
|
+
PG_FUNCTION_ARGS;
|
|
205
|
+
int timeout;
|
|
206
|
+
Oid validator;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
typedef struct pl_proc_desc
|
|
210
|
+
{
|
|
211
|
+
char *proname;
|
|
212
|
+
TransactionId fn_xmin;
|
|
213
|
+
CommandId fn_cmin;
|
|
214
|
+
FmgrInfo result_func;
|
|
215
|
+
Oid result_elem;
|
|
216
|
+
Oid result_oid;
|
|
217
|
+
int result_len;
|
|
218
|
+
bool result_is_array;
|
|
219
|
+
bool result_val;
|
|
220
|
+
bool result_is_setof;
|
|
221
|
+
char result_align;
|
|
222
|
+
int nargs;
|
|
223
|
+
#if PG_PL_VERSION >= 75
|
|
224
|
+
int named_args;
|
|
225
|
+
#endif
|
|
226
|
+
FmgrInfo arg_func[FUNC_MAX_ARGS];
|
|
227
|
+
Oid arg_elem[FUNC_MAX_ARGS];
|
|
228
|
+
Oid arg_type[FUNC_MAX_ARGS];
|
|
229
|
+
int arg_len[FUNC_MAX_ARGS];
|
|
230
|
+
bool arg_is_array[FUNC_MAX_ARGS];
|
|
231
|
+
bool arg_val[FUNC_MAX_ARGS];
|
|
232
|
+
char arg_align[FUNC_MAX_ARGS];
|
|
233
|
+
int arg_is_rel[FUNC_MAX_ARGS];
|
|
234
|
+
char result_type;
|
|
235
|
+
} pl_proc_desc;
|
|
236
|
+
|
|
237
|
+
struct portal_options {
|
|
238
|
+
VALUE argsv;
|
|
239
|
+
int count, output;
|
|
240
|
+
int block, save;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
typedef struct pl_query_desc
|
|
244
|
+
{
|
|
245
|
+
char qname[20];
|
|
246
|
+
void *plan;
|
|
247
|
+
int nargs;
|
|
248
|
+
Oid *argtypes;
|
|
249
|
+
FmgrInfo *arginfuncs;
|
|
250
|
+
Oid *argtypelems;
|
|
251
|
+
int *arglen;
|
|
252
|
+
bool *arg_is_array;
|
|
253
|
+
bool *arg_val;
|
|
254
|
+
char *arg_align;
|
|
255
|
+
int cursor;
|
|
256
|
+
struct portal_options po;
|
|
257
|
+
} pl_query_desc;
|
|
258
|
+
|
|
259
|
+
struct PLportal {
|
|
260
|
+
Portal portal;
|
|
261
|
+
char *nulls;
|
|
262
|
+
Datum *argvalues;
|
|
263
|
+
int *arglen;
|
|
264
|
+
int nargs;
|
|
265
|
+
struct portal_options po;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
#define GetPortal(obj, portal) do { \
|
|
269
|
+
Data_Get_Struct(obj, struct PLportal, portal); \
|
|
270
|
+
if (!portal->portal) { \
|
|
271
|
+
rb_raise(pl_ePLruby, "cursor closed"); \
|
|
272
|
+
} \
|
|
273
|
+
} while (0)
|
|
274
|
+
|
|
275
|
+
#define GetPlan(obj, qdesc) do { \
|
|
276
|
+
Data_Get_Struct(obj, pl_query_desc, qdesc); \
|
|
277
|
+
if (!qdesc->plan) { \
|
|
278
|
+
rb_raise(pl_ePLruby, "plan was dropped during the session"); \
|
|
279
|
+
} \
|
|
280
|
+
} while (0)
|
|
281
|
+
|
|
282
|
+
#define RET_HASH 1
|
|
283
|
+
#define RET_ARRAY 2
|
|
284
|
+
#define RET_DESC 4
|
|
285
|
+
#define RET_DESC_ARR 12
|
|
286
|
+
#define RET_BASIC 16
|
|
287
|
+
|
|
288
|
+
extern VALUE plruby_s_new _((int, VALUE *, VALUE));
|
|
289
|
+
extern VALUE plruby_build_tuple _((HeapTuple, TupleDesc, int));
|
|
290
|
+
extern Datum plruby_to_datum _((VALUE, FmgrInfo *, Oid, Oid, int));
|
|
291
|
+
extern Datum plruby_return_value _((struct pl_thread_st *, pl_proc_desc *,
|
|
292
|
+
VALUE, VALUE));
|
|
293
|
+
extern VALUE plruby_create_args _((struct pl_thread_st *, pl_proc_desc *));
|
|
294
|
+
extern VALUE plruby_i_each _((VALUE, struct portal_options *));
|
|
295
|
+
extern void plruby_exec_output _((VALUE, int, int *));
|
|
296
|
+
extern VALUE plruby_to_s _((VALUE));
|
|
297
|
+
|
|
298
|
+
extern Datum plruby_return_array _((VALUE, pl_proc_desc *));
|
|
299
|
+
extern MemoryContext plruby_spi_context;
|
|
300
|
+
|
|
301
|
+
extern Datum plruby_dfc0 _((PGFunction));
|
|
302
|
+
extern Datum plruby_dfc1 _((PGFunction, Datum));
|
|
303
|
+
extern Datum plruby_dfc2 _((PGFunction, Datum, Datum));
|
|
304
|
+
extern Datum plruby_dfc3 _((PGFunction, Datum, Datum, Datum));
|
|
305
|
+
|
|
306
|
+
#ifdef PLRUBY_ENABLE_CONVERSION
|
|
307
|
+
extern VALUE plruby_classes, plruby_conversions;
|
|
308
|
+
extern Oid plruby_datum_oid _((VALUE, int *));
|
|
309
|
+
extern VALUE plruby_datum_set _((VALUE, Datum));
|
|
310
|
+
extern Datum plruby_datum_get _((VALUE, Oid *));
|
|
311
|
+
extern VALUE plruby_define_void_class _((char *, char *));
|
|
312
|
+
#endif
|
|
313
|
+
|
|
314
|
+
#define DFC1(a_, b_) DirectFunctionCall1((a_), (b_))
|
|
315
|
+
|
|
316
|
+
#define OidGD(a_) ObjectIdGetDatum(a_)
|
|
317
|
+
#define PointerGD(a_) PointerGetDatum(a_)
|
|
318
|
+
#define NameGD(a_) NameGetDatum(a_)
|
|
319
|
+
#define BoolGD(a_) BoolGetDatum(a_)
|
|
320
|
+
#define IntGD(a_) Int32GetDatum(a_)
|
|
321
|
+
#define CStringGD(a_) CStringGetDatum(a_)
|
|
322
|
+
#define TupleGD(a_,b_) TupleGetDatum((a_),(b_))
|
|
323
|
+
|
|
324
|
+
|
data/src/pltrans.c
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#include "plruby.h"
|
|
2
|
+
|
|
3
|
+
#if PG_PL_VERSION >= 75
|
|
4
|
+
|
|
5
|
+
#define PG_PL_READ_UNCOMMITTED 0
|
|
6
|
+
#define PG_PL_READ_COMMITTED 1
|
|
7
|
+
#define PG_PL_REPETABLE_READ 2
|
|
8
|
+
#define PG_PL_SERIALIZABLE 3
|
|
9
|
+
|
|
10
|
+
#define PL_ELOG_DEBUG 0
|
|
11
|
+
|
|
12
|
+
#if PL_ELOG_DEBUG
|
|
13
|
+
#define pl_elog(a,b) elog(a,b)
|
|
14
|
+
#else
|
|
15
|
+
#define pl_elog(a,b)
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
static int pl_in_transaction = 0;
|
|
19
|
+
static VALUE pl_eCatch, pl_ePLruby, pl_cTrans;
|
|
20
|
+
|
|
21
|
+
struct pl_trans {
|
|
22
|
+
VALUE name;
|
|
23
|
+
int commit;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
static void
|
|
27
|
+
pl_trans_mark(void *trans)
|
|
28
|
+
{
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#define GetTrans(obj_, trans_) do { \
|
|
32
|
+
if (TYPE(obj_) != T_DATA || \
|
|
33
|
+
RDATA(obj_)->dmark != (RUBY_DATA_FUNC)pl_trans_mark) { \
|
|
34
|
+
rb_raise(rb_eArgError, \
|
|
35
|
+
"transaction method called with a wrong object"); \
|
|
36
|
+
} \
|
|
37
|
+
Data_Get_Struct(obj_, struct pl_trans, trans_); \
|
|
38
|
+
} while (0)
|
|
39
|
+
|
|
40
|
+
static char *savename = "savepoint_name";
|
|
41
|
+
|
|
42
|
+
static DefElem *
|
|
43
|
+
make_defelem(char *name, VALUE arg)
|
|
44
|
+
{
|
|
45
|
+
DefElem *f = makeNode(DefElem);
|
|
46
|
+
f->defname = name;
|
|
47
|
+
f->arg = (Node *)makeString(RSTRING_PTR(arg));
|
|
48
|
+
return f;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
static VALUE
|
|
53
|
+
pl_intern_commit(VALUE obj)
|
|
54
|
+
{
|
|
55
|
+
struct pl_trans *trans;
|
|
56
|
+
int rc;
|
|
57
|
+
|
|
58
|
+
pl_elog(NOTICE, "==> pl_intern_commit");
|
|
59
|
+
GetTrans(obj, trans);
|
|
60
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
61
|
+
if (NIL_P(trans->name)) {
|
|
62
|
+
if (!trans->commit) {
|
|
63
|
+
pl_elog(NOTICE, "ReleaseCurrentSubTransaction");
|
|
64
|
+
trans->commit = Qtrue;
|
|
65
|
+
if ((rc = SPI_finish()) != SPI_OK_FINISH) {
|
|
66
|
+
elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
|
|
67
|
+
}
|
|
68
|
+
ReleaseCurrentSubTransaction();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
List *list;
|
|
73
|
+
|
|
74
|
+
pl_elog(NOTICE, "ReleaseSavepoint");
|
|
75
|
+
list = list_make1(make_defelem(savename, trans->name));
|
|
76
|
+
trans->name = Qnil;
|
|
77
|
+
trans->commit = Qtrue;
|
|
78
|
+
ReleaseSavepoint(list);
|
|
79
|
+
CommitTransactionCommand();
|
|
80
|
+
StartTransactionCommand();
|
|
81
|
+
}
|
|
82
|
+
PLRUBY_END_PROTECT;
|
|
83
|
+
pl_elog(NOTICE, "<== pl_intern_commit");
|
|
84
|
+
return Qnil;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
struct pl_throw {
|
|
88
|
+
VALUE txn;
|
|
89
|
+
int commit;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
static void
|
|
93
|
+
pl_throw_mark(struct pl_throw *plt)
|
|
94
|
+
{
|
|
95
|
+
rb_gc_mark(plt->txn);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static VALUE
|
|
99
|
+
pl_commit(VALUE obj)
|
|
100
|
+
{
|
|
101
|
+
VALUE res;
|
|
102
|
+
struct pl_throw *plt;
|
|
103
|
+
|
|
104
|
+
pl_elog(NOTICE, "pl_commit");
|
|
105
|
+
if (!IsSubTransaction()) {
|
|
106
|
+
rb_raise(pl_ePLruby, "outside a transaction");
|
|
107
|
+
}
|
|
108
|
+
res = Data_Make_Struct(pl_cTrans, struct pl_throw, pl_throw_mark, free, plt);
|
|
109
|
+
plt->commit = Qtrue;
|
|
110
|
+
plt->txn = obj;
|
|
111
|
+
rb_throw("__plruby__transaction__", res);
|
|
112
|
+
return Qnil;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static VALUE
|
|
116
|
+
pl_intern_abort(VALUE obj)
|
|
117
|
+
{
|
|
118
|
+
struct pl_trans *trans;
|
|
119
|
+
int rc;
|
|
120
|
+
|
|
121
|
+
pl_elog(NOTICE, "==> pl_intern_abort");
|
|
122
|
+
if (!IsSubTransaction()) {
|
|
123
|
+
rb_raise(pl_ePLruby, "outside a transaction");
|
|
124
|
+
}
|
|
125
|
+
GetTrans(obj, trans);
|
|
126
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
127
|
+
if (NIL_P(trans->name)) {
|
|
128
|
+
if (!trans->commit) {
|
|
129
|
+
pl_elog(NOTICE, "RollbackAndReleaseCurrentSubTransaction");
|
|
130
|
+
trans->commit = Qtrue;
|
|
131
|
+
if ((rc = SPI_finish()) != SPI_OK_FINISH) {
|
|
132
|
+
elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
|
|
133
|
+
}
|
|
134
|
+
RollbackAndReleaseCurrentSubTransaction();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
List *list;
|
|
139
|
+
|
|
140
|
+
pl_elog(NOTICE, "RollbackToSavepoint");
|
|
141
|
+
list = list_make1(make_defelem(savename, trans->name));
|
|
142
|
+
trans->name = Qnil;
|
|
143
|
+
trans->commit = Qtrue;
|
|
144
|
+
RollbackToSavepoint(list);
|
|
145
|
+
CommitTransactionCommand();
|
|
146
|
+
RollbackAndReleaseCurrentSubTransaction();
|
|
147
|
+
}
|
|
148
|
+
PLRUBY_END_PROTECT;
|
|
149
|
+
pl_elog(NOTICE, "<== pl_intern_abort");
|
|
150
|
+
return Qnil;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static VALUE
|
|
154
|
+
pl_intern_error(VALUE obj)
|
|
155
|
+
{
|
|
156
|
+
struct pl_trans *trans;
|
|
157
|
+
|
|
158
|
+
pl_elog(NOTICE, "==> pl_intern_error");
|
|
159
|
+
if (!IsSubTransaction()) {
|
|
160
|
+
rb_raise(pl_ePLruby, "outside a transaction");
|
|
161
|
+
}
|
|
162
|
+
GetTrans(obj, trans);
|
|
163
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
164
|
+
pl_elog(NOTICE, "RollbackAndReleaseCurrentSubTransaction");
|
|
165
|
+
trans->commit = Qtrue;
|
|
166
|
+
RollbackAndReleaseCurrentSubTransaction();
|
|
167
|
+
PLRUBY_END_PROTECT;
|
|
168
|
+
pl_elog(NOTICE, "<== pl_intern_error");
|
|
169
|
+
return Qnil;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static VALUE
|
|
173
|
+
pl_abort(VALUE obj)
|
|
174
|
+
{
|
|
175
|
+
VALUE res;
|
|
176
|
+
struct pl_throw *plt;
|
|
177
|
+
|
|
178
|
+
pl_elog(NOTICE, "pl_abort");
|
|
179
|
+
if (!IsSubTransaction()) {
|
|
180
|
+
rb_raise(pl_ePLruby, "outside a transaction");
|
|
181
|
+
}
|
|
182
|
+
res = Data_Make_Struct(pl_cTrans, struct pl_throw, pl_throw_mark, free, plt);
|
|
183
|
+
plt->commit = Qfalse;
|
|
184
|
+
plt->txn = obj;
|
|
185
|
+
rb_throw("__plruby__transaction__", res);
|
|
186
|
+
return Qnil;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static VALUE
|
|
190
|
+
pl_exec(VALUE val, VALUE args, VALUE self)
|
|
191
|
+
{
|
|
192
|
+
rb_yield(args);
|
|
193
|
+
return Qnil;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static VALUE
|
|
197
|
+
pl_catch(VALUE obj)
|
|
198
|
+
{
|
|
199
|
+
VALUE res;
|
|
200
|
+
struct pl_throw *plt;
|
|
201
|
+
|
|
202
|
+
pl_elog(NOTICE, "pl_catch");
|
|
203
|
+
res = rb_catch("__plruby__transaction__", pl_exec, obj);
|
|
204
|
+
if (TYPE(res) == T_DATA &&
|
|
205
|
+
RDATA(res)->dmark == (RUBY_DATA_FUNC)pl_throw_mark) {
|
|
206
|
+
Data_Get_Struct(res, struct pl_throw, plt);
|
|
207
|
+
if (plt->commit) {
|
|
208
|
+
pl_intern_commit(obj);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
pl_intern_abort(obj);
|
|
212
|
+
}
|
|
213
|
+
if (obj != plt->txn) {
|
|
214
|
+
rb_throw("__plruby__transaction__", res);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
pl_intern_commit(obj);
|
|
219
|
+
}
|
|
220
|
+
return Qnil;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
static VALUE
|
|
225
|
+
pl_transaction(VALUE obj)
|
|
226
|
+
{
|
|
227
|
+
struct pl_trans *trans;
|
|
228
|
+
VALUE res;
|
|
229
|
+
int state, rc, begin_sub;
|
|
230
|
+
MemoryContext orig_context = 0;
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
pl_elog(NOTICE, "==> pl_transaction");
|
|
234
|
+
if (!rb_block_given_p()) {
|
|
235
|
+
rb_raise(rb_eArgError, "no block given");
|
|
236
|
+
}
|
|
237
|
+
res = Data_Make_Struct(pl_cTrans, struct pl_trans, pl_trans_mark, 0, trans);
|
|
238
|
+
trans->name = Qnil;
|
|
239
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
240
|
+
if (IsSubTransaction()) {
|
|
241
|
+
char name[1024];
|
|
242
|
+
|
|
243
|
+
pl_elog(NOTICE, "DefineSavepoint");
|
|
244
|
+
sprintf(name, "__plruby__%d__", pl_in_transaction);
|
|
245
|
+
DefineSavepoint(name);
|
|
246
|
+
CommitTransactionCommand();
|
|
247
|
+
StartTransactionCommand();
|
|
248
|
+
pl_in_transaction++;
|
|
249
|
+
begin_sub = Qfalse;
|
|
250
|
+
trans->name = rb_str_new2(name);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
pl_in_transaction = 0;
|
|
254
|
+
pl_elog(NOTICE, "BeginTransactionBlock");
|
|
255
|
+
orig_context = CurrentMemoryContext;
|
|
256
|
+
SPI_push();
|
|
257
|
+
BeginInternalSubTransaction(NULL);
|
|
258
|
+
MemoryContextSwitchTo(orig_context);
|
|
259
|
+
if ((rc = SPI_connect()) != SPI_OK_CONNECT) {
|
|
260
|
+
elog(ERROR, "SPI_connect in transaction failed : %s",
|
|
261
|
+
SPI_result_code_string(rc));
|
|
262
|
+
}
|
|
263
|
+
begin_sub = Qtrue;
|
|
264
|
+
}
|
|
265
|
+
PLRUBY_END_PROTECT;
|
|
266
|
+
pl_elog(NOTICE, "==> rb_protect");
|
|
267
|
+
rb_protect(pl_catch, res, &state);
|
|
268
|
+
pl_elog(NOTICE, "<== rb_protect");
|
|
269
|
+
if (state) {
|
|
270
|
+
VALUE error = rb_gv_get("$!");
|
|
271
|
+
if (begin_sub && CLASS_OF(error) == pl_eCatch) {
|
|
272
|
+
if (!trans->commit) {
|
|
273
|
+
rb_protect(pl_intern_error, res, 0);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
if (!trans->commit) {
|
|
278
|
+
rb_protect(pl_intern_abort, res, 0);
|
|
279
|
+
}
|
|
280
|
+
if (begin_sub) {
|
|
281
|
+
MemoryContextSwitchTo(orig_context);
|
|
282
|
+
SPI_pop();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
rb_jump_tag(state);
|
|
286
|
+
}
|
|
287
|
+
Data_Get_Struct(res, struct pl_trans, trans);
|
|
288
|
+
if (begin_sub) {
|
|
289
|
+
pl_elog(NOTICE, "commit");
|
|
290
|
+
if (!trans->commit) {
|
|
291
|
+
rb_protect(pl_intern_commit, res, 0);
|
|
292
|
+
}
|
|
293
|
+
MemoryContextSwitchTo(orig_context);
|
|
294
|
+
SPI_pop();
|
|
295
|
+
}
|
|
296
|
+
pl_elog(NOTICE, "<== pl_transaction");
|
|
297
|
+
return Qnil;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#endif
|
|
301
|
+
|
|
302
|
+
#if PG_PL_VERSION >= 81
|
|
303
|
+
|
|
304
|
+
static VALUE
|
|
305
|
+
pl_savepoint(VALUE obj, VALUE a)
|
|
306
|
+
{
|
|
307
|
+
if (!IsTransactionBlock() || !IsSubTransaction()) {
|
|
308
|
+
rb_raise(pl_ePLruby, "savepoint called outside a transaction");
|
|
309
|
+
}
|
|
310
|
+
a = plruby_to_s(a);
|
|
311
|
+
pl_elog(NOTICE, "====> definesavepoint");
|
|
312
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
313
|
+
DefineSavepoint(RSTRING_PTR(a));
|
|
314
|
+
CommitTransactionCommand();
|
|
315
|
+
StartTransactionCommand();
|
|
316
|
+
PLRUBY_END_PROTECT;
|
|
317
|
+
pl_elog(NOTICE, "<==== definesavepoint");
|
|
318
|
+
return Qnil;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static VALUE
|
|
322
|
+
pl_release(VALUE obj, VALUE a)
|
|
323
|
+
{
|
|
324
|
+
if (!IsTransactionBlock() || !IsSubTransaction()) {
|
|
325
|
+
rb_raise(pl_ePLruby, "release called outside a transaction");
|
|
326
|
+
}
|
|
327
|
+
a = plruby_to_s(a);
|
|
328
|
+
pl_elog(NOTICE, "====> releasesavepoint");
|
|
329
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
330
|
+
ReleaseSavepoint(list_make1(make_defelem("savepoint_name", a)));
|
|
331
|
+
CommitTransactionCommand();
|
|
332
|
+
StartTransactionCommand();
|
|
333
|
+
PLRUBY_END_PROTECT;
|
|
334
|
+
pl_elog(NOTICE, "<==== releasesavepoint");
|
|
335
|
+
return Qnil;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
static VALUE
|
|
339
|
+
pl_rollback(VALUE obj, VALUE a)
|
|
340
|
+
{
|
|
341
|
+
if (!IsTransactionBlock() || !IsSubTransaction()) {
|
|
342
|
+
rb_raise(pl_ePLruby, "rollback called outside a transaction");
|
|
343
|
+
}
|
|
344
|
+
a = plruby_to_s(a);
|
|
345
|
+
pl_elog(NOTICE, "====> rollbacksavepoint");
|
|
346
|
+
PLRUBY_BEGIN_PROTECT(1);
|
|
347
|
+
RollbackToSavepoint(list_make1(make_defelem("savepoint_name", a)));
|
|
348
|
+
CommitTransactionCommand();
|
|
349
|
+
RollbackAndReleaseCurrentSubTransaction();
|
|
350
|
+
PLRUBY_END_PROTECT;
|
|
351
|
+
pl_elog(NOTICE, "<==== rollbacksavepoint");
|
|
352
|
+
return Qnil;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
#endif
|
|
356
|
+
|
|
357
|
+
void
|
|
358
|
+
Init_plruby_trans()
|
|
359
|
+
{
|
|
360
|
+
#if PG_PL_VERSION >= 75
|
|
361
|
+
VALUE pl_mPL;
|
|
362
|
+
|
|
363
|
+
pl_mPL = rb_const_get(rb_cObject, rb_intern("PL"));
|
|
364
|
+
pl_ePLruby = rb_const_get(pl_mPL, rb_intern("Error"));
|
|
365
|
+
pl_eCatch = rb_const_get(pl_mPL, rb_intern("Catch"));
|
|
366
|
+
|
|
367
|
+
rb_define_global_const("READ_UNCOMMITED", INT2FIX(PG_PL_READ_UNCOMMITTED));
|
|
368
|
+
rb_define_global_const("READ_COMMITED", INT2FIX(PG_PL_READ_COMMITTED));
|
|
369
|
+
rb_define_global_const("REPETABLE_READ", INT2FIX(PG_PL_REPETABLE_READ));
|
|
370
|
+
rb_define_global_const("SERIALIZABLE", INT2FIX(PG_PL_SERIALIZABLE));
|
|
371
|
+
rb_define_global_function("transaction", pl_transaction, 0);
|
|
372
|
+
#if PG_PL_VERSION >= 81
|
|
373
|
+
rb_define_global_function("savepoint", pl_savepoint, 1);
|
|
374
|
+
rb_define_global_function("release_savepoint", pl_release, 1);
|
|
375
|
+
rb_define_global_function("rollback_to_savepoint", pl_rollback, 1);
|
|
376
|
+
#endif
|
|
377
|
+
pl_cTrans = rb_define_class_under(pl_mPL, "Transaction", rb_cObject);
|
|
378
|
+
#if HAVE_RB_DEFINE_ALLOC_FUNC
|
|
379
|
+
rb_undef_alloc_func(pl_cTrans);
|
|
380
|
+
#else
|
|
381
|
+
rb_undef_method(CLASS_OF(pl_cTrans), "allocate");
|
|
382
|
+
#endif
|
|
383
|
+
rb_undef_method(CLASS_OF(pl_cTrans), "new");
|
|
384
|
+
rb_define_method(pl_cTrans, "commit", pl_commit, 0);
|
|
385
|
+
rb_define_method(pl_cTrans, "abort", pl_abort, 0);
|
|
386
|
+
rb_define_method(pl_cTrans, "rollback", pl_abort, 0);
|
|
387
|
+
#endif
|
|
388
|
+
}
|