plpython-table-functions-incremental.patch
text/x-patch
Filename: plpython-table-functions-incremental.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/pl/plpython/expected/plpython_composite.out | 0 | 0 |
| src/pl/plpython/plpython.c | 0 | 0 |
| src/pl/plpython/sql/plpython_composite.sql | 0 | 0 |
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index 70a4571..1576588 100644
*** a/src/pl/plpython/expected/plpython_composite.out
--- b/src/pl/plpython/expected/plpython_composite.out
*************** SELECT * FROM composite_types_table();
*** 273,275 ****
--- 273,309 ----
ERROR: PL/Python functions cannot return type table_record[]
DETAIL: PL/Python does not support conversion to arrays of row types.
CONTEXT: PL/Python function "composite_types_table"
+ -- check what happens if the output record descriptor changes
+ CREATE FUNCTION return_record(t text) RETURNS record AS $$
+ return {'t': t, 'val': 10}
+ $$ LANGUAGE plpythonu;
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+ t | val
+ -----+-----
+ abc | 10
+ (1 row)
+
+ SELECT * FROM return_record('abc') AS r(t text, val bigint);
+ t | val
+ -----+-----
+ abc | 10
+ (1 row)
+
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+ t | val
+ -----+-----
+ abc | 10
+ (1 row)
+
+ SELECT * FROM return_record('abc') AS r(t varchar(30), val integer);
+ t | val
+ -----+-----
+ abc | 10
+ (1 row)
+
+ SELECT * FROM return_record('abc') AS r(t varchar(100), val integer);
+ t | val
+ -----+-----
+ abc | 10
+ (1 row)
+
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index cc7a007..1f93c7b 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** typedef int Py_ssize_t;
*** 100,105 ****
--- 100,106 ----
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
+ #include "parser/parse_coerce.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
*************** static void PLy_input_datum_func(PLyType
*** 334,339 ****
--- 335,341 ----
static void PLy_input_datum_func2(PLyDatumToOb *, Oid, HeapTuple);
static void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc);
static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);
+ static void PLy_output_record_funcs(PLyTypeInfo *, TupleDesc);
/* conversion functions */
static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
*************** PLy_function_build_args(FunctionCallInfo
*** 1262,1269 ****
}
/* Set up output conversion for functions returning RECORD */
! if (proc->result.out.d.typoid == RECORDOID &&
! proc->result.is_rowtype > 1)
{
TupleDesc desc;
--- 1264,1270 ----
}
/* Set up output conversion for functions returning RECORD */
! if (proc->result.out.d.typoid == RECORDOID)
{
TupleDesc desc;
*************** PLy_function_build_args(FunctionCallInfo
*** 1275,1288 ****
errmsg("function returning record called in context "
"that cannot accept type record")));
}
! /* bless the record to make it known to the typcache lookup code */
! BlessTupleDesc(desc);
! /* save the freshly generated typmod */
! proc->result.out.d.typmod = desc->tdtypmod;
! /* proceed with normal I/O function caching */
! PLy_output_tuple_funcs(&(proc->result), desc);
! /* it should change is_rowtype to 1, so we won't go through this again */
! Assert(proc->result.is_rowtype == 1);
}
}
PG_CATCH();
--- 1276,1284 ----
errmsg("function returning record called in context "
"that cannot accept type record")));
}
!
! /* cache the output conversion functions */
! PLy_output_record_funcs(&(proc->result), desc);
}
}
PG_CATCH();
*************** PLy_input_tuple_funcs(PLyTypeInfo *arg,
*** 1769,1774 ****
--- 1765,1809 ----
}
static void
+ PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc)
+ {
+ /*
+ * If the output record functions are already set, we just have to check
+ * if the record descriptor has not changed
+ */
+ bool can_skip = false;
+
+ if (arg->is_rowtype == 1)
+ {
+ int i;
+
+ /* the functions are already set, check the attributes */
+ Assert(arg->out.r.natts == desc->natts);
+ can_skip = true;
+
+ for (i = 0; i < arg->out.r.natts; i++)
+ {
+ if (!IsBinaryCoercible(arg->out.r.atts[i].typoid,
+ desc->attrs[i]->atttypid))
+ can_skip = false;
+ }
+ }
+
+ if (can_skip)
+ return;
+
+ /* bless the record to make it known to the typcache lookup code */
+ BlessTupleDesc(desc);
+ /* save the freshly generated typmod */
+ arg->out.d.typmod = desc->tdtypmod;
+ /* proceed with normal I/O function caching */
+ PLy_output_tuple_funcs(arg, desc);
+ /* it should change is_rowtype to 1, so we won't go through this again
+ * unless the the output record description changes */
+ Assert(arg->is_rowtype == 1);
+ }
+
+ static void
PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
{
int i;
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 1330e26..db4bd73 100644
*** a/src/pl/plpython/sql/plpython_composite.sql
--- b/src/pl/plpython/sql/plpython_composite.sql
*************** yield {'tab': [['first', 1], ['second',
*** 140,142 ****
--- 140,153 ----
$$ LANGUAGE plpythonu;
SELECT * FROM composite_types_table();
+
+ -- check what happens if the output record descriptor changes
+ CREATE FUNCTION return_record(t text) RETURNS record AS $$
+ return {'t': t, 'val': 10}
+ $$ LANGUAGE plpythonu;
+
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+ SELECT * FROM return_record('abc') AS r(t text, val bigint);
+ SELECT * FROM return_record('abc') AS r(t text, val integer);
+ SELECT * FROM return_record('abc') AS r(t varchar(30), val integer);
+ SELECT * FROM return_record('abc') AS r(t varchar(100), val integer);