On 23/12/10 14:50, Jan Urbański wrote:
> Here's a patch implementing properly invalidating functions that have
> composite type arguments after the type changes, as mentioned in
> http://archives.postgresql.org/pgsql-hackers/2010-12/msg01991.php. It's
> an incremental patch on top of the plpython-refactor patch sent eariler.
Updated to master.
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 982005b..cf7b7de 100644
*** a/src/pl/plpython/expected/plpython_types.out
--- b/src/pl/plpython/expected/plpython_types.out
*************** SELECT * FROM test_type_conversion_array
*** 599,604 ****
--- 599,656 ----
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 eeb23b7..e10b060 100644
*** a/src/pl/plpython/expected/plpython_types_3.out
--- b/src/pl/plpython/expected/plpython_types_3.out
*************** SELECT * FROM test_type_conversion_array
*** 599,604 ****
--- 599,656 ----
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 aafe556..54605fc 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** typedef int Py_ssize_t;
*** 101,106 ****
--- 101,107 ----
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "tcop/tcopprot.h"
+ #include "access/transam.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
*************** typedef struct PLyTypeInfo
*** 193,202 ****
* is_rowtype can be: -1 = not known yet (initial state); 0 = scalar
* datatype; 1 = rowtype; 2 = rowtype, but I/O functions not set up yet
*/
! int is_rowtype;
} PLyTypeInfo;
-
/* cached procedure data */
typedef struct PLyProcedure
{
--- 194,206 ----
* is_rowtype can be: -1 = not known yet (initial state); 0 = scalar
* 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;
/* cached procedure data */
typedef struct PLyProcedure
{
*************** PLy_function_delete_args(PLyProcedure *p
*** 1290,1300 ****
static bool
PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
{
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));
}
--- 1294,1343 ----
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 */
! 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;
}
*************** PLy_input_tuple_funcs(PLyTypeInfo *arg,
*** 1702,1707 ****
--- 1745,1776 ----
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;
*************** PLy_typeinfo_init(PLyTypeInfo *arg)
*** 1906,1911 ****
--- 1975,1983 ----
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 2afc2ff..87d7553 100644
*** a/src/pl/plpython/sql/plpython_types.sql
--- b/src/pl/plpython/sql/plpython_types.sql
*************** $$ LANGUAGE plpythonu;
*** 278,283 ****
--- 278,323 ----
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
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers