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