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

Commit2dada0c

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 parent68c903a commit2dada0c

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
@@ -1489,6 +1489,44 @@ PLy_function_delete_args(PLyProcedure *proc)
14891489
PyDict_DelItemString(proc->globals,proc->argnames[i]);
14901490
}
14911491

1492+
/*
1493+
* Check if our cached information about a datatype is still valid
1494+
*/
1495+
staticbool
1496+
PLy_procedure_argument_valid(PLyTypeInfo*arg)
1497+
{
1498+
HeapTuplerelTup;
1499+
boolvalid;
1500+
1501+
/* Nothing to cache unless type is composite */
1502+
if (arg->is_rowtype!=1)
1503+
return true;
1504+
1505+
/*
1506+
* Zero typ_relid means that we got called on an output argument of a
1507+
* function returning a unnamed record type; the info for it can't change.
1508+
*/
1509+
if (!OidIsValid(arg->typ_relid))
1510+
return true;
1511+
1512+
/* Else we should have some cached data */
1513+
Assert(TransactionIdIsValid(arg->typrel_xmin));
1514+
Assert(ItemPointerIsValid(&arg->typrel_tid));
1515+
1516+
/* Get the pg_class tuple for the data type */
1517+
relTup=SearchSysCache1(RELOID,ObjectIdGetDatum(arg->typ_relid));
1518+
if (!HeapTupleIsValid(relTup))
1519+
elog(ERROR,"cache lookup failed for relation %u",arg->typ_relid);
1520+
1521+
/* If it has changed, the cached data is not valid */
1522+
valid= (arg->typrel_xmin==HeapTupleHeaderGetXmin(relTup->t_data)&&
1523+
ItemPointerEquals(&arg->typrel_tid,&relTup->t_self));
1524+
1525+
ReleaseSysCache(relTup);
1526+
1527+
returnvalid;
1528+
}
1529+
14921530
/*
14931531
* Decide whether a cached PLyProcedure struct is still valid
14941532
*/
@@ -1505,39 +1543,21 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
15051543
ItemPointerEquals(&proc->fn_tid,&procTup->t_self)))
15061544
return false;
15071545

1546+
/* Else check the input argument datatypes */
15081547
valid= true;
1509-
/* If there are composite input arguments, they might have changed */
15101548
for (i=0;i<proc->nargs;i++)
15111549
{
1512-
Oidrelid;
1513-
HeapTuplerelTup;
1550+
valid=PLy_procedure_argument_valid(&proc->args[i]);
15141551

15151552
/* Short-circuit on first changed argument */
15161553
if (!valid)
15171554
break;
1518-
1519-
/* Only check input arguments that are composite */
1520-
if (proc->args[i].is_rowtype!=1)
1521-
continue;
1522-
1523-
Assert(OidIsValid(proc->args[i].typ_relid));
1524-
Assert(TransactionIdIsValid(proc->args[i].typrel_xmin));
1525-
Assert(ItemPointerIsValid(&proc->args[i].typrel_tid));
1526-
1527-
/* Get the pg_class tuple for the argument type */
1528-
relid=proc->args[i].typ_relid;
1529-
relTup=SearchSysCache1(RELOID,ObjectIdGetDatum(relid));
1530-
if (!HeapTupleIsValid(relTup))
1531-
elog(ERROR,"cache lookup failed for relation %u",relid);
1532-
1533-
/* If it has changed, the function is not valid */
1534-
if (!(proc->args[i].typrel_xmin==HeapTupleHeaderGetXmin(relTup->t_data)&&
1535-
ItemPointerEquals(&proc->args[i].typrel_tid,&relTup->t_self)))
1536-
valid= false;
1537-
1538-
ReleaseSysCache(relTup);
15391555
}
15401556

1557+
/* if the output type is composite, it might have changed */
1558+
if (valid)
1559+
valid=PLy_procedure_argument_valid(&proc->result);
1560+
15411561
returnvalid;
15421562
}
15431563

@@ -1701,10 +1721,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
17011721

17021722
/*
17031723
* Now get information required for input conversion of the
1704-
* procedure's arguments. Note that we ignore output arguments here
1705-
* --- since we don't support returning record, and that was already
1706-
* checked above, there's no need to worry about multiple output
1707-
* arguments.
1724+
* procedure's arguments. Note that we ignore output arguments here.
1725+
* If the function returns record, those I/O functions will be set up
1726+
* when the function is first called.
17081727
*/
17091728
if (procStruct->pronargs)
17101729
{
@@ -1966,7 +1985,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
19661985
* RECORDOID means we got called to create input functions for a tuple
19671986
* fetched by plpy.execute or for an anonymous record type
19681987
*/
1969-
if (desc->tdtypeid!=RECORDOID&& !TransactionIdIsValid(arg->typrel_xmin))
1988+
if (desc->tdtypeid!=RECORDOID)
19701989
{
19711990
HeapTuplerelTup;
19721991

@@ -1976,7 +1995,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
19761995
if (!HeapTupleIsValid(relTup))
19771996
elog(ERROR,"cache lookup failed for relation %u",arg->typ_relid);
19781997

1979-
/*Extract theXMINvalue tolateruse it in PLy_procedure_valid */
1998+
/*RememberXMINand TID forlatervalidation if cache is still OK */
19801999
arg->typrel_xmin=HeapTupleHeaderGetXmin(relTup->t_data);
19812000
arg->typrel_tid=relTup->t_self;
19822001

@@ -2050,6 +2069,29 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
20502069
arg->out.r.atts=PLy_malloc0(desc->natts*sizeof(PLyDatumToOb));
20512070
}
20522071

2072+
Assert(OidIsValid(desc->tdtypeid));
2073+
2074+
/*
2075+
* RECORDOID means we got called to create output functions for an
2076+
* anonymous record type
2077+
*/
2078+
if (desc->tdtypeid!=RECORDOID)
2079+
{
2080+
HeapTuplerelTup;
2081+
2082+
/* Get the pg_class tuple corresponding to the type of the output */
2083+
arg->typ_relid=typeidTypeRelid(desc->tdtypeid);
2084+
relTup=SearchSysCache1(RELOID,ObjectIdGetDatum(arg->typ_relid));
2085+
if (!HeapTupleIsValid(relTup))
2086+
elog(ERROR,"cache lookup failed for relation %u",arg->typ_relid);
2087+
2088+
/* Remember XMIN and TID for later validation if cache is still OK */
2089+
arg->typrel_xmin=HeapTupleHeaderGetXmin(relTup->t_data);
2090+
arg->typrel_tid=relTup->t_self;
2091+
2092+
ReleaseSysCache(relTup);
2093+
}
2094+
20532095
for (i=0;i<desc->natts;i++)
20542096
{
20552097
HeapTupletypeTup;
@@ -2670,7 +2712,11 @@ PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
26702712
PLyObToDatum*att;
26712713

26722714
if (desc->attrs[i]->attisdropped)
2715+
{
2716+
values[i]= (Datum)0;
2717+
nulls[i]= true;
26732718
continue;
2719+
}
26742720

26752721
key=NameStr(desc->attrs[i]->attname);
26762722
value=NULL;
@@ -2756,7 +2802,11 @@ PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
27562802
PLyObToDatum*att;
27572803

27582804
if (desc->attrs[i]->attisdropped)
2805+
{
2806+
values[i]= (Datum)0;
2807+
nulls[i]= true;
27592808
continue;
2809+
}
27602810

27612811
value=NULL;
27622812
att=&info->out.r.atts[i];
@@ -2819,7 +2869,11 @@ PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
28192869
PLyObToDatum*att;
28202870

28212871
if (desc->attrs[i]->attisdropped)
2872+
{
2873+
values[i]= (Datum)0;
2874+
nulls[i]= true;
28222875
continue;
2876+
}
28232877

28242878
key=NameStr(desc->attrs[i]->attname);
28252879
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