but the implementation is pretty ugly :( - I didn't write C extensions for > Python before, and the extending exception class with some methods isn't > well supported and well documented. >
here is new patch cleaned, all unwanted artefacts removed. I am not sure if used way for method registration is 100% valid, but I didn't find any related documentation. Regards Pavel > > Any help is welcome > > Regards > > Pavel > > >> >> >> -- >> Craig Ringer http://www.2ndQuadrant.com/ >> PostgreSQL Development, 24x7 Support, Training & Services >> > >
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml new file mode 100644 index 015bbad..bf468e1 *** a/doc/src/sgml/plpython.sgml --- b/doc/src/sgml/plpython.sgml *************** $$ LANGUAGE plpythonu; *** 1205,1210 **** --- 1205,1235 ---- approximately the same functionality </para> </sect2> + + <sect2 id="plpython-raising"> + <title>Raising Errors</title> + + <para> + The <literal>plpy</literal> module provides several possibilities to + to raise a exception: + </para> + + <variablelist> + <varlistentry> + <term><literal><function>SPIError</function>([ <replaceable>message</replaceable> [, <replaceable>detail</replaceable> [, <replaceable>hint</replaceable> [, <replaceable>sqlstate</replaceable> [, <replaceable>schema</replaceable> [, <replaceable>table</replaceable> [, <replaceable>column</replaceable> [, <replaceable>datatype</replaceable> [, <replaceable>constraint</replaceable> ]]]]]]]]])</literal></term> + <listitem> + <para> + The constructor of SPIError exception (class) supports keyword parameters. + <programlisting> + DO $$ + raise plpy.SPIError('custom message', hint = 'It is test only'); + $$ LANGUAGE plpythonu; + </programlisting> + </para> + </listitem> + </varlistentry> + </variablelist> + </sect2> </sect1> <sect1 id="plpython-subtransaction"> diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out new file mode 100644 index 1f52af7..ac985c6 *** a/src/pl/plpython/expected/plpython_error.out --- b/src/pl/plpython/expected/plpython_error.out *************** EXCEPTION WHEN SQLSTATE 'SILLY' THEN *** 422,424 **** --- 422,473 ---- -- NOOP END $$ LANGUAGE plpgsql; + /* possibility to set all accessable fields in custom exception + */ + DO $$ + raise plpy.SPIError('This is message text.', + detail = 'This is detail text', + hint = 'This is hint text.') + $$ LANGUAGE plpythonu; + ERROR: plpy.SPIError: This is message text. + DETAIL: This is detail text + HINT: This is hint text. + CONTEXT: Traceback (most recent call last): + PL/Python anonymous code block, line 4, in <module> + hint = 'This is hint text.') + PL/Python anonymous code block + \set VERBOSITY verbose + DO $$ + raise plpy.SPIError('This is message text.', + detail = 'This is detail text', + hint = 'This is hint text.', + sqlstate = 'SILLY', + schema = 'any info about schema', + table = 'any info about table', + column = 'any info about column', + datatype = 'any info about datatype', + constraint = 'any info about constraint') + $$ LANGUAGE plpythonu; + ERROR: SILLY: plpy.SPIError: This is message text. + DETAIL: This is detail text + HINT: This is hint text. + CONTEXT: Traceback (most recent call last): + PL/Python anonymous code block, line 10, in <module> + constraint = 'any info about constraint') + PL/Python anonymous code block + SCHEMA NAME: any info about schema + TABLE NAME: any info about table + COLUMN NAME: any info about column + DATATYPE NAME: any info about datatype + CONSTRAINT NAME: any info about constraint + LOCATION: PLy_elog, plpy_elog.c:122 + \set VERBOSITY default + DO $$ + raise plpy.SPIError(detail = 'This is detail text') + $$ LANGUAGE plpythonu; + ERROR: plpy.SPIError: + DETAIL: This is detail text + CONTEXT: Traceback (most recent call last): + PL/Python anonymous code block, line 2, in <module> + raise plpy.SPIError(detail = 'This is detail text') + PL/Python anonymous code block diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c new file mode 100644 index 15406d6..a835af9 *** a/src/pl/plpython/plpy_elog.c --- b/src/pl/plpython/plpy_elog.c *************** PyObject *PLy_exc_spi_error = NULL; *** 23,29 **** static void PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth); static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, ! char **hint, char **query, int *position); static char *get_source_line(const char *src, int lineno); --- 23,32 ---- static void PLy_traceback(char **xmsg, char **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 char *get_source_line(const char *src, int lineno); *************** PLy_elog(int elevel, const char *fmt,... *** 51,62 **** char *hint = NULL; char *query = NULL; int position = 0; PyErr_Fetch(&exc, &val, &tb); if (exc != NULL) { if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error)) ! PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position); else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal)) elevel = FATAL; } --- 54,73 ---- 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; PyErr_Fetch(&exc, &val, &tb); if (exc != NULL) { 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_fatal)) elevel = FATAL; } *************** PLy_elog(int elevel, const char *fmt,... *** 103,109 **** (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0, (hint) ? errhint("%s", hint) : 0, (query) ? internalerrquery(query) : 0, ! (position) ? internalerrposition(position) : 0)); } PG_CATCH(); { --- 114,126 ---- (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_CATCH(); { *************** PLy_get_spi_sqlerrcode(PyObject *exc, in *** 365,371 **** * Extract the error data from a SPIError */ static void ! PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint, char **query, int *position) { PyObject *spidata = NULL; --- 382,390 ---- * Extract the error data from a SPIError */ 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 = NULL; *************** PLy_get_spi_error_data(PyObject *exc, in *** 373,379 **** if (spidata != NULL) { ! PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position); } else { --- 392,400 ---- if (spidata != NULL) { ! PyArg_ParseTuple(spidata, "izzzizzzzz", sqlerrcode, detail, hint, query, position, ! schema_name, table_name, column_name, ! datatype_name, constraint_name); } else { diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c new file mode 100644 index a44b7fb..6881013 *** a/src/pl/plpython/plpy_plpymodule.c --- b/src/pl/plpython/plpy_plpymodule.c *************** HTAB *PLy_spi_exceptions = NULL; *** 26,31 **** --- 26,32 ---- static void PLy_add_exceptions(PyObject *plpy); static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base); + static void PLy_add_methods_to_dictionary(PyObject *dict, PyMethodDef *methods); /* module functions */ static PyObject *PLy_debug(PyObject *self, PyObject *args); *************** static PyObject *PLy_quote_literal(PyObj *** 39,44 **** --- 40,48 ---- static PyObject *PLy_quote_nullable(PyObject *self, PyObject *args); static PyObject *PLy_quote_ident(PyObject *self, PyObject *args); + /* methods */ + static PyObject *PLy_spi_error__init__(PyObject *self, PyObject *args, PyObject *kw); + /* A list of all known exceptions, generated from backend/utils/errcodes.txt */ typedef struct ExceptionMap *************** static PyMethodDef PLy_exc_methods[] = { *** 99,104 **** --- 103,113 ---- {NULL, NULL, 0, NULL} }; + static PyMethodDef PLy_spi_error_methods[] = { + {"__init__", (PyCFunction) PLy_spi_error__init__, METH_KEYWORDS, NULL}, + {NULL, NULL, 0, NULL} + }; + #if PY_MAJOR_VERSION >= 3 static PyModuleDef PLy_module = { PyModuleDef_HEAD_INIT, /* m_base */ *************** static void *** 185,190 **** --- 194,200 ---- PLy_add_exceptions(PyObject *plpy) { PyObject *excmod; + PyObject *spi_error_dict; HASHCTL hash_ctl; #if PY_MAJOR_VERSION < 3 *************** PLy_add_exceptions(PyObject *plpy) *** 207,215 **** */ Py_INCREF(excmod); PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL); PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL); ! PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL); if (PLy_exc_error == NULL || PLy_exc_fatal == NULL || --- 217,230 ---- */ Py_INCREF(excmod); + /* prepare dictionary with __init__ method for SPIError class */ + spi_error_dict = PyDict_New(); + PLy_add_methods_to_dictionary(spi_error_dict, PLy_spi_error_methods); + PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL); PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL); ! PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, spi_error_dict); ! Py_DECREF(spi_error_dict); if (PLy_exc_error == NULL || PLy_exc_fatal == NULL || *************** PLy_generate_spi_exceptions(PyObject *mo *** 266,271 **** --- 281,420 ---- } } + /* + * Returns dictionary with specified set of methods. It is used for + * definition __init__ method of SPIError class. That is necessary + * for keyword parameters support. + * + * SHOULD BE VERIFIED BEFORE MERGE: the function PyMethod_New is called without + * specified class (third parameter). I cannot to specify class there, + * because in this moment, this class doesn't exists. It is created + * via PyErr_NewException with last dict parameter. Late modifiction of + * used dictionary has zero effect on new class. So dictionary should be + * created before. Tested on Python 2.7.10. + */ + static void + PLy_add_methods_to_dictionary(PyObject *dict, PyMethodDef *methods) + { + PyMethodDef *method; + + for (method = methods; method->ml_name != NULL; method++) + { + PyObject *func; + PyObject *meth; + + func = PyCFunction_New(method, NULL); + if (func == NULL) + PLy_elog(ERROR, "could not import function \"%s\"", method->ml_name); + + meth = PyMethod_New(func, NULL, NULL); + if (meth == NULL) + PLy_elog(ERROR, "could not import method \"%s\"", method->ml_name); + + if (PyDict_SetItemString(dict, method->ml_name, meth)) + PLy_elog(ERROR, "could public method \"%s\" in dictionary", method->ml_name); + + Py_DECREF(meth); + Py_DECREF(func); + } + } + + /* + * Init method for SPIError class. + * + * This constructor allows to enter all user fields in PostgreSQL exception. + * Keywords parameters are supported. + */ + static PyObject * + PLy_spi_error__init__(PyObject *self, PyObject *args, PyObject *kw) + { + int sqlstate = 0; + const char *sqlstatestr = NULL; + const char *message = NULL; + const char *detail = NULL; + const char *hint = NULL; + const char *column = NULL; + const char *constraint = NULL; + const char *datatype = NULL; + const char *table = NULL; + const char *schema = NULL; + + PyObject *exc_args = NULL; + PyObject *spidata = NULL; + + static char *kwlist[] = { "self", "message", "detail", "hint", + "sqlstate", + "schema","table", "column", + "datatype", "constraint", + NULL }; + + /* + * don't try to overwrite default sqlstate field, when constructor + * is called without any parameter. Important for predefined + * spiexception.* exceptions. + */ + if (PyTuple_Size(args) > 1 || PyDict_Size(kw) >= 1) + { + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|sssssssss", + kwlist, &self, + &message, &detail, &hint, + &sqlstatestr, + &schema, &table, &column, + &datatype, &constraint)) + return NULL; + + if (message != NULL) + { + exc_args = Py_BuildValue("(s)", message); + if (!exc_args) + goto failure; + + if (PyObject_SetAttrString(self, "args", exc_args) == -1) + goto failure; + } + + if (sqlstatestr != NULL) + { + if (strlen(sqlstatestr) != 5) + PLy_elog(ERROR, "invalid SQLSTATE code"); + + if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5) + PLy_elog(ERROR, "invalid SQLSTATE code"); + + sqlstate = MAKE_SQLSTATE(sqlstatestr[0], + sqlstatestr[1], + sqlstatestr[2], + sqlstatestr[3], + sqlstatestr[4]); + } + + spidata = Py_BuildValue("(izzzizzzzz)", + sqlstate, detail, hint, + NULL, -1, + schema, table, column, + datatype, constraint); + if (!spidata) + goto failure; + + if (PyObject_SetAttrString(self, "spidata", spidata) == -1) + goto failure; + + Py_XDECREF(exc_args); + Py_DECREF(spidata); + } + + Py_INCREF(Py_None); + return Py_None; + + failure: + Py_XDECREF(exc_args); + Py_XDECREF(spidata); + + PLy_elog(ERROR, "could not create SPIError object"); + + return NULL; + } + /* * the python interface to the elog function diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c new file mode 100644 index d0e255f..4419099 *** a/src/pl/plpython/plpy_spi.c --- b/src/pl/plpython/plpy_spi.c *************** PLy_spi_exception_set(PyObject *excclass *** 548,555 **** if (!spierror) goto failure; ! spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint, ! edata->internalquery, edata->internalpos); if (!spidata) goto failure; --- 548,558 ---- if (!spierror) goto failure; ! spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint, ! edata->internalquery, edata->internalpos, ! edata->schema_name, edata->table_name, edata->column_name, ! edata->datatype_name, edata->constraint_name); ! if (!spidata) goto failure; diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql new file mode 100644 index d0df7e6..2d859af *** a/src/pl/plpython/sql/plpython_error.sql --- b/src/pl/plpython/sql/plpython_error.sql *************** EXCEPTION WHEN SQLSTATE 'SILLY' THEN *** 328,330 **** --- 328,358 ---- -- NOOP END $$ LANGUAGE plpgsql; + + /* possibility to set all accessable fields in custom exception + */ + DO $$ + raise plpy.SPIError('This is message text.', + detail = 'This is detail text', + hint = 'This is hint text.') + $$ LANGUAGE plpythonu; + + \set VERBOSITY verbose + DO $$ + raise plpy.SPIError('This is message text.', + detail = 'This is detail text', + hint = 'This is hint text.', + sqlstate = 'SILLY', + schema = 'any info about schema', + table = 'any info about table', + column = 'any info about column', + datatype = 'any info about datatype', + constraint = 'any info about constraint') + $$ LANGUAGE plpythonu; + + \set VERBOSITY default + + DO $$ + raise plpy.SPIError(detail = 'This is detail text') + $$ LANGUAGE plpythonu; +
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers