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.
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
+ }