globegit-postgresql-plruby 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/Changes +121 -0
  2. data/README.markdown +155 -0
  3. data/Rakefile +48 -0
  4. data/docs/plruby.rb +1931 -0
  5. data/ex_trans.sql +33 -0
  6. data/extconf.rb +267 -0
  7. data/plruby.html +1454 -0
  8. data/plruby.rd +1571 -0
  9. data/postgresql-plruby.gemspec +56 -0
  10. data/src/conversions.h +5 -0
  11. data/src/conversions/basic/conversions.h +25 -0
  12. data/src/conversions/basic/extconf.rb +8 -0
  13. data/src/conversions/basic/plruby_basic.c +357 -0
  14. data/src/conversions/bitstring/bitstring.sql +75 -0
  15. data/src/conversions/bitstring/conversions.h +15 -0
  16. data/src/conversions/bitstring/extconf.rb +8 -0
  17. data/src/conversions/bitstring/plruby_bitstring.c +579 -0
  18. data/src/conversions/convcommon.h +129 -0
  19. data/src/conversions/datetime/conversions.h +13 -0
  20. data/src/conversions/datetime/extconf.rb +8 -0
  21. data/src/conversions/datetime/plruby_datetime.c +269 -0
  22. data/src/conversions/geometry/conversions.h +37 -0
  23. data/src/conversions/geometry/extconf.rb +8 -0
  24. data/src/conversions/geometry/geometry.sql +196 -0
  25. data/src/conversions/geometry/plruby_geometry.c +2494 -0
  26. data/src/conversions/network/conversions.h +21 -0
  27. data/src/conversions/network/extconf.rb +8 -0
  28. data/src/conversions/network/network.sql +63 -0
  29. data/src/conversions/network/plruby_network.c +537 -0
  30. data/src/package.h +20 -0
  31. data/src/plpl.c +1708 -0
  32. data/src/plplan.c +893 -0
  33. data/src/plruby.c +1676 -0
  34. data/src/plruby.h +324 -0
  35. data/src/pltrans.c +388 -0
  36. data/test/conv_bitstring/b.rb +45 -0
  37. data/test/conv_bitstring/runtest +26 -0
  38. data/test/conv_bitstring/test.expected.73 +148 -0
  39. data/test/conv_bitstring/test.expected.74 +148 -0
  40. data/test/conv_bitstring/test.expected.80 +148 -0
  41. data/test/conv_bitstring/test.expected.81 +148 -0
  42. data/test/conv_bitstring/test.expected.82 +148 -0
  43. data/test/conv_bitstring/test.expected.83 +148 -0
  44. data/test/conv_bitstring/test.expected.84 +148 -0
  45. data/test/conv_bitstring/test.out +148 -0
  46. data/test/conv_bitstring/test_mklang.sql +8 -0
  47. data/test/conv_bitstring/test_queries.sql +63 -0
  48. data/test/conv_bitstring/test_queries.sql.in +63 -0
  49. data/test/conv_geometry/b.rb +45 -0
  50. data/test/conv_geometry/runtest +26 -0
  51. data/test/conv_geometry/test.expected.73 +265 -0
  52. data/test/conv_geometry/test.expected.74 +265 -0
  53. data/test/conv_geometry/test.expected.80 +265 -0
  54. data/test/conv_geometry/test.expected.81 +265 -0
  55. data/test/conv_geometry/test.expected.82 +265 -0
  56. data/test/conv_geometry/test.expected.83 +265 -0
  57. data/test/conv_geometry/test.expected.84 +265 -0
  58. data/test/conv_geometry/test.out +265 -0
  59. data/test/conv_geometry/test_mklang.sql +8 -0
  60. data/test/conv_geometry/test_queries.sql +194 -0
  61. data/test/conv_geometry/test_queries.sql.in +194 -0
  62. data/test/conv_network/b.rb +45 -0
  63. data/test/conv_network/runtest +26 -0
  64. data/test/conv_network/test.expected.73 +213 -0
  65. data/test/conv_network/test.expected.74 +237 -0
  66. data/test/conv_network/test.expected.80 +237 -0
  67. data/test/conv_network/test.expected.81 +237 -0
  68. data/test/conv_network/test.expected.82 +237 -0
  69. data/test/conv_network/test.expected.83 +237 -0
  70. data/test/conv_network/test.expected.84 +237 -0
  71. data/test/conv_network/test.out +237 -0
  72. data/test/conv_network/test_mklang.sql +8 -0
  73. data/test/conv_network/test_queries.sql +60 -0
  74. data/test/conv_network/test_queries.sql.in +60 -0
  75. data/test/plp/b.rb +34 -0
  76. data/test/plp/runtest +29 -0
  77. data/test/plp/test.expected.73 +472 -0
  78. data/test/plp/test.expected.74 +472 -0
  79. data/test/plp/test.expected.75 +472 -0
  80. data/test/plp/test.expected.80 +472 -0
  81. data/test/plp/test.expected.81 +472 -0
  82. data/test/plp/test.expected.82 +472 -0
  83. data/test/plp/test.expected.83 +472 -0
  84. data/test/plp/test.expected.84 +472 -0
  85. data/test/plp/test.out +472 -0
  86. data/test/plp/test_mklang.sql +8 -0
  87. data/test/plp/test_queries.sql +273 -0
  88. data/test/plp/test_setup.sql +931 -0
  89. data/test/plp/test_setup.sql.in +931 -0
  90. data/test/plt/b.rb +34 -0
  91. data/test/plt/runtest +29 -0
  92. data/test/plt/test.expected.73 +178 -0
  93. data/test/plt/test.expected.74 +178 -0
  94. data/test/plt/test.expected.75 +178 -0
  95. data/test/plt/test.expected.80 +178 -0
  96. data/test/plt/test.expected.81 +178 -0
  97. data/test/plt/test.expected.82 +178 -0
  98. data/test/plt/test.expected.83 +164 -0
  99. data/test/plt/test.expected.84 +168 -0
  100. data/test/plt/test.out +168 -0
  101. data/test/plt/test_mklang.sql +8 -0
  102. data/test/plt/test_queries.sql +72 -0
  103. data/test/plt/test_setup.sql +252 -0
  104. data/test/plt/test_setup.sql.in +252 -0
  105. data/test/range/b.rb +45 -0
  106. data/test/range/runtest +26 -0
  107. data/test/range/test.expected.73 +396 -0
  108. data/test/range/test.expected.73.in +396 -0
  109. data/test/range/test.expected.74 +396 -0
  110. data/test/range/test.expected.74.in +396 -0
  111. data/test/range/test.expected.75 +396 -0
  112. data/test/range/test.expected.75.in +396 -0
  113. data/test/range/test.expected.80 +396 -0
  114. data/test/range/test.expected.81 +397 -0
  115. data/test/range/test.expected.82 +397 -0
  116. data/test/range/test.expected.83 +397 -0
  117. data/test/range/test.expected.84 +399 -0
  118. data/test/range/test.out +399 -0
  119. data/test/range/test_mklang.sql +8 -0
  120. data/test/range/test_queries.sql +249 -0
  121. data/test/range/test_queries.sql.in +249 -0
  122. metadata +207 -0
@@ -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
+
@@ -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
+ }