Commit 510e1b8e authored by Heikki Linnakangas's avatar Heikki Linnakangas

Give a hint, when [] is incorrectly used for a composite type in array.

That used to be accepted, so let's try to give a hint to users on why
their PL/python functions no longer work.

Reviewed by Pavel Stehule.

Discussion: <CAH38_tmbqwaUyKs9yagyRra=SMaT45FPBxk1pmTYcM0TyXGG7Q@mail.gmail.com>
parent 94aceed3
...@@ -579,3 +579,16 @@ SELECT * FROM composite_type_as_list(); ...@@ -579,3 +579,16 @@ SELECT * FROM composite_type_as_list();
{{"(first,1)","(second,1)"},{"(first,2)","(second,2)"},{"(first,3)","(second,3)"}} {{"(first,1)","(second,1)"},{"(first,2)","(second,2)"},{"(first,3)","(second,3)"}}
(1 row) (1 row)
-- Starting with PostgreSQL 10, a composite type in an array cannot be
-- represented as a Python list, because it's ambiguous with multi-dimensional
-- arrays. So this throws an error now. The error should contain a useful hint
-- on the issue.
CREATE FUNCTION composite_type_as_list_broken() RETURNS type_record[] AS $$
return [['first', 1]];
$$ LANGUAGE plpythonu;
SELECT * FROM composite_type_as_list_broken();
ERROR: malformed record literal: "first"
DETAIL: Missing left parenthesis.
HINT: To return a composite type in an array, return the composite type as a Python tuple, e.g. "[('foo')]"
CONTEXT: while creating return value
PL/Python function "composite_type_as_list_broken"
...@@ -240,7 +240,8 @@ PLy_cursor_plan(PyObject *ob, PyObject *args) ...@@ -240,7 +240,8 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
plan->values[j] = plan->values[j] =
plan->args[j].out.d.func(&(plan->args[j].out.d), plan->args[j].out.d.func(&(plan->args[j].out.d),
-1, -1,
elem); elem,
false);
} }
PG_CATCH(); PG_CATCH();
{ {
......
...@@ -245,7 +245,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc) ...@@ -245,7 +245,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
proc->result.out.d.typmod); proc->result.out.d.typmod);
rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv); rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv, false);
fcinfo->isnull = (rv == (Datum) NULL); fcinfo->isnull = (rv == (Datum) NULL);
ReleaseTupleDesc(desc); ReleaseTupleDesc(desc);
...@@ -253,7 +253,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc) ...@@ -253,7 +253,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
else else
{ {
fcinfo->isnull = false; fcinfo->isnull = false;
rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv); rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv, false);
} }
} }
PG_CATCH(); PG_CATCH();
...@@ -984,7 +984,8 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, ...@@ -984,7 +984,8 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
modvalues[i] = (att->func) (att, modvalues[i] = (att->func) (att,
tupdesc->attrs[atti]->atttypmod, tupdesc->attrs[atti]->atttypmod,
plval); plval,
false);
modnulls[i] = ' '; modnulls[i] = ' ';
} }
else else
......
...@@ -264,7 +264,8 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) ...@@ -264,7 +264,8 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
plan->values[j] = plan->values[j] =
plan->args[j].out.d.func(&(plan->args[j].out.d), plan->args[j].out.d.func(&(plan->args[j].out.d),
-1, -1,
elem); elem,
false);
} }
PG_CATCH(); PG_CATCH();
{ {
......
This diff is collapsed.
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
#include "fmgr.h" #include "fmgr.h"
#include "storage/itemptr.h" #include "storage/itemptr.h"
/*
* Conversion from PostgreSQL Datum to a Python object.
*/
struct PLyDatumToOb; struct PLyDatumToOb;
typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb *, Datum); typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb *arg, Datum val);
typedef struct PLyDatumToOb typedef struct PLyDatumToOb
{ {
...@@ -39,11 +42,15 @@ typedef union PLyTypeInput ...@@ -39,11 +42,15 @@ typedef union PLyTypeInput
PLyTupleToOb r; PLyTupleToOb r;
} PLyTypeInput; } PLyTypeInput;
/* convert PyObject to a Postgresql Datum or tuple. /*
* output from Python * Conversion from Python object to a Postgresql Datum.
*
* The 'inarray' argument to the conversion function is true, if the
* converted value was in an array (Python list). It is used to give a
* better error message in some cases.
*/ */
struct PLyObToDatum; struct PLyObToDatum;
typedef Datum (*PLyObToDatumFunc) (struct PLyObToDatum *, int32, PyObject *); typedef Datum (*PLyObToDatumFunc) (struct PLyObToDatum *arg, int32 typmod, PyObject *val, bool inarray);
typedef struct PLyObToDatum typedef struct PLyObToDatum
{ {
...@@ -104,7 +111,7 @@ extern void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc); ...@@ -104,7 +111,7 @@ extern void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);
extern void PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc); extern void PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc);
/* conversion from Python objects to composite Datums */ /* conversion from Python objects to composite Datums */
extern Datum PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv); extern Datum PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv, bool isarray);
/* conversion from heap tuples to Python dictionaries */ /* conversion from heap tuples to Python dictionaries */
extern PyObject *PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc); extern PyObject *PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc);
......
...@@ -213,3 +213,12 @@ CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$ ...@@ -213,3 +213,12 @@ CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$
return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]]; return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT * FROM composite_type_as_list(); SELECT * FROM composite_type_as_list();
-- Starting with PostgreSQL 10, a composite type in an array cannot be
-- represented as a Python list, because it's ambiguous with multi-dimensional
-- arrays. So this throws an error now. The error should contain a useful hint
-- on the issue.
CREATE FUNCTION composite_type_as_list_broken() RETURNS type_record[] AS $$
return [['first', 1]];
$$ LANGUAGE plpythonu;
SELECT * FROM composite_type_as_list_broken();
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