Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf26474e

Browse files
committed
Fix two issues in plpython's handling of composite results.
Dropped columns within a composite type were not handled correctly.Also, we did not check for whether a composite result type had changedsince we cached the information about it.Jan Urbański, per a bug report from Jean-Baptiste Quenot
1 parent22a55b3 commitf26474e

File tree

3 files changed

+120
-30
lines changed

3 files changed

+120
-30
lines changed

‎src/pl/plpython/expected/plpython_record.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,27 @@ SELECT * FROM test_inout_params('test_in');
308308
test_in_inout
309309
(1 row)
310310

311+
-- try changing the return types and call functions again
312+
ALTER TABLE table_record DROP COLUMN first;
313+
ALTER TABLE table_record DROP COLUMN second;
314+
ALTER TABLE table_record ADD COLUMN first text;
315+
ALTER TABLE table_record ADD COLUMN second int4;
316+
SELECT * FROM test_table_record_as('obj', 'one', 1, false);
317+
first | second
318+
-------+--------
319+
one | 1
320+
(1 row)
321+
322+
ALTER TYPE type_record DROP ATTRIBUTE first;
323+
ALTER TYPE type_record DROP ATTRIBUTE second;
324+
ALTER TYPE type_record ADD ATTRIBUTE first text;
325+
ALTER TYPE type_record ADD ATTRIBUTE second int4;
326+
SELECT * FROM test_type_record_as('obj', 'one', 1, false);
327+
first | second
328+
-------+--------
329+
one | 1
330+
(1 row)
331+
311332
-- errors cases
312333
CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
313334
return { 'first': 'first' }

‎src/pl/plpython/plpython.c

Lines changed: 84 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,44 @@ PLy_function_delete_args(PLyProcedure *proc)
14491449
PyDict_DelItemString(proc->globals,proc->argnames[i]);
14501450
}
14511451

1452+
/*
1453+
* Check if our cached information about a datatype is still valid
1454+
*/
1455+
staticbool
1456+
PLy_procedure_argument_valid(PLyTypeInfo*arg)
1457+
{
1458+
HeapTuplerelTup;
1459+
boolvalid;
1460+
1461+
/* Nothing to cache unless type is composite */
1462+
if (arg->is_rowtype!=1)
1463+
return true;
1464+
1465+
/*
1466+
* Zero typ_relid means that we got called on an output argument of a
1467+
* function returning a unnamed record type; the info for it can't change.
1468+
*/
1469+
if (!OidIsValid(arg->typ_relid))
1470+
return true;
1471+
1472+
/* Else we should have some cached data */
1473+
Assert(TransactionIdIsValid(arg->typrel_xmin));
1474+
Assert(ItemPointerIsValid(&arg->typrel_tid));
1475+
1476+
/* Get the pg_class tuple for the data type */
1477+
relTup=SearchSysCache1(RELOID,ObjectIdGetDatum(arg->typ_relid));
1478+
if (!HeapTupleIsValid(relTup))
1479+
elog(ERROR,"cache lookup failed for relation %u",arg->typ_relid);
1480+
1481+
/* If it has changed, the cached data is not valid */
1482+
valid= (arg->typrel_xmin==HeapTupleHeaderGetXmin(relTup->t_data)&&
1483+
ItemPointerEquals(&arg->typrel_tid,&relTup->t_self));
1484+
1485+
ReleaseSysCache(relTup);
1486+
1487+
returnvalid;
1488+
}
1489+
14521490
/*
14531491
* Decide whether a cached PLyProcedure struct is still valid
14541492
*/
@@ -1465,39 +1503,21 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
14651503
ItemPointerEquals(&proc->fn_tid,&procTup->t_self)))
14661504
return false;
14671505

1506+
/* Else check the input argument datatypes */
14681507
valid= true;
1469-
/* If there are composite input arguments, they might have changed */
14701508
for (i=0;i<proc->nargs;i++)
14711509
{
1472-
Oidrelid;
1473-
HeapTuplerelTup;
1510+
valid=PLy_procedure_argument_valid(&proc->args[i]);
14741511

14751512
/* Short-circuit on first changed argument */
14761513
if (!valid)
14771514
break;
1478-
1479-
/* Only check input arguments that are composite */
1480-
if (proc->args[i].is_rowtype!=1)
1481-
continue;
1482-
1483-
Assert(OidIsValid(proc->args[i].typ_relid));
1484-
Assert(TransactionIdIsValid(proc->args[i].typrel_xmin));
1485-
Assert(ItemPointerIsValid(&proc->args[i].typrel_tid));
1486-
1487-
/* Get the pg_class tuple for the argument type */
1488-
relid=proc->args[i].typ_relid;
1489-
relTup=SearchSysCache1(RELOID,ObjectIdGetDatum(relid));
1490-
if (!HeapTupleIsValid(relTup))
1491-
elog(ERROR,"cache lookup failed for relation %u",relid);
1492-
1493-
/* If it has changed, the function is not valid */
1494-
if (!(proc->args[i].typrel_xmin==HeapTupleHeaderGetXmin(relTup->t_data)&&
1495-
ItemPointerEquals(&proc->args[i].typrel_tid,&relTup->t_self)))
1496-
valid= false;
1497-
1498-
ReleaseSysCache(relTup);
14991515
}
15001516

1517+
/* if the output type is composite, it might have changed */
1518+
if (valid)
1519+
valid=PLy_procedure_argument_valid(&proc->result);
1520+
15011521
returnvalid;
15021522
}
15031523

@@ -1661,10 +1681,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
16611681

16621682
/*
16631683
* Now get information required for input conversion of the
1664-
* procedure's arguments. Note that we ignore output arguments here
1665-
* --- since we don't support returning record, and that was already
1666-
* checked above, there's no need to worry about multiple output
1667-
* arguments.
1684+
* procedure's arguments. Note that we ignore output arguments here.
1685+
* If the function returns record, those I/O functions will be set up
1686+
* when the function is first called.
16681687
*/
16691688
if (procStruct->pronargs)
16701689
{
@@ -1926,7 +1945,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
19261945
* RECORDOID means we got called to create input functions for a tuple
19271946
* fetched by plpy.execute or for an anonymous record type
19281947
*/
1929-
if (desc->tdtypeid!=RECORDOID&& !TransactionIdIsValid(arg->typrel_xmin))
1948+
if (desc->tdtypeid!=RECORDOID)
19301949
{
19311950
HeapTuplerelTup;
19321951

@@ -1936,7 +1955,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
19361955
if (!HeapTupleIsValid(relTup))
19371956
elog(ERROR,"cache lookup failed for relation %u",arg->typ_relid);
19381957

1939-
/*Extract theXMINvalue tolateruse it in PLy_procedure_valid */
1958+
/*RememberXMINand TID forlatervalidation if cache is still OK */
19401959
arg->typrel_xmin=HeapTupleHeaderGetXmin(relTup->t_data);
19411960
arg->typrel_tid=relTup->t_self;
19421961

@@ -2010,6 +2029,29 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
20102029
arg->out.r.atts=PLy_malloc0(desc->natts*sizeof(PLyDatumToOb));
20112030
}
20122031

2032+
Assert(OidIsValid(desc->tdtypeid));
2033+
2034+
/*
2035+
* RECORDOID means we got called to create output functions for an
2036+
* anonymous record type
2037+
*/
2038+
if (desc->tdtypeid!=RECORDOID)
2039+
{
2040+
HeapTuplerelTup;
2041+
2042+
/* Get the pg_class tuple corresponding to the type of the output */
2043+
arg->typ_relid=typeidTypeRelid(desc->tdtypeid);
2044+
relTup=SearchSysCache1(RELOID,ObjectIdGetDatum(arg->typ_relid));
2045+
if (!HeapTupleIsValid(relTup))
2046+
elog(ERROR,"cache lookup failed for relation %u",arg->typ_relid);
2047+
2048+
/* Remember XMIN and TID for later validation if cache is still OK */
2049+
arg->typrel_xmin=HeapTupleHeaderGetXmin(relTup->t_data);
2050+
arg->typrel_tid=relTup->t_self;
2051+
2052+
ReleaseSysCache(relTup);
2053+
}
2054+
20132055
for (i=0;i<desc->natts;i++)
20142056
{
20152057
HeapTupletypeTup;
@@ -2630,7 +2672,11 @@ PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
26302672
PLyObToDatum*att;
26312673

26322674
if (desc->attrs[i]->attisdropped)
2675+
{
2676+
values[i]= (Datum)0;
2677+
nulls[i]= true;
26332678
continue;
2679+
}
26342680

26352681
key=NameStr(desc->attrs[i]->attname);
26362682
value=NULL;
@@ -2716,7 +2762,11 @@ PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
27162762
PLyObToDatum*att;
27172763

27182764
if (desc->attrs[i]->attisdropped)
2765+
{
2766+
values[i]= (Datum)0;
2767+
nulls[i]= true;
27192768
continue;
2769+
}
27202770

27212771
value=NULL;
27222772
att=&info->out.r.atts[i];
@@ -2779,7 +2829,11 @@ PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
27792829
PLyObToDatum*att;
27802830

27812831
if (desc->attrs[i]->attisdropped)
2832+
{
2833+
values[i]= (Datum)0;
2834+
nulls[i]= true;
27822835
continue;
2836+
}
27832837

27842838
key=NameStr(desc->attrs[i]->attname);
27852839
value=NULL;

‎src/pl/plpython/sql/plpython_record.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,21 @@ SELECT * FROM test_in_out_params('test_in');
112112
SELECT*FROM test_in_out_params_multi('test_in');
113113
SELECT*FROM test_inout_params('test_in');
114114

115+
-- try changing the return types and call functions again
116+
117+
ALTERTABLE table_record DROP COLUMN first;
118+
ALTERTABLE table_record DROP COLUMN second;
119+
ALTERTABLE table_record ADD COLUMN firsttext;
120+
ALTERTABLE table_record ADD COLUMN second int4;
121+
122+
SELECT*FROM test_table_record_as('obj','one',1, false);
123+
124+
ALTERTYPE type_record DROP ATTRIBUTE first;
125+
ALTERTYPE type_record DROP ATTRIBUTE second;
126+
ALTERTYPE type_record ADD ATTRIBUTE firsttext;
127+
ALTERTYPE type_record ADD ATTRIBUTE second int4;
128+
129+
SELECT*FROM test_type_record_as('obj','one',1, false);
115130

116131
-- errors cases
117132

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp