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,893 @@
1
+ #include "plruby.h"
2
+
3
+ static VALUE pl_cPLPlan, pl_cPLCursor, pl_ePLruby;
4
+ static VALUE pl_eCatch;
5
+
6
+ static void
7
+ query_free(qdesc)
8
+ pl_query_desc *qdesc;
9
+ {
10
+ if (qdesc->argtypes) free(qdesc->argtypes);
11
+ if (qdesc->arginfuncs) free(qdesc->arginfuncs);
12
+ if (qdesc->argtypelems) free(qdesc->argtypelems);
13
+ if (qdesc->arglen) free(qdesc->arglen);
14
+ if (qdesc->arg_is_array) free(qdesc->arg_is_array);
15
+ if (qdesc->arg_val) free(qdesc->arg_val);
16
+ if (qdesc->arg_align) free(qdesc->arg_align);
17
+ free(qdesc);
18
+ }
19
+
20
+ static void
21
+ query_mark(qdesc)
22
+ pl_query_desc *qdesc;
23
+ {
24
+ rb_gc_mark(qdesc->po.argsv);
25
+ }
26
+
27
+ static VALUE
28
+ pl_plan_s_alloc(VALUE obj)
29
+ {
30
+ pl_query_desc *qdesc;
31
+ return Data_Make_Struct(obj, pl_query_desc, query_mark,
32
+ query_free, qdesc);
33
+ }
34
+
35
+ static VALUE
36
+ pl_plan_save(VALUE obj)
37
+ {
38
+ pl_query_desc *qdesc;
39
+ void *tmp;
40
+
41
+ GetPlan(obj, qdesc);
42
+
43
+ PLRUBY_BEGIN_PROTECT(1);
44
+ tmp = qdesc->plan;
45
+ qdesc->plan = SPI_saveplan(tmp);
46
+ SPI_freeplan(tmp);
47
+ PLRUBY_END_PROTECT;
48
+
49
+ if (qdesc->plan == NULL) {
50
+ char buf[128];
51
+ char *reason;
52
+
53
+ switch (SPI_result) {
54
+ case SPI_ERROR_ARGUMENT:
55
+ reason = "SPI_ERROR_ARGUMENT";
56
+ break;
57
+ case SPI_ERROR_UNCONNECTED:
58
+ reason = "SPI_ERROR_UNCONNECTED";
59
+ break;
60
+ default:
61
+ sprintf(buf, "unknown RC %d", SPI_result);
62
+ reason = buf;
63
+ break;
64
+ }
65
+ rb_raise(pl_ePLruby, "SPI_saveplan() failed - %s", reason);
66
+ }
67
+ return obj;
68
+ }
69
+
70
+ static VALUE
71
+ pl_plan_init(int argc, VALUE *argv, VALUE obj)
72
+ {
73
+ pl_query_desc *qdesc;
74
+ void *plan;
75
+ int i;
76
+ HeapTuple typeTup;
77
+ VALUE a, b, c, d;
78
+
79
+ Data_Get_Struct(obj, pl_query_desc, qdesc);
80
+ if (argc && TYPE(argv[argc - 1]) == T_HASH) {
81
+ rb_iterate(rb_each, argv[argc - 1], plruby_i_each, (VALUE)&(qdesc->po));
82
+ argc--;
83
+ }
84
+ switch (rb_scan_args(argc, argv, "13", &a, &b, &c, &d)) {
85
+ case 4:
86
+ plruby_exec_output(d, RET_ARRAY, &(qdesc->po.output));
87
+ /* ... */
88
+ case 3:
89
+ if (!NIL_P(c)) {
90
+ qdesc->po.count = NUM2INT(c);
91
+ }
92
+ /* ... */
93
+ case 2:
94
+ if (!NIL_P(b)) {
95
+ if (TYPE(b) != T_ARRAY) {
96
+ rb_raise(pl_ePLruby, "second argument must be an ARRAY");
97
+ }
98
+ qdesc->po.argsv = b;
99
+ }
100
+ break;
101
+ }
102
+ if (TYPE(a) != T_STRING) {
103
+ rb_raise(pl_ePLruby, "first argument must be a STRING");
104
+ }
105
+ sprintf(qdesc->qname, "%lx", (long) qdesc);
106
+ if (RTEST(qdesc->po.argsv)) {
107
+ if (TYPE(qdesc->po.argsv) != T_ARRAY) {
108
+ rb_raise(pl_ePLruby, "expected an Array");
109
+ }
110
+ qdesc->nargs = RARRAY_LEN(qdesc->po.argsv);
111
+ }
112
+ qdesc->argtypes = NULL;
113
+ if (qdesc->nargs) {
114
+ qdesc->argtypes = ALLOC_N(Oid, qdesc->nargs);
115
+ MEMZERO(qdesc->argtypes, Oid, qdesc->nargs);
116
+ qdesc->arginfuncs = ALLOC_N(FmgrInfo, qdesc->nargs);
117
+ MEMZERO(qdesc->arginfuncs, FmgrInfo, qdesc->nargs);
118
+ qdesc->argtypelems = ALLOC_N(Oid, qdesc->nargs);
119
+ MEMZERO(qdesc->argtypelems, Oid, qdesc->nargs);
120
+ qdesc->arglen = ALLOC_N(int, qdesc->nargs);
121
+ MEMZERO(qdesc->arglen, int, qdesc->nargs);
122
+ qdesc->arg_is_array = ALLOC_N(bool, qdesc->nargs);
123
+ MEMZERO(qdesc->arg_is_array, bool, qdesc->nargs);
124
+ qdesc->arg_val = ALLOC_N(bool, qdesc->nargs);
125
+ MEMZERO(qdesc->arg_val, bool, qdesc->nargs);
126
+ qdesc->arg_align = ALLOC_N(char, qdesc->nargs);
127
+ MEMZERO(qdesc->arg_align, char, qdesc->nargs);
128
+ for (i = 0; i < qdesc->nargs; i++) {
129
+ char *argcopy;
130
+ List *names = NIL;
131
+ #if PG_PL_VERSION >= 75
132
+ ListCell *lp;
133
+ #else
134
+ List *lp;
135
+ #endif
136
+ TypeName *typename;
137
+ Form_pg_type fpgt;
138
+ int arg_is_array = 0;
139
+ VALUE args = plruby_to_s(RARRAY_PTR(qdesc->po.argsv)[i]);
140
+
141
+ PLRUBY_BEGIN_PROTECT(1);
142
+ argcopy = pstrdup(RSTRING_PTR(args));
143
+ SplitIdentifierString(argcopy, '.', &names);
144
+ typename = makeNode(TypeName);
145
+ foreach (lp, names)
146
+ typename->names = lappend(typename->names, makeString(lfirst(lp)));
147
+ #if PG_PL_VERSION >= 82
148
+ #if PG_PL_VERSION >= 83
149
+ typeTup = typenameType(NULL, typename, NULL);
150
+ #else
151
+ typeTup = typenameType(NULL, typename);
152
+ #endif
153
+ #else
154
+ typeTup = typenameType(typename);
155
+ #endif
156
+ qdesc->argtypes[i] = HeapTupleGetOid(typeTup);
157
+ fpgt = (Form_pg_type) GETSTRUCT(typeTup);
158
+ arg_is_array = qdesc->arg_is_array[i] = NameStr(fpgt->typname)[0] == '_';
159
+ if (qdesc->arg_is_array[i]) {
160
+ Oid elemtyp;
161
+ HeapTuple typeTuple;
162
+
163
+ #if PG_PL_VERSION >= 75
164
+ elemtyp = getTypeIOParam(typeTup);
165
+ #else
166
+ elemtyp = fpgt->typelem;
167
+ #endif
168
+ typeTuple = SearchSysCache(TYPEOID, OidGD(elemtyp), 0, 0, 0);
169
+
170
+ if (!HeapTupleIsValid(typeTuple)) {
171
+ elog(ERROR, "cache lookup failed for type %u", elemtyp);
172
+ }
173
+ fpgt = (Form_pg_type) GETSTRUCT(typeTuple);
174
+ fmgr_info(fpgt->typinput, &(qdesc->arginfuncs[i]));
175
+ qdesc->arglen[i] = fpgt->typlen;
176
+ qdesc->arg_val[i] = fpgt->typbyval;
177
+ qdesc->arg_align[i] = fpgt->typalign;
178
+ ReleaseSysCache(typeTuple);
179
+ }
180
+ #if PG_PL_VERSION >= 75
181
+ qdesc->argtypelems[i] = getTypeIOParam(typeTup);
182
+ #else
183
+ qdesc->argtypelems[i] = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
184
+ #endif
185
+ if (!arg_is_array) {
186
+ fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput,
187
+ &(qdesc->arginfuncs[i]));
188
+ qdesc->arglen[i] = (int) (((Form_pg_type) GETSTRUCT(typeTup))->typlen);
189
+ }
190
+ ReleaseSysCache(typeTup);
191
+ #if PG_PL_VERSION >= 75
192
+ #define freeList(a_) list_free(a_)
193
+ #endif
194
+ freeList(typename->names);
195
+ pfree(typename);
196
+ freeList(names);
197
+ pfree(argcopy);
198
+ PLRUBY_END_PROTECT;
199
+
200
+ }
201
+ }
202
+
203
+ {
204
+ #ifdef PG_PL_TRYCATCH
205
+ PG_TRY();
206
+ {
207
+ plan = SPI_prepare(RSTRING_PTR(a), qdesc->nargs, qdesc->argtypes);
208
+ }
209
+ PG_CATCH();
210
+ {
211
+ plan = NULL;
212
+ }
213
+ PG_END_TRY();
214
+ #else
215
+ sigjmp_buf save_restart;
216
+ extern bool InError;
217
+
218
+ memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
219
+ if (sigsetjmp(Warn_restart, 1) == 0) {
220
+ PLRUBY_BEGIN_PROTECT(1);
221
+ plan = SPI_prepare(RSTRING_PTR(a), qdesc->nargs, qdesc->argtypes);
222
+ memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
223
+ PLRUBY_END_PROTECT;
224
+ }
225
+ else {
226
+ memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
227
+ InError = 0;
228
+ plan = NULL;
229
+ }
230
+ #endif
231
+ }
232
+
233
+ if (plan == NULL) {
234
+ char buf[128];
235
+ char *reason;
236
+
237
+ switch (SPI_result) {
238
+ case SPI_ERROR_ARGUMENT:
239
+ reason = "SPI_ERROR_ARGUMENT";
240
+ break;
241
+ case SPI_ERROR_UNCONNECTED:
242
+ reason = "SPI_ERROR_UNCONNECTED";
243
+ break;
244
+ case SPI_ERROR_COPY:
245
+ reason = "SPI_ERROR_COPY";
246
+ break;
247
+ case SPI_ERROR_CURSOR:
248
+ reason = "SPI_ERROR_CURSOR";
249
+ break;
250
+ case SPI_ERROR_TRANSACTION:
251
+ reason = "SPI_ERROR_TRANSACTION";
252
+ break;
253
+ case SPI_ERROR_OPUNKNOWN:
254
+ reason = "SPI_ERROR_OPUNKNOWN";
255
+ break;
256
+ case 0:
257
+ reason = "SPI_PARSE_ERROR";
258
+ break;
259
+ default:
260
+ sprintf(buf, "unknown RC %d", SPI_result);
261
+ reason = buf;
262
+ break;
263
+ }
264
+ rb_raise(pl_ePLruby, "SPI_prepare() failed - %s\n%s",
265
+ reason, RSTRING_PTR(a));
266
+ }
267
+ qdesc->plan = plan;
268
+ if (qdesc->po.save) {
269
+ pl_plan_save(obj);
270
+ }
271
+ return obj;
272
+ }
273
+
274
+ static VALUE
275
+ pl_plan_prepare(int argc, VALUE *argv, VALUE obj)
276
+ {
277
+ if (!argc || TYPE(argv[argc - 1]) != T_HASH) {
278
+ argv[argc] = rb_hash_new();
279
+ ++argc;
280
+ }
281
+ rb_hash_aset(argv[argc - 1], rb_str_new2("save"), Qtrue);
282
+ return plruby_s_new(argc, argv, pl_cPLPlan);
283
+ }
284
+
285
+ static void
286
+ process_args(pl_query_desc *qdesc, VALUE vortal)
287
+ {
288
+ struct PLportal *portal;
289
+ int callnargs, j;
290
+ VALUE argsv;
291
+
292
+ Data_Get_Struct(vortal, struct PLportal, portal);
293
+ if (qdesc->nargs > 0) {
294
+ argsv = portal->po.argsv;
295
+ if (TYPE(argsv) != T_ARRAY) {
296
+ rb_raise(pl_ePLruby, "array expected for arguments");
297
+ }
298
+ if (RARRAY_LEN(argsv) != qdesc->nargs) {
299
+ rb_raise(pl_ePLruby, "length of arguments doesn't match # of arguments");
300
+ }
301
+ callnargs = RARRAY_LEN(argsv);
302
+ portal->nargs = callnargs;
303
+ portal->nulls = ALLOC_N(char, callnargs + 1);
304
+ MEMZERO(portal->nulls, char, callnargs + 1);
305
+ portal->argvalues = ALLOC_N(Datum, callnargs);
306
+ MEMZERO(portal->argvalues, Datum, callnargs);
307
+ portal->arglen = ALLOC_N(int, callnargs);
308
+ MEMZERO(portal->arglen, int, callnargs);
309
+ for (j = 0; j < callnargs; j++) {
310
+ if (NIL_P(RARRAY_PTR(argsv)[j])) {
311
+ portal->nulls[j] = 'n';
312
+ portal->argvalues[j] = (Datum)NULL;
313
+ }
314
+ else {
315
+ if (qdesc->arg_is_array[j]) {
316
+ pl_proc_desc prodesc;
317
+
318
+ MEMZERO(&prodesc, pl_proc_desc, 1);
319
+ prodesc.result_func = qdesc->arginfuncs[j];
320
+ prodesc.result_oid = qdesc->argtypes[j];
321
+ prodesc.result_elem = qdesc->argtypelems[j];
322
+ prodesc.result_len = qdesc->arglen[j];
323
+ prodesc.result_val = qdesc->arg_val[j];
324
+ prodesc.result_align = qdesc->arg_align[j];
325
+
326
+ portal->nulls[j] = ' ';
327
+ portal->arglen[j] = qdesc->arglen[j];
328
+
329
+ portal->argvalues[j] =
330
+ plruby_return_array(RARRAY_PTR(argsv)[j], &prodesc);
331
+ }
332
+ else {
333
+ VALUE args = RARRAY_PTR(argsv)[j];
334
+ portal->nulls[j] = ' ';
335
+ portal->arglen[j] = qdesc->arglen[j];
336
+ portal->argvalues[j] =
337
+ plruby_to_datum(args, &qdesc->arginfuncs[j],
338
+ qdesc->argtypes[j],
339
+ qdesc->argtypelems[j],
340
+ -1);
341
+ }
342
+
343
+ }
344
+ }
345
+ portal->nulls[callnargs] = '\0';
346
+ }
347
+ return;
348
+ }
349
+
350
+ static void
351
+ free_args(struct PLportal *portal)
352
+ {
353
+ int j;
354
+
355
+ for (j = 0; j < portal->nargs; j++) {
356
+ if (portal->arglen[j] < 0 &&
357
+ portal->argvalues[j] != (Datum) NULL) {
358
+ pfree((char *) (portal->argvalues[j]));
359
+ portal->argvalues[j] = (Datum) NULL;
360
+ }
361
+ }
362
+ if (portal->argvalues) {
363
+ free(portal->argvalues);
364
+ portal->argvalues = 0;
365
+ }
366
+ if (portal->arglen) {
367
+ free(portal->arglen);
368
+ portal->arglen = 0;
369
+ }
370
+ if (portal->nulls) {
371
+ free(portal->nulls);
372
+ portal->nulls = 0;
373
+ }
374
+ }
375
+
376
+ static void
377
+ portal_free(struct PLportal *portal)
378
+ {
379
+ portal->nargs = 0;
380
+ free_args(portal);
381
+ free(portal);
382
+ }
383
+
384
+ static void
385
+ portal_mark(struct PLportal *portal)
386
+ {
387
+ rb_gc_mark(portal->po.argsv);
388
+ }
389
+
390
+ VALUE
391
+ plruby_i_each(VALUE obj, struct portal_options *po)
392
+ {
393
+ VALUE key, value;
394
+ char *options;
395
+
396
+ key = rb_ary_entry(obj, 0);
397
+ value = rb_ary_entry(obj, 1);
398
+ key = plruby_to_s(key);
399
+ options = RSTRING_PTR(key);
400
+ if (strcmp(options, "values") == 0 ||
401
+ strcmp(options, "types") == 0) {
402
+ po->argsv = value;
403
+ }
404
+ else if (strcmp(options, "count") == 0) {
405
+ po->count = NUM2INT(value);
406
+ }
407
+ else if (strcmp(options, "output") == 0) {
408
+ plruby_exec_output(value, RET_ARRAY, &(po->output));
409
+ }
410
+ else if (strcmp(options, "block") == 0) {
411
+ po->block = NUM2INT(value);
412
+ }
413
+ else if (strcmp(options, "save") == 0) {
414
+ po->save = RTEST(value);
415
+ }
416
+ return Qnil;
417
+ }
418
+
419
+ static VALUE
420
+ create_vortal(int argc, VALUE *argv, VALUE obj)
421
+ {
422
+ VALUE vortal, argsv, countv, c;
423
+ struct PLportal *portal;
424
+ pl_query_desc *qdesc;
425
+
426
+ GetPlan(obj, qdesc);
427
+ vortal = Data_Make_Struct(pl_cPLCursor, struct PLportal, portal_mark,
428
+ portal_free, portal);
429
+
430
+ MEMCPY(&(portal->po), &(qdesc->po), struct portal_options, 1);
431
+ portal->po.argsv = Qnil;
432
+ if (!portal->po.output) {
433
+ portal->po.output = RET_HASH;
434
+ }
435
+ if (argc && TYPE(argv[argc - 1]) == T_HASH) {
436
+ rb_iterate(rb_each, argv[argc - 1], plruby_i_each, (VALUE)&portal->po);
437
+ argc--;
438
+ }
439
+ switch (rb_scan_args(argc, argv, "03", &argsv, &countv, &c)) {
440
+ case 3:
441
+ plruby_exec_output(c, RET_ARRAY, &(portal->po.output));
442
+ /* ... */
443
+ case 2:
444
+ if (!NIL_P(countv)) {
445
+ portal->po.count = NUM2INT(countv);
446
+ }
447
+ /* ... */
448
+ case 1:
449
+ portal->po.argsv = argsv;
450
+ }
451
+ process_args(qdesc, vortal);
452
+ portal->po.argsv = 0;
453
+ return vortal;
454
+ }
455
+
456
+ static VALUE
457
+ pl_plan_execp(argc, argv, obj)
458
+ int argc;
459
+ VALUE *argv;
460
+ VALUE obj;
461
+ {
462
+ int i, spi_rc, count, typout;
463
+ VALUE result;
464
+ VALUE vortal;
465
+ pl_query_desc *qdesc;
466
+ int ntuples;
467
+ HeapTuple *tuples = NULL;
468
+ TupleDesc tupdesc = NULL;
469
+ struct PLportal *portal;
470
+
471
+ GetPlan(obj, qdesc);
472
+ vortal = create_vortal(argc, argv, obj);
473
+ Data_Get_Struct(vortal, struct PLportal, portal);
474
+ PLRUBY_BEGIN_PROTECT(1);
475
+ spi_rc = SPI_execp(qdesc->plan, portal->argvalues,
476
+ portal->nulls, portal->po.count);
477
+ Data_Get_Struct(vortal, struct PLportal, portal);
478
+ free_args(portal);
479
+ PLRUBY_END_PROTECT;
480
+ count = portal->po.count;
481
+ typout = portal->po.output;
482
+
483
+ switch (spi_rc) {
484
+ case SPI_OK_UTILITY:
485
+ if (SPI_tuptable == NULL) {
486
+ SPI_freetuptable(SPI_tuptable);
487
+ return Qtrue;
488
+ }
489
+ break;
490
+
491
+ case SPI_OK_SELINTO:
492
+ case SPI_OK_INSERT:
493
+ case SPI_OK_DELETE:
494
+ case SPI_OK_UPDATE:
495
+ SPI_freetuptable(SPI_tuptable);
496
+ return INT2NUM(SPI_processed);
497
+ case SPI_OK_SELECT:
498
+ #ifdef SPI_OK_INSERT_RETURNING
499
+ case SPI_OK_INSERT_RETURNING:
500
+ case SPI_OK_DELETE_RETURNING:
501
+ case SPI_OK_UPDATE_RETURNING:
502
+ #endif
503
+ break;
504
+
505
+ case SPI_ERROR_ARGUMENT:
506
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_ARGUMENT");
507
+ case SPI_ERROR_UNCONNECTED:
508
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_UNCONNECTED");
509
+ case SPI_ERROR_COPY:
510
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_COPY");
511
+ case SPI_ERROR_CURSOR:
512
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_CURSOR");
513
+ case SPI_ERROR_TRANSACTION:
514
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_TRANSACTION");
515
+ case SPI_ERROR_OPUNKNOWN:
516
+ rb_raise(pl_ePLruby, "SPI_exec() failed - SPI_ERROR_OPUNKNOWN");
517
+ default:
518
+ rb_raise(pl_ePLruby, "SPI_exec() failed - unknown RC %d", spi_rc);
519
+ }
520
+
521
+ ntuples = SPI_processed;
522
+ if (ntuples <= 0) {
523
+ SPI_freetuptable(SPI_tuptable);
524
+ if (rb_block_given_p() || count == 1) {
525
+ return Qfalse;
526
+ }
527
+ else {
528
+ return rb_ary_new2(0);
529
+ }
530
+ }
531
+ tuples = SPI_tuptable->vals;
532
+ tupdesc = SPI_tuptable->tupdesc;
533
+ if (rb_block_given_p()) {
534
+ if (count == 1) {
535
+ int form = typout;
536
+ if (!(form & RET_DESC)) {
537
+ form |= RET_BASIC;
538
+ }
539
+ plruby_build_tuple(tuples[0], tupdesc, form);
540
+ }
541
+ else {
542
+ for (i = 0; i < ntuples; i++) {
543
+ rb_yield(plruby_build_tuple(tuples[i], tupdesc, typout));
544
+ }
545
+ }
546
+ result = Qtrue;
547
+ }
548
+ else {
549
+ if (count == 1) {
550
+ result = plruby_build_tuple(tuples[0], tupdesc, typout);
551
+ }
552
+ else {
553
+ result = rb_ary_new2(ntuples);
554
+ for (i = 0; i < ntuples; i++) {
555
+ rb_ary_push(result,
556
+ plruby_build_tuple(tuples[i], tupdesc, typout));
557
+ }
558
+ }
559
+ }
560
+ SPI_freetuptable(SPI_tuptable);
561
+ return result;
562
+ }
563
+
564
+ #if PG_PL_VERSION == 74
565
+ #define PORTAL_ACTIVE(port) ((port)->portalActive)
566
+ #elif WITH_GREENPLUM == 1
567
+ #define PORTAL_ACTIVE(port) ((port)->portal_status == PORTAL_ACTIVE)
568
+ #elif PG_PL_VERSION > 74
569
+ #define PORTAL_ACTIVE(port) ((port)->status == PORTAL_ACTIVE)
570
+ #else
571
+ #define PORTAL_ACTIVE(port) 0
572
+ #endif
573
+
574
+ static VALUE
575
+ pl_close(VALUE vortal)
576
+ {
577
+ struct PLportal *portal;
578
+
579
+ GetPortal(vortal, portal);
580
+ PLRUBY_BEGIN_PROTECT(1);
581
+ if (!PORTAL_ACTIVE(portal->portal)) {
582
+ SPI_cursor_close(portal->portal);
583
+ }
584
+ portal->portal = 0;
585
+ PLRUBY_END_PROTECT;
586
+ return Qnil;
587
+ }
588
+
589
+ static VALUE
590
+ pl_portal_name(VALUE vortal)
591
+ {
592
+ struct PLportal *portal;
593
+
594
+ GetPortal(vortal, portal);
595
+ return rb_tainted_str_new2(portal->portal->name);
596
+ }
597
+
598
+ static VALUE
599
+ pl_fetch(VALUE vortal)
600
+ {
601
+ struct PLportal *portal;
602
+ HeapTuple *tuples = NULL;
603
+ TupleDesc tupdesc = NULL;
604
+ SPITupleTable *tuptab;
605
+ int i, proces, pcount, block, count;
606
+
607
+ GetPortal(vortal, portal);
608
+ count = 0;
609
+ block = portal->po.block + 1;
610
+ if (portal->po.count) pcount = portal->po.count;
611
+ else pcount = -1;
612
+ while (count != pcount) {
613
+ PLRUBY_BEGIN_PROTECT(1);
614
+ SPI_cursor_fetch(portal->portal, true, block);
615
+ PLRUBY_END_PROTECT;
616
+ if (SPI_processed <= 0) {
617
+ return Qnil;
618
+ }
619
+ proces = SPI_processed;
620
+ tuptab = SPI_tuptable;
621
+ tuples = tuptab->vals;
622
+ tupdesc = tuptab->tupdesc;
623
+ for (i = 0; i < proces && count != pcount; ++i, ++count) {
624
+ rb_yield(plruby_build_tuple(tuples[i], tupdesc, portal->po.output));
625
+ }
626
+ SPI_freetuptable(tuptab);
627
+ }
628
+ return Qnil;
629
+ }
630
+
631
+
632
+ static VALUE
633
+ pl_plan_each(argc, argv, obj)
634
+ int argc;
635
+ VALUE *argv;
636
+ VALUE obj;
637
+ {
638
+ pl_query_desc *qdesc;
639
+ Portal pgportal;
640
+ struct PLportal *portal;
641
+ VALUE vortal;
642
+
643
+ if (!rb_block_given_p()) {
644
+ rb_raise(pl_ePLruby, "a block must be given");
645
+ }
646
+ GetPlan(obj, qdesc);
647
+ vortal = create_vortal(argc, argv, obj);
648
+ Data_Get_Struct(vortal, struct PLportal, portal);
649
+ PLRUBY_BEGIN_PROTECT(1);
650
+ #if PG_PL_VERSION >= 80
651
+ pgportal = SPI_cursor_open(NULL, qdesc->plan, portal->argvalues,
652
+ portal->nulls, false);
653
+ #else
654
+ pgportal = SPI_cursor_open(NULL, qdesc->plan,
655
+ portal->argvalues, portal->nulls);
656
+ #endif
657
+ Data_Get_Struct(vortal, struct PLportal, portal);
658
+ free_args(portal);
659
+ PLRUBY_END_PROTECT;
660
+ if (pgportal == NULL) {
661
+ rb_raise(pl_ePLruby, "SPI_cursor_open() failed");
662
+ }
663
+ portal->portal = pgportal;
664
+ rb_ensure(pl_fetch, vortal, pl_close, vortal);
665
+ return Qnil;
666
+ }
667
+
668
+ static VALUE
669
+ pl_plan_cursor(int argc, VALUE *argv, VALUE obj)
670
+ {
671
+ char *name = NULL;
672
+ pl_query_desc *qdesc;
673
+ Portal pgportal;
674
+ struct PLportal *portal;
675
+ VALUE vortal;
676
+
677
+ GetPlan(obj, qdesc);
678
+ if (argc && TYPE(argv[0]) != T_HASH) {
679
+ if (!NIL_P(argv[0])) {
680
+ if (TYPE(argv[0]) != T_STRING) {
681
+ rb_raise(pl_ePLruby, "invalid cursor name");
682
+ }
683
+ name = RSTRING_PTR(argv[0]);
684
+ }
685
+ --argc; ++argv;
686
+ }
687
+ vortal = create_vortal(argc, argv, obj);
688
+ Data_Get_Struct(vortal, struct PLportal, portal);
689
+ PLRUBY_BEGIN_PROTECT(1);
690
+ #if PG_PL_VERSION >= 80
691
+ pgportal = SPI_cursor_open(name, qdesc->plan, portal->argvalues,
692
+ portal->nulls, false);
693
+ #else
694
+ pgportal = SPI_cursor_open(name, qdesc->plan,
695
+ portal->argvalues, portal->nulls);
696
+ #endif
697
+ PLRUBY_END_PROTECT;
698
+ if (pgportal == NULL) {
699
+ rb_raise(pl_ePLruby, "SPI_cursor_open() failed");
700
+ }
701
+ portal->portal = pgportal;
702
+ return vortal;
703
+ }
704
+
705
+ static VALUE
706
+ pl_plan_release(VALUE obj)
707
+ {
708
+ pl_query_desc *qdesc;
709
+ int spi_rc;
710
+
711
+ GetPlan(obj, qdesc);
712
+ PLRUBY_BEGIN_PROTECT(1);
713
+ spi_rc = SPI_freeplan(qdesc->plan);
714
+ qdesc->plan = 0;
715
+ PLRUBY_END_PROTECT;
716
+ if (spi_rc) {
717
+ rb_raise(pl_ePLruby, "SPI_freeplan() failed");
718
+ }
719
+ return Qnil;
720
+ }
721
+
722
+ static VALUE
723
+ pl_cursor_move(VALUE obj, VALUE a)
724
+ {
725
+ struct PLportal *portal;
726
+ int forward, count;
727
+
728
+ GetPortal(obj, portal);
729
+ count = NUM2INT(a);
730
+ if (count) {
731
+ if (count < 0) {
732
+ forward = 0;
733
+ count *= -1;
734
+ }
735
+ else {
736
+ forward = 1;
737
+ }
738
+ PLRUBY_BEGIN_PROTECT(1);
739
+ SPI_cursor_move(portal->portal, forward, count);
740
+ PLRUBY_END_PROTECT;
741
+ }
742
+ return obj;
743
+ }
744
+
745
+ static VALUE
746
+ pl_cursor_fetch(int argc, VALUE *argv, VALUE obj)
747
+ {
748
+ struct PLportal *portal;
749
+ SPITupleTable *tup;
750
+ int proces, forward, count, i;
751
+ VALUE a, res;
752
+
753
+ GetPortal(obj, portal);
754
+ forward = count = 1;
755
+ if (rb_scan_args(argc, argv, "01", &a)) {
756
+ if (!NIL_P(a)) {
757
+ count = NUM2INT(a);
758
+ }
759
+ if (count < 0) {
760
+ forward = 0;
761
+ count *= -1;
762
+ }
763
+ }
764
+ if (!count) {
765
+ return Qnil;
766
+ }
767
+ PLRUBY_BEGIN_PROTECT(1);
768
+ SPI_cursor_fetch(portal->portal, forward, count);
769
+ PLRUBY_END_PROTECT;
770
+ proces = SPI_processed;
771
+ tup = SPI_tuptable;
772
+ if (proces <= 0) {
773
+ return Qnil;
774
+ }
775
+ if (proces == 1) {
776
+ res = plruby_build_tuple(tup->vals[0], tup->tupdesc, portal->po.output);
777
+ }
778
+ else {
779
+ res = rb_ary_new2(proces);
780
+ for (i = 0; i < proces; ++i) {
781
+ rb_ary_push(res, plruby_build_tuple(tup->vals[i], tup->tupdesc,
782
+ portal->po.output));
783
+ }
784
+ }
785
+ SPI_freetuptable(tup);
786
+ return res;
787
+ }
788
+
789
+ static VALUE
790
+ cursor_i_fetch(VALUE obj)
791
+ {
792
+ VALUE res;
793
+
794
+ while (1) {
795
+ res = rb_funcall2(obj, rb_intern("fetch"), 0, 0);
796
+ if (NIL_P(res)) break;
797
+ rb_yield(res);
798
+ }
799
+ return obj;
800
+ }
801
+
802
+ static VALUE
803
+ pl_cursor_each(VALUE obj)
804
+ {
805
+ if (!rb_block_given_p()) {
806
+ rb_raise(pl_ePLruby, "called without a block");
807
+ }
808
+ rb_iterate(cursor_i_fetch, obj, rb_yield, 0);
809
+ return obj;
810
+ }
811
+
812
+ static VALUE
813
+ cursor_r_fetch(VALUE obj)
814
+ {
815
+ VALUE res;
816
+
817
+ while (1) {
818
+ res = rb_funcall(obj, rb_intern("fetch"), 1, INT2NUM(-1));
819
+ if (NIL_P(res)) break;
820
+ rb_yield(res);
821
+ }
822
+ return obj;
823
+ }
824
+
825
+ static VALUE
826
+ pl_cursor_rev_each(VALUE obj)
827
+ {
828
+ if (!rb_block_given_p()) {
829
+ rb_raise(pl_ePLruby, "called without a block");
830
+ }
831
+ rb_iterate(cursor_r_fetch, obj, rb_yield, 0);
832
+ return obj;
833
+ }
834
+
835
+ static VALUE
836
+ pl_cursor_rewind(VALUE obj)
837
+ {
838
+ struct PLportal *portal;
839
+ int proces = 12;
840
+
841
+ GetPortal(obj, portal);
842
+ while (proces) {
843
+ PLRUBY_BEGIN_PROTECT(1);
844
+ SPI_cursor_move(portal->portal, 0, 12);
845
+ PLRUBY_END_PROTECT;
846
+ proces = SPI_processed;
847
+ }
848
+ return obj;
849
+ }
850
+
851
+ void Init_plruby_plan()
852
+ {
853
+ VALUE pl_mPL;
854
+
855
+ pl_mPL = rb_const_get(rb_cObject, rb_intern("PL"));
856
+ pl_ePLruby = rb_const_get(pl_mPL, rb_intern("Error"));
857
+ pl_eCatch = rb_const_get(pl_mPL, rb_intern("Catch"));
858
+ /* deprecated */
859
+ rb_define_module_function(pl_mPL, "spi_prepare", pl_plan_prepare, -1);
860
+ rb_define_module_function(pl_mPL, "prepare", pl_plan_prepare, -1);
861
+ /* ... */
862
+ pl_cPLPlan = rb_define_class_under(pl_mPL, "Plan", rb_cObject);
863
+ rb_include_module(pl_cPLPlan, rb_mEnumerable);
864
+ rb_const_set(rb_cObject, rb_intern("PLrubyPlan"), pl_cPLPlan);
865
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
866
+ rb_define_alloc_func(pl_cPLPlan, pl_plan_s_alloc);
867
+ #else
868
+ rb_define_singleton_method(pl_cPLPlan, "allocate", pl_plan_s_alloc, 0);
869
+ #endif
870
+ rb_define_singleton_method(pl_cPLPlan, "new", plruby_s_new, -1);
871
+ rb_define_private_method(pl_cPLPlan, "initialize", pl_plan_init, -1);
872
+ rb_define_method(pl_cPLPlan, "save", pl_plan_save, 0);
873
+ rb_define_method(pl_cPLPlan, "spi_execp", pl_plan_execp, -1);
874
+ rb_define_method(pl_cPLPlan, "execp", pl_plan_execp, -1);
875
+ rb_define_method(pl_cPLPlan, "exec", pl_plan_execp, -1);
876
+ rb_define_method(pl_cPLPlan, "spi_fetch", pl_plan_each, -1);
877
+ rb_define_method(pl_cPLPlan, "each", pl_plan_each, -1);
878
+ rb_define_method(pl_cPLPlan, "fetch", pl_plan_each, -1);
879
+ rb_define_method(pl_cPLPlan, "cursor", pl_plan_cursor, -1);
880
+ rb_define_method(pl_cPLPlan, "release", pl_plan_release, 0);
881
+ pl_cPLCursor = rb_define_class_under(pl_mPL, "Cursor", rb_cObject);
882
+ rb_undef_method(CLASS_OF(pl_cPLCursor), "allocate");
883
+ rb_undef_method(CLASS_OF(pl_cPLCursor), "new");
884
+ rb_include_module(pl_cPLCursor, rb_mEnumerable);
885
+ rb_define_method(pl_cPLCursor, "each", pl_cursor_each, 0);
886
+ rb_define_method(pl_cPLCursor, "reverse_each", pl_cursor_rev_each, 0);
887
+ rb_define_method(pl_cPLCursor, "close", pl_close, 0);
888
+ rb_define_method(pl_cPLCursor, "portal_name", pl_portal_name, 0);
889
+ rb_define_method(pl_cPLCursor, "fetch", pl_cursor_fetch, -1);
890
+ rb_define_method(pl_cPLCursor, "row", pl_cursor_fetch, -1);
891
+ rb_define_method(pl_cPLCursor, "move", pl_cursor_move, 1);
892
+ rb_define_method(pl_cPLCursor, "rewind", pl_cursor_rewind, 0);
893
+ }