Commit f8c8386a authored by Peter Eisentraut's avatar Peter Eisentraut

Cleanup pass over PL/Python NLS. Add translation support to PLy_elog and

PLy_exception_set, and clarify some error messages.
parent d0fe3df7
# $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.31 2008/10/02 08:11:11 petere Exp $ # $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.32 2009/01/15 13:49:56 petere Exp $
subdir = src/pl/plpython subdir = src/pl/plpython
top_builddir = ../../.. top_builddir = ../../..
...@@ -54,7 +54,7 @@ python${pytverstr}.def: $(WD)/system32/python${pytverstr}.dll ...@@ -54,7 +54,7 @@ python${pytverstr}.def: $(WD)/system32/python${pytverstr}.dll
endif endif
SHLIB_LINK = $(python_libspec) $(python_additional_libs) SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS))
REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=plpythonu REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=plpythonu
REGRESS = plpython_schema plpython_populate plpython_function plpython_test plpython_error plpython_drop REGRESS = plpython_schema plpython_populate plpython_function plpython_test plpython_error plpython_drop
......
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
-- the trigger handler once. the errors and subsequent core dump were -- the trigger handler once. the errors and subsequent core dump were
-- interesting. -- interesting.
SELECT invalid_type_uncaught('rick'); SELECT invalid_type_uncaught('rick');
WARNING: plpython: in function invalid_type_uncaught: WARNING: PL/Python: in PL/Python function "invalid_type_uncaught"
DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare DETAIL: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
ERROR: type "test" does not exist ERROR: type "test" does not exist
SELECT invalid_type_caught('rick'); SELECT invalid_type_caught('rick');
WARNING: plpython: in function invalid_type_caught: WARNING: PL/Python: in PL/Python function "invalid_type_caught"
DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare DETAIL: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
ERROR: type "test" does not exist ERROR: type "test" does not exist
SELECT invalid_type_reraised('rick'); SELECT invalid_type_reraised('rick');
WARNING: plpython: in function invalid_type_reraised: WARNING: PL/Python: in PL/Python function "invalid_type_reraised"
DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare DETAIL: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
ERROR: type "test" does not exist ERROR: type "test" does not exist
SELECT valid_type('rick'); SELECT valid_type('rick');
valid_type valid_type
...@@ -23,16 +23,16 @@ SELECT valid_type('rick'); ...@@ -23,16 +23,16 @@ SELECT valid_type('rick');
-- Test Unicode error handling. -- Test Unicode error handling.
-- --
SELECT unicode_return_error(); SELECT unicode_return_error();
ERROR: plpython: function "unicode_return_error" could not create return value ERROR: PL/Python: could not create string representation of Python object in PL/Python function "unicode_return_error" while creating return value
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
INSERT INTO unicode_test (testvalue) VALUES ('test'); INSERT INTO unicode_test (testvalue) VALUES ('test');
ERROR: plpython: function "unicode_trigger_error" could not modify tuple ERROR: PL/Python: could not compute string representation of Python object in PL/Python function "unicode_trigger_error" while modifying trigger row
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
SELECT unicode_plan_error1(); SELECT unicode_plan_error1();
WARNING: plpython: in function unicode_plan_error1: WARNING: PL/Python: in PL/Python function "unicode_plan_error1"
DETAIL: plpy.Error: Unknown error in PLy_spi_execute_plan DETAIL: <class 'plpy.Error'>: unrecognized error in PLy_spi_execute_plan
ERROR: plpython: function "unicode_plan_error1" could not execute plan ERROR: PL/Python: PL/Python function "unicode_plan_error1" could not execute plan
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
SELECT unicode_plan_error2(); SELECT unicode_plan_error2();
ERROR: plpython: function "unicode_plan_error2" could not execute plan ERROR: PL/Python: PL/Python function "unicode_plan_error2" could not execute plan
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
...@@ -190,8 +190,7 @@ SELECT test_void_func1(), test_void_func1() IS NULL AS "is null"; ...@@ -190,8 +190,7 @@ SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
(1 row) (1 row)
SELECT test_void_func2(); -- should fail SELECT test_void_func2(); -- should fail
ERROR: invalid return value from plpython function ERROR: PL/Python function with return type "void" did not return None
DETAIL: Functions returning type "void" must return None.
SELECT test_return_none(), test_return_none() IS NULL AS "is null"; SELECT test_return_none(), test_return_none() IS NULL AS "is null";
test_return_none | is null test_return_none | is null
------------------+--------- ------------------+---------
...@@ -547,7 +546,7 @@ SELECT * FROM test_in_out_params('test_in'); ...@@ -547,7 +546,7 @@ SELECT * FROM test_in_out_params('test_in');
-- this doesn't work yet :-( -- this doesn't work yet :-(
SELECT * FROM test_in_out_params_multi('test_in'); SELECT * FROM test_in_out_params_multi('test_in');
ERROR: plpython functions cannot return type record ERROR: PL/Python functions cannot return type record
SELECT * FROM test_inout_params('test_in'); SELECT * FROM test_inout_params('test_in');
first first
--------------- ---------------
......
# $PostgreSQL: pgsql/src/pl/plpython/nls.mk,v 1.1 2008/10/09 17:24:05 alvherre Exp $ # $PostgreSQL: pgsql/src/pl/plpython/nls.mk,v 1.2 2009/01/15 13:49:56 petere Exp $
CATALOG_NAME := plpython CATALOG_NAME := plpython
AVAIL_LANGUAGES := AVAIL_LANGUAGES :=
GETTEXT_FILES := plpython.c GETTEXT_FILES := plpython.c
GETTEXT_TRIGGERS:= _ errmsg errdetail errdetail_log errhint errcontext write_stderr yyerror GETTEXT_TRIGGERS:= errmsg errdetail errdetail_log errhint errcontext PLy_elog:2 PLy_exception_set:2
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.117 2008/12/11 07:34:09 petere Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.118 2009/01/15 13:49:56 petere Exp $
* *
********************************************************************* *********************************************************************
*/ */
...@@ -204,7 +204,7 @@ PG_FUNCTION_INFO_V1(plpython_call_handler); ...@@ -204,7 +204,7 @@ PG_FUNCTION_INFO_V1(plpython_call_handler);
static void PLy_init_interp(void); static void PLy_init_interp(void);
static void PLy_init_plpy(void); static void PLy_init_plpy(void);
/* call PyErr_SetString with a vprint interface */ /* call PyErr_SetString with a vprint interface and translation support */
static void static void
PLy_exception_set(PyObject *, const char *,...) PLy_exception_set(PyObject *, const char *,...)
__attribute__((format(printf, 2, 3))); __attribute__((format(printf, 2, 3)));
...@@ -213,7 +213,8 @@ __attribute__((format(printf, 2, 3))); ...@@ -213,7 +213,8 @@ __attribute__((format(printf, 2, 3)));
static char *PLy_procedure_name(PLyProcedure *); static char *PLy_procedure_name(PLyProcedure *);
/* some utility functions */ /* some utility functions */
static void PLy_elog(int, const char *,...); static void PLy_elog(int, const char *,...)
__attribute__((format(printf, 2, 3)));
static char *PLy_traceback(int *); static char *PLy_traceback(int *);
static void *PLy_malloc(size_t); static void *PLy_malloc(size_t);
...@@ -331,7 +332,7 @@ plpython_call_handler(PG_FUNCTION_ARGS) ...@@ -331,7 +332,7 @@ plpython_call_handler(PG_FUNCTION_ARGS)
PLyProcedure *volatile proc = NULL; PLyProcedure *volatile proc = NULL;
if (SPI_connect() != SPI_OK_CONNECT) if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager"); elog(ERROR, "SPI_connect failed");
save_curr_proc = PLy_curr_procedure; save_curr_proc = PLy_curr_procedure;
...@@ -417,7 +418,7 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -417,7 +418,7 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("unexpected return value from trigger procedure"), errmsg("unexpected return value from trigger procedure"),
errdetail("Expected None or a String."))); errdetail("Expected None or a string.")));
srv = PyString_AsString(plrv); srv = PyString_AsString(plrv);
if (pg_strcasecmp(srv, "SKIP") == 0) if (pg_strcasecmp(srv, "SKIP") == 0)
...@@ -430,7 +431,8 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -430,7 +431,8 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
rv = PLy_modify_tuple(proc, plargs, tdata, rv); rv = PLy_modify_tuple(proc, plargs, tdata, rv);
else else
elog(WARNING, "ignoring modified tuple in DELETE trigger"); ereport(WARNING,
(errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored")));
} }
else if (pg_strcasecmp(srv, "OK") != 0) else if (pg_strcasecmp(srv, "OK") != 0)
{ {
...@@ -487,9 +489,11 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, ...@@ -487,9 +489,11 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
PG_TRY(); PG_TRY();
{ {
if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL) if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
elog(ERROR, "TD[\"new\"] deleted, cannot modify tuple"); ereport(ERROR,
(errmsg("TD[\"new\"] deleted, cannot modify row")));
if (!PyDict_Check(plntup)) if (!PyDict_Check(plntup))
elog(ERROR, "TD[\"new\"] is not a dictionary object"); ereport(ERROR,
(errmsg("TD[\"new\"] is not a dictionary")));
Py_INCREF(plntup); Py_INCREF(plntup);
plkeys = PyDict_Keys(plntup); plkeys = PyDict_Keys(plntup);
...@@ -507,16 +511,18 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, ...@@ -507,16 +511,18 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
platt = PyList_GetItem(plkeys, i); platt = PyList_GetItem(plkeys, i);
if (!PyString_Check(platt)) if (!PyString_Check(platt))
elog(ERROR, "attribute name is not a string"); ereport(ERROR,
(errmsg("name of TD[\"new\"] attribute at ordinal position %d is not a string", i)));
attn = SPI_fnumber(tupdesc, PyString_AsString(platt)); attn = SPI_fnumber(tupdesc, PyString_AsString(platt));
if (attn == SPI_ERROR_NOATTRIBUTE) if (attn == SPI_ERROR_NOATTRIBUTE)
elog(ERROR, "invalid attribute \"%s\" in tuple", ereport(ERROR,
PyString_AsString(platt)); (errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
PyString_AsString(platt))));
atti = attn - 1; atti = attn - 1;
plval = PyDict_GetItem(plntup, platt); plval = PyDict_GetItem(plntup, platt);
if (plval == NULL) if (plval == NULL)
elog(FATAL, "python interpreter is probably corrupted"); elog(FATAL, "Python interpreter is probably corrupted");
Py_INCREF(plval); Py_INCREF(plval);
...@@ -531,7 +537,7 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, ...@@ -531,7 +537,7 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
{ {
plstr = PyObject_Str(plval); plstr = PyObject_Str(plval);
if (!plstr) if (!plstr)
PLy_elog(ERROR, "function \"%s\" could not modify tuple", PLy_elog(ERROR, "could not compute string representation of Python object in PL/Python function \"%s\" while modifying trigger row",
proc->proname); proc->proname);
src = PyString_AsString(plstr); src = PyString_AsString(plstr);
...@@ -562,7 +568,7 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, ...@@ -562,7 +568,7 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
rtup = SPI_modifytuple(tdata->tg_relation, otup, natts, rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
modattrs, modvalues, modnulls); modattrs, modvalues, modnulls);
if (rtup == NULL) if (rtup == NULL)
elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result); elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result);
} }
PG_CATCH(); PG_CATCH();
{ {
...@@ -613,7 +619,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple * ...@@ -613,7 +619,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
{ {
pltdata = PyDict_New(); pltdata = PyDict_New();
if (!pltdata) if (!pltdata)
PLy_elog(ERROR, "could not build arguments for trigger procedure"); PLy_elog(ERROR, "could not create new dictionary while building trigger arguments");
pltname = PyString_FromString(tdata->tg_trigger->tgname); pltname = PyString_FromString(tdata->tg_trigger->tgname);
PyDict_SetItemString(pltdata, "name", pltname); PyDict_SetItemString(pltdata, "name", pltname);
...@@ -821,7 +827,8 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -821,7 +827,8 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("only value per call is allowed"))); errmsg("unsupported set function return mode"),
errdetail("PL/Python set-returning functions only support returning only value per call.")));
} }
rsi->returnMode = SFRM_ValuePerCall; rsi->returnMode = SFRM_ValuePerCall;
...@@ -834,7 +841,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -834,7 +841,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("returned object cannot be iterated"), errmsg("returned object cannot be iterated"),
errdetail("SETOF must be returned as iterable object"))); errdetail("PL/Python set-returning functions must return an iterable object.")));
} }
/* Fetch next from iterator */ /* Fetch next from iterator */
...@@ -880,8 +887,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -880,8 +887,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
if (plrv != Py_None) if (plrv != Py_None)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid return value from plpython function"), errmsg("PL/Python function with return type \"void\" did not return None")));
errdetail("Functions returning type \"void\" must return None.")));
fcinfo->isnull = false; fcinfo->isnull = false;
rv = (Datum) 0; rv = (Datum) 0;
...@@ -928,7 +934,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -928,7 +934,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
fcinfo->isnull = false; fcinfo->isnull = false;
plrv_so = PyObject_Str(plrv); plrv_so = PyObject_Str(plrv);
if (!plrv_so) if (!plrv_so)
PLy_elog(ERROR, "function \"%s\" could not create return value", proc->proname); PLy_elog(ERROR, "could not create string representation of Python object in PL/Python function \"%s\" while creating return value", proc->proname);
plrv_sc = PyString_AsString(plrv_so); plrv_sc = PyString_AsString(plrv_so);
rv = InputFunctionCall(&proc->result.out.d.typfunc, rv = InputFunctionCall(&proc->result.out.d.typfunc,
plrv_sc, plrv_sc,
...@@ -977,7 +983,7 @@ PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs) ...@@ -977,7 +983,7 @@ PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs)
if (rv == NULL || PyErr_Occurred()) if (rv == NULL || PyErr_Occurred())
{ {
Py_XDECREF(rv); Py_XDECREF(rv);
PLy_elog(ERROR, "function \"%s\" failed", proc->proname); PLy_elog(ERROR, "PL/Python function \"%s\" failed", proc->proname);
} }
return rv; return rv;
...@@ -1049,7 +1055,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc) ...@@ -1049,7 +1055,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
if (PyList_SetItem(args, i, arg) == -1 || if (PyList_SetItem(args, i, arg) == -1 ||
(proc->argnames && (proc->argnames &&
PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1)) PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1))
PLy_elog(ERROR, "problem setting up arguments for \"%s\"", proc->proname); PLy_elog(ERROR, "PyDict_SetItemString() failed for PL/Python function \"%s\" while setting up arguments", proc->proname);
arg = NULL; arg = NULL;
} }
} }
...@@ -1230,7 +1236,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key) ...@@ -1230,7 +1236,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpython functions cannot return type %s", errmsg("PL/Python functions cannot return type %s",
format_type_be(procStruct->prorettype)))); format_type_be(procStruct->prorettype))));
} }
...@@ -1309,7 +1315,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key) ...@@ -1309,7 +1315,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
/* Disallow pseudotype argument */ /* Disallow pseudotype argument */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpython functions cannot take type %s", errmsg("PL/Python functions cannot accept type %s",
format_type_be(types[i])))); format_type_be(types[i]))));
break; break;
case TYPTYPE_COMPOSITE: case TYPTYPE_COMPOSITE:
...@@ -1403,7 +1409,7 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src) ...@@ -1403,7 +1409,7 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
else else
Py_XDECREF(crv); Py_XDECREF(crv);
PLy_elog(ERROR, "could not compile function \"%s\"", proc->proname); PLy_elog(ERROR, "could not compile PL/Python function \"%s\"", proc->proname);
} }
static char * static char *
...@@ -1480,8 +1486,9 @@ PLy_procedure_delete(PLyProcedure * proc) ...@@ -1480,8 +1486,9 @@ PLy_procedure_delete(PLyProcedure * proc)
PLy_free(proc->argnames); PLy_free(proc->argnames);
} }
/* conversion functions. remember output from python is /*
* input to postgresql, and vis versa. * Conversion functions. Remember output from Python is input to
* PostgreSQL, and vice versa.
*/ */
static void static void
PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc) PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
...@@ -1714,7 +1721,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc) ...@@ -1714,7 +1721,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
dict = PyDict_New(); dict = PyDict_New();
if (dict == NULL) if (dict == NULL)
PLy_elog(ERROR, "could not create tuple dictionary"); PLy_elog(ERROR, "could not create new dictionary");
PG_TRY(); PG_TRY();
{ {
...@@ -1801,7 +1808,7 @@ PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping) ...@@ -1801,7 +1808,7 @@ PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping)
so = PyObject_Str(value); so = PyObject_Str(value);
if (so == NULL) if (so == NULL)
PLy_elog(ERROR, "cannot convert mapping type"); PLy_elog(ERROR, "could not compute string representation of Python object");
valuestr = PyString_AsString(so); valuestr = PyString_AsString(so);
values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
...@@ -1815,9 +1822,9 @@ PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping) ...@@ -1815,9 +1822,9 @@ PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping)
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("no mapping found with key \"%s\"", key), errmsg("key \"%s\" not found in mapping", key),
errhint("to return null in specific column, " errhint("To return null in a column, "
"add value None to map with key named after column"))); "add the value None to the mapping with the key named after the column.")));
Py_XDECREF(value); Py_XDECREF(value);
value = NULL; value = NULL;
...@@ -1860,7 +1867,7 @@ PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence) ...@@ -1860,7 +1867,7 @@ PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence)
if (PySequence_Length(sequence) != desc->natts) if (PySequence_Length(sequence) != desc->natts)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("returned sequence's length must be same as tuple's length"))); errmsg("length of returned sequence did not match number of columns in row")));
if (info->is_rowtype == 2) if (info->is_rowtype == 2)
PLy_output_tuple_funcs(info, desc); PLy_output_tuple_funcs(info, desc);
...@@ -1890,7 +1897,7 @@ PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence) ...@@ -1890,7 +1897,7 @@ PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence)
so = PyObject_Str(value); so = PyObject_Str(value);
if (so == NULL) if (so == NULL)
PLy_elog(ERROR, "cannot convert sequence type"); PLy_elog(ERROR, "could not compute string representation of Python object");
valuestr = PyString_AsString(so); valuestr = PyString_AsString(so);
values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
,valuestr ,valuestr
...@@ -1961,7 +1968,7 @@ PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object) ...@@ -1961,7 +1968,7 @@ PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object)
so = PyObject_Str(value); so = PyObject_Str(value);
if (so == NULL) if (so == NULL)
PLy_elog(ERROR, "cannot convert object type"); PLy_elog(ERROR, "could not compute string representation of Python object");
valuestr = PyString_AsString(so); valuestr = PyString_AsString(so);
values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
,valuestr ,valuestr
...@@ -1974,10 +1981,10 @@ PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object) ...@@ -1974,10 +1981,10 @@ PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object)
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("no attribute named \"%s\"", key), errmsg("attribute \"%s\" does not exist in Python object", key),
errhint("to return null in specific column, " errhint("To return null in a column, "
"let returned object to have attribute named " "let the returned object have an attribute named "
"after column with value None"))); "after column with value None.")));
Py_XDECREF(value); Py_XDECREF(value);
value = NULL; value = NULL;
...@@ -2197,7 +2204,7 @@ PLy_plan_status(PyObject * self, PyObject * args) ...@@ -2197,7 +2204,7 @@ PLy_plan_status(PyObject * self, PyObject * args)
return Py_True; return Py_True;
/* return PyInt_FromLong(self->status); */ /* return PyInt_FromLong(self->status); */
} }
PyErr_SetString(PLy_exc_error, "plan.status() takes no arguments"); PLy_exception_set(PLy_exc_error, "plan.status takes no arguments");
return NULL; return NULL;
} }
...@@ -2327,21 +2334,21 @@ PLy_spi_prepare(PyObject * self, PyObject * args) ...@@ -2327,21 +2334,21 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
/* Can't execute more if we have an unhandled error */ /* Can't execute more if we have an unhandled error */
if (PLy_error_in_progress) if (PLy_error_in_progress)
{ {
PyErr_SetString(PLy_exc_error, "Transaction aborted."); PLy_exception_set(PLy_exc_error, "transaction aborted");
return NULL; return NULL;
} }
if (!PyArg_ParseTuple(args, "s|O", &query, &list)) if (!PyArg_ParseTuple(args, "s|O", &query, &list))
{ {
PyErr_SetString(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"Invalid arguments for plpy.prepare()"); "invalid arguments for plpy.prepare");
return NULL; return NULL;
} }
if (list && (!PySequence_Check(list))) if (list && (!PySequence_Check(list)))
{ {
PyErr_SetString(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"Second argument in plpy.prepare() must be a sequence"); "second argument of plpy.prepare must be a sequence");
return NULL; return NULL;
} }
...@@ -2385,7 +2392,8 @@ PLy_spi_prepare(PyObject * self, PyObject * args) ...@@ -2385,7 +2392,8 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
optr = PySequence_GetItem(list, i); optr = PySequence_GetItem(list, i);
if (!PyString_Check(optr)) if (!PyString_Check(optr))
elog(ERROR, "Type names must be strings."); ereport(ERROR,
(errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
sptr = PyString_AsString(optr); sptr = PyString_AsString(optr);
/******************************************************** /********************************************************
...@@ -2410,7 +2418,9 @@ PLy_spi_prepare(PyObject * self, PyObject * args) ...@@ -2410,7 +2418,9 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
if (typeStruct->typtype != TYPTYPE_COMPOSITE) if (typeStruct->typtype != TYPTYPE_COMPOSITE)
PLy_output_datum_func(&plan->args[i], typeTup); PLy_output_datum_func(&plan->args[i], typeTup);
else else
elog(ERROR, "tuples not handled in plpy.prepare, yet."); ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpy.prepare does not support composite types")));
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
} }
} }
...@@ -2437,10 +2447,10 @@ PLy_spi_prepare(PyObject * self, PyObject * args) ...@@ -2437,10 +2447,10 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
Py_DECREF(plan); Py_DECREF(plan);
Py_XDECREF(optr); Py_XDECREF(optr);
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PyErr_SetString(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"Unknown error in PLy_spi_prepare"); "unrecognized error in PLy_spi_prepare");
/* XXX this oughta be replaced with errcontext mechanism */ /* XXX this oughta be replaced with errcontext mechanism */
PLy_elog(WARNING, "in function %s:", PLy_elog(WARNING, "in PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure)); PLy_procedure_name(PLy_curr_procedure));
return NULL; return NULL;
} }
...@@ -2463,7 +2473,7 @@ PLy_spi_execute(PyObject * self, PyObject * args) ...@@ -2463,7 +2473,7 @@ PLy_spi_execute(PyObject * self, PyObject * args)
/* Can't execute more if we have an unhandled error */ /* Can't execute more if we have an unhandled error */
if (PLy_error_in_progress) if (PLy_error_in_progress)
{ {
PyErr_SetString(PLy_exc_error, "Transaction aborted."); PLy_exception_set(PLy_exc_error, "transaction aborted");
return NULL; return NULL;
} }
...@@ -2476,7 +2486,7 @@ PLy_spi_execute(PyObject * self, PyObject * args) ...@@ -2476,7 +2486,7 @@ PLy_spi_execute(PyObject * self, PyObject * args)
is_PLyPlanObject(plan)) is_PLyPlanObject(plan))
return PLy_spi_execute_plan(plan, list, limit); return PLy_spi_execute_plan(plan, list, limit);
PyErr_SetString(PLy_exc_error, "Expected a query or plan."); PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
return NULL; return NULL;
} }
...@@ -2493,9 +2503,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) ...@@ -2493,9 +2503,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
{ {
if (!PySequence_Check(list) || PyString_Check(list)) if (!PySequence_Check(list) || PyString_Check(list))
{ {
char *msg = "plpy.execute() takes a sequence as its second argument"; PLy_exception_set(PLy_exc_spi_error, "plpy.execute takes a sequence as its second argument");
PyErr_SetString(PLy_exc_spi_error, msg);
return NULL; return NULL;
} }
nargs = PySequence_Length(list); nargs = PySequence_Length(list);
...@@ -2511,11 +2519,11 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) ...@@ -2511,11 +2519,11 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
PyObject *so = PyObject_Str(list); PyObject *so = PyObject_Str(list);
if (!so) if (!so)
PLy_elog(ERROR, "function \"%s\" could not execute plan", PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
PLy_procedure_name(PLy_curr_procedure)); PLy_procedure_name(PLy_curr_procedure));
sv = PyString_AsString(so); sv = PyString_AsString(so);
PLy_exception_set(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"Expected sequence of %d arguments, got %d. %s", "Expected sequence of %d arguments, got %d: %s",
plan->nargs, nargs, sv); plan->nargs, nargs, sv);
Py_DECREF(so); Py_DECREF(so);
...@@ -2538,7 +2546,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) ...@@ -2538,7 +2546,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
{ {
so = PyObject_Str(elem); so = PyObject_Str(elem);
if (!so) if (!so)
PLy_elog(ERROR, "function \"%s\" could not execute plan", PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
PLy_procedure_name(PLy_curr_procedure)); PLy_procedure_name(PLy_curr_procedure));
Py_DECREF(elem); Py_DECREF(elem);
...@@ -2601,10 +2609,10 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) ...@@ -2601,10 +2609,10 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
} }
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PyErr_SetString(PLy_exc_error, PLy_exception_set(PLy_exc_error,
"Unknown error in PLy_spi_execute_plan"); "unrecognized error in PLy_spi_execute_plan");
/* XXX this oughta be replaced with errcontext mechanism */ /* XXX this oughta be replaced with errcontext mechanism */
PLy_elog(WARNING, "in function %s:", PLy_elog(WARNING, "in PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure)); PLy_procedure_name(PLy_curr_procedure));
return NULL; return NULL;
} }
...@@ -2648,10 +2656,10 @@ PLy_spi_execute_query(char *query, long limit) ...@@ -2648,10 +2656,10 @@ PLy_spi_execute_query(char *query, long limit)
PLy_error_in_progress = CopyErrorData(); PLy_error_in_progress = CopyErrorData();
FlushErrorState(); FlushErrorState();
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PyErr_SetString(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"Unknown error in PLy_spi_execute_query"); "unrecognized error in PLy_spi_execute_query");
/* XXX this oughta be replaced with errcontext mechanism */ /* XXX this oughta be replaced with errcontext mechanism */
PLy_elog(WARNING, "in function %s:", PLy_elog(WARNING, "in PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure)); PLy_procedure_name(PLy_curr_procedure));
return NULL; return NULL;
} }
...@@ -2719,8 +2727,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) ...@@ -2719,8 +2727,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
PLy_error_in_progress = CopyErrorData(); PLy_error_in_progress = CopyErrorData();
FlushErrorState(); FlushErrorState();
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PyErr_SetString(PLy_exc_error, PLy_exception_set(PLy_exc_error,
"Unknown error in PLy_spi_execute_fetch_result"); "unrecognized error in PLy_spi_execute_fetch_result");
Py_DECREF(result); Py_DECREF(result);
PLy_typeinfo_dealloc(&args); PLy_typeinfo_dealloc(&args);
return NULL; return NULL;
...@@ -2771,7 +2779,7 @@ PLy_init_interp(void) ...@@ -2771,7 +2779,7 @@ PLy_init_interp(void)
mainmod = PyImport_AddModule("__main__"); mainmod = PyImport_AddModule("__main__");
if (mainmod == NULL || PyErr_Occurred()) if (mainmod == NULL || PyErr_Occurred())
PLy_elog(ERROR, "could not import \"__main__\" module."); PLy_elog(ERROR, "could not import \"__main__\" module");
Py_INCREF(mainmod); Py_INCREF(mainmod);
PLy_interp_globals = PyModule_GetDict(mainmod); PLy_interp_globals = PyModule_GetDict(mainmod);
PLy_interp_safe_globals = PyDict_New(); PLy_interp_safe_globals = PyDict_New();
...@@ -2794,9 +2802,9 @@ PLy_init_plpy(void) ...@@ -2794,9 +2802,9 @@ PLy_init_plpy(void)
* initialize plpy module * initialize plpy module
*/ */
if (PyType_Ready(&PLy_PlanType) < 0) if (PyType_Ready(&PLy_PlanType) < 0)
elog(ERROR, "could not init PLy_PlanType"); elog(ERROR, "could not initialize PLy_PlanType");
if (PyType_Ready(&PLy_ResultType) < 0) if (PyType_Ready(&PLy_ResultType) < 0)
elog(ERROR, "could not init PLy_ResultType"); elog(ERROR, "could not initialize PLy_ResultType");
plpy = Py_InitModule("plpy", PLy_methods); plpy = Py_InitModule("plpy", PLy_methods);
plpy_dict = PyModule_GetDict(plpy); plpy_dict = PyModule_GetDict(plpy);
...@@ -2818,7 +2826,7 @@ PLy_init_plpy(void) ...@@ -2818,7 +2826,7 @@ PLy_init_plpy(void)
plpy_mod = PyImport_AddModule("plpy"); plpy_mod = PyImport_AddModule("plpy");
PyDict_SetItemString(main_dict, "plpy", plpy_mod); PyDict_SetItemString(main_dict, "plpy", plpy_mod);
if (PyErr_Occurred()) if (PyErr_Occurred())
elog(ERROR, "could not init plpy"); elog(ERROR, "could not initialize plpy");
} }
/* the python interface to the elog function /* the python interface to the elog function
...@@ -2880,7 +2888,7 @@ PLy_output(volatile int level, PyObject * self, PyObject * args) ...@@ -2880,7 +2888,7 @@ PLy_output(volatile int level, PyObject * self, PyObject * args)
if (so == NULL || ((sv = PyString_AsString(so)) == NULL)) if (so == NULL || ((sv = PyString_AsString(so)) == NULL))
{ {
level = ERROR; level = ERROR;
sv = "could not parse error message in `plpy.elog'"; sv = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
} }
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
...@@ -2940,7 +2948,7 @@ PLy_exception_set(PyObject * exc, const char *fmt,...) ...@@ -2940,7 +2948,7 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap); vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
va_end(ap); va_end(ap);
PyErr_SetString(exc, buf); PyErr_SetString(exc, buf);
...@@ -2966,7 +2974,7 @@ PLy_elog(int elevel, const char *fmt,...) ...@@ -2966,7 +2974,7 @@ PLy_elog(int elevel, const char *fmt,...)
bool success; bool success;
va_start(ap, fmt); va_start(ap, fmt);
success = appendStringInfoVA(&emsg, fmt, ap); success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
va_end(ap); va_end(ap);
if (success) if (success)
break; break;
...@@ -2976,7 +2984,7 @@ PLy_elog(int elevel, const char *fmt,...) ...@@ -2976,7 +2984,7 @@ PLy_elog(int elevel, const char *fmt,...)
PG_TRY(); PG_TRY();
{ {
ereport(elevel, ereport(elevel,
(errmsg("plpython: %s", emsg.data), (errmsg("PL/Python: %s", emsg.data),
(xmsg) ? errdetail("%s", xmsg) : 0)); (xmsg) ? errdetail("%s", xmsg) : 0));
} }
PG_CATCH(); PG_CATCH();
...@@ -3026,7 +3034,7 @@ PLy_traceback(int *xlevel) ...@@ -3026,7 +3034,7 @@ PLy_traceback(int *xlevel)
if (v && ((vob = PyObject_Str(v)) != NULL)) if (v && ((vob = PyObject_Str(v)) != NULL))
vstr = PyString_AsString(vob); vstr = PyString_AsString(vob);
else else
vstr = "Unknown"; vstr = "unknown";
/* /*
* I'm not sure what to do if eob is NULL here -- we can't call PLy_elog * I'm not sure what to do if eob is NULL here -- we can't call PLy_elog
...@@ -3034,7 +3042,7 @@ PLy_traceback(int *xlevel) ...@@ -3034,7 +3042,7 @@ PLy_traceback(int *xlevel)
* recursion. I'm not even sure if eob could be NULL here -- would an * recursion. I'm not even sure if eob could be NULL here -- would an
* Assert() be more appropriate? * Assert() be more appropriate?
*/ */
estr = eob ? PyString_AsString(eob) : "Unknown Exception"; estr = eob ? PyString_AsString(eob) : "unrecognized exception";
initStringInfo(&xstr); initStringInfo(&xstr);
appendStringInfo(&xstr, "%s: %s", estr, vstr); appendStringInfo(&xstr, "%s: %s", estr, vstr);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment