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