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,20 @@
1
+ #ifdef PACKAGE_NAME
2
+ #undef PACKAGE_NAME
3
+ #endif
4
+
5
+ #ifdef PACKAGE_TARNAME
6
+ #undef PACKAGE_TARNAME
7
+ #endif
8
+
9
+ #ifdef PACKAGE_VERSION
10
+ #undef PACKAGE_VERSION
11
+ #endif
12
+
13
+ #ifdef PACKAGE_STRING
14
+ #undef PACKAGE_STRING
15
+ #endif
16
+
17
+ #ifdef PACKAGE_BUGREPORT
18
+ #undef PACKAGE_BUGREPORT
19
+ #endif
20
+
@@ -0,0 +1,1708 @@
1
+ #include "plruby.h"
2
+
3
+ static VALUE pl_ePLruby, pl_mPLtemp;
4
+ static VALUE pl_mPL, pl_cPLPlan, pl_eCatch;
5
+
6
+ static ID id_thr;
7
+
8
+ static VALUE pl_SPI_exec _((int, VALUE *, VALUE));
9
+
10
+ #ifndef HAVE_RB_HASH_DELETE
11
+ static ID id_delete;
12
+
13
+ #define rb_hash_delete(a, b) rb_funcall((a), id_delete, 1, (b))
14
+
15
+ #endif
16
+
17
+ static char *names =
18
+ "SELECT a.attname FROM pg_class c, pg_attribute a, pg_namespace n"
19
+ " WHERE c.relname = '%s' AND a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid"
20
+ " AND c.relnamespace = n.oid AND n.nspname = '%s'"
21
+ " ORDER BY a.attnum";
22
+
23
+ static VALUE
24
+ pl_column_name(VALUE obj, VALUE table)
25
+ {
26
+ VALUE *query, res;
27
+ char *tmp;
28
+ char *nsp, *tbl, *c;
29
+
30
+ if (TYPE(table) != T_STRING || !RSTRING_PTR(table)) {
31
+ rb_raise(pl_ePLruby, "expected a String");
32
+ }
33
+ tmp = ALLOCA_N(char, strlen(names) + RSTRING_LEN(table) + 1);
34
+ nsp = ALLOCA_N(char, RSTRING_LEN(table) + 1);
35
+ tbl = ALLOCA_N(char, RSTRING_LEN(table) + 1);
36
+ strcpy(nsp, RSTRING_PTR(table));
37
+ if ((c = strchr(nsp, '.')) != NULL) {
38
+ *c = 0;
39
+ strcpy(tbl, c + 1);
40
+ }
41
+ else {
42
+ strcpy(tbl, nsp);
43
+ strcpy(nsp, "public");
44
+ }
45
+ sprintf(tmp, names, tbl, nsp);
46
+ query = ALLOCA_N(VALUE, 3);
47
+ MEMZERO(query, VALUE, 3);
48
+ query[0] = rb_str_new2(tmp);
49
+ query[1] = Qnil;
50
+ query[2] = rb_str_new2("value");
51
+ res = pl_SPI_exec(3, query, pl_mPL);
52
+ rb_funcall2(res, rb_intern("flatten!"), 0, 0);
53
+ return res;
54
+ }
55
+
56
+ static char *types =
57
+ "SELECT t.typname FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n"
58
+ " WHERE c.relname = '%s' and a.attnum > 0 AND NOT a.attisdropped"
59
+ " AND a.attrelid = c.oid and a.atttypid = t.oid"
60
+ " AND c.relnamespace = n.oid AND n.nspname = '%s'"
61
+ " ORDER BY a.attnum";
62
+
63
+ static VALUE
64
+ pl_column_type(VALUE obj, VALUE table)
65
+ {
66
+ VALUE *query, res;
67
+ char *tmp;
68
+ char *nsp, *tbl, *c;
69
+
70
+ if (TYPE(table) != T_STRING || !RSTRING_PTR(table)) {
71
+ rb_raise(pl_ePLruby, "expected a String");
72
+ }
73
+ tmp = ALLOCA_N(char, strlen(types) + RSTRING_LEN(table) + 1);
74
+ nsp = ALLOCA_N(char, RSTRING_LEN(table) + 1);
75
+ tbl = ALLOCA_N(char, RSTRING_LEN(table) + 1);
76
+ strcpy(nsp, RSTRING_PTR(table));
77
+ if ((c = strchr(nsp, '.')) != NULL) {
78
+ *c = 0;
79
+ strcpy(tbl, c + 1);
80
+ }
81
+ else {
82
+ strcpy(tbl, nsp);
83
+ strcpy(nsp, "public");
84
+ }
85
+ sprintf(tmp, types, tbl, nsp);
86
+ query = ALLOCA_N(VALUE, 3);
87
+ MEMZERO(query, VALUE, 3);
88
+ query[0] = rb_str_new2(tmp);
89
+ query[1] = Qnil;
90
+ query[2] = rb_str_new2("value");
91
+ res = pl_SPI_exec(3, query, pl_mPL);
92
+ rb_funcall2(res, rb_intern("flatten!"), 0, 0);
93
+ return res;
94
+ }
95
+
96
+ struct pl_tuple {
97
+ MemoryContext cxt;
98
+ AttInMetadata *att;
99
+ pl_proc_desc *pro;
100
+ TupleDesc dsc;
101
+ Tuplestorestate *out;
102
+ PG_FUNCTION_ARGS;
103
+ };
104
+
105
+ #if PG_PL_VERSION >= 75
106
+ #define SortMem work_mem
107
+ #endif
108
+
109
+ extern int SortMem;
110
+
111
+ static void pl_thr_mark(struct pl_tuple *tpl) {}
112
+
113
+ #define GetTuple(tmp_, tpl_) do { \
114
+ if (TYPE(tmp_) != T_DATA || \
115
+ RDATA(tmp_)->dmark != (RUBY_DATA_FUNC)pl_thr_mark) { \
116
+ rb_raise(pl_ePLruby, "invalid thread local variable"); \
117
+ } \
118
+ Data_Get_Struct(tmp_, struct pl_tuple, tpl_); \
119
+ } while(0)
120
+
121
+
122
+ static VALUE
123
+ pl_query_name(VALUE obj)
124
+ {
125
+ VALUE res, tmp;
126
+ struct pl_tuple *tpl;
127
+ char * attname;
128
+ int i;
129
+
130
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
131
+ if (NIL_P(tmp)) {
132
+ return Qnil;
133
+ }
134
+ GetTuple(tmp, tpl);
135
+ if (!tpl->dsc) {
136
+ return Qnil;
137
+ }
138
+ res = rb_ary_new2(tpl->dsc->natts);
139
+ for (i = 0; i < tpl->dsc->natts; i++) {
140
+ if (tpl->dsc->attrs[i]->attisdropped) {
141
+ attname = "";
142
+ }
143
+ else {
144
+ attname = NameStr(tpl->dsc->attrs[i]->attname);
145
+ }
146
+ rb_ary_push(res, rb_tainted_str_new2(attname));
147
+ }
148
+ return res;
149
+ }
150
+
151
+ static VALUE
152
+ pl_query_type(VALUE obj)
153
+ {
154
+ struct pl_tuple *tpl;
155
+ VALUE res, tmp;
156
+ char * attname;
157
+ HeapTuple typeTup;
158
+ Form_pg_type fpgt;
159
+ int i;
160
+
161
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
162
+ if (NIL_P(tmp)) {
163
+ return Qnil;
164
+ }
165
+ GetTuple(tmp, tpl);
166
+ if (!tpl->dsc) {
167
+ PLRUBY_BEGIN(1);
168
+ typeTup = SearchSysCache(TYPEOID, OidGD(tpl->pro->result_oid),0,0,0);
169
+ PLRUBY_END;
170
+ if (!HeapTupleIsValid(typeTup)) {
171
+ rb_raise(pl_ePLruby, "Cache lookup for result type %ld failed",
172
+ tpl->pro->result_oid);
173
+ }
174
+ fpgt = (Form_pg_type) GETSTRUCT(typeTup);
175
+ res = rb_tainted_str_new2(NameStr(fpgt->typname));
176
+ ReleaseSysCache(typeTup);
177
+ return res;
178
+ }
179
+ res = rb_ary_new2(tpl->dsc->natts);
180
+ for (i = 0; i < tpl->dsc->natts; i++) {
181
+ if (tpl->dsc->attrs[i]->attisdropped)
182
+ continue;
183
+ PLRUBY_BEGIN(1);
184
+ attname = NameStr(tpl->dsc->attrs[i]->attname);
185
+ typeTup = SearchSysCache(TYPEOID, OidGD(tpl->dsc->attrs[i]->atttypid),
186
+ 0, 0, 0);
187
+ PLRUBY_END;
188
+ if (!HeapTupleIsValid(typeTup)) {
189
+ rb_raise(pl_ePLruby, "Cache lookup for attribute '%s' type %ld failed",
190
+ attname, OidGD(tpl->dsc->attrs[i]->atttypid));
191
+ }
192
+ fpgt = (Form_pg_type) GETSTRUCT(typeTup);
193
+ rb_ary_push(res, rb_tainted_str_new2(NameStr(fpgt->typname)));
194
+ ReleaseSysCache(typeTup);
195
+ }
196
+ return res;
197
+ }
198
+
199
+ static VALUE
200
+ pl_query_description(VALUE obj)
201
+ {
202
+ VALUE name, types, res;
203
+ VALUE tt_virg, tt_blc;
204
+ int i;
205
+
206
+ tt_virg = rb_str_new2(", ");
207
+ tt_blc = rb_str_new2(" ");
208
+ name = pl_query_name(obj);
209
+ if (NIL_P(name)) {
210
+ return Qnil;
211
+ }
212
+ types = pl_query_type(obj);
213
+ if (TYPE(name) != T_ARRAY || TYPE(types) != T_ARRAY ||
214
+ RARRAY_LEN(name) != RARRAY_LEN(types)) {
215
+ rb_raise(pl_ePLruby, "unknown error");
216
+ }
217
+ res = rb_tainted_str_new2("");
218
+ for (i = 0; i < RARRAY_LEN(name); ++i) {
219
+ rb_str_concat(res, RARRAY_PTR(name)[i]);
220
+ rb_str_concat(res, tt_blc);
221
+ rb_str_concat(res, RARRAY_PTR(types)[i]);
222
+ if (i != (RARRAY_LEN(name) - 1)) {
223
+ rb_str_concat(res, tt_virg);
224
+ }
225
+ }
226
+ return res;
227
+ }
228
+
229
+ static VALUE
230
+ pl_query_lgth(VALUE obj)
231
+ {
232
+ VALUE tmp;
233
+ struct pl_tuple *tpl;
234
+
235
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
236
+ if (NIL_P(tmp)) {
237
+ return Qnil;
238
+ }
239
+ GetTuple(tmp, tpl);
240
+ if (!tpl->dsc) {
241
+ return Qnil;
242
+ }
243
+ return INT2NUM(tpl->dsc->natts);
244
+ }
245
+
246
+ static VALUE
247
+ pl_args_type(VALUE obj)
248
+ {
249
+ struct pl_tuple *tpl;
250
+ VALUE res, tmp;
251
+ HeapTuple typeTup;
252
+ Form_pg_type fpgt;
253
+ int i;
254
+
255
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
256
+ if (NIL_P(tmp)) {
257
+ return Qnil;
258
+ }
259
+ GetTuple(tmp, tpl);
260
+ res = rb_ary_new2(tpl->pro->nargs);
261
+ for (i = 0; i < tpl->pro->nargs; i++) {
262
+ PLRUBY_BEGIN(1);
263
+ typeTup = SearchSysCache(TYPEOID, OidGD(tpl->pro->arg_type[i]),0,0,0);
264
+ PLRUBY_END;
265
+ if (!HeapTupleIsValid(typeTup)) {
266
+ rb_raise(pl_ePLruby, "Cache lookup for type %ld failed",
267
+ OidGD(tpl->pro->arg_type[i]));
268
+ }
269
+ fpgt = (Form_pg_type) GETSTRUCT(typeTup);
270
+ rb_ary_push(res, rb_tainted_str_new2(NameStr(fpgt->typname)));
271
+ ReleaseSysCache(typeTup);
272
+ }
273
+ return res;
274
+ }
275
+
276
+ static VALUE PLcontext;
277
+
278
+ struct PL_node
279
+ {
280
+ NodeTag type;
281
+ VALUE value;
282
+ };
283
+
284
+ static VALUE
285
+ pl_context_get(VALUE obj)
286
+ {
287
+ struct pl_tuple *tpl;
288
+ VALUE tmp;
289
+
290
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
291
+ if (NIL_P(tmp)) {
292
+ return Qnil;
293
+ }
294
+ GetTuple(tmp, tpl);
295
+ if (!tpl->fcinfo || !tpl->fcinfo->context ||
296
+ !IsA(tpl->fcinfo->context, Invalid)) {
297
+ return Qnil;
298
+ }
299
+ return ((struct PL_node *)tpl->fcinfo->context)->value;
300
+ }
301
+
302
+ static VALUE
303
+ pl_context_set(VALUE obj, VALUE a)
304
+ {
305
+ struct pl_tuple *tpl;
306
+ VALUE tmp;
307
+
308
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
309
+ GetTuple(tmp, tpl);
310
+ if (tpl->fcinfo && tpl->fcinfo->context) {
311
+ if (!IsA(tpl->fcinfo->context, Invalid)) {
312
+ rb_raise(pl_ePLruby, "trying to change a valid context");
313
+ }
314
+ rb_hash_delete(PLcontext, ((struct PL_node *)tpl->fcinfo->context)->value);
315
+ }
316
+ else {
317
+ if (!tpl->fcinfo) {
318
+ rb_raise(pl_ePLruby, "no function info");
319
+ }
320
+ tpl->fcinfo->context = newNode(sizeof(struct PL_node), T_Invalid);
321
+ }
322
+ ((struct PL_node *)tpl->fcinfo->context)->value = a;
323
+ rb_hash_aset(PLcontext, a, Qnil);
324
+ return a;
325
+ }
326
+
327
+ static void
328
+ pl_context_remove()
329
+ {
330
+ struct pl_tuple *tpl;
331
+ VALUE tmp;
332
+
333
+ tmp = rb_thread_local_aref(rb_thread_current(), id_thr);
334
+ GetTuple(tmp, tpl);
335
+ if (tpl->fcinfo && tpl->fcinfo->context) {
336
+ rb_hash_delete(PLcontext, ((struct PL_node *)tpl->fcinfo->context)->value);
337
+ pfree(tpl->fcinfo->context);
338
+ }
339
+ }
340
+
341
+ static VALUE
342
+ pl_tuple_s_new(PG_FUNCTION_ARGS, pl_proc_desc *prodesc)
343
+ {
344
+ VALUE res;
345
+ ReturnSetInfo *rsi;
346
+ struct pl_tuple *tpl;
347
+
348
+ if (!fcinfo || !fcinfo->resultinfo) {
349
+ rb_raise(pl_ePLruby, "no description given");
350
+ }
351
+ rsi = (ReturnSetInfo *)fcinfo->resultinfo;
352
+ if ((rsi->allowedModes & SFRM_Materialize) == 0 || !rsi->expectedDesc) {
353
+ rb_raise(pl_ePLruby, "context don't accept set");
354
+ }
355
+ res = rb_thread_local_aref(rb_thread_current(), id_thr);
356
+ if (NIL_P(res)) {
357
+ res = Data_Make_Struct(rb_cData, struct pl_tuple, pl_thr_mark, free, tpl);
358
+ }
359
+ GetTuple(res, tpl);
360
+ tpl->cxt = rsi->econtext->ecxt_per_query_memory;
361
+ tpl->dsc = rsi->expectedDesc;
362
+ tpl->att = TupleDescGetAttInMetadata(tpl->dsc);
363
+ tpl->pro = prodesc;
364
+ rb_thread_local_aset(rb_thread_current(), id_thr, res);
365
+ return res;
366
+ }
367
+
368
+ #ifdef PLRUBY_ENABLE_CONVERSION
369
+
370
+ static ID id_from_datum;
371
+ static ID id_to_datum;
372
+
373
+ struct datum_value {
374
+ Datum d;
375
+ Oid typoid;
376
+ int typlen;
377
+ };
378
+
379
+ static void pl_conv_mark() {}
380
+
381
+ Oid plruby_datum_oid(VALUE obj, int *typlen)
382
+ {
383
+ struct datum_value *dv;
384
+
385
+ if (TYPE(obj) != T_DATA ||
386
+ RDATA(obj)->dmark != (RUBY_DATA_FUNC)pl_conv_mark) {
387
+ rb_raise(pl_ePLruby, "invalid Datum value");
388
+ }
389
+ Data_Get_Struct(obj, struct datum_value, dv);
390
+ if (typlen) *typlen = dv->typlen;
391
+ return dv->typoid;
392
+ }
393
+
394
+ VALUE plruby_datum_set(VALUE obj, Datum d)
395
+ {
396
+ struct datum_value *dv;
397
+
398
+ if (TYPE(obj) != T_DATA ||
399
+ RDATA(obj)->dmark != (RUBY_DATA_FUNC)pl_conv_mark) {
400
+ rb_raise(pl_ePLruby, "invalid Datum value");
401
+ }
402
+ Data_Get_Struct(obj, struct datum_value, dv);
403
+ dv->d = d;
404
+ return obj;
405
+ }
406
+
407
+ Datum plruby_datum_get(VALUE obj, Oid *typoid)
408
+ {
409
+ struct datum_value *dv;
410
+
411
+ if (TYPE(obj) != T_DATA ||
412
+ RDATA(obj)->dmark != (RUBY_DATA_FUNC)pl_conv_mark) {
413
+ rb_raise(pl_ePLruby, "invalid Datum value");
414
+ }
415
+ Data_Get_Struct(obj, struct datum_value, dv);
416
+ if (typoid) *typoid = dv->typoid;
417
+ return dv->d;
418
+ }
419
+
420
+ #endif
421
+
422
+ Datum
423
+ plruby_to_datum(VALUE obj, FmgrInfo *finfo, Oid typoid,
424
+ Oid typelem, int typlen)
425
+ {
426
+ Datum d;
427
+ VALUE tmp;
428
+
429
+ tmp = rb_attr_get(obj, rb_intern("plruby_tuple"));
430
+ if (TYPE(tmp) == T_DATA) {
431
+ return (Datum)DATA_PTR(tmp);
432
+ }
433
+ if (typoid == BOOLOID) {
434
+ return BoolGD(RTEST(obj));
435
+ }
436
+ #ifdef PLRUBY_ENABLE_CONVERSION
437
+ if (rb_respond_to(obj, id_to_datum)) {
438
+ struct datum_value *dv;
439
+ VALUE res;
440
+
441
+ res = Data_Make_Struct(rb_cData, struct datum_value, pl_conv_mark, free, dv);
442
+ dv->typoid = typoid;
443
+ dv->typlen = typlen;
444
+ res = rb_funcall(obj, id_to_datum, 1, res);
445
+ if (TYPE(res) == T_DATA &&
446
+ RDATA(res)->dmark == (RUBY_DATA_FUNC)pl_conv_mark) {
447
+ Data_Get_Struct(res, struct datum_value, dv);
448
+ if (dv->typoid == typoid && dv->typlen == typlen && dv->d) {
449
+ return dv->d;
450
+ }
451
+ }
452
+ }
453
+ #endif
454
+ obj = plruby_to_s(obj);
455
+ PLRUBY_BEGIN_PROTECT(1);
456
+ d = FunctionCall3(finfo, PointerGD(RSTRING_PTR(obj)),
457
+ OidGD(typelem), IntGD(typlen));
458
+ PLRUBY_END_PROTECT;
459
+ return d;
460
+ }
461
+
462
+ Datum
463
+ plruby_return_array(VALUE ary, pl_proc_desc *p)
464
+ {
465
+ VALUE tmp;
466
+ int i, total, ndim, *dim, *lbs;
467
+ Datum *values;
468
+ ArrayType *array;
469
+
470
+ tmp = rb_Array(ary);
471
+ total = 1;
472
+ dim = ALLOCA_N(int, MAXDIM);
473
+ MEMZERO(dim, int, MAXDIM);
474
+ lbs = ALLOCA_N(int, MAXDIM);
475
+ MEMZERO(lbs, int, MAXDIM);
476
+ i = 0;
477
+ while (TYPE(tmp) == T_ARRAY) {
478
+ lbs[i] = 1;
479
+ dim[i++] = RARRAY_LEN(tmp);
480
+ if (i == MAXDIM) {
481
+ rb_raise(pl_ePLruby, "too many dimensions -- max %d", MAXDIM);
482
+ }
483
+ if (RARRAY_LEN(tmp)) {
484
+ total *= RARRAY_LEN(tmp);
485
+ }
486
+ tmp = RARRAY_PTR(tmp)[0];
487
+ }
488
+ ndim = i;
489
+ #if PG_PL_VERSION < 74
490
+ if (ndim != 1) {
491
+ rb_raise(rb_eNotImpError, "multi-dimensional array only for >= 7.4");
492
+ }
493
+ #endif
494
+ ary = rb_funcall2(ary, rb_intern("flatten"), 0, 0);
495
+ if (RARRAY_LEN(ary) != total) {
496
+ #ifdef WARNING
497
+ elog(WARNING, "not a regular array");
498
+ #else
499
+ elog(NOTICE, "not a regular array");
500
+ #endif
501
+ }
502
+ values = (Datum *)palloc(RARRAY_LEN(ary) * sizeof(Datum));
503
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
504
+ values[i] = plruby_to_datum(RARRAY_PTR(ary)[i],
505
+ &p->result_func,
506
+ p->result_oid, p->result_elem,
507
+ -1);
508
+ }
509
+ PLRUBY_BEGIN_PROTECT(1);
510
+ #if PG_PL_VERSION >= 74
511
+ #if PG_PL_VERSION >= 82
512
+ array = construct_md_array(values, NULL, ndim, dim, lbs,
513
+ p->result_elem, p->result_len,
514
+ p->result_val, p->result_align);
515
+ #else
516
+ array = construct_md_array(values, ndim, dim, lbs,
517
+ p->result_elem, p->result_len,
518
+ p->result_val, p->result_align);
519
+ #endif
520
+ #else
521
+ array = construct_array(values, dim[0], p->result_elem, p->result_len,
522
+ p->result_val, p->result_align);
523
+ #endif
524
+ PLRUBY_END_PROTECT;
525
+ return PointerGD(array);
526
+ }
527
+
528
+ static Datum
529
+ return_base_type(VALUE c, pl_proc_desc *prodesc)
530
+ {
531
+ Datum retval;
532
+
533
+ if (prodesc->result_is_array) {
534
+ retval = plruby_return_array(c, prodesc);
535
+ }
536
+ else {
537
+ retval = plruby_to_datum(c, &prodesc->result_func,
538
+ prodesc->result_oid,
539
+ prodesc->result_elem,
540
+ -1);
541
+ }
542
+ return retval;
543
+ }
544
+
545
+ struct each_st {
546
+ VALUE res;
547
+ TupleDesc tup;
548
+ };
549
+
550
+ static HeapTuple
551
+ pl_tuple_heap(VALUE c, VALUE tuple)
552
+ {
553
+ HeapTuple retval;
554
+ struct pl_tuple *tpl;
555
+ TupleDesc tupdesc = 0;
556
+ Datum *dvalues;
557
+ Oid typid;
558
+ char *nulls;
559
+ int i;
560
+
561
+
562
+ GetTuple(tuple, tpl);
563
+ if (tpl->att) {
564
+ tupdesc = tpl->att->tupdesc;
565
+ }
566
+ if (!tupdesc) {
567
+ rb_raise(pl_ePLruby, "Invalid descriptor");
568
+ }
569
+ if (TYPE(c) != T_ARRAY) {
570
+ if (NIL_P(c) || (TYPE(c) == T_STRING && !RSTRING_LEN(c))) {
571
+ c = rb_ary_new2(1);
572
+ rb_ary_push(c, rb_str_new2(""));
573
+ }
574
+ else {
575
+ c = rb_Array(c);
576
+ }
577
+ }
578
+ if (TYPE(c) != T_ARRAY || !RARRAY_PTR(c)) {
579
+ rb_raise(pl_ePLruby, "expected an Array");
580
+ }
581
+ if (tupdesc->natts != RARRAY_LEN(c)) {
582
+ rb_raise(pl_ePLruby, "Invalid number of rows (%d expected %d)",
583
+ RARRAY_LEN(c), tupdesc->natts);
584
+ }
585
+ dvalues = ALLOCA_N(Datum, RARRAY_LEN(c));
586
+ MEMZERO(dvalues, Datum, RARRAY_LEN(c));
587
+ nulls = ALLOCA_N(char, RARRAY_LEN(c));
588
+ MEMZERO(nulls, char, RARRAY_LEN(c));
589
+ for (i = 0; i < RARRAY_LEN(c); i++) {
590
+ if (NIL_P(RARRAY_PTR(c)[i]) ||
591
+ tupdesc->attrs[i]->attisdropped) {
592
+ dvalues[i] = (Datum)0;
593
+ nulls[i] = 'n';
594
+ }
595
+ else {
596
+ nulls[i] = ' ';
597
+ typid = tupdesc->attrs[i]->atttypid;
598
+ if (tupdesc->attrs[i]->attndims != 0 ||
599
+ tpl->att->attinfuncs[i].fn_addr == (PGFunction)array_in) {
600
+ pl_proc_desc prodesc;
601
+ FmgrInfo func;
602
+ HeapTuple hp;
603
+ Form_pg_type fpg;
604
+
605
+ MEMZERO(&prodesc, pl_proc_desc, 1);
606
+ PLRUBY_BEGIN(1);
607
+ hp = SearchSysCache(TYPEOID, OidGD(typid), 0, 0, 0);
608
+ if (!HeapTupleIsValid(hp)) {
609
+ rb_raise(pl_ePLruby, "cache lookup failed for type %u",
610
+ typid);
611
+ }
612
+ fpg = (Form_pg_type) GETSTRUCT(hp);
613
+ #if PG_PL_VERSION >= 75
614
+ typid = getTypeIOParam(hp);
615
+ #else
616
+ typid = fpg->typelem;
617
+ #endif
618
+ ReleaseSysCache(hp);
619
+ hp = SearchSysCache(TYPEOID, OidGD(typid), 0, 0, 0);
620
+ PLRUBY_END;
621
+ if (!HeapTupleIsValid(hp)) {
622
+ rb_raise(pl_ePLruby, "cache lookup failed for type %u",
623
+ typid);
624
+ }
625
+ fpg = (Form_pg_type) GETSTRUCT(hp);
626
+ fmgr_info(fpg->typinput, &func);
627
+ prodesc.result_func = func;
628
+ prodesc.result_oid = typid;
629
+ prodesc.result_elem = typid;
630
+ prodesc.result_val = fpg->typbyval;
631
+ prodesc.result_len = fpg->typlen;
632
+ prodesc.result_align = fpg->typalign;
633
+ ReleaseSysCache(hp);
634
+ dvalues[i] = plruby_return_array(RARRAY_PTR(c)[i], &prodesc);
635
+ }
636
+ else {
637
+ #if PG_PL_VERSION >= 75
638
+ dvalues[i] = plruby_to_datum(RARRAY_PTR(c)[i],
639
+ &tpl->att->attinfuncs[i],
640
+ typid,
641
+ tpl->att->attioparams[i],
642
+ tpl->att->atttypmods[i]);
643
+ #else
644
+ dvalues[i] = plruby_to_datum(RARRAY_PTR(c)[i],
645
+ &tpl->att->attinfuncs[i],
646
+ typid,
647
+ tpl->att->attelems[i],
648
+ tpl->att->atttypmods[i]);
649
+ #endif
650
+ }
651
+ }
652
+ }
653
+ PLRUBY_BEGIN_PROTECT(1);
654
+ retval = heap_formtuple(tupdesc, dvalues, nulls);
655
+ PLRUBY_END_PROTECT;
656
+ return retval;
657
+ }
658
+
659
+ static VALUE
660
+ pl_tuple_put(VALUE c, VALUE tuple)
661
+ {
662
+ HeapTuple retval;
663
+ MemoryContext oldcxt;
664
+ struct pl_tuple *tpl;
665
+
666
+ GetTuple(tuple, tpl);
667
+ retval = pl_tuple_heap(c, tuple);
668
+ PLRUBY_BEGIN_PROTECT(1);
669
+ oldcxt = MemoryContextSwitchTo(tpl->cxt);
670
+ if (!tpl->out) {
671
+ #if PG_PL_VERSION >= 74
672
+ tpl->out = tuplestore_begin_heap(true, false, SortMem);
673
+ #else
674
+ tpl->out = tuplestore_begin_heap(true, SortMem);
675
+ #endif
676
+ }
677
+ tuplestore_puttuple(tpl->out, retval);
678
+ MemoryContextSwitchTo(oldcxt);
679
+ PLRUBY_END_PROTECT;
680
+ return Qnil;
681
+ }
682
+
683
+ static VALUE
684
+ pl_ary_collect(VALUE c, VALUE ary)
685
+ {
686
+ PLRUBY_BEGIN_PROTECT(1);
687
+ rb_ary_push(ary,c);
688
+ PLRUBY_END_PROTECT;
689
+ return Qnil;
690
+ }
691
+
692
+ static Datum
693
+ pl_tuple_datum(VALUE c, VALUE tuple)
694
+ {
695
+ Datum retval;
696
+ HeapTuple tmp;
697
+ struct pl_tuple *tpl;
698
+
699
+ GetTuple(tuple, tpl);
700
+ tmp = pl_tuple_heap(c, tuple);
701
+ PLRUBY_BEGIN_PROTECT(1);
702
+ retval = TupleGD(TupleDescGetSlot(tpl->att->tupdesc), tmp);
703
+ PLRUBY_END_PROTECT;
704
+ return retval;
705
+ }
706
+
707
+ struct pl_arg {
708
+ ID id;
709
+ int named;
710
+ VALUE ary;
711
+ };
712
+
713
+ static void
714
+ pl_arg_mark(struct pl_arg *args)
715
+ {
716
+ rb_gc_mark(args->ary);
717
+ }
718
+
719
+ static VALUE
720
+ pl_func(VALUE arg)
721
+ {
722
+ #if HAVE_RB_BLOCK_CALL
723
+ return Qtrue;
724
+ #else
725
+ struct pl_arg *args;
726
+
727
+ Data_Get_Struct(arg, struct pl_arg, args);
728
+ if (args->named) {
729
+ return rb_funcall2(pl_mPLtemp, args->id, RARRAY_LEN(args->ary),
730
+ RARRAY_PTR(args->ary));
731
+ }
732
+ return rb_funcall(pl_mPLtemp, args->id, 1, args->ary);
733
+ #endif
734
+ }
735
+
736
+ static VALUE
737
+ pl_string(VALUE arg)
738
+ {
739
+ struct pl_arg *args;
740
+ VALUE tmp[2], plan;
741
+
742
+ Data_Get_Struct(arg, struct pl_arg, args);
743
+ tmp[0] = args->ary;
744
+ tmp[1] = rb_hash_new();
745
+ rb_hash_aset(tmp[1], rb_str_new2("block"), INT2NUM(50));
746
+ rb_hash_aset(tmp[1], rb_str_new2("output"), rb_str_new2("value"));
747
+ plan = plruby_s_new(2, tmp, pl_cPLPlan);
748
+ #if HAVE_RB_BLOCK_CALL
749
+ return plan;
750
+ #else
751
+ rb_funcall2(plan, rb_intern("each"), 0, 0);
752
+ return Qnil;
753
+ #endif
754
+ }
755
+
756
+ static VALUE
757
+ pl_warn(argc, argv, obj)
758
+ int argc;
759
+ VALUE *argv;
760
+ VALUE obj;
761
+ {
762
+ int level, indice;
763
+ VALUE res;
764
+
765
+ level = NOTICE;
766
+ indice = 0;
767
+ switch (argc) {
768
+ case 2:
769
+ indice = 1;
770
+ switch (level = NUM2INT(argv[0])) {
771
+ #ifdef DEBUG
772
+ case DEBUG:
773
+ #endif
774
+ #ifdef DEBUG1
775
+ case DEBUG1:
776
+ #endif
777
+ #ifdef DEBUG2
778
+ case DEBUG2:
779
+ #endif
780
+ #ifdef DEBUG3
781
+ case DEBUG3:
782
+ #endif
783
+ #ifdef DEBUG4
784
+ case DEBUG4:
785
+ #endif
786
+ #ifdef DEBUG5
787
+ case DEBUG5:
788
+ #endif
789
+ #ifdef NOTICE
790
+ case NOTICE:
791
+ #endif
792
+ #ifdef LOG
793
+ #if !defined(DEBUG) || LOG != DEBUG
794
+ case LOG:
795
+ #endif
796
+ #endif
797
+ #ifdef NOIND
798
+ case NOIND:
799
+ #endif
800
+ #ifdef WARNING
801
+ case WARNING:
802
+ #endif
803
+ #ifdef WARN
804
+ case WARN:
805
+ #endif
806
+ #ifdef ERROR
807
+ case ERROR:
808
+ #endif
809
+ #ifdef FATAL
810
+ case FATAL:
811
+ #endif
812
+ break;
813
+ default:
814
+ rb_raise(pl_ePLruby, "invalid level %d", level);
815
+ }
816
+ case 1:
817
+ res = argv[indice];
818
+ if (NIL_P(res)) {
819
+ return Qnil;
820
+ }
821
+ res = plruby_to_s(res);
822
+ break;
823
+ default:
824
+ rb_raise(pl_ePLruby, "invalid syntax");
825
+ }
826
+ PLRUBY_BEGIN_PROTECT(1);
827
+ elog(level, "%s", RSTRING_PTR(res));
828
+ PLRUBY_END_PROTECT;
829
+ return Qnil;
830
+ }
831
+
832
+ static VALUE
833
+ pl_quote(obj, mes)
834
+ VALUE obj, mes;
835
+ {
836
+ char *tmp, *cp1, *cp2;
837
+
838
+ if (TYPE(mes) != T_STRING || !RSTRING_PTR(mes)) {
839
+ rb_raise(pl_ePLruby, "quote: string expected");
840
+ }
841
+ tmp = ALLOCA_N(char, RSTRING_LEN(mes) * 2 + 1);
842
+ cp1 = RSTRING_PTR(mes);
843
+ cp2 = tmp;
844
+ while (*cp1) {
845
+ if (*cp1 == '\'')
846
+ *cp2++ = '\'';
847
+ else {
848
+ if (*cp1 == '\\')
849
+ *cp2++ = '\\';
850
+ }
851
+ *cp2++ = *cp1++;
852
+ }
853
+ *cp2 = '\0';
854
+ return rb_tainted_str_new2(tmp);
855
+ }
856
+
857
+ void
858
+ plruby_exec_output(VALUE option, int compose, int *result)
859
+ {
860
+ if (TYPE(option) != T_STRING || RSTRING_PTR(option) == 0 || !result) {
861
+ rb_raise(pl_ePLruby, "string expected for optional output");
862
+ }
863
+ if (strcmp(RSTRING_PTR(option), "array") == 0) {
864
+ *result = compose|RET_DESC_ARR;
865
+ }
866
+ else if (strcmp(RSTRING_PTR(option), "hash") == 0) {
867
+ *result = compose|RET_DESC;
868
+ }
869
+ else if (strcmp(RSTRING_PTR(option), "value") == 0) {
870
+ *result = RET_ARRAY;
871
+ }
872
+ }
873
+
874
+ static VALUE
875
+ pl_SPI_exec(argc, argv, obj)
876
+ int argc;
877
+ VALUE *argv;
878
+ VALUE obj;
879
+ {
880
+ int spi_rc, count, array;
881
+ int i, comp, ntuples;
882
+ struct portal_options po;
883
+ VALUE a, b, c, result;
884
+ HeapTuple *tuples;
885
+ TupleDesc tupdesc = NULL;
886
+
887
+ count = 0;
888
+ array = comp = RET_HASH;
889
+ if (argc && TYPE(argv[argc - 1]) == T_HASH) {
890
+ MEMZERO(&po, struct portal_options, 1);
891
+ rb_iterate(rb_each, argv[argc - 1], plruby_i_each, (VALUE)&po);
892
+ comp = po.output;
893
+ count = po.count;
894
+ argc--;
895
+ }
896
+ switch (rb_scan_args(argc, argv, "12", &a, &b, &c)) {
897
+ case 3:
898
+ plruby_exec_output(c, RET_HASH, &comp);
899
+ /* ... */
900
+ case 2:
901
+ if (!NIL_P(b)) {
902
+ count = NUM2INT(b);
903
+ }
904
+ }
905
+ if (TYPE(a) != T_STRING) {
906
+ rb_raise(pl_ePLruby, "exec: first argument must be a string");
907
+ }
908
+ array = comp;
909
+ PLRUBY_BEGIN_PROTECT(1);
910
+ spi_rc = SPI_exec(RSTRING_PTR(a), count);
911
+ PLRUBY_END_PROTECT;
912
+
913
+ switch (spi_rc) {
914
+ case SPI_OK_UTILITY:
915
+ if (SPI_tuptable == NULL) {
916
+ SPI_freetuptable(SPI_tuptable);
917
+ return Qtrue;
918
+ }
919
+ break;
920
+ case SPI_OK_SELINTO:
921
+ case SPI_OK_INSERT:
922
+ case SPI_OK_DELETE:
923
+ case SPI_OK_UPDATE:
924
+ SPI_freetuptable(SPI_tuptable);
925
+ return INT2NUM(SPI_processed);
926
+ case SPI_OK_SELECT:
927
+ #ifdef SPI_OK_INSERT_RETURNING
928
+ case SPI_OK_INSERT_RETURNING:
929
+ case SPI_OK_DELETE_RETURNING:
930
+ case SPI_OK_UPDATE_RETURNING:
931
+ #endif
932
+ break;
933
+ case SPI_ERROR_ARGUMENT:
934
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_ARGUMENT");
935
+ case SPI_ERROR_UNCONNECTED:
936
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_UNCONNECTED");
937
+ case SPI_ERROR_COPY:
938
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_COPY");
939
+ case SPI_ERROR_CURSOR:
940
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_CURSOR");
941
+ case SPI_ERROR_TRANSACTION:
942
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_TRANSACTION");
943
+ case SPI_ERROR_OPUNKNOWN:
944
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_OPUNKNOWN");
945
+ default:
946
+ rb_raise(pl_ePLruby, "SPI_exec() failed - unknown RC %d", spi_rc);
947
+ }
948
+
949
+ ntuples = SPI_processed;
950
+ if (ntuples <= 0) {
951
+ SPI_freetuptable(SPI_tuptable);
952
+ if (rb_block_given_p() || count == 1)
953
+ return Qfalse;
954
+ else
955
+ return rb_ary_new2(0);
956
+ }
957
+ tuples = SPI_tuptable->vals;
958
+ tupdesc = SPI_tuptable->tupdesc;
959
+ if (rb_block_given_p()) {
960
+ if (count == 1) {
961
+ if (!(array & RET_DESC)) {
962
+ array |= RET_BASIC;
963
+ }
964
+ plruby_build_tuple(tuples[0], tupdesc, array);
965
+ }
966
+ else {
967
+ for (i = 0; i < ntuples; i++) {
968
+ rb_yield(plruby_build_tuple(tuples[i], tupdesc, array));
969
+ }
970
+ }
971
+ result = Qtrue;
972
+ }
973
+ else {
974
+ if (count == 1) {
975
+ result = plruby_build_tuple(tuples[0], tupdesc, array);
976
+ }
977
+ else {
978
+ result = rb_ary_new2(ntuples);
979
+ for (i = 0; i < ntuples; i++) {
980
+ rb_ary_push(result, plruby_build_tuple(tuples[i], tupdesc, array));
981
+ }
982
+ }
983
+ }
984
+ SPI_freetuptable(SPI_tuptable);
985
+ return result;
986
+ }
987
+
988
+ static VALUE
989
+ pl_convert_arg(Datum value, Oid typoid, FmgrInfo *finfo, Oid typelem,
990
+ int attlen)
991
+ {
992
+ VALUE result;
993
+ char *outstr;
994
+
995
+ if (typoid == BOOLOID) {
996
+ return DatumGetBool(value)?Qtrue:Qfalse;
997
+ }
998
+ #ifdef PLRUBY_ENABLE_CONVERSION
999
+ {
1000
+ VALUE vid, klass;
1001
+
1002
+ vid = INT2NUM(typoid);
1003
+ klass = rb_hash_aref(plruby_classes, vid);
1004
+ if (NIL_P(klass)) {
1005
+ klass = rb_hash_aref(plruby_conversions, vid);
1006
+ if (NIL_P(klass)) {
1007
+ st_insert(RHASH_TBL(plruby_classes), vid, Qfalse);
1008
+ }
1009
+ else {
1010
+ klass = rb_const_get(rb_cObject, NUM2INT(klass));
1011
+ st_insert(RHASH_TBL(plruby_classes), vid, klass);
1012
+ }
1013
+ }
1014
+ if (RTEST(klass)) {
1015
+ struct datum_value *dv;
1016
+ VALUE res;
1017
+
1018
+
1019
+ res = Data_Make_Struct(rb_cData, struct datum_value,
1020
+ pl_conv_mark, free, dv);
1021
+ dv->d = value;
1022
+ dv->typoid = typoid;
1023
+ dv->typlen = attlen;
1024
+ res = rb_funcall(klass, id_from_datum, 1, res);
1025
+ return res;
1026
+ }
1027
+ }
1028
+ #endif
1029
+ PLRUBY_BEGIN_PROTECT(1);
1030
+ outstr = DatumGetCString(FunctionCall3(finfo, value, OidGD(typelem),
1031
+ IntGD(attlen)));
1032
+ result = rb_tainted_str_new2(outstr);
1033
+ pfree(outstr);
1034
+ PLRUBY_END_PROTECT;
1035
+ return result;
1036
+ }
1037
+
1038
+ static VALUE
1039
+ create_array(index, ndim, dim, p, prodesc, curr, typoid)
1040
+ int index, ndim, *dim, curr;
1041
+ Oid typoid;
1042
+ char **p;
1043
+ pl_proc_desc *prodesc;
1044
+ {
1045
+ VALUE res, tmp;
1046
+ Datum itemvalue;
1047
+ int i;
1048
+
1049
+ res = rb_ary_new2(dim[index]);
1050
+ for (i = 0; i < dim[index]; ++i) {
1051
+ if (index == ndim - 1) {
1052
+ itemvalue = fetch_att(*p, prodesc->arg_val[curr],
1053
+ prodesc->arg_len[curr]);
1054
+ tmp = pl_convert_arg(itemvalue, typoid,
1055
+ &prodesc->arg_func[curr], (Datum)0, -1);
1056
+ #ifdef att_addlength_pointer
1057
+ *p = att_addlength_pointer(*p, prodesc->arg_len[curr], PointerGD(*p));
1058
+ *p = (char *) att_align_nominal(*p, prodesc->arg_align[curr]);
1059
+ #else
1060
+ *p = att_addlength(*p, prodesc->arg_len[curr], PointerGD(*p));
1061
+ *p = (char *) att_align(*p, prodesc->arg_align[curr]);
1062
+ #endif
1063
+ rb_ary_push(res, tmp);
1064
+ }
1065
+ else {
1066
+ for (i = 0; i < dim[index]; ++i) {
1067
+ rb_ary_push(res, create_array(index + 1, ndim, dim, p,
1068
+ prodesc, curr, typoid));
1069
+ }
1070
+ }
1071
+ }
1072
+ return res;
1073
+ }
1074
+
1075
+ VALUE
1076
+ plruby_build_tuple(HeapTuple tuple, TupleDesc tupdesc, int type_ret)
1077
+ {
1078
+ int i;
1079
+ VALUE output, res = Qnil;
1080
+ Datum attr;
1081
+ bool isnull;
1082
+ char *attname;
1083
+ HeapTuple typeTup;
1084
+ Oid typoutput;
1085
+ Oid typelem;
1086
+ Form_pg_type fpgt;
1087
+
1088
+ output = Qnil;
1089
+ if (type_ret & RET_ARRAY) {
1090
+ output = rb_ary_new();
1091
+ }
1092
+ else if (type_ret & RET_HASH) {
1093
+ output = rb_hash_new();
1094
+ }
1095
+ if (!tuple) {
1096
+ return output;
1097
+ }
1098
+
1099
+ for (i = 0; i < tupdesc->natts; i++) {
1100
+ if (tupdesc->attrs[i]->attisdropped)
1101
+ continue;
1102
+ PLRUBY_BEGIN(1);
1103
+ attname = NameStr(tupdesc->attrs[i]->attname);
1104
+ attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
1105
+ typeTup = SearchSysCache(TYPEOID, OidGD(tupdesc->attrs[i]->atttypid),
1106
+ 0, 0, 0);
1107
+ PLRUBY_END;
1108
+
1109
+ if (!HeapTupleIsValid(typeTup)) {
1110
+ rb_raise(pl_ePLruby, "Cache lookup for attribute '%s' type %ld failed",
1111
+ attname, OidGD(tupdesc->attrs[i]->atttypid));
1112
+ }
1113
+
1114
+ fpgt = (Form_pg_type) GETSTRUCT(typeTup);
1115
+ typoutput = (Oid) (fpgt->typoutput);
1116
+ #if PG_PL_VERSION >= 75
1117
+ typelem = getTypeIOParam(typeTup);
1118
+ #else
1119
+ typelem = (Oid) (fpgt->typelem);
1120
+ #endif
1121
+ if (type_ret & RET_DESC) {
1122
+ Oid typeid;
1123
+ char *typname;
1124
+ int alen;
1125
+
1126
+ typname = NameStr(fpgt->typname);
1127
+ alen = tupdesc->attrs[i]->attlen;
1128
+ typeid = tupdesc->attrs[i]->atttypid;
1129
+ if (strcmp(typname, "text") == 0) {
1130
+ alen = -1;
1131
+ }
1132
+ else if (strcmp(typname, "bpchar") == 0 ||
1133
+ strcmp(typname, "varchar") == 0) {
1134
+ if (tupdesc->attrs[i]->atttypmod == -1) {
1135
+ alen = 0;
1136
+ }
1137
+ else {
1138
+ alen = tupdesc->attrs[i]->atttypmod - 4;
1139
+ }
1140
+ }
1141
+ if ((type_ret & RET_DESC_ARR) == RET_DESC_ARR) {
1142
+ res = rb_ary_new();
1143
+ rb_ary_push(res, rb_tainted_str_new2(attname));
1144
+ rb_ary_push(res, Qnil);
1145
+ rb_ary_push(res, rb_tainted_str_new2(typname));
1146
+ rb_ary_push(res, INT2FIX(alen));
1147
+ rb_ary_push(res, INT2FIX(typeid));
1148
+ }
1149
+ else {
1150
+ res = rb_hash_new();
1151
+ rb_hash_aset(res, rb_tainted_str_new2("name"), rb_tainted_str_new2(attname));
1152
+ rb_hash_aset(res, rb_tainted_str_new2("type"), rb_tainted_str_new2(typname));
1153
+ rb_hash_aset(res, rb_tainted_str_new2("typeid"), INT2FIX(typeid));
1154
+ rb_hash_aset(res, rb_tainted_str_new2("len"), INT2FIX(alen));
1155
+ }
1156
+ }
1157
+ ReleaseSysCache(typeTup);
1158
+ if (!isnull && OidIsValid(typoutput)) {
1159
+ VALUE s;
1160
+
1161
+ PLRUBY_BEGIN_PROTECT(1);
1162
+ if (NameStr(fpgt->typname)[0] == '_') {
1163
+ ArrayType *array;
1164
+ int ndim, *dim;
1165
+
1166
+ array = (ArrayType *)attr;
1167
+ ndim = ARR_NDIM(array);
1168
+ dim = ARR_DIMS(array);
1169
+ if (ArrayGetNItems(ndim, dim) == 0) {
1170
+ s = rb_ary_new2(0);
1171
+ }
1172
+ else {
1173
+ pl_proc_desc prodesc;
1174
+ HeapTuple typeTuple;
1175
+ Form_pg_type typeStruct;
1176
+ Oid elemtyp;
1177
+ char *p = ARR_DATA_PTR(array);
1178
+
1179
+ typeTuple =
1180
+ SearchSysCache(TYPEOID, OidGD(typelem), 0, 0, 0);
1181
+ if (!HeapTupleIsValid(typeTuple)) {
1182
+ elog(ERROR, "cache lookup failed for type %u",
1183
+ typelem);
1184
+ }
1185
+
1186
+ typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
1187
+
1188
+ fmgr_info(typeStruct->typoutput, &(prodesc.arg_func[0]));
1189
+ prodesc.arg_val[0] = typeStruct->typbyval;
1190
+ prodesc.arg_len[0] = typeStruct->typlen;
1191
+ prodesc.arg_align[0] = typeStruct->typalign;
1192
+ elemtyp = ARR_ELEMTYPE(array);
1193
+ ReleaseSysCache(typeTuple);
1194
+ s = create_array(0, ndim, dim, &p, &prodesc, 0, elemtyp);
1195
+ }
1196
+ }
1197
+ else {
1198
+ FmgrInfo finfo;
1199
+
1200
+ fmgr_info(typoutput, &finfo);
1201
+
1202
+ s = pl_convert_arg(attr, tupdesc->attrs[i]->atttypid,
1203
+ &finfo, typelem,tupdesc->attrs[i]->attlen);
1204
+ }
1205
+ PLRUBY_END_PROTECT;
1206
+
1207
+ if (type_ret & RET_DESC) {
1208
+ if (TYPE(res) == T_ARRAY) {
1209
+ RARRAY_PTR(res)[1] = s;
1210
+ }
1211
+ else {
1212
+ rb_hash_aset(res, rb_tainted_str_new2("value"), s);
1213
+ }
1214
+ if (TYPE(output) == T_ARRAY) {
1215
+ rb_ary_push(output, res);
1216
+ }
1217
+ else {
1218
+ rb_yield(res);
1219
+ }
1220
+ }
1221
+ else {
1222
+ if (type_ret & RET_BASIC) {
1223
+ rb_yield(rb_assoc_new(rb_tainted_str_new2(attname), s));
1224
+ }
1225
+ else {
1226
+ switch (TYPE(output)) {
1227
+ case T_HASH:
1228
+ rb_hash_aset(output, rb_tainted_str_new2(attname), s);
1229
+ break;
1230
+ case T_ARRAY:
1231
+ rb_ary_push(output, s);
1232
+ break;
1233
+ }
1234
+ }
1235
+ }
1236
+ }
1237
+ else {
1238
+ if (isnull) {
1239
+ if (type_ret & RET_DESC) {
1240
+ if (TYPE(res) == T_HASH) {
1241
+ rb_hash_aset(res, rb_tainted_str_new2("value"), Qnil);
1242
+ }
1243
+ if (TYPE(output) == T_ARRAY) {
1244
+ rb_ary_push(output, res);
1245
+ }
1246
+ else {
1247
+ rb_yield(res);
1248
+ }
1249
+ }
1250
+ else {
1251
+ if (type_ret & RET_BASIC) {
1252
+ rb_yield(rb_assoc_new(rb_tainted_str_new2(attname), Qnil));
1253
+ }
1254
+ else {
1255
+ switch (TYPE(output)) {
1256
+ case T_HASH:
1257
+ rb_hash_aset(output, rb_tainted_str_new2(attname), Qnil);
1258
+ break;
1259
+ case T_ARRAY:
1260
+ rb_ary_push(output, Qnil);
1261
+ break;
1262
+ }
1263
+ }
1264
+ }
1265
+ }
1266
+ }
1267
+ }
1268
+ return output;
1269
+ }
1270
+
1271
+ VALUE
1272
+ plruby_create_args(struct pl_thread_st *plth, pl_proc_desc *prodesc)
1273
+ {
1274
+ VALUE ary;
1275
+ int i;
1276
+ PG_FUNCTION_ARGS;
1277
+
1278
+ fcinfo = plth->fcinfo;
1279
+ {
1280
+ VALUE res;
1281
+ struct pl_tuple *tpl;
1282
+
1283
+ res = rb_thread_local_aref(rb_thread_current(), id_thr);
1284
+ if (NIL_P(res)) {
1285
+ res = Data_Make_Struct(rb_cData, struct pl_tuple, pl_thr_mark, free, tpl);
1286
+ }
1287
+ GetTuple(res, tpl);
1288
+ tpl->fcinfo = fcinfo;
1289
+ tpl->pro = prodesc;
1290
+ rb_thread_local_aset(rb_thread_current(), id_thr, res);
1291
+ }
1292
+
1293
+ ary = rb_ary_new2(prodesc->nargs);
1294
+ for (i = 0; i < prodesc->nargs; i++) {
1295
+ if (fcinfo->argnull[i]) {
1296
+ rb_ary_push(ary, Qnil);
1297
+ }
1298
+ else if (prodesc->arg_is_rel[i]) {
1299
+ VALUE tmp;
1300
+
1301
+ #if PG_PL_VERSION >= 75
1302
+ HeapTupleHeader td;
1303
+ Oid tupType;
1304
+ int32 tupTypmod;
1305
+ TupleDesc tupdesc;
1306
+ HeapTupleData tmptup;
1307
+
1308
+ td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
1309
+ tupType = HeapTupleHeaderGetTypeId(td);
1310
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
1311
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1312
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
1313
+ tmptup.t_data = td;
1314
+ tmp = plruby_build_tuple(&tmptup, tupdesc, RET_HASH);
1315
+ #else
1316
+ TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
1317
+ tmp = plruby_build_tuple(slot->val, slot->ttc_tupleDescriptor, RET_HASH);
1318
+ #endif
1319
+ rb_iv_set(tmp, "plruby_tuple",
1320
+ Data_Wrap_Struct(rb_cData, 0, 0, (void *)fcinfo->arg[i]));
1321
+ rb_ary_push(ary, tmp);
1322
+ }
1323
+ else if (prodesc->arg_is_array[i]) {
1324
+ ArrayType *array;
1325
+ int ndim, *dim;
1326
+ char *p;
1327
+
1328
+ array = (ArrayType *)fcinfo->arg[i];
1329
+ ndim = ARR_NDIM(array);
1330
+ dim = ARR_DIMS(array);
1331
+ if (ArrayGetNItems(ndim, dim) == 0) {
1332
+ rb_ary_push(ary, rb_ary_new2(0));
1333
+ }
1334
+ else {
1335
+ Oid elemtyp;
1336
+ elemtyp = ARR_ELEMTYPE(array);
1337
+ p = ARR_DATA_PTR(array);
1338
+ rb_ary_push(ary, create_array(0, ndim, dim, &p, prodesc, i,
1339
+ elemtyp));
1340
+ }
1341
+ }
1342
+ else {
1343
+ VALUE res;
1344
+
1345
+ res = pl_convert_arg(fcinfo->arg[i],
1346
+ prodesc->arg_type[i],
1347
+ &prodesc->arg_func[i],
1348
+ prodesc->arg_elem[i],
1349
+ prodesc->arg_len[i]);
1350
+ rb_ary_push(ary, res);
1351
+ }
1352
+ }
1353
+ return ary;
1354
+ }
1355
+
1356
+ Datum
1357
+ plruby_return_value(struct pl_thread_st *plth, pl_proc_desc *prodesc,
1358
+ VALUE value_proname, VALUE ary)
1359
+ {
1360
+ VALUE c;
1361
+ int expr_multiple;
1362
+ PG_FUNCTION_ARGS;
1363
+
1364
+ fcinfo = plth->fcinfo;
1365
+ expr_multiple = 0;
1366
+ if (prodesc->result_type && prodesc->result_type != 'x' &&
1367
+ prodesc->result_type != 'y') {
1368
+ ReturnSetInfo *rsi;
1369
+
1370
+ if (!fcinfo || !fcinfo->resultinfo) {
1371
+ rb_raise(pl_ePLruby, "no description given");
1372
+ }
1373
+ rsi = (ReturnSetInfo *)fcinfo->resultinfo;
1374
+ if (prodesc->result_is_setof && !rsi->expectedDesc) {
1375
+ VALUE res, retary, arg;
1376
+ struct pl_arg *args;
1377
+ TupleDesc tupdesc;
1378
+ FuncCallContext *funcctx;
1379
+ Datum result;
1380
+
1381
+ arg = Data_Make_Struct(rb_cObject, struct pl_arg, pl_arg_mark, free, args);
1382
+ args->id = rb_intern(RSTRING_PTR(value_proname));
1383
+ args->ary = ary;
1384
+ #if PG_PL_VERSION >= 75
1385
+ args->named = prodesc->named_args;
1386
+ #endif
1387
+
1388
+ if (SRF_IS_FIRSTCALL())
1389
+ {
1390
+ MemoryContext oldcontext;
1391
+
1392
+ funcctx = SRF_FIRSTCALL_INIT();
1393
+
1394
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1395
+
1396
+ /* Build a tuple descriptor for our result type */
1397
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1398
+ ereport(ERROR,
1399
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1400
+ errmsg("function returning record called in context "
1401
+ "that cannot accept type record")));
1402
+ /*
1403
+ * generate attribute metadata needed later to produce tuples from raw
1404
+ * C strings
1405
+ */
1406
+ funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1407
+
1408
+ MemoryContextSwitchTo(oldcontext);
1409
+
1410
+ retary = rb_ary_new();
1411
+ #if HAVE_RB_BLOCK_CALL
1412
+ if (args->named) {
1413
+ res = rb_block_call(pl_mPLtemp, args->id,
1414
+ RARRAY_LEN(args->ary),
1415
+ RARRAY_PTR(args->ary),
1416
+ pl_ary_collect, retary);
1417
+ }
1418
+ else {
1419
+ res = rb_block_call(pl_mPLtemp, args->id,
1420
+ 1, &args->ary,
1421
+ pl_ary_collect, retary);
1422
+ }
1423
+ #else
1424
+ res = rb_iterate(pl_func, arg, pl_ary_collect, retary);
1425
+ #endif
1426
+ elog(NOTICE, "returned array len is: %ld", RARRAY_LEN(retary) );
1427
+
1428
+ funcctx->max_calls = RARRAY_LEN(retary) ;
1429
+ funcctx->user_fctx = (void *)retary;
1430
+
1431
+ }
1432
+ funcctx = SRF_PERCALL_SETUP();
1433
+
1434
+ retary = (VALUE)funcctx->user_fctx;
1435
+
1436
+ if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more left to send */
1437
+ {
1438
+ char ** values;
1439
+ HeapTuple tuple;
1440
+ size_t idx;
1441
+ VALUE resary;
1442
+
1443
+ resary = RARRAY_PTR(retary)[funcctx->call_cntr];
1444
+ values = (char **)palloc(RARRAY_LEN(resary) * sizeof(char *));
1445
+
1446
+ for ( idx = 0; idx < RARRAY_LEN(resary); idx++ )
1447
+ {
1448
+ VALUE str = rb_ary_entry( resary, idx );
1449
+ if (TYPE(str) != T_STRING) {
1450
+ str = rb_obj_as_string(str);
1451
+ }
1452
+ values[idx] = pstrdup( StringValueCStr( str ) );
1453
+ }
1454
+
1455
+ /* build a tuple */
1456
+ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
1457
+
1458
+ /* make the tuple into a datum */
1459
+ result = HeapTupleGetDatum(tuple);
1460
+ }
1461
+
1462
+ PLRUBY_BEGIN_PROTECT(1);
1463
+ {
1464
+ MemoryContext oldcxt;
1465
+ int rc;
1466
+
1467
+ oldcxt = MemoryContextSwitchTo(plruby_spi_context);
1468
+ if ((rc = SPI_finish()) != SPI_OK_FINISH) {
1469
+ elog(ERROR, "SPI_finish() failed : %d", rc);
1470
+ }
1471
+ MemoryContextSwitchTo(oldcxt);
1472
+ }
1473
+ PLRUBY_END_PROTECT;
1474
+
1475
+ if ( funcctx->call_cntr < funcctx->max_calls )
1476
+ {
1477
+ SRF_RETURN_NEXT(funcctx, result);
1478
+ }
1479
+ else
1480
+ {
1481
+ SRF_RETURN_DONE(funcctx);
1482
+ }
1483
+
1484
+ } else if ((rsi->allowedModes & SFRM_Materialize) && rsi->expectedDesc) {
1485
+ VALUE tuple, res, arg;
1486
+ struct pl_arg *args;
1487
+ struct pl_tuple *tpl;
1488
+ VALUE (*pl_call)(VALUE);
1489
+
1490
+ tuple = pl_tuple_s_new(fcinfo, prodesc);
1491
+ arg = Data_Make_Struct(rb_cObject, struct pl_arg, pl_arg_mark, free, args);
1492
+ args->id = rb_intern(RSTRING_PTR(value_proname));
1493
+ args->ary = ary;
1494
+ #if PG_PL_VERSION >= 75
1495
+ args->named = prodesc->named_args;
1496
+ #endif
1497
+ pl_call = pl_func;
1498
+ while (1) {
1499
+ #if HAVE_RB_BLOCK_CALL
1500
+ if (pl_call == pl_func) {
1501
+ if (args->named) {
1502
+ res = rb_block_call(pl_mPLtemp, args->id,
1503
+ RARRAY_LEN(args->ary),
1504
+ RARRAY_PTR(args->ary),
1505
+ pl_tuple_put, tuple);
1506
+ }
1507
+ else {
1508
+ res = rb_block_call(pl_mPLtemp, args->id,
1509
+ 1, &args->ary,
1510
+ pl_tuple_put, tuple);
1511
+ }
1512
+ }
1513
+ else {
1514
+ res = rb_block_call(pl_string(arg), rb_intern("each"),
1515
+ 0, 0, pl_tuple_put, tuple);
1516
+ }
1517
+ #else
1518
+ res = rb_iterate(pl_call, arg, pl_tuple_put, tuple);
1519
+ #endif
1520
+ Data_Get_Struct(tuple, struct pl_tuple, tpl);
1521
+ if (NIL_P(res) && !tpl->out) {
1522
+ MemoryContext oldcxt;
1523
+
1524
+ PLRUBY_BEGIN_PROTECT(1);
1525
+ oldcxt = MemoryContextSwitchTo(tpl->cxt);
1526
+ #if PG_PL_VERSION >= 74
1527
+ tpl->out = tuplestore_begin_heap(true, false, SortMem);
1528
+ #else
1529
+ tpl->out = tuplestore_begin_heap(true, SortMem);
1530
+ #endif
1531
+ MemoryContextSwitchTo(oldcxt);
1532
+ PLRUBY_END_PROTECT;
1533
+ }
1534
+ if (tpl->out) {
1535
+ MemoryContext oldcxt;
1536
+
1537
+ PLRUBY_BEGIN_PROTECT(1);
1538
+ oldcxt = MemoryContextSwitchTo(tpl->cxt);
1539
+ tuplestore_donestoring(tpl->out);
1540
+ MemoryContextSwitchTo(oldcxt);
1541
+ PLRUBY_END_PROTECT;
1542
+ ((ReturnSetInfo *)fcinfo->resultinfo)->setResult = tpl->out;
1543
+ ((ReturnSetInfo *)fcinfo->resultinfo)->returnMode = SFRM_Materialize;
1544
+ break;
1545
+ }
1546
+ if (NIL_P(res)) {
1547
+ break;
1548
+ }
1549
+ if (TYPE(res) != T_STRING || RSTRING_PTR(res) == 0) {
1550
+ rb_raise(pl_ePLruby, "invalid return type for a SET");
1551
+ }
1552
+ args->ary = res;
1553
+ pl_call = pl_string;
1554
+ }
1555
+ c = Qnil;
1556
+ }
1557
+ else if (IsA(rsi, ReturnSetInfo)) {
1558
+ expr_multiple = 1;
1559
+ #if PG_PL_VERSION >= 75
1560
+ if (prodesc->named_args) {
1561
+ c = rb_funcall2(pl_mPLtemp, rb_intern(RSTRING_PTR(value_proname)),
1562
+ RARRAY_LEN(ary), RARRAY_PTR(ary));
1563
+ }
1564
+ else {
1565
+ c = rb_funcall(pl_mPLtemp, rb_intern(RSTRING_PTR(value_proname)),
1566
+ 1, ary);
1567
+ }
1568
+ #else
1569
+ c = rb_funcall(pl_mPLtemp, rb_intern(RSTRING_PTR(value_proname)),
1570
+ 1, ary);
1571
+ #endif
1572
+ }
1573
+ else {
1574
+ rb_raise(pl_ePLruby, "context don't accept set");
1575
+ }
1576
+ }
1577
+ else {
1578
+ #if PG_PL_VERSION >= 75
1579
+ if (prodesc->named_args) {
1580
+ c = rb_funcall2(pl_mPLtemp, rb_intern(RSTRING_PTR(value_proname)),
1581
+ RARRAY_LEN(ary), RARRAY_PTR(ary));
1582
+ }
1583
+ else {
1584
+ c = rb_funcall(pl_mPLtemp, rb_intern(RSTRING_PTR(value_proname)),
1585
+ 1, ary);
1586
+ }
1587
+ #else
1588
+ c = rb_funcall(pl_mPLtemp, rb_intern(RSTRING_PTR(value_proname)),
1589
+ 1, ary);
1590
+ #endif
1591
+ }
1592
+
1593
+ PLRUBY_BEGIN_PROTECT(1);
1594
+ {
1595
+ MemoryContext oldcxt;
1596
+ int rc;
1597
+
1598
+ oldcxt = MemoryContextSwitchTo(plruby_spi_context);
1599
+ if ((rc = SPI_finish()) != SPI_OK_FINISH) {
1600
+ elog(ERROR, "SPI_finish() failed : %d", rc);
1601
+ }
1602
+ MemoryContextSwitchTo(oldcxt);
1603
+ }
1604
+ PLRUBY_END_PROTECT;
1605
+
1606
+ if (c == Qnil) {
1607
+ if (expr_multiple) {
1608
+ pl_context_remove();
1609
+ fcinfo->context = NULL;
1610
+ ((ReturnSetInfo *)fcinfo->resultinfo)->isDone = ExprEndResult;
1611
+ }
1612
+ PG_RETURN_NULL();
1613
+ }
1614
+ if (fcinfo->resultinfo) {
1615
+ if (fcinfo->flinfo->fn_retset) {
1616
+ ((ReturnSetInfo *)fcinfo->resultinfo)->isDone = ExprMultipleResult;
1617
+ return return_base_type(c, prodesc);
1618
+ }
1619
+ if (!prodesc->result_type) {
1620
+ return return_base_type(c, prodesc);
1621
+ }
1622
+ return pl_tuple_datum(c, pl_tuple_s_new(fcinfo, prodesc));
1623
+ }
1624
+ if (prodesc->result_type == 'x') {
1625
+ VALUE res;
1626
+ Datum retval;
1627
+
1628
+ res = rb_funcall2(c, rb_intern("portal_name"), 0, 0);
1629
+ res = plruby_to_s(res);
1630
+ PLRUBY_BEGIN_PROTECT(1);
1631
+ retval = DFC1(textin, CStringGD(RSTRING_PTR(res)));
1632
+ PLRUBY_END_PROTECT;
1633
+ return retval;
1634
+ }
1635
+ #if PG_PL_VERSION >= 81
1636
+ if (prodesc->result_type == 'y') {
1637
+ TupleDesc tupdesc;
1638
+
1639
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) == TYPEFUNC_COMPOSITE) {
1640
+ VALUE tmp;
1641
+ struct pl_tuple *tpl;
1642
+
1643
+ tmp = Data_Make_Struct(rb_cData, struct pl_tuple, pl_thr_mark,
1644
+ free, tpl);
1645
+ GetTuple(tmp, tpl);
1646
+ tpl->pro = prodesc;
1647
+ tpl->dsc = tupdesc;
1648
+ tpl->att = TupleDescGetAttInMetadata(tupdesc);
1649
+ return pl_tuple_datum(c, tmp);
1650
+ }
1651
+ }
1652
+ #endif
1653
+ return return_base_type(c, prodesc);
1654
+ }
1655
+
1656
+ extern void Init_plruby_plan();
1657
+
1658
+ void Init_plruby_pl()
1659
+ {
1660
+ VALUE pl_sPLtemp;
1661
+
1662
+ pl_mPL = rb_define_module("PL");
1663
+ rb_const_set(rb_cObject, rb_intern("PLruby"), pl_mPL);
1664
+ rb_define_const(pl_mPL, "OK", INT2FIX(TG_OK));
1665
+ rb_define_const(pl_mPL, "SKIP", INT2FIX(TG_SKIP));
1666
+ rb_define_const(pl_mPL, "BEFORE", INT2FIX(TG_BEFORE));
1667
+ rb_define_const(pl_mPL, "AFTER", INT2FIX(TG_AFTER));
1668
+ rb_define_const(pl_mPL, "ROW", INT2FIX(TG_ROW));
1669
+ rb_define_const(pl_mPL, "STATEMENT", INT2FIX(TG_STATEMENT));
1670
+ rb_define_const(pl_mPL, "INSERT", INT2FIX(TG_INSERT));
1671
+ rb_define_const(pl_mPL, "DELETE", INT2FIX(TG_DELETE));
1672
+ rb_define_const(pl_mPL, "UPDATE", INT2FIX(TG_UPDATE));
1673
+ rb_define_const(pl_mPL, "UNKNOWN", INT2FIX(TG_UNKNOWN));
1674
+ rb_define_global_function("warn", pl_warn, -1);
1675
+ rb_define_module_function(pl_mPL, "quote", pl_quote, 1);
1676
+ rb_define_module_function(pl_mPL, "spi_exec", pl_SPI_exec, -1);
1677
+ rb_define_module_function(pl_mPL, "exec", pl_SPI_exec, -1);
1678
+ rb_define_module_function(pl_mPL, "column_name", pl_column_name, 1);
1679
+ rb_define_module_function(pl_mPL, "column_type", pl_column_type, 1);
1680
+ rb_define_module_function(pl_mPL, "result_name", pl_query_name, 0);
1681
+ rb_define_module_function(pl_mPL, "result_type", pl_query_type, 0);
1682
+ rb_define_module_function(pl_mPL, "result_size", pl_query_lgth, 0);
1683
+ rb_define_module_function(pl_mPL, "result_description", pl_query_description, 0);
1684
+ rb_define_module_function(pl_mPL, "args_type", pl_args_type, 0);
1685
+ rb_define_module_function(pl_mPL, "context", pl_context_get, 0);
1686
+ rb_define_module_function(pl_mPL, "context=", pl_context_set, 1);
1687
+ pl_ePLruby = rb_define_class_under(pl_mPL, "Error", rb_eStandardError);
1688
+ pl_eCatch = rb_define_class_under(pl_mPL, "Catch", rb_eStandardError);
1689
+ pl_mPLtemp = rb_define_module("PLtemp");
1690
+ pl_sPLtemp = rb_singleton_class(pl_mPLtemp);
1691
+ PLcontext = rb_hash_new();
1692
+ rb_global_variable(&PLcontext);
1693
+ if (MAIN_SAFE_LEVEL >= 3) {
1694
+ rb_obj_taint(pl_mPLtemp);
1695
+ rb_obj_taint(pl_sPLtemp);
1696
+ rb_obj_taint(PLcontext);
1697
+ }
1698
+ id_thr = rb_intern("__functype__");
1699
+ #ifndef HAVE_RB_HASH_DELETE
1700
+ id_delete = rb_intern("delete");
1701
+ #endif
1702
+ #ifdef PLRUBY_ENABLE_CONVERSION
1703
+ id_from_datum = rb_intern("from_datum");
1704
+ id_to_datum = rb_intern("to_datum");
1705
+ #endif
1706
+ Init_plruby_plan();
1707
+ pl_cPLPlan = rb_const_get(pl_mPL, rb_intern("Plan"));
1708
+ }