Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b05186f

Browse files
committed
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
1 parent 964b46d commit b05186f

File tree

4 files changed

+223
-2
lines changed

4 files changed

+223
-2
lines changed

src/pl/plpython/expected/plpython_types.out

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,58 @@ SELECT * FROM test_type_conversion_array_error();
603603
ERROR: return value of function with array return type is not a Python sequence
604604
CONTEXT: while creating return value
605605
PL/Python function "test_type_conversion_array_error"
606+
---
607+
--- Composite types
608+
---
609+
CREATE TABLE employee (
610+
name text,
611+
basesalary integer,
612+
bonus integer
613+
);
614+
INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
615+
CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
616+
return e['basesalary'] + e['bonus']
617+
$$ LANGUAGE plpythonu;
618+
SELECT name, test_composite_table_input(employee.*) FROM employee;
619+
name | test_composite_table_input
620+
------+----------------------------
621+
John | 110
622+
Mary | 210
623+
(2 rows)
624+
625+
ALTER TABLE employee DROP bonus;
626+
SELECT name, test_composite_table_input(employee.*) FROM employee;
627+
ERROR: KeyError: 'bonus'
628+
CONTEXT: PL/Python function "test_composite_table_input"
629+
ALTER TABLE employee ADD bonus integer;
630+
UPDATE employee SET bonus = 10;
631+
SELECT name, test_composite_table_input(employee.*) FROM employee;
632+
name | test_composite_table_input
633+
------+----------------------------
634+
John | 110
635+
Mary | 210
636+
(2 rows)
637+
638+
CREATE TYPE named_pair AS (
639+
i integer,
640+
j integer
641+
);
642+
CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
643+
return sum(p.values())
644+
$$ LANGUAGE plpythonu;
645+
SELECT test_composite_type_input(row(1, 2));
646+
test_composite_type_input
647+
---------------------------
648+
3
649+
(1 row)
650+
651+
ALTER TYPE named_pair RENAME TO named_pair_2;
652+
SELECT test_composite_type_input(row(1, 2));
653+
test_composite_type_input
654+
---------------------------
655+
3
656+
(1 row)
657+
606658
--
607659
-- Prepared statements
608660
--

src/pl/plpython/expected/plpython_types_3.out

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,58 @@ SELECT * FROM test_type_conversion_array_error();
603603
ERROR: return value of function with array return type is not a Python sequence
604604
CONTEXT: while creating return value
605605
PL/Python function "test_type_conversion_array_error"
606+
---
607+
--- Composite types
608+
---
609+
CREATE TABLE employee (
610+
name text,
611+
basesalary integer,
612+
bonus integer
613+
);
614+
INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
615+
CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
616+
return e['basesalary'] + e['bonus']
617+
$$ LANGUAGE plpython3u;
618+
SELECT name, test_composite_table_input(employee.*) FROM employee;
619+
name | test_composite_table_input
620+
------+----------------------------
621+
John | 110
622+
Mary | 210
623+
(2 rows)
624+
625+
ALTER TABLE employee DROP bonus;
626+
SELECT name, test_composite_table_input(employee.*) FROM employee;
627+
ERROR: KeyError: 'bonus'
628+
CONTEXT: PL/Python function "test_composite_table_input"
629+
ALTER TABLE employee ADD bonus integer;
630+
UPDATE employee SET bonus = 10;
631+
SELECT name, test_composite_table_input(employee.*) FROM employee;
632+
name | test_composite_table_input
633+
------+----------------------------
634+
John | 110
635+
Mary | 210
636+
(2 rows)
637+
638+
CREATE TYPE named_pair AS (
639+
i integer,
640+
j integer
641+
);
642+
CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
643+
return sum(p.values())
644+
$$ LANGUAGE plpython3u;
645+
SELECT test_composite_type_input(row(1, 2));
646+
test_composite_type_input
647+
---------------------------
648+
3
649+
(1 row)
650+
651+
ALTER TYPE named_pair RENAME TO named_pair_2;
652+
SELECT test_composite_type_input(row(1, 2));
653+
test_composite_type_input
654+
---------------------------
655+
3
656+
(1 row)
657+
606658
--
607659
-- Prepared statements
608660
--

src/pl/plpython/plpython.c

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ typedef int Py_ssize_t;
101101
#include "nodes/makefuncs.h"
102102
#include "parser/parse_type.h"
103103
#include "tcop/tcopprot.h"
104+
#include "access/transam.h"
104105
#include "access/xact.h"
105106
#include "utils/builtins.h"
106107
#include "utils/hsearch.h"
@@ -195,6 +196,10 @@ typedef struct PLyTypeInfo
195196
* datatype; 1 = rowtype; 2 = rowtype, but I/O functions not set up yet
196197
*/
197198
int is_rowtype;
199+
/* used to check if the type has been modified */
200+
Oid typ_relid;
201+
TransactionId typrel_xmin;
202+
ItemPointerData typrel_tid;
198203
} PLyTypeInfo;
199204

200205

@@ -1335,11 +1340,50 @@ PLy_function_delete_args(PLyProcedure *proc)
13351340
static bool
13361341
PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
13371342
{
1343+
int i;
1344+
bool valid;
1345+
13381346
Assert(proc != NULL);
13391347

13401348
/* If the pg_proc tuple has changed, it's not valid */
1341-
return (proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
1342-
ItemPointerEquals(&proc->fn_tid, &procTup->t_self));
1349+
if (!(proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
1350+
ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
1351+
return false;
1352+
1353+
valid = true;
1354+
/* If there are composite input arguments, they might have changed */
1355+
for (i = 0; i < proc->nargs; i++)
1356+
{
1357+
Oid relid;
1358+
HeapTuple relTup;
1359+
1360+
/* Short-circuit on first changed argument */
1361+
if (!valid)
1362+
break;
1363+
1364+
/* Only check input arguments that are composite */
1365+
if (proc->args[i].is_rowtype != 1)
1366+
continue;
1367+
1368+
Assert(OidIsValid(proc->args[i].typ_relid));
1369+
Assert(TransactionIdIsValid(proc->args[i].typrel_xmin));
1370+
Assert(ItemPointerIsValid(&proc->args[i].typrel_tid));
1371+
1372+
/* Get the pg_class tuple for the argument type */
1373+
relid = proc->args[i].typ_relid;
1374+
relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1375+
if (!HeapTupleIsValid(relTup))
1376+
elog(ERROR, "cache lookup failed for relation %u", relid);
1377+
1378+
/* If it has changed, the function is not valid */
1379+
if (!(proc->args[i].typrel_xmin == HeapTupleHeaderGetXmin(relTup->t_data) &&
1380+
ItemPointerEquals(&proc->args[i].typrel_tid, &relTup->t_self)))
1381+
valid = false;
1382+
1383+
ReleaseSysCache(relTup);
1384+
}
1385+
1386+
return valid;
13431387
}
13441388

13451389

@@ -1747,6 +1791,33 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
17471791
arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
17481792
}
17491793

1794+
/* Can this be an unnamed tuple? If not, then an Assert would be enough */
1795+
if (desc->tdtypmod != -1)
1796+
elog(ERROR, "received unnamed record type as input");
1797+
1798+
Assert(OidIsValid(desc->tdtypeid));
1799+
1800+
/*
1801+
* RECORDOID means we got called to create input functions for a tuple
1802+
* fetched by plpy.execute or for an anonymous record type
1803+
*/
1804+
if (desc->tdtypeid != RECORDOID && !TransactionIdIsValid(arg->typrel_xmin))
1805+
{
1806+
HeapTuple relTup;
1807+
1808+
/* Get the pg_class tuple corresponding to the type of the input */
1809+
arg->typ_relid = typeidTypeRelid(desc->tdtypeid);
1810+
relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid));
1811+
if (!HeapTupleIsValid(relTup))
1812+
elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
1813+
1814+
/* Extract the XMIN value to later use it in PLy_procedure_valid */
1815+
arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data);
1816+
arg->typrel_tid = relTup->t_self;
1817+
1818+
ReleaseSysCache(relTup);
1819+
}
1820+
17501821
for (i = 0; i < desc->natts; i++)
17511822
{
17521823
HeapTuple typeTup;
@@ -1951,6 +2022,9 @@ PLy_typeinfo_init(PLyTypeInfo *arg)
19512022
arg->in.r.natts = arg->out.r.natts = 0;
19522023
arg->in.r.atts = NULL;
19532024
arg->out.r.atts = NULL;
2025+
arg->typ_relid = InvalidOid;
2026+
arg->typrel_xmin = InvalidTransactionId;
2027+
ItemPointerSetInvalid(&arg->typrel_tid);
19542028
}
19552029

19562030
static void

src/pl/plpython/sql/plpython_types.sql

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,49 @@ $$ LANGUAGE plpythonu;
279279
SELECT * FROM test_type_conversion_array_error();
280280

281281

282+
---
283+
--- Composite types
284+
---
285+
286+
CREATE TABLE employee (
287+
name text,
288+
basesalary integer,
289+
bonus integer
290+
);
291+
292+
INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
293+
294+
CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
295+
return e['basesalary'] + e['bonus']
296+
$$ LANGUAGE plpythonu;
297+
298+
SELECT name, test_composite_table_input(employee.*) FROM employee;
299+
300+
ALTER TABLE employee DROP bonus;
301+
302+
SELECT name, test_composite_table_input(employee.*) FROM employee;
303+
304+
ALTER TABLE employee ADD bonus integer;
305+
UPDATE employee SET bonus = 10;
306+
307+
SELECT name, test_composite_table_input(employee.*) FROM employee;
308+
309+
CREATE TYPE named_pair AS (
310+
i integer,
311+
j integer
312+
);
313+
314+
CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
315+
return sum(p.values())
316+
$$ LANGUAGE plpythonu;
317+
318+
SELECT test_composite_type_input(row(1, 2));
319+
320+
ALTER TYPE named_pair RENAME TO named_pair_2;
321+
322+
SELECT test_composite_type_input(row(1, 2));
323+
324+
282325
--
283326
-- Prepared statements
284327
--

0 commit comments

Comments
 (0)