@ -124,6 +124,11 @@ static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbyt
static ArrayType * array_fill_internal ( ArrayType * dims , ArrayType * lbs ,
Datum value , bool isnull , Oid elmtype ,
FunctionCallInfo fcinfo ) ;
static ArrayType * array_replace_internal ( ArrayType * array ,
Datum search , bool search_isnull ,
Datum replace , bool replace_isnull ,
bool remove , Oid collation ,
FunctionCallInfo fcinfo ) ;
/*
@ -5174,3 +5179,304 @@ array_unnest(PG_FUNCTION_ARGS)
SRF_RETURN_DONE ( funcctx ) ;
}
}
/*
* array_replace / array_remove support
*
* Find all array entries matching ( not distinct from ) search / search_isnull ,
* and delete them if remove is true , else replace them with
* replace / replace_isnull . Comparisons are done using the specified
* collation . fcinfo is passed only for caching purposes .
*/
static ArrayType *
array_replace_internal ( ArrayType * array ,
Datum search , bool search_isnull ,
Datum replace , bool replace_isnull ,
bool remove , Oid collation ,
FunctionCallInfo fcinfo )
{
ArrayType * result ;
Oid element_type ;
Datum * values ;
bool * nulls ;
int * dim ;
int ndim ;
int nitems ,
nresult ;
int i ;
int32 nbytes = 0 ;
int32 dataoffset ;
bool hasnulls ;
int typlen ;
bool typbyval ;
char typalign ;
char * arraydataptr ;
bits8 * bitmap ;
int bitmask ;
bool changed = false ;
TypeCacheEntry * typentry ;
FunctionCallInfoData locfcinfo ;
element_type = ARR_ELEMTYPE ( array ) ;
ndim = ARR_NDIM ( array ) ;
dim = ARR_DIMS ( array ) ;
nitems = ArrayGetNItems ( ndim , dim ) ;
/* Return input array unmodified if it is empty */
if ( nitems < = 0 )
return array ;
/*
* We can ' t remove elements from multi - dimensional arrays , since the
* result might not be rectangular .
*/
if ( remove & & ndim > 1 )
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " removing elements from multidimensional arrays is not supported " ) ) ) ;
/*
* We arrange to look up the equality function only once per series of
* calls , assuming the element type doesn ' t change underneath us .
*/
typentry = ( TypeCacheEntry * ) fcinfo - > flinfo - > fn_extra ;
if ( typentry = = NULL | |
typentry - > type_id ! = element_type )
{
typentry = lookup_type_cache ( element_type ,
TYPECACHE_EQ_OPR_FINFO ) ;
if ( ! OidIsValid ( typentry - > eq_opr_finfo . fn_oid ) )
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_FUNCTION ) ,
errmsg ( " could not identify an equality operator for type %s " ,
format_type_be ( element_type ) ) ) ) ;
fcinfo - > flinfo - > fn_extra = ( void * ) typentry ;
}
typlen = typentry - > typlen ;
typbyval = typentry - > typbyval ;
typalign = typentry - > typalign ;
/*
* Detoast values if they are toasted . The replacement value must be
* detoasted for insertion into the result array , while detoasting the
* search value only once saves cycles .
*/
if ( typlen = = - 1 )
{
if ( ! search_isnull )
search = PointerGetDatum ( PG_DETOAST_DATUM ( search ) ) ;
if ( ! replace_isnull )
replace = PointerGetDatum ( PG_DETOAST_DATUM ( replace ) ) ;
}
/* Prepare to apply the comparison operator */
InitFunctionCallInfoData ( locfcinfo , & typentry - > eq_opr_finfo , 2 ,
collation , NULL , NULL ) ;
/* Allocate temporary arrays for new values */
values = ( Datum * ) palloc ( nitems * sizeof ( Datum ) ) ;
nulls = ( bool * ) palloc ( nitems * sizeof ( bool ) ) ;
/* Loop over source data */
arraydataptr = ARR_DATA_PTR ( array ) ;
bitmap = ARR_NULLBITMAP ( array ) ;
bitmask = 1 ;
hasnulls = false ;
nresult = 0 ;
for ( i = 0 ; i < nitems ; i + + )
{
Datum elt ;
bool isNull ;
bool oprresult ;
bool skip = false ;
/* Get source element, checking for NULL */
if ( bitmap & & ( * bitmap & bitmask ) = = 0 )
{
isNull = true ;
/* If searching for NULL, we have a match */
if ( search_isnull )
{
if ( remove )
{
skip = true ;
changed = true ;
}
else if ( ! replace_isnull )
{
values [ nresult ] = replace ;
isNull = false ;
changed = true ;
}
}
}
else
{
isNull = false ;
elt = fetch_att ( arraydataptr , typbyval , typlen ) ;
arraydataptr = att_addlength_datum ( arraydataptr , typlen , elt ) ;
arraydataptr = ( char * ) att_align_nominal ( arraydataptr , typalign ) ;
if ( search_isnull )
{
/* no match possible, keep element */
values [ nresult ] = elt ;
}
else
{
/*
* Apply the operator to the element pair
*/
locfcinfo . arg [ 0 ] = elt ;
locfcinfo . arg [ 1 ] = search ;
locfcinfo . argnull [ 0 ] = false ;
locfcinfo . argnull [ 1 ] = false ;
locfcinfo . isnull = false ;
oprresult = DatumGetBool ( FunctionCallInvoke ( & locfcinfo ) ) ;
if ( ! oprresult )
{
/* no match, keep element */
values [ nresult ] = elt ;
}
else
{
/* match, so replace or delete */
changed = true ;
if ( remove )
skip = true ;
else
{
values [ nresult ] = replace ;
isNull = replace_isnull ;
}
}
}
}
if ( ! skip )
{
nulls [ nresult ] = isNull ;
if ( isNull )
hasnulls = true ;
else
{
/* Update total result size */
nbytes = att_addlength_datum ( nbytes , typlen , values [ nresult ] ) ;
nbytes = att_align_nominal ( nbytes , typalign ) ;
/* check for overflow of total request */
if ( ! AllocSizeIsValid ( nbytes ) )
ereport ( ERROR ,
( errcode ( ERRCODE_PROGRAM_LIMIT_EXCEEDED ) ,
errmsg ( " array size exceeds the maximum allowed (%d) " ,
( int ) MaxAllocSize ) ) ) ;
}
nresult + + ;
}
/* advance bitmap pointer if any */
if ( bitmap )
{
bitmask < < = 1 ;
if ( bitmask = = 0x100 )
{
bitmap + + ;
bitmask = 1 ;
}
}
}
/*
* If not changed just return the original array
*/
if ( ! changed )
{
pfree ( values ) ;
pfree ( nulls ) ;
return array ;
}
/* Allocate and initialize the result array */
if ( hasnulls )
{
dataoffset = ARR_OVERHEAD_WITHNULLS ( ndim , nresult ) ;
nbytes + = dataoffset ;
}
else
{
dataoffset = 0 ; /* marker for no null bitmap */
nbytes + = ARR_OVERHEAD_NONULLS ( ndim ) ;
}
result = ( ArrayType * ) palloc0 ( nbytes ) ;
SET_VARSIZE ( result , nbytes ) ;
result - > ndim = ndim ;
result - > dataoffset = dataoffset ;
result - > elemtype = element_type ;
memcpy ( ARR_DIMS ( result ) , ARR_DIMS ( array ) , 2 * ndim * sizeof ( int ) ) ;
if ( remove )
{
/* Adjust the result length */
ARR_DIMS ( result ) [ 0 ] = nresult ;
}
/* Insert data into result array */
CopyArrayEls ( result ,
values , nulls , nresult ,
typlen , typbyval , typalign ,
false ) ;
pfree ( values ) ;
pfree ( nulls ) ;
return result ;
}
/*
* Remove any occurrences of an element from an array
*
* If used on a multi - dimensional array this will raise an error .
*/
Datum
array_remove ( PG_FUNCTION_ARGS )
{
ArrayType * array ;
Datum search = PG_GETARG_DATUM ( 1 ) ;
bool search_isnull = PG_ARGISNULL ( 1 ) ;
if ( PG_ARGISNULL ( 0 ) )
PG_RETURN_NULL ( ) ;
array = PG_GETARG_ARRAYTYPE_P ( 0 ) ;
array = array_replace_internal ( array ,
search , search_isnull ,
( Datum ) 0 , true ,
true , PG_GET_COLLATION ( ) ,
fcinfo ) ;
PG_RETURN_ARRAYTYPE_P ( array ) ;
}
/*
* Replace any occurrences of an element in an array
*/
Datum
array_replace ( PG_FUNCTION_ARGS )
{
ArrayType * array ;
Datum search = PG_GETARG_DATUM ( 1 ) ;
bool search_isnull = PG_ARGISNULL ( 1 ) ;
Datum replace = PG_GETARG_DATUM ( 2 ) ;
bool replace_isnull = PG_ARGISNULL ( 2 ) ;
if ( PG_ARGISNULL ( 0 ) )
PG_RETURN_NULL ( ) ;
array = PG_GETARG_ARRAYTYPE_P ( 0 ) ;
array = array_replace_internal ( array ,
search , search_isnull ,
replace , replace_isnull ,
false , PG_GET_COLLATION ( ) ,
fcinfo ) ;
PG_RETURN_ARRAYTYPE_P ( array ) ;
}