LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_plpymodule.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 170 189 89.9 %
Date: 2024-11-21 09:14:53 Functions: 16 17 94.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14