@ -1489,6 +1489,44 @@ PLy_function_delete_args(PLyProcedure *proc)
PyDict_DelItemString ( proc - > globals , proc - > argnames [ i ] ) ;
PyDict_DelItemString ( proc - > globals , proc - > argnames [ i ] ) ;
}
}
/*
* Check if our cached information about a datatype is still valid
*/
static bool
PLy_procedure_argument_valid ( PLyTypeInfo * arg )
{
HeapTuple relTup ;
bool valid ;
/* Nothing to cache unless type is composite */
if ( arg - > is_rowtype ! = 1 )
return true ;
/*
* Zero typ_relid means that we got called on an output argument of a
* function returning a unnamed record type ; the info for it can ' t change .
*/
if ( ! OidIsValid ( arg - > typ_relid ) )
return true ;
/* Else we should have some cached data */
Assert ( TransactionIdIsValid ( arg - > typrel_xmin ) ) ;
Assert ( ItemPointerIsValid ( & arg - > typrel_tid ) ) ;
/* Get the pg_class tuple for the data type */
relTup = SearchSysCache1 ( RELOID , ObjectIdGetDatum ( arg - > typ_relid ) ) ;
if ( ! HeapTupleIsValid ( relTup ) )
elog ( ERROR , " cache lookup failed for relation %u " , arg - > typ_relid ) ;
/* If it has changed, the cached data is not valid */
valid = ( arg - > typrel_xmin = = HeapTupleHeaderGetXmin ( relTup - > t_data ) & &
ItemPointerEquals ( & arg - > typrel_tid , & relTup - > t_self ) ) ;
ReleaseSysCache ( relTup ) ;
return valid ;
}
/*
/*
* Decide whether a cached PLyProcedure struct is still valid
* Decide whether a cached PLyProcedure struct is still valid
*/
*/
@ -1505,39 +1543,21 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
ItemPointerEquals ( & proc - > fn_tid , & procTup - > t_self ) ) )
ItemPointerEquals ( & proc - > fn_tid , & procTup - > t_self ) ) )
return false ;
return false ;
/* Else check the input argument datatypes */
valid = true ;
valid = true ;
/* If there are composite input arguments, they might have changed */
for ( i = 0 ; i < proc - > nargs ; i + + )
for ( i = 0 ; i < proc - > nargs ; i + + )
{
{
Oid relid ;
valid = PLy_procedure_argument_valid ( & proc - > args [ i ] ) ;
HeapTuple relTup ;
/* Short-circuit on first changed argument */
/* Short-circuit on first changed argument */
if ( ! valid )
if ( ! valid )
break ;
break ;
/* Only check input arguments that are composite */
if ( proc - > args [ i ] . is_rowtype ! = 1 )
continue ;
Assert ( OidIsValid ( proc - > args [ i ] . typ_relid ) ) ;
Assert ( TransactionIdIsValid ( proc - > args [ i ] . typrel_xmin ) ) ;
Assert ( ItemPointerIsValid ( & proc - > args [ i ] . typrel_tid ) ) ;
/* Get the pg_class tuple for the argument type */
relid = proc - > args [ i ] . typ_relid ;
relTup = SearchSysCache1 ( RELOID , ObjectIdGetDatum ( relid ) ) ;
if ( ! HeapTupleIsValid ( relTup ) )
elog ( ERROR , " cache lookup failed for relation %u " , relid ) ;
/* If it has changed, the function is not valid */
if ( ! ( proc - > args [ i ] . typrel_xmin = = HeapTupleHeaderGetXmin ( relTup - > t_data ) & &
ItemPointerEquals ( & proc - > args [ i ] . typrel_tid , & relTup - > t_self ) ) )
valid = false ;
ReleaseSysCache ( relTup ) ;
}
}
/* if the output type is composite, it might have changed */
if ( valid )
valid = PLy_procedure_argument_valid ( & proc - > result ) ;
return valid ;
return valid ;
}
}
@ -1701,10 +1721,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
/*
/*
* Now get information required for input conversion of the
* Now get information required for input conversion of the
* procedure ' s arguments . Note that we ignore output arguments here
* procedure ' s arguments . Note that we ignore output arguments here .
* - - - since we don ' t support returning record , and that was already
* If the function returns record , those I / O functions will be set up
* checked above , there ' s no need to worry about multiple output
* when the function is first called .
* arguments .
*/
*/
if ( procStruct - > pronargs )
if ( procStruct - > pronargs )
{
{
@ -1966,7 +1985,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
* RECORDOID means we got called to create input functions for a tuple
* RECORDOID means we got called to create input functions for a tuple
* fetched by plpy . execute or for an anonymous record type
* fetched by plpy . execute or for an anonymous record type
*/
*/
if ( desc - > tdtypeid ! = RECORDOID & & ! TransactionIdIsValid ( arg - > typrel_xmin ) )
if ( desc - > tdtypeid ! = RECORDOID )
{
{
HeapTuple relTup ;
HeapTuple relTup ;
@ -1976,7 +1995,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
if ( ! HeapTupleIsValid ( relTup ) )
if ( ! HeapTupleIsValid ( relTup ) )
elog ( ERROR , " cache lookup failed for relation %u " , arg - > typ_relid ) ;
elog ( ERROR , " cache lookup failed for relation %u " , arg - > typ_relid ) ;
/* Extract the XMIN value to later use it in PLy_procedure_valid */
/* Remember XMIN and TID for later validation if cache is still OK */
arg - > typrel_xmin = HeapTupleHeaderGetXmin ( relTup - > t_data ) ;
arg - > typrel_xmin = HeapTupleHeaderGetXmin ( relTup - > t_data ) ;
arg - > typrel_tid = relTup - > t_self ;
arg - > typrel_tid = relTup - > t_self ;
@ -2050,6 +2069,29 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
arg - > out . r . atts = PLy_malloc0 ( desc - > natts * sizeof ( PLyDatumToOb ) ) ;
arg - > out . r . atts = PLy_malloc0 ( desc - > natts * sizeof ( PLyDatumToOb ) ) ;
}
}
Assert ( OidIsValid ( desc - > tdtypeid ) ) ;
/*
* RECORDOID means we got called to create output functions for an
* anonymous record type
*/
if ( desc - > tdtypeid ! = RECORDOID )
{
HeapTuple relTup ;
/* Get the pg_class tuple corresponding to the type of the output */
arg - > typ_relid = typeidTypeRelid ( desc - > tdtypeid ) ;
relTup = SearchSysCache1 ( RELOID , ObjectIdGetDatum ( arg - > typ_relid ) ) ;
if ( ! HeapTupleIsValid ( relTup ) )
elog ( ERROR , " cache lookup failed for relation %u " , arg - > typ_relid ) ;
/* Remember XMIN and TID for later validation if cache is still OK */
arg - > typrel_xmin = HeapTupleHeaderGetXmin ( relTup - > t_data ) ;
arg - > typrel_tid = relTup - > t_self ;
ReleaseSysCache ( relTup ) ;
}
for ( i = 0 ; i < desc - > natts ; i + + )
for ( i = 0 ; i < desc - > natts ; i + + )
{
{
HeapTuple typeTup ;
HeapTuple typeTup ;
@ -2670,7 +2712,11 @@ PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
PLyObToDatum * att ;
PLyObToDatum * att ;
if ( desc - > attrs [ i ] - > attisdropped )
if ( desc - > attrs [ i ] - > attisdropped )
{
values [ i ] = ( Datum ) 0 ;
nulls [ i ] = true ;
continue ;
continue ;
}
key = NameStr ( desc - > attrs [ i ] - > attname ) ;
key = NameStr ( desc - > attrs [ i ] - > attname ) ;
value = NULL ;
value = NULL ;
@ -2756,7 +2802,11 @@ PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
PLyObToDatum * att ;
PLyObToDatum * att ;
if ( desc - > attrs [ i ] - > attisdropped )
if ( desc - > attrs [ i ] - > attisdropped )
{
values [ i ] = ( Datum ) 0 ;
nulls [ i ] = true ;
continue ;
continue ;
}
value = NULL ;
value = NULL ;
att = & info - > out . r . atts [ i ] ;
att = & info - > out . r . atts [ i ] ;
@ -2819,7 +2869,11 @@ PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
PLyObToDatum * att ;
PLyObToDatum * att ;
if ( desc - > attrs [ i ] - > attisdropped )
if ( desc - > attrs [ i ] - > attisdropped )
{
values [ i ] = ( Datum ) 0 ;
nulls [ i ] = true ;
continue ;
continue ;
}
key = NameStr ( desc - > attrs [ i ] - > attname ) ;
key = NameStr ( desc - > attrs [ i ] - > attname ) ;
value = NULL ;
value = NULL ;