@@ -1489,6 +1489,44 @@ PLy_function_delete_args(PLyProcedure *proc)
1489
1489
PyDict_DelItemString (proc -> globals , proc -> argnames [i ]);
1490
1490
}
1491
1491
1492
+ /*
1493
+ * Check if our cached information about a datatype is still valid
1494
+ */
1495
+ static bool
1496
+ PLy_procedure_argument_valid (PLyTypeInfo * arg )
1497
+ {
1498
+ HeapTuple relTup ;
1499
+ bool valid ;
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
+ return valid ;
1528
+ }
1529
+
1492
1530
/*
1493
1531
* Decide whether a cached PLyProcedure struct is still valid
1494
1532
*/
@@ -1505,39 +1543,21 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
1505
1543
ItemPointerEquals (& proc -> fn_tid , & procTup -> t_self )))
1506
1544
return false;
1507
1545
1546
+ /* Else check the input argument datatypes */
1508
1547
valid = true;
1509
- /* If there are composite input arguments, they might have changed */
1510
1548
for (i = 0 ; i < proc -> nargs ; i ++ )
1511
1549
{
1512
- Oid relid ;
1513
- HeapTuple relTup ;
1550
+ valid = PLy_procedure_argument_valid (& proc -> args [i ]);
1514
1551
1515
1552
/* Short-circuit on first changed argument */
1516
1553
if (!valid )
1517
1554
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 );
1539
1555
}
1540
1556
1557
+ /* if the output type is composite, it might have changed */
1558
+ if (valid )
1559
+ valid = PLy_procedure_argument_valid (& proc -> result );
1560
+
1541
1561
return valid ;
1542
1562
}
1543
1563
@@ -1701,10 +1721,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
1701
1721
1702
1722
/*
1703
1723
* 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.
1708
1727
*/
1709
1728
if (procStruct -> pronargs )
1710
1729
{
@@ -1966,7 +1985,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
1966
1985
* RECORDOID means we got called to create input functions for a tuple
1967
1986
* fetched by plpy.execute or for an anonymous record type
1968
1987
*/
1969
- if (desc -> tdtypeid != RECORDOID && ! TransactionIdIsValid ( arg -> typrel_xmin ) )
1988
+ if (desc -> tdtypeid != RECORDOID )
1970
1989
{
1971
1990
HeapTuple relTup ;
1972
1991
@@ -1976,7 +1995,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
1976
1995
if (!HeapTupleIsValid (relTup ))
1977
1996
elog (ERROR , "cache lookup failed for relation %u" , arg -> typ_relid );
1978
1997
1979
- /* Extract the XMIN value to later use it in PLy_procedure_valid */
1998
+ /* Remember XMIN and TID for later validation if cache is still OK */
1980
1999
arg -> typrel_xmin = HeapTupleHeaderGetXmin (relTup -> t_data );
1981
2000
arg -> typrel_tid = relTup -> t_self ;
1982
2001
@@ -2050,6 +2069,29 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
2050
2069
arg -> out .r .atts = PLy_malloc0 (desc -> natts * sizeof (PLyDatumToOb ));
2051
2070
}
2052
2071
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
+ HeapTuple relTup ;
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
+
2053
2095
for (i = 0 ; i < desc -> natts ; i ++ )
2054
2096
{
2055
2097
HeapTuple typeTup ;
@@ -2670,7 +2712,11 @@ PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
2670
2712
PLyObToDatum * att ;
2671
2713
2672
2714
if (desc -> attrs [i ]-> attisdropped )
2715
+ {
2716
+ values [i ] = (Datum ) 0 ;
2717
+ nulls [i ] = true;
2673
2718
continue ;
2719
+ }
2674
2720
2675
2721
key = NameStr (desc -> attrs [i ]-> attname );
2676
2722
value = NULL ;
@@ -2756,7 +2802,11 @@ PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
2756
2802
PLyObToDatum * att ;
2757
2803
2758
2804
if (desc -> attrs [i ]-> attisdropped )
2805
+ {
2806
+ values [i ] = (Datum ) 0 ;
2807
+ nulls [i ] = true;
2759
2808
continue ;
2809
+ }
2760
2810
2761
2811
value = NULL ;
2762
2812
att = & info -> out .r .atts [i ];
@@ -2819,7 +2869,11 @@ PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
2819
2869
PLyObToDatum * att ;
2820
2870
2821
2871
if (desc -> attrs [i ]-> attisdropped )
2872
+ {
2873
+ values [i ] = (Datum ) 0 ;
2874
+ nulls [i ] = true;
2822
2875
continue ;
2876
+ }
2823
2877
2824
2878
key = NameStr (desc -> attrs [i ]-> attname );
2825
2879
value = NULL ;
0 commit comments