Age Owner TLA 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
4130 peter_e 129 CBC 23 : PyInit_plpy(void)
130 : {
131 : PyObject *m;
132 :
133 23 : m = PyModule_Create(&PLy_module);
134 23 : if (m == NULL)
4130 peter_e 135 UBC 0 : return NULL;
136 :
4130 peter_e 137 CBC 23 : PLy_add_exceptions(m);
138 :
139 23 : return m;
140 : }
141 :
142 : void
143 23 : PLy_init_plpy(void)
144 : {
145 : PyObject *main_mod,
146 : *main_dict,
147 : *plpy_mod;
148 :
149 : /*
150 : * initialize plpy module
151 : */
152 23 : PLy_plan_init_type();
153 23 : PLy_result_init_type();
154 23 : PLy_subtransaction_init_type();
155 23 : PLy_cursor_init_type();
156 :
157 23 : PyModule_Create(&PLy_module);
158 :
159 : /* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
160 :
161 : /*
162 : * initialize main module, and add plpy
163 : */
164 23 : main_mod = PyImport_AddModule("__main__");
165 23 : main_dict = PyModule_GetDict(main_mod);
166 23 : plpy_mod = PyImport_AddModule("plpy");
4044 tgl 167 23 : if (plpy_mod == NULL)
4001 peter_e 168 UBC 0 : PLy_elog(ERROR, "could not import \"plpy\" module");
4130 peter_e 169 CBC 23 : PyDict_SetItemString(main_dict, "plpy", plpy_mod);
170 23 : if (PyErr_Occurred())
4001 peter_e 171 UBC 0 : PLy_elog(ERROR, "could not import \"plpy\" module");
4130 peter_e 172 CBC 23 : }
173 :
174 : static void
175 23 : PLy_add_exceptions(PyObject *plpy)
176 : {
177 : PyObject *excmod;
178 : HASHCTL hash_ctl;
179 :
180 23 : excmod = PyModule_Create(&PLy_exc_module);
2312 tgl 181 23 : if (excmod == NULL)
2312 tgl 182 UBC 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 : */
4130 peter_e 188 CBC 23 : Py_INCREF(excmod);
2312 tgl 189 23 : if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
2312 tgl 190 UBC 0 : PLy_elog(ERROR, "could not add the spiexceptions module");
191 :
2312 tgl 192 CBC 23 : PLy_exc_error = PLy_create_exception("plpy.Error", NULL, NULL,
193 : "Error", plpy);
194 23 : PLy_exc_fatal = PLy_create_exception("plpy.Fatal", NULL, NULL,
195 : "Fatal", plpy);
196 23 : PLy_exc_spi_error = PLy_create_exception("plpy.SPIError", NULL, NULL,
197 : "SPIError", plpy);
198 :
4130 peter_e 199 23 : hash_ctl.keysize = sizeof(int);
200 23 : hash_ctl.entrysize = sizeof(PLyExceptionEntry);
2312 tgl 201 23 : PLy_spi_exceptions = hash_create("PL/Python SPI exceptions", 256,
202 : &hash_ctl, HASH_ELEM | HASH_BLOBS);
203 :
4130 peter_e 204 23 : PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
205 23 : }
206 :
207 : /*
208 : * Create an exception object and add it to the module
209 : */
210 : static PyObject *
2312 tgl 211 5796 : PLy_create_exception(char *name, PyObject *base, PyObject *dict,
212 : const char *modname, PyObject *mod)
213 : {
214 : PyObject *exc;
215 :
216 5796 : exc = PyErr_NewException(name, base, dict);
217 5796 : if (exc == NULL)
1986 peter_e 218 UBC 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 : */
2312 tgl 224 CBC 5796 : Py_INCREF(exc);
225 5796 : 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 5796 : Py_INCREF(exc);
233 5796 : return exc;
234 : }
235 :
236 : /*
237 : * Add all the autogenerated exceptions as subclasses of SPIError
238 : */
239 : static void
4130 peter_e 240 23 : PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
241 : {
242 : int i;
243 :
244 5750 : for (i = 0; exception_map[i].name != NULL; i++)
245 : {
246 : bool found;
247 : PyObject *exc;
248 : PLyExceptionEntry *entry;
249 : PyObject *sqlstate;
250 5727 : PyObject *dict = PyDict_New();
251 :
4044 tgl 252 5727 : if (dict == NULL)
1986 peter_e 253 UBC 0 : PLy_elog(ERROR, NULL);
254 :
398 andres 255 CBC 5727 : sqlstate = PLyUnicode_FromString(unpack_sql_state(exception_map[i].sqlstate));
4044 tgl 256 5727 : if (sqlstate == NULL)
4044 tgl 257 UBC 0 : PLy_elog(ERROR, "could not generate SPI exceptions");
258 :
4130 peter_e 259 CBC 5727 : PyDict_SetItemString(dict, "sqlstate", sqlstate);
260 5727 : Py_DECREF(sqlstate);
261 :
2312 tgl 262 5727 : exc = PLy_create_exception(exception_map[i].name, base, dict,
263 5727 : exception_map[i].classname, mod);
264 :
4130 peter_e 265 5727 : entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate,
266 : HASH_ENTER, &found);
267 5727 : Assert(!found);
2312 tgl 268 5727 : entry->exc = exc;
269 : }
4130 peter_e 270 23 : }
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 *
2557 teodor 281 2 : PLy_debug(PyObject *self, PyObject *args, PyObject *kw)
282 : {
283 2 : return PLy_output(DEBUG2, self, args, kw);
284 : }
285 :
286 : static PyObject *
287 2 : PLy_log(PyObject *self, PyObject *args, PyObject *kw)
288 : {
289 2 : return PLy_output(LOG, self, args, kw);
290 : }
291 :
292 : static PyObject *
293 112 : PLy_info(PyObject *self, PyObject *args, PyObject *kw)
294 : {
295 112 : return PLy_output(INFO, self, args, kw);
296 : }
297 :
298 : static PyObject *
299 202 : PLy_notice(PyObject *self, PyObject *args, PyObject *kw)
300 : {
301 202 : return PLy_output(NOTICE, self, args, kw);
302 : }
303 :
304 : static PyObject *
305 5 : PLy_warning(PyObject *self, PyObject *args, PyObject *kw)
306 : {
307 5 : return PLy_output(WARNING, self, args, kw);
308 : }
309 :
310 : static PyObject *
311 11 : PLy_error(PyObject *self, PyObject *args, PyObject *kw)
312 : {
313 11 : return PLy_output(ERROR, self, args, kw);
314 : }
315 :
316 : static PyObject *
2557 teodor 317 UBC 0 : PLy_fatal(PyObject *self, PyObject *args, PyObject *kw)
318 : {
319 0 : return PLy_output(FATAL, self, args, kw);
320 : }
321 :
322 : static PyObject *
4130 peter_e 323 CBC 6 : PLy_quote_literal(PyObject *self, PyObject *args)
324 : {
325 : const char *str;
326 : char *quoted;
327 : PyObject *ret;
328 :
2355 329 6 : if (!PyArg_ParseTuple(args, "s:quote_literal", &str))
4130 peter_e 330 UBC 0 : return NULL;
331 :
4130 peter_e 332 CBC 6 : quoted = quote_literal_cstr(str);
398 andres 333 6 : ret = PLyUnicode_FromString(quoted);
4130 peter_e 334 6 : pfree(quoted);
335 :
336 6 : return ret;
337 : }
338 :
339 : static PyObject *
340 6 : PLy_quote_nullable(PyObject *self, PyObject *args)
341 : {
342 : const char *str;
343 : char *quoted;
344 : PyObject *ret;
345 :
2355 346 6 : if (!PyArg_ParseTuple(args, "z:quote_nullable", &str))
4130 peter_e 347 UBC 0 : return NULL;
348 :
4130 peter_e 349 CBC 6 : if (str == NULL)
398 andres 350 1 : return PLyUnicode_FromString("NULL");
351 :
4130 peter_e 352 5 : quoted = quote_literal_cstr(str);
398 andres 353 5 : ret = PLyUnicode_FromString(quoted);
4130 peter_e 354 5 : pfree(quoted);
355 :
356 5 : return ret;
357 : }
358 :
359 : static PyObject *
360 3 : PLy_quote_ident(PyObject *self, PyObject *args)
361 : {
362 : const char *str;
363 : const char *quoted;
364 : PyObject *ret;
365 :
2355 366 3 : if (!PyArg_ParseTuple(args, "s:quote_ident", &str))
4130 peter_e 367 UBC 0 : return NULL;
368 :
4130 peter_e 369 CBC 3 : quoted = quote_identifier(str);
398 andres 370 3 : ret = PLyUnicode_FromString(quoted);
371 :
4130 peter_e 372 3 : return ret;
373 : }
374 :
375 : /* enforce cast of object to string */
376 : static char *
2557 teodor 377 58 : object_to_string(PyObject *obj)
378 : {
379 58 : if (obj)
380 : {
2495 rhaas 381 58 : PyObject *so = PyObject_Str(obj);
382 :
2557 teodor 383 58 : if (so != NULL)
384 : {
385 : char *str;
386 :
398 andres 387 58 : str = pstrdup(PLyUnicode_AsString(so));
2557 teodor 388 58 : Py_DECREF(so);
389 :
390 58 : return str;
391 : }
392 : }
393 :
2557 teodor 394 UBC 0 : return NULL;
395 : }
396 :
397 : static PyObject *
2557 teodor 398 CBC 334 : PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
399 : {
2495 rhaas 400 334 : int sqlstate = 0;
401 334 : char *volatile sqlstatestr = NULL;
402 334 : char *volatile message = NULL;
403 334 : char *volatile detail = NULL;
404 334 : char *volatile hint = NULL;
2493 peter_e 405 334 : char *volatile column_name = NULL;
406 334 : char *volatile constraint_name = NULL;
407 334 : char *volatile datatype_name = NULL;
408 334 : char *volatile table_name = NULL;
409 334 : char *volatile schema_name = NULL;
410 : volatile MemoryContext oldcontext;
411 : PyObject *key,
412 : *value;
413 : PyObject *volatile so;
2495 rhaas 414 334 : Py_ssize_t pos = 0;
415 :
4130 peter_e 416 334 : if (PyTuple_Size(args) == 1)
417 : {
418 : /*
419 : * Treat single argument specially to avoid undesirable ('tuple',)
420 : * decoration.
421 : */
422 : PyObject *o;
423 :
4044 tgl 424 248 : if (!PyArg_UnpackTuple(args, "plpy.elog", 1, 1, &o))
4044 tgl 425 UBC 0 : PLy_elog(ERROR, "could not unpack arguments in plpy.elog");
4130 peter_e 426 CBC 248 : so = PyObject_Str(o);
427 : }
428 : else
429 86 : so = PyObject_Str(args);
430 :
398 andres 431 334 : if (so == NULL || ((message = PLyUnicode_AsString(so)) == NULL))
432 : {
4130 peter_e 433 UBC 0 : level = ERROR;
2557 teodor 434 0 : message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
435 : }
2554 tgl 436 CBC 334 : message = pstrdup(message);
437 :
2557 teodor 438 334 : Py_XDECREF(so);
439 :
440 334 : if (kw != NULL)
441 : {
442 80 : while (PyDict_Next(kw, &pos, &key, &value))
443 : {
398 andres 444 61 : char *keyword = PLyUnicode_AsString(key);
445 :
2557 teodor 446 61 : if (strcmp(keyword, "message") == 0)
447 : {
448 : /* the message should not be overwritten */
449 9 : if (PyTuple_Size(args) != 0)
450 : {
2145 alvherre 451 2 : PLy_exception_set(PyExc_TypeError, "argument 'message' given by name and position");
2472 peter_e 452 2 : return NULL;
453 : }
454 :
2554 tgl 455 7 : if (message)
456 7 : pfree(message);
2557 teodor 457 7 : message = object_to_string(value);
458 : }
459 52 : else if (strcmp(keyword, "detail") == 0)
460 15 : detail = object_to_string(value);
461 37 : else if (strcmp(keyword, "hint") == 0)
462 7 : hint = object_to_string(value);
463 30 : else if (strcmp(keyword, "sqlstate") == 0)
464 7 : sqlstatestr = object_to_string(value);
2493 peter_e 465 23 : else if (strcmp(keyword, "schema_name") == 0)
466 4 : schema_name = object_to_string(value);
467 19 : else if (strcmp(keyword, "table_name") == 0)
468 5 : table_name = object_to_string(value);
469 14 : else if (strcmp(keyword, "column_name") == 0)
470 4 : column_name = object_to_string(value);
471 10 : else if (strcmp(keyword, "datatype_name") == 0)
472 5 : datatype_name = object_to_string(value);
473 5 : else if (strcmp(keyword, "constraint_name") == 0)
474 4 : constraint_name = object_to_string(value);
475 : else
476 : {
2472 477 1 : PLy_exception_set(PyExc_TypeError,
478 : "'%s' is an invalid keyword argument for this function",
479 : keyword);
480 1 : return NULL;
481 : }
482 : }
483 : }
484 :
2557 teodor 485 331 : if (sqlstatestr != NULL)
486 : {
487 7 : if (strlen(sqlstatestr) != 5)
488 : {
2472 peter_e 489 1 : PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code");
490 1 : return NULL;
491 : }
492 :
2557 teodor 493 6 : if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
494 : {
2472 peter_e 495 UBC 0 : PLy_exception_set(PyExc_ValueError, "invalid SQLSTATE code");
496 0 : return NULL;
497 : }
498 :
2557 teodor 499 CBC 6 : sqlstate = MAKE_SQLSTATE(sqlstatestr[0],
500 : sqlstatestr[1],
501 : sqlstatestr[2],
502 : sqlstatestr[3],
503 : sqlstatestr[4]);
504 : }
505 :
4130 peter_e 506 330 : oldcontext = CurrentMemoryContext;
507 330 : PG_TRY();
508 : {
2557 teodor 509 330 : if (message != NULL)
510 330 : pg_verifymbstr(message, strlen(message), false);
511 330 : if (detail != NULL)
512 15 : pg_verifymbstr(detail, strlen(detail), false);
513 330 : if (hint != NULL)
514 7 : pg_verifymbstr(hint, strlen(hint), false);
2493 peter_e 515 330 : if (schema_name != NULL)
516 4 : pg_verifymbstr(schema_name, strlen(schema_name), false);
517 330 : if (table_name != NULL)
518 5 : pg_verifymbstr(table_name, strlen(table_name), false);
519 330 : if (column_name != NULL)
520 4 : pg_verifymbstr(column_name, strlen(column_name), false);
521 330 : if (datatype_name != NULL)
522 5 : pg_verifymbstr(datatype_name, strlen(datatype_name), false);
523 330 : if (constraint_name != NULL)
524 4 : pg_verifymbstr(constraint_name, strlen(constraint_name), false);
525 :
2557 teodor 526 330 : 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 : }
4130 peter_e 542 11 : PG_CATCH();
543 : {
544 : ErrorData *edata;
545 :
546 11 : MemoryContextSwitchTo(oldcontext);
547 11 : edata = CopyErrorData();
548 11 : FlushErrorState();
549 :
2557 teodor 550 11 : PLy_exception_set_with_details(PLy_exc_error, edata);
551 11 : FreeErrorData(edata);
552 :
4130 peter_e 553 11 : return NULL;
554 : }
555 319 : PG_END_TRY();
556 :
557 : /*
558 : * return a legal object so the interpreter will continue on its merry way
559 : */
2018 560 319 : Py_RETURN_NONE;
561 : }
|