LCOV - differential code coverage report
Current view: top level - src/pl/plpython - plpy_elog.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 82.9 % 258 214 44 214
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 11 11 11
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 82.9 % 258 214 44 214
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 100.0 % 11 11 11

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*
                                  2                 :  * reporting Python exceptions as PostgreSQL errors
                                  3                 :  *
                                  4                 :  * src/pl/plpython/plpy_elog.c
                                  5                 :  */
                                  6                 : 
                                  7                 : #include "postgres.h"
                                  8                 : 
                                  9                 : #include "lib/stringinfo.h"
                                 10                 : #include "plpy_elog.h"
                                 11                 : #include "plpy_main.h"
                                 12                 : #include "plpy_procedure.h"
                                 13                 : #include "plpython.h"
                                 14                 : 
                                 15                 : PyObject   *PLy_exc_error = NULL;
                                 16                 : PyObject   *PLy_exc_fatal = NULL;
                                 17                 : PyObject   *PLy_exc_spi_error = NULL;
                                 18                 : 
                                 19                 : 
                                 20                 : static void PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
                                 21                 :                           char **xmsg, char **tbmsg, int *tb_depth);
                                 22                 : static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
                                 23                 :                                    char **hint, char **query, int *position,
                                 24                 :                                    char **schema_name, char **table_name, char **column_name,
                                 25                 :                                    char **datatype_name, char **constraint_name);
                                 26                 : static void PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail,
                                 27                 :                                char **hint, char **schema_name, char **table_name, char **column_name,
                                 28                 :                                char **datatype_name, char **constraint_name);
                                 29                 : static char *get_source_line(const char *src, int lineno);
                                 30                 : 
                                 31                 : static void get_string_attr(PyObject *obj, char *attrname, char **str);
                                 32                 : static bool set_string_attr(PyObject *obj, char *attrname, char *str);
                                 33                 : 
                                 34                 : /*
                                 35                 :  * Emit a PG error or notice, together with any available info about
                                 36                 :  * the current Python error, previously set by PLy_exception_set().
                                 37                 :  * This should be used to propagate Python errors into PG.  If fmt is
                                 38                 :  * NULL, the Python error becomes the primary error message, otherwise
                                 39                 :  * it becomes the detail.  If there is a Python traceback, it is put
                                 40                 :  * in the context.
                                 41                 :  */
                                 42                 : void
 2056 peter_e                    43 CBC          57 : PLy_elog_impl(int elevel, const char *fmt,...)
                                 44                 : {
 1656 tgl                        45              57 :     int         save_errno = errno;
                                 46                 :     char       *xmsg;
                                 47                 :     char       *tbmsg;
                                 48                 :     int         tb_depth;
                                 49                 :     StringInfoData emsg;
                                 50                 :     PyObject   *exc,
                                 51                 :                *val,
                                 52                 :                *tb;
 4130 peter_e                    53              57 :     const char *primary = NULL;
 3955 bruce                      54              57 :     int         sqlerrcode = 0;
 4130 peter_e                    55              57 :     char       *detail = NULL;
                                 56              57 :     char       *hint = NULL;
                                 57              57 :     char       *query = NULL;
                                 58              57 :     int         position = 0;
 2557 teodor                     59              57 :     char       *schema_name = NULL;
                                 60              57 :     char       *table_name = NULL;
                                 61              57 :     char       *column_name = NULL;
                                 62              57 :     char       *datatype_name = NULL;
                                 63              57 :     char       *constraint_name = NULL;
                                 64                 : 
 4130 peter_e                    65              57 :     PyErr_Fetch(&exc, &val, &tb);
                                 66                 : 
                                 67              57 :     if (exc != NULL)
                                 68                 :     {
 2555 tgl                        69              57 :         PyErr_NormalizeException(&exc, &val, &tb);
                                 70                 : 
 4130 peter_e                    71              57 :         if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
 2555 tgl                        72              22 :             PLy_get_spi_error_data(val, &sqlerrcode,
                                 73                 :                                    &detail, &hint, &query, &position,
                                 74                 :                                    &schema_name, &table_name, &column_name,
                                 75                 :                                    &datatype_name, &constraint_name);
 2557 teodor                     76              35 :         else if (PyErr_GivenExceptionMatches(val, PLy_exc_error))
                                 77              13 :             PLy_get_error_data(val, &sqlerrcode, &detail, &hint,
                                 78                 :                                &schema_name, &table_name, &column_name,
                                 79                 :                                &datatype_name, &constraint_name);
 4130 peter_e                    80              22 :         else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
 4130 peter_e                    81 UBC           0 :             elevel = FATAL;
                                 82                 :     }
                                 83                 : 
                                 84                 :     /* this releases our refcount on tb! */
 2555 tgl                        85 CBC          57 :     PLy_traceback(exc, val, tb,
                                 86                 :                   &xmsg, &tbmsg, &tb_depth);
                                 87                 : 
 4130 peter_e                    88              57 :     if (fmt)
                                 89                 :     {
                                 90               3 :         initStringInfo(&emsg);
                                 91                 :         for (;;)
 4130 peter_e                    92 UBC           0 :         {
                                 93                 :             va_list     ap;
                                 94                 :             int         needed;
                                 95                 : 
 1656 tgl                        96 CBC           3 :             errno = save_errno;
 4130 peter_e                    97               3 :             va_start(ap, fmt);
 3454 tgl                        98               3 :             needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
 4130 peter_e                    99               3 :             va_end(ap);
 3454 tgl                       100               3 :             if (needed == 0)
 4130 peter_e                   101               3 :                 break;
 3454 tgl                       102 UBC           0 :             enlargeStringInfo(&emsg, needed);
                                103                 :         }
 4130 peter_e                   104 CBC           3 :         primary = emsg.data;
                                105                 : 
                                106                 :         /* Since we have a format string, we cannot have a SPI detail. */
                                107               3 :         Assert(detail == NULL);
                                108                 : 
                                109                 :         /* If there's an exception message, it goes in the detail. */
                                110               3 :         if (xmsg)
                                111               3 :             detail = xmsg;
                                112                 :     }
                                113                 :     else
                                114                 :     {
                                115              54 :         if (xmsg)
                                116              54 :             primary = xmsg;
                                117                 :     }
                                118                 : 
                                119              57 :     PG_TRY();
                                120                 :     {
                                121              57 :         ereport(elevel,
                                122                 :                 (errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
                                123                 :                  errmsg_internal("%s", primary ? primary : "no exception data"),
                                124                 :                  (detail) ? errdetail_internal("%s", detail) : 0,
                                125                 :                  (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
                                126                 :                  (hint) ? errhint("%s", hint) : 0,
                                127                 :                  (query) ? internalerrquery(query) : 0,
                                128                 :                  (position) ? internalerrposition(position) : 0,
                                129                 :                  (schema_name) ? err_generic_string(PG_DIAG_SCHEMA_NAME,
                                130                 :                                                     schema_name) : 0,
                                131                 :                  (table_name) ? err_generic_string(PG_DIAG_TABLE_NAME,
                                132                 :                                                    table_name) : 0,
                                133                 :                  (column_name) ? err_generic_string(PG_DIAG_COLUMN_NAME,
                                134                 :                                                     column_name) : 0,
                                135                 :                  (datatype_name) ? err_generic_string(PG_DIAG_DATATYPE_NAME,
                                136                 :                                                       datatype_name) : 0,
                                137                 :                  (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME,
                                138                 :                                                         constraint_name) : 0));
                                139                 :     }
 1255 peter                     140              57 :     PG_FINALLY();
                                141                 :     {
 4130 peter_e                   142              57 :         if (fmt)
                                143               3 :             pfree(emsg.data);
                                144              57 :         if (xmsg)
                                145              57 :             pfree(xmsg);
                                146              57 :         if (tbmsg)
                                147              57 :             pfree(tbmsg);
 2555 tgl                       148              57 :         Py_XDECREF(exc);
                                149              57 :         Py_XDECREF(val);
                                150                 :     }
 4130 peter_e                   151              57 :     PG_END_TRY();
 4130 peter_e                   152 UBC           0 : }
                                153                 : 
                                154                 : /*
                                155                 :  * Extract a Python traceback from the given exception data.
                                156                 :  *
                                157                 :  * The exception error message is returned in xmsg, the traceback in
                                158                 :  * tbmsg (both as palloc'd strings) and the traceback depth in
                                159                 :  * tb_depth.
                                160                 :  *
                                161                 :  * We release refcounts on all the Python objects in the traceback stack,
                                162                 :  * but not on e or v.
                                163                 :  */
                                164                 : static void
 2555 tgl                       165 CBC          57 : PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
                                166                 :               char **xmsg, char **tbmsg, int *tb_depth)
                                167                 : {
                                168                 :     PyObject   *e_type_o;
                                169                 :     PyObject   *e_module_o;
 4130 peter_e                   170              57 :     char       *e_type_s = NULL;
                                171              57 :     char       *e_module_s = NULL;
                                172              57 :     PyObject   *vob = NULL;
                                173                 :     char       *vstr;
                                174                 :     StringInfoData xstr;
                                175                 :     StringInfoData tbstr;
                                176                 : 
                                177                 :     /*
                                178                 :      * if no exception, return nulls
                                179                 :      */
                                180              57 :     if (e == NULL)
                                181                 :     {
 4130 peter_e                   182 UBC           0 :         *xmsg = NULL;
                                183               0 :         *tbmsg = NULL;
                                184               0 :         *tb_depth = 0;
                                185                 : 
                                186               0 :         return;
                                187                 :     }
                                188                 : 
                                189                 :     /*
                                190                 :      * Format the exception and its value and put it in xmsg.
                                191                 :      */
                                192                 : 
 4130 peter_e                   193 CBC          57 :     e_type_o = PyObject_GetAttrString(e, "__name__");
                                194              57 :     e_module_o = PyObject_GetAttrString(e, "__module__");
                                195              57 :     if (e_type_o)
  398 andres                    196              57 :         e_type_s = PLyUnicode_AsString(e_type_o);
 4130 peter_e                   197              57 :     if (e_type_s)
  398 andres                    198              57 :         e_module_s = PLyUnicode_AsString(e_module_o);
                                199                 : 
 4130 peter_e                   200              57 :     if (v && ((vob = PyObject_Str(v)) != NULL))
  398 andres                    201              57 :         vstr = PLyUnicode_AsString(vob);
                                202                 :     else
 4130 peter_e                   203 UBC           0 :         vstr = "unknown";
                                204                 : 
 4130 peter_e                   205 CBC          57 :     initStringInfo(&xstr);
                                206              57 :     if (!e_type_s || !e_module_s)
                                207                 :     {
                                208                 :         /* shouldn't happen */
  398 andres                    209 UBC           0 :         appendStringInfoString(&xstr, "unrecognized exception");
                                210                 :     }
                                211                 :     /* mimics behavior of traceback.format_exception_only */
 4130 peter_e                   212 CBC          57 :     else if (strcmp(e_module_s, "builtins") == 0
                                213              35 :              || strcmp(e_module_s, "__main__") == 0
                                214              35 :              || strcmp(e_module_s, "exceptions") == 0)
  906 drowley                   215              22 :         appendStringInfoString(&xstr, e_type_s);
                                216                 :     else
 4130 peter_e                   217              35 :         appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
                                218              57 :     appendStringInfo(&xstr, ": %s", vstr);
                                219                 : 
                                220              57 :     *xmsg = xstr.data;
                                221                 : 
                                222                 :     /*
                                223                 :      * Now format the traceback and put it in tbmsg.
                                224                 :      */
                                225                 : 
                                226              57 :     *tb_depth = 0;
                                227              57 :     initStringInfo(&tbstr);
                                228                 :     /* Mimic Python traceback reporting as close as possible. */
                                229              57 :     appendStringInfoString(&tbstr, "Traceback (most recent call last):");
                                230             179 :     while (tb != NULL && tb != Py_None)
                                231                 :     {
                                232             122 :         PyObject   *volatile tb_prev = NULL;
                                233             122 :         PyObject   *volatile frame = NULL;
                                234             122 :         PyObject   *volatile code = NULL;
                                235             122 :         PyObject   *volatile name = NULL;
                                236             122 :         PyObject   *volatile lineno = NULL;
                                237             122 :         PyObject   *volatile filename = NULL;
                                238                 : 
                                239             122 :         PG_TRY();
                                240                 :         {
                                241             122 :             lineno = PyObject_GetAttrString(tb, "tb_lineno");
                                242             122 :             if (lineno == NULL)
 4130 peter_e                   243 UBC           0 :                 elog(ERROR, "could not get line number from Python traceback");
                                244                 : 
 4130 peter_e                   245 CBC         122 :             frame = PyObject_GetAttrString(tb, "tb_frame");
                                246             122 :             if (frame == NULL)
 4130 peter_e                   247 UBC           0 :                 elog(ERROR, "could not get frame from Python traceback");
                                248                 : 
 4130 peter_e                   249 CBC         122 :             code = PyObject_GetAttrString(frame, "f_code");
                                250             122 :             if (code == NULL)
 4130 peter_e                   251 UBC           0 :                 elog(ERROR, "could not get code object from Python frame");
                                252                 : 
 4130 peter_e                   253 CBC         122 :             name = PyObject_GetAttrString(code, "co_name");
                                254             122 :             if (name == NULL)
 4130 peter_e                   255 UBC           0 :                 elog(ERROR, "could not get function name from Python code object");
                                256                 : 
 4130 peter_e                   257 CBC         122 :             filename = PyObject_GetAttrString(code, "co_filename");
                                258             122 :             if (filename == NULL)
 4130 peter_e                   259 UBC           0 :                 elog(ERROR, "could not get file name from Python code object");
                                260                 :         }
                                261               0 :         PG_CATCH();
                                262                 :         {
                                263               0 :             Py_XDECREF(frame);
                                264               0 :             Py_XDECREF(code);
                                265               0 :             Py_XDECREF(name);
                                266               0 :             Py_XDECREF(lineno);
                                267               0 :             Py_XDECREF(filename);
                                268               0 :             PG_RE_THROW();
                                269                 :         }
 4130 peter_e                   270 CBC         122 :         PG_END_TRY();
                                271                 : 
                                272                 :         /* The first frame always points at <module>, skip it. */
                                273             122 :         if (*tb_depth > 0)
                                274                 :         {
 3955 bruce                     275              68 :             PLyExecutionContext *exec_ctx = PLy_current_execution_context();
                                276                 :             char       *proname;
                                277                 :             char       *fname;
                                278                 :             char       *line;
                                279                 :             char       *plain_filename;
                                280                 :             long        plain_lineno;
                                281                 : 
                                282                 :             /*
                                283                 :              * The second frame points at the internal function, but to mimic
                                284                 :              * Python error reporting we want to say <module>.
                                285                 :              */
 4130 peter_e                   286              68 :             if (*tb_depth == 1)
                                287              54 :                 fname = "<module>";
                                288                 :             else
  398 andres                    289              14 :                 fname = PLyUnicode_AsString(name);
                                290                 : 
 4044 tgl                       291              68 :             proname = PLy_procedure_name(exec_ctx->curr_proc);
  398 andres                    292              68 :             plain_filename = PLyUnicode_AsString(filename);
                                293              68 :             plain_lineno = PyLong_AsLong(lineno);
                                294                 : 
 4130 peter_e                   295              68 :             if (proname == NULL)
 1165 alvherre                  296              13 :                 appendStringInfo(&tbstr, "\n  PL/Python anonymous code block, line %ld, in %s",
                                297                 :                                  plain_lineno - 1, fname);
                                298                 :             else
                                299              55 :                 appendStringInfo(&tbstr, "\n  PL/Python function \"%s\", line %ld, in %s",
                                300                 :                                  proname, plain_lineno - 1, fname);
                                301                 : 
                                302                 :             /*
                                303                 :              * function code object was compiled with "<string>" as the
                                304                 :              * filename
                                305                 :              */
 4044 tgl                       306              68 :             if (exec_ctx->curr_proc && plain_filename != NULL &&
 4130 peter_e                   307              68 :                 strcmp(plain_filename, "<string>") == 0)
                                308                 :             {
                                309                 :                 /*
                                310                 :                  * If we know the current procedure, append the exact line
                                311                 :                  * from the source, again mimicking Python's traceback.py
                                312                 :                  * module behavior.  We could store the already line-split
                                313                 :                  * source to avoid splitting it every time, but producing a
                                314                 :                  * traceback is not the most important scenario to optimize
                                315                 :                  * for.  But we do not go as far as traceback.py in reading
                                316                 :                  * the source of imported modules.
                                317                 :                  */
 4044 tgl                       318              68 :                 line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
 4130 peter_e                   319              68 :                 if (line)
                                320                 :                 {
                                321              68 :                     appendStringInfo(&tbstr, "\n    %s", line);
                                322              68 :                     pfree(line);
                                323                 :                 }
                                324                 :             }
                                325                 :         }
                                326                 : 
                                327             122 :         Py_DECREF(frame);
                                328             122 :         Py_DECREF(code);
                                329             122 :         Py_DECREF(name);
                                330             122 :         Py_DECREF(lineno);
                                331             122 :         Py_DECREF(filename);
                                332                 : 
                                333                 :         /* Release the current frame and go to the next one. */
                                334             122 :         tb_prev = tb;
                                335             122 :         tb = PyObject_GetAttrString(tb, "tb_next");
                                336             122 :         Assert(tb_prev != Py_None);
                                337             122 :         Py_DECREF(tb_prev);
                                338             122 :         if (tb == NULL)
 4130 peter_e                   339 UBC           0 :             elog(ERROR, "could not traverse Python traceback");
 4130 peter_e                   340 CBC         122 :         (*tb_depth)++;
                                341                 :     }
                                342                 : 
                                343                 :     /* Return the traceback. */
                                344              57 :     *tbmsg = tbstr.data;
                                345                 : 
                                346              57 :     Py_XDECREF(e_type_o);
                                347              57 :     Py_XDECREF(e_module_o);
                                348              57 :     Py_XDECREF(vob);
                                349                 : }
                                350                 : 
                                351                 : /*
                                352                 :  * Extract error code from SPIError's sqlstate attribute.
                                353                 :  */
                                354                 : static void
 2557 teodor                    355              17 : PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
                                356                 : {
                                357                 :     PyObject   *sqlstate;
                                358                 :     char       *buffer;
                                359                 : 
 3723 heikki.linnakangas        360              17 :     sqlstate = PyObject_GetAttrString(exc, "sqlstate");
                                361              17 :     if (sqlstate == NULL)
                                362               4 :         return;
                                363                 : 
  398 andres                    364              13 :     buffer = PLyUnicode_AsString(sqlstate);
 3723 heikki.linnakangas        365              13 :     if (strlen(buffer) == 5 &&
                                366              13 :         strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
                                367                 :     {
                                368              13 :         *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2],
                                369                 :                                     buffer[3], buffer[4]);
                                370                 :     }
                                371                 : 
                                372              13 :     Py_DECREF(sqlstate);
                                373                 : }
                                374                 : 
                                375                 : /*
                                376                 :  * Extract the error data from a SPIError
                                377                 :  */
                                378                 : static void
 2557 teodor                    379              22 : PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
                                380                 :                        char **hint, char **query, int *position,
                                381                 :                        char **schema_name, char **table_name,
                                382                 :                        char **column_name,
                                383                 :                        char **datatype_name, char **constraint_name)
                                384                 : {
                                385                 :     PyObject   *spidata;
                                386                 : 
 4130 peter_e                   387              22 :     spidata = PyObject_GetAttrString(exc, "spidata");
                                388                 : 
 3723 heikki.linnakangas        389              22 :     if (spidata != NULL)
                                390                 :     {
 2555 tgl                       391              18 :         PyArg_ParseTuple(spidata, "izzzizzzzz",
                                392                 :                          sqlerrcode, detail, hint, query, position,
                                393                 :                          schema_name, table_name, column_name,
                                394                 :                          datatype_name, constraint_name);
                                395                 :     }
                                396                 :     else
                                397                 :     {
                                398                 :         /*
                                399                 :          * If there's no spidata, at least set the sqlerrcode. This can happen
                                400                 :          * if someone explicitly raises a SPI exception from Python code.
                                401                 :          */
 2557 teodor                    402               4 :         PLy_get_sqlerrcode(exc, sqlerrcode);
                                403                 :     }
                                404                 : 
 4130 peter_e                   405              22 :     Py_XDECREF(spidata);
                                406              22 : }
                                407                 : 
                                408                 : /*
                                409                 :  * Extract the error data from an Error.
                                410                 :  *
                                411                 :  * Note: position and query attributes are never set for Error so, unlike
                                412                 :  * PLy_get_spi_error_data, this function doesn't return them.
                                413                 :  */
                                414                 : static void
 2557 teodor                    415              13 : PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint,
                                416                 :                    char **schema_name, char **table_name, char **column_name,
                                417                 :                    char **datatype_name, char **constraint_name)
                                418                 : {
                                419              13 :     PLy_get_sqlerrcode(exc, sqlerrcode);
                                420              13 :     get_string_attr(exc, "detail", detail);
                                421              13 :     get_string_attr(exc, "hint", hint);
                                422              13 :     get_string_attr(exc, "schema_name", schema_name);
                                423              13 :     get_string_attr(exc, "table_name", table_name);
                                424              13 :     get_string_attr(exc, "column_name", column_name);
                                425              13 :     get_string_attr(exc, "datatype_name", datatype_name);
                                426              13 :     get_string_attr(exc, "constraint_name", constraint_name);
                                427              13 : }
                                428                 : 
                                429                 : /*
                                430                 :  * Get the given source line as a palloc'd string
                                431                 :  */
                                432                 : static char *
 4130 peter_e                   433              68 : get_source_line(const char *src, int lineno)
                                434                 : {
                                435              68 :     const char *s = NULL;
                                436              68 :     const char *next = src;
                                437              68 :     int         current = 0;
                                438                 : 
                                439                 :     /* sanity check */
 4044 tgl                       440              68 :     if (lineno <= 0)
 4044 tgl                       441 UBC           0 :         return NULL;
                                442                 : 
 4130 peter_e                   443 CBC         488 :     while (current < lineno)
                                444                 :     {
                                445             420 :         s = next;
                                446             420 :         next = strchr(s + 1, '\n');
                                447             420 :         current++;
                                448             420 :         if (next == NULL)
 4130 peter_e                   449 UBC           0 :             break;
                                450                 :     }
                                451                 : 
 4130 peter_e                   452 CBC          68 :     if (current != lineno)
 4130 peter_e                   453 UBC           0 :         return NULL;
                                454                 : 
 4130 peter_e                   455 CBC         318 :     while (*s && isspace((unsigned char) *s))
                                456             250 :         s++;
                                457                 : 
                                458              68 :     if (next == NULL)
 4130 peter_e                   459 UBC           0 :         return pstrdup(s);
                                460                 : 
                                461                 :     /*
                                462                 :      * Sanity check, next < s if the line was all-whitespace, which should
                                463                 :      * never happen if Python reported a frame created on that line, but check
                                464                 :      * anyway.
                                465                 :      */
 4130 peter_e                   466 CBC          68 :     if (next < s)
 4130 peter_e                   467 UBC           0 :         return NULL;
                                468                 : 
 4130 peter_e                   469 CBC          68 :     return pnstrdup(s, next - s);
                                470                 : }
                                471                 : 
                                472                 : 
                                473                 : /* call PyErr_SetString with a vprint interface and translation support */
                                474                 : void
                                475              18 : PLy_exception_set(PyObject *exc, const char *fmt,...)
                                476                 : {
                                477                 :     char        buf[1024];
                                478                 :     va_list     ap;
                                479                 : 
                                480              18 :     va_start(ap, fmt);
                                481              18 :     vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
                                482              18 :     va_end(ap);
                                483                 : 
                                484              18 :     PyErr_SetString(exc, buf);
                                485              18 : }
                                486                 : 
                                487                 : /* same, with pluralized message */
                                488                 : void
                                489               1 : PLy_exception_set_plural(PyObject *exc,
                                490                 :                          const char *fmt_singular, const char *fmt_plural,
                                491                 :                          unsigned long n,...)
                                492                 : {
                                493                 :     char        buf[1024];
                                494                 :     va_list     ap;
                                495                 : 
                                496               1 :     va_start(ap, n);
                                497               1 :     vsnprintf(buf, sizeof(buf),
                                498               1 :               dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
                                499                 :               ap);
                                500               1 :     va_end(ap);
                                501                 : 
                                502               1 :     PyErr_SetString(exc, buf);
                                503               1 : }
                                504                 : 
                                505                 : /* set attributes of the given exception to details from ErrorData */
                                506                 : void
 2557 teodor                    507              11 : PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
                                508                 : {
                                509              11 :     PyObject   *args = NULL;
                                510              11 :     PyObject   *error = NULL;
                                511                 : 
                                512              11 :     args = Py_BuildValue("(s)", edata->message);
                                513              11 :     if (!args)
 2557 teodor                    514 UBC           0 :         goto failure;
                                515                 : 
                                516                 :     /* create a new exception with the error message as the parameter */
 2557 teodor                    517 CBC          11 :     error = PyObject_CallObject(excclass, args);
                                518              11 :     if (!error)
 2557 teodor                    519 UBC           0 :         goto failure;
                                520                 : 
 2557 teodor                    521 CBC          11 :     if (!set_string_attr(error, "sqlstate",
                                522                 :                          unpack_sql_state(edata->sqlerrcode)))
 2557 teodor                    523 UBC           0 :         goto failure;
                                524                 : 
 2557 teodor                    525 CBC          11 :     if (!set_string_attr(error, "detail", edata->detail))
 2557 teodor                    526 UBC           0 :         goto failure;
                                527                 : 
 2557 teodor                    528 CBC          11 :     if (!set_string_attr(error, "hint", edata->hint))
 2557 teodor                    529 UBC           0 :         goto failure;
                                530                 : 
 2557 teodor                    531 CBC          11 :     if (!set_string_attr(error, "query", edata->internalquery))
 2557 teodor                    532 UBC           0 :         goto failure;
                                533                 : 
 2557 teodor                    534 CBC          11 :     if (!set_string_attr(error, "schema_name", edata->schema_name))
 2557 teodor                    535 UBC           0 :         goto failure;
                                536                 : 
 2557 teodor                    537 CBC          11 :     if (!set_string_attr(error, "table_name", edata->table_name))
 2557 teodor                    538 UBC           0 :         goto failure;
                                539                 : 
 2557 teodor                    540 CBC          11 :     if (!set_string_attr(error, "column_name", edata->column_name))
 2557 teodor                    541 UBC           0 :         goto failure;
                                542                 : 
 2557 teodor                    543 CBC          11 :     if (!set_string_attr(error, "datatype_name", edata->datatype_name))
 2557 teodor                    544 UBC           0 :         goto failure;
                                545                 : 
 2557 teodor                    546 CBC          11 :     if (!set_string_attr(error, "constraint_name", edata->constraint_name))
 2557 teodor                    547 UBC           0 :         goto failure;
                                548                 : 
 2557 teodor                    549 CBC          11 :     PyErr_SetObject(excclass, error);
                                550                 : 
                                551              11 :     Py_DECREF(args);
                                552              11 :     Py_DECREF(error);
                                553                 : 
                                554              11 :     return;
                                555                 : 
 2557 teodor                    556 UBC           0 : failure:
                                557               0 :     Py_XDECREF(args);
                                558               0 :     Py_XDECREF(error);
                                559                 : 
                                560               0 :     elog(ERROR, "could not convert error to Python exception");
                                561                 : }
                                562                 : 
                                563                 : /* get string value of an object attribute */
                                564                 : static void
 2557 teodor                    565 CBC          91 : get_string_attr(PyObject *obj, char *attrname, char **str)
                                566                 : {
                                567                 :     PyObject   *val;
                                568                 : 
                                569              91 :     val = PyObject_GetAttrString(obj, attrname);
                                570              91 :     if (val != NULL && val != Py_None)
                                571                 :     {
  398 andres                    572              28 :         *str = pstrdup(PLyUnicode_AsString(val));
                                573                 :     }
 2557 teodor                    574              91 :     Py_XDECREF(val);
                                575              91 : }
                                576                 : 
                                577                 : /* set an object attribute to a string value, returns true when the set was
                                578                 :  * successful
                                579                 :  */
                                580                 : static bool
                                581              99 : set_string_attr(PyObject *obj, char *attrname, char *str)
                                582                 : {
                                583                 :     int         result;
                                584                 :     PyObject   *val;
                                585                 : 
                                586              99 :     if (str != NULL)
                                587                 :     {
  398 andres                    588              39 :         val = PLyUnicode_FromString(str);
 2557 teodor                    589              39 :         if (!val)
 2557 teodor                    590 UBC           0 :             return false;
                                591                 :     }
                                592                 :     else
                                593                 :     {
 2557 teodor                    594 CBC          60 :         val = Py_None;
                                595              60 :         Py_INCREF(Py_None);
                                596                 :     }
                                597                 : 
                                598              99 :     result = PyObject_SetAttrString(obj, attrname, val);
                                599              99 :     Py_DECREF(val);
                                600                 : 
                                601              99 :     return result != -1;
                                602                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a