@@ -1449,6 +1449,44 @@ PLy_function_delete_args(PLyProcedure *proc)
1449
1449
PyDict_DelItemString (proc -> globals ,proc -> argnames [i ]);
1450
1450
}
1451
1451
1452
+ /*
1453
+ * Check if our cached information about a datatype is still valid
1454
+ */
1455
+ static bool
1456
+ PLy_procedure_argument_valid (PLyTypeInfo * arg )
1457
+ {
1458
+ HeapTuple relTup ;
1459
+ bool valid ;
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
+ return valid ;
1488
+ }
1489
+
1452
1490
/*
1453
1491
* Decide whether a cached PLyProcedure struct is still valid
1454
1492
*/
@@ -1465,39 +1503,21 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
1465
1503
ItemPointerEquals (& proc -> fn_tid ,& procTup -> t_self )))
1466
1504
return false;
1467
1505
1506
+ /* Else check the input argument datatypes */
1468
1507
valid = true;
1469
- /* If there are composite input arguments, they might have changed */
1470
1508
for (i = 0 ;i < proc -> nargs ;i ++ )
1471
1509
{
1472
- Oid relid ;
1473
- HeapTuple relTup ;
1510
+ valid = PLy_procedure_argument_valid (& proc -> args [i ]);
1474
1511
1475
1512
/* Short-circuit on first changed argument */
1476
1513
if (!valid )
1477
1514
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 );
1499
1515
}
1500
1516
1517
+ /* if the output type is composite, it might have changed */
1518
+ if (valid )
1519
+ valid = PLy_procedure_argument_valid (& proc -> result );
1520
+
1501
1521
return valid ;
1502
1522
}
1503
1523
@@ -1661,10 +1681,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
1661
1681
1662
1682
/*
1663
1683
* 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.
1668
1687
*/
1669
1688
if (procStruct -> pronargs )
1670
1689
{
@@ -1926,7 +1945,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
1926
1945
* RECORDOID means we got called to create input functions for a tuple
1927
1946
* fetched by plpy.execute or for an anonymous record type
1928
1947
*/
1929
- if (desc -> tdtypeid != RECORDOID && ! TransactionIdIsValid ( arg -> typrel_xmin ) )
1948
+ if (desc -> tdtypeid != RECORDOID )
1930
1949
{
1931
1950
HeapTuple relTup ;
1932
1951
@@ -1936,7 +1955,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
1936
1955
if (!HeapTupleIsValid (relTup ))
1937
1956
elog (ERROR ,"cache lookup failed for relation %u" ,arg -> typ_relid );
1938
1957
1939
- /*Extract the XMINvalue to lateruse it in PLy_procedure_valid */
1958
+ /*Remember XMINand TID for latervalidation if cache is still OK */
1940
1959
arg -> typrel_xmin = HeapTupleHeaderGetXmin (relTup -> t_data );
1941
1960
arg -> typrel_tid = relTup -> t_self ;
1942
1961
@@ -2010,6 +2029,29 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
2010
2029
arg -> out .r .atts = PLy_malloc0 (desc -> natts * sizeof (PLyDatumToOb ));
2011
2030
}
2012
2031
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
+ HeapTuple relTup ;
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
+
2013
2055
for (i = 0 ;i < desc -> natts ;i ++ )
2014
2056
{
2015
2057
HeapTuple typeTup ;
@@ -2630,7 +2672,11 @@ PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
2630
2672
PLyObToDatum * att ;
2631
2673
2632
2674
if (desc -> attrs [i ]-> attisdropped )
2675
+ {
2676
+ values [i ]= (Datum )0 ;
2677
+ nulls [i ]= true;
2633
2678
continue ;
2679
+ }
2634
2680
2635
2681
key = NameStr (desc -> attrs [i ]-> attname );
2636
2682
value = NULL ;
@@ -2716,7 +2762,11 @@ PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
2716
2762
PLyObToDatum * att ;
2717
2763
2718
2764
if (desc -> attrs [i ]-> attisdropped )
2765
+ {
2766
+ values [i ]= (Datum )0 ;
2767
+ nulls [i ]= true;
2719
2768
continue ;
2769
+ }
2720
2770
2721
2771
value = NULL ;
2722
2772
att = & info -> out .r .atts [i ];
@@ -2779,7 +2829,11 @@ PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
2779
2829
PLyObToDatum * att ;
2780
2830
2781
2831
if (desc -> attrs [i ]-> attisdropped )
2832
+ {
2833
+ values [i ]= (Datum )0 ;
2834
+ nulls [i ]= true;
2782
2835
continue ;
2836
+ }
2783
2837
2784
2838
key = NameStr (desc -> attrs [i ]-> attname );
2785
2839
value = NULL ;