LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_plpymodule.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17beta1 Lines: 170 189 89.9 %
Date: 2024-06-14 19:10:57 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 "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       11592 : PLy_create_exception(char *name, PyObject *base, PyObject *dict,
     212             :                      const char *modname, PyObject *mod)
     213             : {
     214             :     PyObject   *exc;
     215             : 
     216       11592 :     exc = PyErr_NewException(name, base, dict);
     217       11592 :     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       11592 :     Py_INCREF(exc);
     225       11592 :     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       11592 :     Py_INCREF(exc);
     233       11592 :     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       11500 :     for (i = 0; exception_map[i].name != NULL; i++)
     245             :     {
     246             :         bool        found;
     247             :         PyObject   *exc;
     248             :         PLyExceptionEntry *entry;
     249             :         PyObject   *sqlstate;
     250       11454 :         PyObject   *dict = PyDict_New();
     251             : 
     252       11454 :         if (dict == NULL)
     253           0 :             PLy_elog(ERROR, NULL);
     254             : 
     255       11454 :         sqlstate = PLyUnicode_FromString(unpack_sql_state(exception_map[i].sqlstate));
     256       11454 :         if (sqlstate == NULL)
     257           0 :             PLy_elog(ERROR, "could not generate SPI exceptions");
     258             : 
     259       11454 :         PyDict_SetItemString(dict, "sqlstate", sqlstate);
     260       11454 :         Py_DECREF(sqlstate);
     261             : 
     262       11454 :         exc = PLy_create_exception(exception_map[i].name, base, dict,
     263       11454 :                                    exception_map[i].classname, mod);
     264             : 
     265       11454 :         entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate,
     266             :                             HASH_ENTER, &found);
     267             :         Assert(!found);
     268       11454 :         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             : }

Generated by: LCOV version 1.14