Line data Source code
1 : /*
2 : * the plpy module
3 : *
4 : * src/pl/plpython/plpy_plpymodule.c
5 : */
6 :
7 : #include "postgres.h"
8 :
9 : #include "access/xact.h"
10 : #include "mb/pg_wchar.h"
11 : #include "plpy_cursorobject.h"
12 : #include "plpy_elog.h"
13 : #include "plpy_main.h"
14 : #include "plpy_planobject.h"
15 : #include "plpy_plpymodule.h"
16 : #include "plpy_resultobject.h"
17 : #include "plpy_spi.h"
18 : #include "plpy_subxactobject.h"
19 : #include "plpython.h"
20 : #include "utils/builtins.h"
21 : #include "utils/snapmgr.h"
22 :
23 : HTAB *PLy_spi_exceptions = NULL;
24 :
25 :
26 : static void PLy_add_exceptions(PyObject *plpy);
27 : static PyObject *PLy_create_exception(char *name,
28 : PyObject *base, PyObject *dict,
29 : const char *modname, PyObject *mod);
30 : static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base);
31 :
32 : /* module functions */
33 : static PyObject *PLy_debug(PyObject *self, PyObject *args, PyObject *kw);
34 : static PyObject *PLy_log(PyObject *self, PyObject *args, PyObject *kw);
35 : static PyObject *PLy_info(PyObject *self, PyObject *args, PyObject *kw);
36 : static PyObject *PLy_notice(PyObject *self, PyObject *args, PyObject *kw);
37 : static PyObject *PLy_warning(PyObject *self, PyObject *args, PyObject *kw);
38 : static PyObject *PLy_error(PyObject *self, PyObject *args, PyObject *kw);
39 : static PyObject *PLy_fatal(PyObject *self, PyObject *args, PyObject *kw);
40 : static PyObject *PLy_quote_literal(PyObject *self, PyObject *args);
41 : static PyObject *PLy_quote_nullable(PyObject *self, PyObject *args);
42 : static PyObject *PLy_quote_ident(PyObject *self, PyObject *args);
43 :
44 :
45 : /* A list of all known exceptions, generated from backend/utils/errcodes.txt */
46 : typedef struct ExceptionMap
47 : {
48 : char *name;
49 : char *classname;
50 : int sqlstate;
51 : } ExceptionMap;
52 :
53 : static const ExceptionMap exception_map[] = {
54 : #include "spiexceptions.h"
55 : {NULL, NULL, 0}
56 : };
57 :
58 : static PyMethodDef PLy_methods[] = {
59 : /*
60 : * logging methods
61 : */
62 : {"debug", (PyCFunction) (pg_funcptr_t) PLy_debug, METH_VARARGS | METH_KEYWORDS, NULL},
63 : {"log", (PyCFunction) (pg_funcptr_t) PLy_log, METH_VARARGS | METH_KEYWORDS, NULL},
64 : {"info", (PyCFunction) (pg_funcptr_t) PLy_info, METH_VARARGS | METH_KEYWORDS, NULL},
65 : {"notice", (PyCFunction) (pg_funcptr_t) PLy_notice, METH_VARARGS | METH_KEYWORDS, NULL},
66 : {"warning", (PyCFunction) (pg_funcptr_t) PLy_warning, METH_VARARGS | METH_KEYWORDS, NULL},
67 : {"error", (PyCFunction) (pg_funcptr_t) PLy_error, METH_VARARGS | METH_KEYWORDS, NULL},
68 : {"fatal", (PyCFunction) (pg_funcptr_t) PLy_fatal, METH_VARARGS | METH_KEYWORDS, NULL},
69 :
70 : /*
71 : * create a stored plan
72 : */
73 : {"prepare", PLy_spi_prepare, METH_VARARGS, NULL},
74 :
75 : /*
76 : * execute a plan or query
77 : */
78 : {"execute", PLy_spi_execute, METH_VARARGS, NULL},
79 :
80 : /*
81 : * escaping strings
82 : */
83 : {"quote_literal", PLy_quote_literal, METH_VARARGS, NULL},
84 : {"quote_nullable", PLy_quote_nullable, METH_VARARGS, NULL},
85 : {"quote_ident", PLy_quote_ident, METH_VARARGS, NULL},
86 :
87 : /*
88 : * create the subtransaction context manager
89 : */
90 : {"subtransaction", PLy_subtransaction_new, METH_NOARGS, NULL},
91 :
92 : /*
93 : * create a cursor
94 : */
95 : {"cursor", PLy_cursor, METH_VARARGS, NULL},
96 :
97 : /*
98 : * transaction control
99 : */
100 : {"commit", PLy_commit, METH_NOARGS, NULL},
101 : {"rollback", PLy_rollback, METH_NOARGS, NULL},
102 :
103 : {NULL, NULL, 0, NULL}
104 : };
105 :
106 : static PyMethodDef PLy_exc_methods[] = {
107 : {NULL, NULL, 0, NULL}
108 : };
109 :
110 : static PyModuleDef PLy_module = {
111 : PyModuleDef_HEAD_INIT,
112 : .m_name = "plpy",
113 : .m_size = -1,
114 : .m_methods = PLy_methods,
115 : };
116 :
117 : static PyModuleDef PLy_exc_module = {
118 : PyModuleDef_HEAD_INIT,
119 : .m_name = "spiexceptions",
120 : .m_size = -1,
121 : .m_methods = PLy_exc_methods,
122 : };
123 :
124 : /*
125 : * Must have external linkage, because PyMODINIT_FUNC does dllexport on
126 : * Windows-like platforms.
127 : */
128 : PyMODINIT_FUNC
129 46 : PyInit_plpy(void)
130 : {
131 : PyObject *m;
132 :
133 46 : m = PyModule_Create(&PLy_module);
134 46 : if (m == NULL)
135 0 : return NULL;
136 :
137 46 : PLy_add_exceptions(m);
138 :
139 46 : return m;
140 : }
141 :
142 : void
143 46 : PLy_init_plpy(void)
144 : {
145 : PyObject *main_mod,
146 : *main_dict,
147 : *plpy_mod;
148 :
149 : /*
150 : * initialize plpy module
151 : */
152 46 : PLy_plan_init_type();
153 46 : PLy_result_init_type();
154 46 : PLy_subtransaction_init_type();
155 46 : PLy_cursor_init_type();
156 :
157 46 : PyModule_Create(&PLy_module);
158 :
159 : /* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
160 :
161 : /*
162 : * initialize main module, and add plpy
163 : */
164 46 : main_mod = PyImport_AddModule("__main__");
165 46 : main_dict = PyModule_GetDict(main_mod);
166 46 : plpy_mod = PyImport_AddModule("plpy");
167 46 : if (plpy_mod == NULL)
168 0 : PLy_elog(ERROR, "could not import \"plpy\" module");
169 46 : PyDict_SetItemString(main_dict, "plpy", plpy_mod);
170 46 : if (PyErr_Occurred())
171 0 : PLy_elog(ERROR, "could not import \"plpy\" module");
172 46 : }
173 :
174 : static void
175 46 : PLy_add_exceptions(PyObject *plpy)
176 : {
177 : PyObject *excmod;
178 : HASHCTL hash_ctl;
179 :
180 46 : excmod = PyModule_Create(&PLy_exc_module);
181 46 : if (excmod == NULL)
182 0 : PLy_elog(ERROR, "could not create the spiexceptions module");
183 :
184 : /*
185 : * PyModule_AddObject does not add a refcount to the object, for some odd
186 : * reason; we must do that.
187 : */
188 46 : Py_INCREF(excmod);
189 46 : if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
190 0 : PLy_elog(ERROR, "could not add the spiexceptions module");
191 :
192 46 : PLy_exc_error = PLy_create_exception("plpy.Error", NULL, NULL,
193 : "Error", plpy);
194 46 : PLy_exc_fatal = PLy_create_exception("plpy.Fatal", NULL, NULL,
195 : "Fatal", plpy);
196 46 : PLy_exc_spi_error = PLy_create_exception("plpy.SPIError", NULL, NULL,
197 : "SPIError", plpy);
198 :
199 46 : hash_ctl.keysize = sizeof(int);
200 46 : hash_ctl.entrysize = sizeof(PLyExceptionEntry);
201 46 : PLy_spi_exceptions = hash_create("PL/Python SPI exceptions", 256,
202 : &hash_ctl, HASH_ELEM | HASH_BLOBS);
203 :
204 46 : PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
205 46 : }
206 :
207 : /*
208 : * Create an exception object and add it to the module
209 : */
210 : static PyObject *
211 11684 : PLy_create_exception(char *name, PyObject *base, PyObject *dict,
212 : const char *modname, PyObject *mod)
213 : {
214 : PyObject *exc;
215 :
216 11684 : exc = PyErr_NewException(name, base, dict);
217 11684 : if (exc == NULL)
218 0 : PLy_elog(ERROR, NULL);
219 :
220 : /*
221 : * PyModule_AddObject does not add a refcount to the object, for some odd
222 : * reason; we must do that.
223 : */
224 11684 : Py_INCREF(exc);
225 11684 : PyModule_AddObject(mod, modname, exc);
226 :
227 : /*
228 : * The caller will also store a pointer to the exception object in some
229 : * permanent variable, so add another ref to account for that. This is
230 : * probably excessively paranoid, but let's be sure.
231 : */
232 11684 : Py_INCREF(exc);
233 11684 : return exc;
234 : }
235 :
236 : /*
237 : * Add all the autogenerated exceptions as subclasses of SPIError
238 : */
239 : static void
240 46 : PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
241 : {
242 : int i;
243 :
244 11592 : for (i = 0; exception_map[i].name != NULL; i++)
245 : {
246 : bool found;
247 : PyObject *exc;
248 : PLyExceptionEntry *entry;
249 : PyObject *sqlstate;
250 11546 : PyObject *dict = PyDict_New();
251 :
252 11546 : if (dict == NULL)
253 0 : PLy_elog(ERROR, NULL);
254 :
255 11546 : sqlstate = PLyUnicode_FromString(unpack_sql_state(exception_map[i].sqlstate));
256 11546 : if (sqlstate == NULL)
257 0 : PLy_elog(ERROR, "could not generate SPI exceptions");
258 :
259 11546 : PyDict_SetItemString(dict, "sqlstate", sqlstate);
260 11546 : Py_DECREF(sqlstate);
261 :
262 11546 : exc = PLy_create_exception(exception_map[i].name, base, dict,
263 11546 : exception_map[i].classname, mod);
264 :
265 11546 : entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate,
266 : HASH_ENTER, &found);
267 : Assert(!found);
268 11546 : entry->exc = exc;
269 : }
270 46 : }
271 :
272 :
273 : /*
274 : * the python interface to the elog function
275 : * don't confuse these with PLy_elog
276 : */
277 : static PyObject *PLy_output(volatile int level, PyObject *self,
278 : PyObject *args, PyObject *kw);
279 :
280 : static PyObject *
281 4 : PLy_debug(PyObject *self, PyObject *args, PyObject *kw)
282 : {
283 4 : return PLy_output(DEBUG2, self, args, kw);
284 : }
285 :
286 : static PyObject *
287 4 : PLy_log(PyObject *self, PyObject *args, PyObject *kw)
288 : {
289 4 : return PLy_output(LOG, self, args, kw);
290 : }
291 :
292 : static PyObject *
293 224 : PLy_info(PyObject *self, PyObject *args, PyObject *kw)
294 : {
295 224 : return PLy_output(INFO, self, args, kw);
296 : }
297 :
298 : static PyObject *
299 410 : PLy_notice(PyObject *self, PyObject *args, PyObject *kw)
300 : {
301 410 : return PLy_output(NOTICE, self, args, kw);
302 : }
303 :
304 : static PyObject *
305 10 : PLy_warning(PyObject *self, PyObject *args, PyObject *kw)
306 : {
307 10 : return PLy_output(WARNING, self, args, kw);
308 : }
309 :
310 : static PyObject *
311 22 : PLy_error(PyObject *self, PyObject *args, PyObject *kw)
312 : {
313 22 : return PLy_output(ERROR, self, args, kw);
314 : }
315 :
316 : static PyObject *
317 0 : PLy_fatal(PyObject *self, PyObject *args, PyObject *kw)
318 : {
319 0 : return PLy_output(FATAL, self, args, kw);
320 : }
321 :
322 : static PyObject *
323 12 : PLy_quote_literal(PyObject *self, PyObject *args)
324 : {
325 : const char *str;
326 : char *quoted;
327 : PyObject *ret;
328 :
329 12 : if (!PyArg_ParseTuple(args, "s:quote_literal", &str))
330 0 : return NULL;
331 :
332 12 : quoted = quote_literal_cstr(str);
333 12 : ret = PLyUnicode_FromString(quoted);
334 12 : pfree(quoted);
335 :
336 12 : return ret;
337 : }
338 :
339 : static PyObject *
340 12 : PLy_quote_nullable(PyObject *self, PyObject *args)
341 : {
342 : const char *str;
343 : char *quoted;
344 : PyObject *ret;
345 :
346 12 : if (!PyArg_ParseTuple(args, "z:quote_nullable", &str))
347 0 : return NULL;
348 :
349 12 : if (str == NULL)
350 2 : return PLyUnicode_FromString("NULL");
351 :
352 10 : quoted = quote_literal_cstr(str);
353 10 : ret = PLyUnicode_FromString(quoted);
354 10 : pfree(quoted);
355 :
356 10 : return ret;
357 : }
358 :
359 : static PyObject *
360 6 : PLy_quote_ident(PyObject *self, PyObject *args)
361 : {
362 : const char *str;
363 : const char *quoted;
364 : PyObject *ret;
365 :
366 6 : if (!PyArg_ParseTuple(args, "s:quote_ident", &str))
367 0 : return NULL;
368 :
369 6 : quoted = quote_identifier(str);
370 6 : ret = PLyUnicode_FromString(quoted);
371 :
372 6 : return ret;
373 : }
374 :
375 : /* enforce cast of object to string */
376 : static char *
377 116 : object_to_string(PyObject *obj)
378 : {
379 116 : if (obj)
380 : {
381 116 : PyObject *so = PyObject_Str(obj);
382 :
383 116 : if (so != NULL)
384 : {
385 : char *str;
386 :
387 116 : str = pstrdup(PLyUnicode_AsString(so));
388 116 : Py_DECREF(so);
389 :
390 116 : return str;
391 : }
392 : }
393 :
394 0 : return NULL;
395 : }
396 :
397 : static PyObject *
398 674 : PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
399 : {
400 674 : int sqlstate = 0;
401 674 : char *volatile sqlstatestr = NULL;
402 674 : char *volatile message = NULL;
403 674 : char *volatile detail = NULL;
404 674 : char *volatile hint = NULL;
405 674 : char *volatile column_name = NULL;
406 674 : char *volatile constraint_name = NULL;
407 674 : char *volatile datatype_name = NULL;
408 674 : char *volatile table_name = NULL;
409 674 : char *volatile schema_name = NULL;
410 : volatile MemoryContext oldcontext;
411 : PyObject *key,
412 : *value;
413 : PyObject *volatile so;
414 674 : Py_ssize_t pos = 0;
415 :
416 674 : if (PyTuple_Size(args) == 1)
417 : {
418 : /*
419 : * Treat single argument specially to avoid undesirable ('tuple',)
420 : * decoration.
421 : */
422 : PyObject *o;
423 :
424 502 : if (!PyArg_UnpackTuple(args, "plpy.elog", 1, 1, &o))
425 0 : PLy_elog(ERROR, "could not unpack arguments in plpy.elog");
426 502 : so = PyObject_Str(o);
427 : }
428 : else
429 172 : so = PyObject_Str(args);
430 :
431 674 : if (so == NULL || ((message = PLyUnicode_AsString(so)) == NULL))
432 : {
433 0 : level = ERROR;
434 0 : message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
435 : }
436 674 : message = pstrdup(message);
437 :
438 674 : Py_XDECREF(so);
439 :
440 674 : if (kw != NULL)
441 : {
442 160 : while (PyDict_Next(kw, &pos, &key, &value))
443 : {
444 122 : char *keyword = PLyUnicode_AsString(key);
445 :
446 122 : if (strcmp(keyword, "message") == 0)
447 : {
448 : /* the message should not be overwritten */
449 18 : if (PyTuple_Size(args) != 0)
450 : {
451 4 : PLy_exception_set(PyExc_TypeError, "argument 'message' given by name and position");
452 4 : return NULL;
453 : }
454 :
455 14 : if (message)
456 14 : pfree(message);
457 14 : message = object_to_string(value);
458 : }
459 104 : else if (strcmp(keyword, "detail") == 0)
460 30 : detail = object_to_string(value);
461 74 : else if (strcmp(keyword, "hint") == 0)
462 14 : hint = object_to_string(value);
463 60 : else if (strcmp(keyword, "sqlstate") == 0)
464 14 : sqlstatestr = object_to_string(value);
465 46 : else if (strcmp(keyword, "schema_name") == 0)
466 8 : schema_name = object_to_string(value);
467 38 : else if (strcmp(keyword, "table_name") == 0)
468 10 : table_name = object_to_string(value);
469 28 : else if (strcmp(keyword, "column_name") == 0)
470 8 : column_name = object_to_string(value);
471 20 : else if (strcmp(keyword, "datatype_name") == 0)
472 10 : datatype_name = object_to_string(value);
473 10 : else if (strcmp(keyword, "constraint_name") == 0)
474 8 : constraint_name = object_to_string(value);
475 : else
476 : {
477 2 : PLy_exception_set(PyExc_TypeError,
478 : "'%s' is an invalid keyword argument for this function",
479 : keyword);
480 2 : return NULL;
481 : }
482 : }
483 : }
484 :
485 668 : if (sqlstatestr != NULL)
486 : {
487 14 : if (strlen(sqlstatestr) != 5)
488 : {
489 2 : PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code");
490 2 : return NULL;
491 : }
492 :
493 12 : if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
494 : {
495 0 : PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code");
496 0 : return NULL;
497 : }
498 :
499 12 : sqlstate = MAKE_SQLSTATE(sqlstatestr[0],
500 : sqlstatestr[1],
501 : sqlstatestr[2],
502 : sqlstatestr[3],
503 : sqlstatestr[4]);
504 : }
505 :
506 666 : oldcontext = CurrentMemoryContext;
507 666 : PG_TRY();
508 : {
509 666 : if (message != NULL)
510 666 : pg_verifymbstr(message, strlen(message), false);
511 666 : if (detail != NULL)
512 30 : pg_verifymbstr(detail, strlen(detail), false);
513 666 : if (hint != NULL)
514 14 : pg_verifymbstr(hint, strlen(hint), false);
515 666 : if (schema_name != NULL)
516 8 : pg_verifymbstr(schema_name, strlen(schema_name), false);
517 666 : if (table_name != NULL)
518 10 : pg_verifymbstr(table_name, strlen(table_name), false);
519 666 : if (column_name != NULL)
520 8 : pg_verifymbstr(column_name, strlen(column_name), false);
521 666 : if (datatype_name != NULL)
522 10 : pg_verifymbstr(datatype_name, strlen(datatype_name), false);
523 666 : if (constraint_name != NULL)
524 8 : pg_verifymbstr(constraint_name, strlen(constraint_name), false);
525 :
526 666 : ereport(level,
527 : ((sqlstate != 0) ? errcode(sqlstate) : 0,
528 : (message != NULL) ? errmsg_internal("%s", message) : 0,
529 : (detail != NULL) ? errdetail_internal("%s", detail) : 0,
530 : (hint != NULL) ? errhint("%s", hint) : 0,
531 : (column_name != NULL) ?
532 : err_generic_string(PG_DIAG_COLUMN_NAME, column_name) : 0,
533 : (constraint_name != NULL) ?
534 : err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0,
535 : (datatype_name != NULL) ?
536 : err_generic_string(PG_DIAG_DATATYPE_NAME, datatype_name) : 0,
537 : (table_name != NULL) ?
538 : err_generic_string(PG_DIAG_TABLE_NAME, table_name) : 0,
539 : (schema_name != NULL) ?
540 : err_generic_string(PG_DIAG_SCHEMA_NAME, schema_name) : 0));
541 : }
542 22 : PG_CATCH();
543 : {
544 : ErrorData *edata;
545 :
546 22 : MemoryContextSwitchTo(oldcontext);
547 22 : edata = CopyErrorData();
548 22 : FlushErrorState();
549 :
550 22 : PLy_exception_set_with_details(PLy_exc_error, edata);
551 22 : FreeErrorData(edata);
552 :
553 22 : return NULL;
554 : }
555 644 : PG_END_TRY();
556 :
557 : /*
558 : * return a legal object so the interpreter will continue on its merry way
559 : */
560 644 : Py_RETURN_NONE;
561 : }
|