summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPeter Eisentraut2011-02-19 14:52:24 +0000
committerPeter Eisentraut2011-02-19 14:56:02 +0000
commitb05186f8a403c7dcd1bd974948273f8c00edb127 (patch)
treed1c3c0fd1aa8f09fc55225d2f3508aec8bfc3b35 /src
parent964b46d00ec2222c1273bec3ead369f3e167d482 (diff)
Invalidate PL/Python functions with composite type argument when the
type changes. The invalidation will cause the type information to be refetched, and everything will work. Jan UrbaƄski, reviewed by Alex Hunsaker
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpython/expected/plpython_types.out52
-rw-r--r--src/pl/plpython/expected/plpython_types_3.out52
-rw-r--r--src/pl/plpython/plpython.c78
-rw-r--r--src/pl/plpython/sql/plpython_types.sql43
4 files changed, 223 insertions, 2 deletions
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index e74a4009d46..d5f2c703fad 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -603,6 +603,58 @@ SELECT * FROM test_type_conversion_array_error();
ERROR: return value of function with array return type is not a Python sequence
CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_error"
+---
+--- Composite types
+---
+CREATE TABLE employee (
+ name text,
+ basesalary integer,
+ bonus integer
+);
+INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
+CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
+return e['basesalary'] + e['bonus']
+$$ LANGUAGE plpythonu;
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+ name | test_composite_table_input
+------+----------------------------
+ John | 110
+ Mary | 210
+(2 rows)
+
+ALTER TABLE employee DROP bonus;
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+ERROR: KeyError: 'bonus'
+CONTEXT: PL/Python function "test_composite_table_input"
+ALTER TABLE employee ADD bonus integer;
+UPDATE employee SET bonus = 10;
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+ name | test_composite_table_input
+------+----------------------------
+ John | 110
+ Mary | 210
+(2 rows)
+
+CREATE TYPE named_pair AS (
+ i integer,
+ j integer
+);
+CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
+return sum(p.values())
+$$ LANGUAGE plpythonu;
+SELECT test_composite_type_input(row(1, 2));
+ test_composite_type_input
+---------------------------
+ 3
+(1 row)
+
+ALTER TYPE named_pair RENAME TO named_pair_2;
+SELECT test_composite_type_input(row(1, 2));
+ test_composite_type_input
+---------------------------
+ 3
+(1 row)
+
--
-- Prepared statements
--
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
index 577c1fff4e9..ca81b08a052 100644
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ b/src/pl/plpython/expected/plpython_types_3.out
@@ -603,6 +603,58 @@ SELECT * FROM test_type_conversion_array_error();
ERROR: return value of function with array return type is not a Python sequence
CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_error"
+---
+--- Composite types
+---
+CREATE TABLE employee (
+ name text,
+ basesalary integer,
+ bonus integer
+);
+INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
+CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
+return e['basesalary'] + e['bonus']
+$$ LANGUAGE plpython3u;
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+ name | test_composite_table_input
+------+----------------------------
+ John | 110
+ Mary | 210
+(2 rows)
+
+ALTER TABLE employee DROP bonus;
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+ERROR: KeyError: 'bonus'
+CONTEXT: PL/Python function "test_composite_table_input"
+ALTER TABLE employee ADD bonus integer;
+UPDATE employee SET bonus = 10;
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+ name | test_composite_table_input
+------+----------------------------
+ John | 110
+ Mary | 210
+(2 rows)
+
+CREATE TYPE named_pair AS (
+ i integer,
+ j integer
+);
+CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
+return sum(p.values())
+$$ LANGUAGE plpython3u;
+SELECT test_composite_type_input(row(1, 2));
+ test_composite_type_input
+---------------------------
+ 3
+(1 row)
+
+ALTER TYPE named_pair RENAME TO named_pair_2;
+SELECT test_composite_type_input(row(1, 2));
+ test_composite_type_input
+---------------------------
+ 3
+(1 row)
+
--
-- Prepared statements
--
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 82baf940e31..4e54d3e8b0e 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -101,6 +101,7 @@ typedef int Py_ssize_t;
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "tcop/tcopprot.h"
+#include "access/transam.h"
#include "access/xact.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -195,6 +196,10 @@ typedef struct PLyTypeInfo
* datatype; 1 = rowtype; 2 = rowtype, but I/O functions not set up yet
*/
int is_rowtype;
+ /* used to check if the type has been modified */
+ Oid typ_relid;
+ TransactionId typrel_xmin;
+ ItemPointerData typrel_tid;
} PLyTypeInfo;
@@ -1335,11 +1340,50 @@ PLy_function_delete_args(PLyProcedure *proc)
static bool
PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
{
+ int i;
+ bool valid;
+
Assert(proc != NULL);
/* If the pg_proc tuple has changed, it's not valid */
- return (proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
- ItemPointerEquals(&proc->fn_tid, &procTup->t_self));
+ if (!(proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
+ return false;
+
+ valid = true;
+ /* If there are composite input arguments, they might have changed */
+ for (i = 0; i < proc->nargs; i++)
+ {
+ Oid relid;
+ HeapTuple relTup;
+
+ /* Short-circuit on first changed argument */
+ if (!valid)
+ break;
+
+ /* Only check input arguments that are composite */
+ if (proc->args[i].is_rowtype != 1)
+ continue;
+
+ Assert(OidIsValid(proc->args[i].typ_relid));
+ Assert(TransactionIdIsValid(proc->args[i].typrel_xmin));
+ Assert(ItemPointerIsValid(&proc->args[i].typrel_tid));
+
+ /* Get the pg_class tuple for the argument type */
+ relid = proc->args[i].typ_relid;
+ relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ /* If it has changed, the function is not valid */
+ if (!(proc->args[i].typrel_xmin == HeapTupleHeaderGetXmin(relTup->t_data) &&
+ ItemPointerEquals(&proc->args[i].typrel_tid, &relTup->t_self)))
+ valid = false;
+
+ ReleaseSysCache(relTup);
+ }
+
+ return valid;
}
@@ -1747,6 +1791,33 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
}
+ /* Can this be an unnamed tuple? If not, then an Assert would be enough */
+ if (desc->tdtypmod != -1)
+ elog(ERROR, "received unnamed record type as input");
+
+ Assert(OidIsValid(desc->tdtypeid));
+
+ /*
+ * RECORDOID means we got called to create input functions for a tuple
+ * fetched by plpy.execute or for an anonymous record type
+ */
+ if (desc->tdtypeid != RECORDOID && !TransactionIdIsValid(arg->typrel_xmin))
+ {
+ HeapTuple relTup;
+
+ /* Get the pg_class tuple corresponding to the type of the input */
+ arg->typ_relid = typeidTypeRelid(desc->tdtypeid);
+ relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid));
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
+
+ /* Extract the XMIN value to later use it in PLy_procedure_valid */
+ arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data);
+ arg->typrel_tid = relTup->t_self;
+
+ ReleaseSysCache(relTup);
+ }
+
for (i = 0; i < desc->natts; i++)
{
HeapTuple typeTup;
@@ -1951,6 +2022,9 @@ PLy_typeinfo_init(PLyTypeInfo *arg)
arg->in.r.natts = arg->out.r.natts = 0;
arg->in.r.atts = NULL;
arg->out.r.atts = NULL;
+ arg->typ_relid = InvalidOid;
+ arg->typrel_xmin = InvalidTransactionId;
+ ItemPointerSetInvalid(&arg->typrel_tid);
}
static void
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 2afc2ffcc11..82cd9d42680 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -279,6 +279,49 @@ $$ LANGUAGE plpythonu;
SELECT * FROM test_type_conversion_array_error();
+---
+--- Composite types
+---
+
+CREATE TABLE employee (
+ name text,
+ basesalary integer,
+ bonus integer
+);
+
+INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
+
+CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
+return e['basesalary'] + e['bonus']
+$$ LANGUAGE plpythonu;
+
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+
+ALTER TABLE employee DROP bonus;
+
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+
+ALTER TABLE employee ADD bonus integer;
+UPDATE employee SET bonus = 10;
+
+SELECT name, test_composite_table_input(employee.*) FROM employee;
+
+CREATE TYPE named_pair AS (
+ i integer,
+ j integer
+);
+
+CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
+return sum(p.values())
+$$ LANGUAGE plpythonu;
+
+SELECT test_composite_type_input(row(1, 2));
+
+ALTER TYPE named_pair RENAME TO named_pair_2;
+
+SELECT test_composite_type_input(row(1, 2));
+
+
--
-- Prepared statements
--