Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  plpy_elog.c

  Sprache: C
 

/*
 * reporting Python exceptions as PostgreSQL errors
 *
 * src/pl/plpython/plpy_elog.c
 */


#include "postgres.h"

#include "lib/stringinfo.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_procedure.h"
#include "plpy_util.h"

PyObject   *PLy_exc_error = NULL;
PyObject   *PLy_exc_fatal = NULL;
PyObject   *PLy_exc_spi_error = NULL;


static void PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
        char *volatile *xmsg, char *volatile *tbmsg,
        int *tb_depth);
static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
           char **hint, char **query, int *position,
           char **schema_name, char **table_name, char **column_name,
           char **datatype_name, char **constraint_name);
static void PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail,
          char **hint, char **schema_name, char **table_name, char **column_name,
          char **datatype_name, char **constraint_name);
static char *get_source_line(const char *src, int lineno);

static void get_string_attr(PyObject *obj, char *attrname, char **str);
static bool set_string_attr(PyObject *obj, char *attrname, char *str);

/*
 * Emit a PG error or notice, together with any available info about
 * the current Python error, previously set by PLy_exception_set().
 * This should be used to propagate Python errors into PG.  If fmt is
 * NULL, the Python error becomes the primary error message, otherwise
 * it becomes the detail.  If there is a Python traceback, it is put
 * in the context.
 */

void
PLy_elog_impl(int elevel, const char *fmt,...)
{
 int   save_errno = errno;
 char    *volatile xmsg = NULL;
 char    *volatile tbmsg = NULL;
 int   tb_depth;
 StringInfoData emsg;
 PyObject   *exc,
      *val,
      *tb;

 /* If we'll need emsg, must initialize it before entering PG_TRY */
 if (fmt)
  initStringInfo(&emsg);

 PyErr_Fetch(&exc, &val, &tb);

 /* Use a PG_TRY block to ensure we release the PyObjects just acquired */
 PG_TRY();
 {
  const char *primary = NULL;
  int   sqlerrcode = 0;
  char    *detail = NULL;
  char    *hint = NULL;
  char    *query = NULL;
  int   position = 0;
  char    *schema_name = NULL;
  char    *table_name = NULL;
  char    *column_name = NULL;
  char    *datatype_name = NULL;
  char    *constraint_name = NULL;

  if (exc != NULL)
  {
   PyErr_NormalizeException(&exc, &val, &tb);

   if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
    PLy_get_spi_error_data(val, &sqlerrcode,
            &detail, &hint, &query, &position,
            &schema_name, &table_name, &column_name,
            &datatype_name, &constraint_name);
   else if (PyErr_GivenExceptionMatches(val, PLy_exc_error))
    PLy_get_error_data(val, &sqlerrcode, &detail, &hint,
           &schema_name, &table_name, &column_name,
           &datatype_name, &constraint_name);
   else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
    elevel = FATAL;
  }

  PLy_traceback(exc, val, tb,
       &xmsg, &tbmsg, &tb_depth);

  if (fmt)
  {
   for (;;)
   {
    va_list  ap;
    int   needed;

    errno = save_errno;
    va_start(ap, fmt);
    needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
    va_end(ap);
    if (needed == 0)
     break;
    enlargeStringInfo(&emsg, needed);
   }
   primary = emsg.data;

   /* If there's an exception message, it goes in the detail. */
   if (xmsg)
    detail = xmsg;
  }
  else
  {
   if (xmsg)
    primary = xmsg;
  }

  ereport(elevel,
    (errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
     errmsg_internal("%s", primary ? primary : "no exception data"),
     (detail) ? errdetail_internal("%s", detail) : 0,
     (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
     (hint) ? errhint("%s", hint) : 0,
     (query) ? internalerrquery(query) : 0,
     (position) ? internalerrposition(position) : 0,
     (schema_name) ? err_generic_string(PG_DIAG_SCHEMA_NAME,
             schema_name) : 0,
     (table_name) ? err_generic_string(PG_DIAG_TABLE_NAME,
               table_name) : 0,
     (column_name) ? err_generic_string(PG_DIAG_COLUMN_NAME,
             column_name) : 0,
     (datatype_name) ? err_generic_string(PG_DIAG_DATATYPE_NAME,
               datatype_name) : 0,
     (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME,
              constraint_name) : 0));
 }
 PG_FINALLY();
 {
  Py_XDECREF(exc);
  Py_XDECREF(val);
  Py_XDECREF(tb);
  /* For neatness' sake, also release our string buffers */
  if (fmt)
   pfree(emsg.data);
  if (xmsg)
   pfree(xmsg);
  if (tbmsg)
   pfree(tbmsg);
 }
 PG_END_TRY();
}

/*
 * Extract a Python traceback from the given exception data.
 *
 * The exception error message is returned in xmsg, the traceback in
 * tbmsg (both as palloc'd strings) and the traceback depth in
 * tb_depth.
 */

static void
PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
     char *volatile *xmsg, char *volatile *tbmsg, int *tb_depth)
{
 PyObject   *volatile e_type_o = NULL;
 PyObject   *volatile e_module_o = NULL;
 PyObject   *volatile vob = NULL;
 StringInfoData tbstr;

 /*
  * if no exception, return nulls
 */

 if (e == NULL)
 {
  *xmsg = NULL;
  *tbmsg = NULL;
  *tb_depth = 0;

  return;
 }

 /*
  * Format the exception and its value and put it in xmsg.
 */

 PG_TRY();
 {
  char    *e_type_s = NULL;
  char    *e_module_s = NULL;
  const char *vstr;
  StringInfoData xstr;

  e_type_o = PyObject_GetAttrString(e, "__name__");
  e_module_o = PyObject_GetAttrString(e, "__module__");
  if (e_type_o)
   e_type_s = PLyUnicode_AsString(e_type_o);
  if (e_module_o)
   e_module_s = PLyUnicode_AsString(e_module_o);

  if (v && ((vob = PyObject_Str(v)) != NULL))
   vstr = PLyUnicode_AsString(vob);
  else
   vstr = "unknown";

  initStringInfo(&xstr);
  if (!e_type_s || !e_module_s)
  {
   /* shouldn't happen */
   appendStringInfoString(&xstr, "unrecognized exception");
  }
  /* mimics behavior of traceback.format_exception_only */
  else if (strcmp(e_module_s, "builtins") == 0
     || strcmp(e_module_s, "__main__") == 0
     || strcmp(e_module_s, "exceptions") == 0)
   appendStringInfoString(&xstr, e_type_s);
  else
   appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
  appendStringInfo(&xstr, ": %s", vstr);

  *xmsg = xstr.data;
 }
 PG_FINALLY();
 {
  Py_XDECREF(e_type_o);
  Py_XDECREF(e_module_o);
  Py_XDECREF(vob);
 }
 PG_END_TRY();

 /*
  * Now format the traceback and put it in tbmsg.
 */

 *tb_depth = 0;
 initStringInfo(&tbstr);
 /* Mimic Python traceback reporting as close as possible. */
 appendStringInfoString(&tbstr, "Traceback (most recent call last):");
 while (tb != NULL && tb != Py_None)
 {
  PyObject   *volatile frame = NULL;
  PyObject   *volatile code = NULL;
  PyObject   *volatile name = NULL;
  PyObject   *volatile lineno = NULL;
  PyObject   *volatile filename = NULL;

  PG_TRY();
  {
   lineno = PyObject_GetAttrString(tb, "tb_lineno");
   if (lineno == NULL)
    elog(ERROR, "could not get line number from Python traceback");

   frame = PyObject_GetAttrString(tb, "tb_frame");
   if (frame == NULL)
    elog(ERROR, "could not get frame from Python traceback");

   code = PyObject_GetAttrString(frame, "f_code");
   if (code == NULL)
    elog(ERROR, "could not get code object from Python frame");

   name = PyObject_GetAttrString(code, "co_name");
   if (name == NULL)
    elog(ERROR, "could not get function name from Python code object");

   filename = PyObject_GetAttrString(code, "co_filename");
   if (filename == NULL)
    elog(ERROR, "could not get file name from Python code object");

   /* The first frame always points at <module>, skip it. */
   if (*tb_depth > 0)
   {
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();
    char    *proname;
    char    *fname;
    char    *line;
    char    *plain_filename;
    long  plain_lineno;

    /*
     * The second frame points at the internal function, but to
     * mimic Python error reporting we want to say <module>.
 */

    if (*tb_depth == 1)
     fname = "<module>";
    else
     fname = PLyUnicode_AsString(name);

    proname = PLy_procedure_name(exec_ctx->curr_proc);
    plain_filename = PLyUnicode_AsString(filename);
    plain_lineno = PyLong_AsLong(lineno);

    if (proname == NULL)
     appendStringInfo(&tbstr, "\n  PL/Python anonymous code block, line %ld, in %s",
          plain_lineno - 1, fname);
    else
     appendStringInfo(&tbstr, "\n  PL/Python function \"%s\", line %ld, in %s",
          proname, plain_lineno - 1, fname);

    /*
     * function code object was compiled with "<string>" as the
     * filename
 */

    if (exec_ctx->curr_proc && plain_filename != NULL &&
     strcmp(plain_filename, "<string>") == 0)
    {
     /*
      * If we know the current procedure, append the exact line
      * from the source, again mimicking Python's traceback.py
      * module behavior.  We could store the already line-split
      * source to avoid splitting it every time, but producing
      * a traceback is not the most important scenario to
      * optimize for.  But we do not go as far as traceback.py
      * in reading the source of imported modules.
 */

     line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
     if (line)
     {
      appendStringInfo(&tbstr, "\n    %s", line);
      pfree(line);
     }
    }
   }
  }
  PG_FINALLY();
  {
   Py_XDECREF(frame);
   Py_XDECREF(code);
   Py_XDECREF(name);
   Py_XDECREF(lineno);
   Py_XDECREF(filename);
  }
  PG_END_TRY();

  /* Advance to the next frame. */
  tb = PyObject_GetAttrString(tb, "tb_next");
  if (tb == NULL)
   elog(ERROR, "could not traverse Python traceback");

  /*
   * Release the refcount that PyObject_GetAttrString acquired on the
   * next frame object.  We don't need it, because our caller has a
   * refcount on the first frame object and the frame objects each have
   * a refcount on the next one.  If we tried to hold this refcount
   * longer, it would greatly complicate cleanup in the event of a
   * failure in the above PG_TRY block.
 */

  Py_DECREF(tb);

  (*tb_depth)++;
 }

 /* Return the traceback. */
 *tbmsg = tbstr.data;
}

/*
 * Extract error code from SPIError's sqlstate attribute.
 */

static void
PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
{
 PyObject   *sqlstate;
 char    *buffer;

 sqlstate = PyObject_GetAttrString(exc, "sqlstate");
 if (sqlstate == NULL)
  return;

 buffer = PLyUnicode_AsString(sqlstate);
 if (strlen(buffer) == 5 &&
  strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
 {
  *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2],
         buffer[3], buffer[4]);
 }

 Py_DECREF(sqlstate);
}

/*
 * Extract the error data from a SPIError
 *
 * Note: the returned string values are pointers into the given PyObject.
 * They must not be free()'d, and are not guaranteed to be valid once
 * we stop holding a reference on the PyObject.
 */

static void
PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
        char **hint, char **query, int *position,
        char **schema_name, char **table_name,
        char **column_name,
        char **datatype_name, char **constraint_name)
{
 PyObject   *spidata;

 spidata = PyObject_GetAttrString(exc, "spidata");

 if (spidata != NULL)
 {
  PyArg_ParseTuple(spidata, "izzzizzzzz",
       sqlerrcode, detail, hint, query, position,
       schema_name, table_name, column_name,
       datatype_name, constraint_name);
 }
 else
 {
  /*
   * If there's no spidata, at least set the sqlerrcode. This can happen
   * if someone explicitly raises a SPI exception from Python code.
 */

  PLy_get_sqlerrcode(exc, sqlerrcode);
 }

 Py_XDECREF(spidata);
}

/*
 * Extract the error data from an Error.
 *
 * Note: position and query attributes are never set for Error so, unlike
 * PLy_get_spi_error_data, this function doesn't return them.
 *
 * Note: the returned string values are palloc'd in the current context.
 * While our caller could pfree them later, there's no real need to do so,
 * and it would be complicated to handle both this convention and that of
 * PLy_get_spi_error_data.
 */

static void
PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint,
       char **schema_name, char **table_name, char **column_name,
       char **datatype_name, char **constraint_name)
{
 PLy_get_sqlerrcode(exc, sqlerrcode);
 get_string_attr(exc, "detail", detail);
 get_string_attr(exc, "hint", hint);
 get_string_attr(exc, "schema_name", schema_name);
 get_string_attr(exc, "table_name", table_name);
 get_string_attr(exc, "column_name", column_name);
 get_string_attr(exc, "datatype_name", datatype_name);
 get_string_attr(exc, "constraint_name", constraint_name);
}

/*
 * Get the given source line as a palloc'd string
 */

static char *
get_source_line(const char *src, int lineno)
{
 const char *s = NULL;
 const char *next = src;
 int   current = 0;

 /* sanity check */
 if (lineno <= 0)
  return NULL;

 while (current < lineno)
 {
  s = next;
  next = strchr(s + 1'\n');
  current++;
  if (next == NULL)
   break;
 }

 if (current != lineno)
  return NULL;

 while (*s && isspace((unsigned char) *s))
  s++;

 if (next == NULL)
  return pstrdup(s);

 /*
  * Sanity check, next < s if the line was all-whitespace, which should
  * never happen if Python reported a frame created on that line, but check
  * anyway.
 */

 if (next < s)
  return NULL;

 return pnstrdup(s, next - s);
}


/* call PyErr_SetString with a vprint interface and translation support */
void
PLy_exception_set(PyObject *exc, const char *fmt,...)
{
 char  buf[1024];
 va_list  ap;

 va_start(ap, fmt);
 vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
 va_end(ap);

 PyErr_SetString(exc, buf);
}

/* same, with pluralized message */
void
PLy_exception_set_plural(PyObject *exc,
       const char *fmt_singular, const char *fmt_plural,
       unsigned long n,...)
{
 char  buf[1024];
 va_list  ap;

 va_start(ap, n);
 vsnprintf(buf, sizeof(buf),
     dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
     ap);
 va_end(ap);

 PyErr_SetString(exc, buf);
}

/* set attributes of the given exception to details from ErrorData */
void
PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
{
 PyObject   *args = NULL;
 PyObject   *error = NULL;

 args = Py_BuildValue("(s)", edata->message);
 if (!args)
  goto failure;

 /* create a new exception with the error message as the parameter */
 error = PyObject_CallObject(excclass, args);
 if (!error)
  goto failure;

 if (!set_string_attr(error, "sqlstate",
       unpack_sql_state(edata->sqlerrcode)))
  goto failure;

 if (!set_string_attr(error, "detail", edata->detail))
  goto failure;

 if (!set_string_attr(error, "hint", edata->hint))
  goto failure;

 if (!set_string_attr(error, "query", edata->internalquery))
  goto failure;

 if (!set_string_attr(error, "schema_name", edata->schema_name))
  goto failure;

 if (!set_string_attr(error, "table_name", edata->table_name))
  goto failure;

 if (!set_string_attr(error, "column_name", edata->column_name))
  goto failure;

 if (!set_string_attr(error, "datatype_name", edata->datatype_name))
  goto failure;

 if (!set_string_attr(error, "constraint_name", edata->constraint_name))
  goto failure;

 PyErr_SetObject(excclass, error);

 Py_DECREF(args);
 Py_DECREF(error);

 return;

failure:
 Py_XDECREF(args);
 Py_XDECREF(error);

 elog(ERROR, "could not convert error to Python exception");
}

/* get string value of an object attribute */
static void
get_string_attr(PyObject *obj, char *attrname, char **str)
{
 PyObject   *val;

 val = PyObject_GetAttrString(obj, attrname);
 if (val != NULL && val != Py_None)
 {
  *str = pstrdup(PLyUnicode_AsString(val));
 }
 Py_XDECREF(val);
}

/* set an object attribute to a string value, returns true when the set was
 * successful
 */

static bool
set_string_attr(PyObject *obj, char *attrname, char *str)
{
 int   result;
 PyObject   *val;

 if (str != NULL)
 {
  val = PLyUnicode_FromString(str);
  if (!val)
   return false;
 }
 else
 {
  val = Py_None;
  Py_INCREF(Py_None);
 }

 result = PyObject_SetAttrString(obj, attrname, val);
 Py_DECREF(val);

 return result != -1;
}

Messung V0.5 in Prozent
C=95 H=88 G=91

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet am  2026-08-05) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002