@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ Header : / cvsroot / pgsql / src / backend / utils / adt / arrayfuncs . c , v 1.91 2003 / 06 / 25 21 : 30 : 32 momjian Exp $
* $ Header : / cvsroot / pgsql / src / backend / utils / adt / arrayfuncs . c , v 1.92 2003 / 06 / 27 00 : 33 : 25 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -21,8 +21,10 @@
# include "catalog/pg_type.h"
# include "libpq/pqformat.h"
# include "parser/parse_coerce.h"
# include "parser/parse_oper.h"
# include "utils/array.h"
# include "utils/builtins.h"
# include "utils/datum.h"
# include "utils/memutils.h"
# include "utils/lsyscache.h"
# include "utils/syscache.h"
@ -70,16 +72,6 @@
# define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
/* I/O function selector for system_cache_lookup */
typedef enum IOFuncSelector
{
IOFunc_input ,
IOFunc_output ,
IOFunc_receive ,
IOFunc_send
} IOFuncSelector ;
static int ArrayCount ( char * str , int * dim , char typdelim ) ;
static Datum * ReadArrayStr ( char * arrayStr , int nitems , int ndim , int * dim ,
FmgrInfo * inputproc , Oid typelem , int32 typmod ,
@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
static void CopyArrayEls ( char * p , Datum * values , int nitems ,
int typlen , bool typbyval , char typalign ,
bool freedata ) ;
static void system_cache_lookup ( Oid element_type , IOFuncSelector which_func ,
int * typlen , bool * typbyval ,
char * typdelim , Oid * typelem ,
Oid * proc , char * typalign ) ;
static Datum ArrayCast ( char * value , bool byval , int len ) ;
static int ArrayCastAndSet ( Datum src ,
int typlen , bool typbyval , char typalign ,
@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
char * destPtr ,
int * st , int * endp , char * srcPtr ,
int typlen , bool typbyval , char typalign ) ;
static int array_cmp ( FunctionCallInfo fcinfo ) ;
/*---------------------------------------------------------------------
* array_in :
@ -139,12 +127,11 @@ array_in(PG_FUNCTION_ARGS)
* elements */
int typlen ;
bool typbyval ;
char typalign ;
char typdelim ;
Oid typinput ;
Oid typelem ;
char * string_save ,
* p ;
FmgrInfo inputproc ;
int i ,
nitems ;
int32 nbytes ;
@ -153,13 +140,38 @@ array_in(PG_FUNCTION_ARGS)
int ndim ,
dim [ MAXDIM ] ,
lBound [ MAXDIM ] ;
char typalign ;
ArrayMetaState * my_extra ;
/* Get info about element type, including its input conversion proc */
system_cache_lookup ( element_type , IOFunc_input ,
& typlen , & typbyval , & typdelim ,
& typelem , & typinput , & typalign ) ;
fmgr_info ( typinput , & inputproc ) ;
/*
* We arrange to look up info about element type , including its input
* conversion proc , only once per series of calls , assuming the element
* type doesn ' t change underneath us .
*/
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
if ( my_extra = = NULL )
{
fcinfo - > flinfo - > fn_extra = MemoryContextAlloc ( fcinfo - > flinfo - > fn_mcxt ,
sizeof ( ArrayMetaState ) ) ;
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
my_extra - > element_type = InvalidOid ;
}
if ( my_extra - > element_type ! = element_type )
{
/* Get info about element type, including its input conversion proc */
get_type_io_data ( element_type , IOFunc_input ,
& my_extra - > typlen , & my_extra - > typbyval ,
& my_extra - > typalign , & my_extra - > typdelim ,
& my_extra - > typelem , & my_extra - > typiofunc ) ;
fmgr_info_cxt ( my_extra - > typiofunc , & my_extra - > proc ,
fcinfo - > flinfo - > fn_mcxt ) ;
my_extra - > element_type = element_type ;
}
typlen = my_extra - > typlen ;
typbyval = my_extra - > typbyval ;
typalign = my_extra - > typalign ;
typdelim = my_extra - > typdelim ;
typelem = my_extra - > typelem ;
/* Make a modifiable copy of the input */
/* XXX why are we allocating an extra 2 bytes here? */
@ -262,7 +274,7 @@ array_in(PG_FUNCTION_ARGS)
if ( * p ! = ' { ' )
elog ( ERROR , " array_in: missing left brace " ) ;
dataPtr = ReadArrayStr ( p , nitems , ndim , dim , & input proc, typelem ,
dataPtr = ReadArrayStr ( p , nitems , ndim , dim , & my_extra - > proc , typelem ,
typmod , typdelim , typlen , typbyval , typalign ,
& nbytes ) ;
nbytes + = ARR_OVERHEAD ( ndim ) ;
@ -618,11 +630,9 @@ array_out(PG_FUNCTION_ARGS)
Oid element_type ;
int typlen ;
bool typbyval ;
char typdelim ;
Oid typoutput ,
typelem ;
FmgrInfo outputproc ;
char typalign ;
char typdelim ;
Oid typelem ;
char * p ,
* tmp ,
* retval ,
@ -636,12 +646,40 @@ array_out(PG_FUNCTION_ARGS)
indx [ MAXDIM ] ;
int ndim ,
* dim ;
ArrayMetaState * my_extra ;
element_type = ARR_ELEMTYPE ( v ) ;
system_cache_lookup ( element_type , IOFunc_output ,
& typlen , & typbyval , & typdelim ,
& typelem , & typoutput , & typalign ) ;
fmgr_info ( typoutput , & outputproc ) ;
/*
* We arrange to look up info about element type , including its output
* conversion proc , only once per series of calls , assuming the element
* type doesn ' t change underneath us .
*/
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
if ( my_extra = = NULL )
{
fcinfo - > flinfo - > fn_extra = MemoryContextAlloc ( fcinfo - > flinfo - > fn_mcxt ,
sizeof ( ArrayMetaState ) ) ;
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
my_extra - > element_type = InvalidOid ;
}
if ( my_extra - > element_type ! = element_type )
{
/* Get info about element type, including its output conversion proc */
get_type_io_data ( element_type , IOFunc_output ,
& my_extra - > typlen , & my_extra - > typbyval ,
& my_extra - > typalign , & my_extra - > typdelim ,
& my_extra - > typelem , & my_extra - > typiofunc ) ;
fmgr_info_cxt ( my_extra - > typiofunc , & my_extra - > proc ,
fcinfo - > flinfo - > fn_mcxt ) ;
my_extra - > element_type = element_type ;
}
typlen = my_extra - > typlen ;
typbyval = my_extra - > typbyval ;
typalign = my_extra - > typalign ;
typdelim = my_extra - > typdelim ;
typelem = my_extra - > typelem ;
ndim = ARR_NDIM ( v ) ;
dim = ARR_DIMS ( v ) ;
@ -668,7 +706,7 @@ array_out(PG_FUNCTION_ARGS)
bool nq ;
itemvalue = fetch_att ( p , typbyval , typlen ) ;
values [ i ] = DatumGetCString ( FunctionCall3 ( & output proc,
values [ i ] = DatumGetCString ( FunctionCall3 ( & my_extra - > proc ,
itemvalue ,
ObjectIdGetDatum ( typelem ) ,
Int32GetDatum ( - 1 ) ) ) ;
@ -786,10 +824,8 @@ array_recv(PG_FUNCTION_ARGS)
Oid element_type ;
int typlen ;
bool typbyval ;
char typdelim ;
Oid typreceive ;
char typalign ;
Oid typelem ;
FmgrInfo receiveproc ;
int i ,
nitems ;
int32 nbytes ;
@ -799,7 +835,7 @@ array_recv(PG_FUNCTION_ARGS)
flags ,
dim [ MAXDIM ] ,
lBound [ MAXDIM ] ;
char typalign ;
ArrayMetaState * my_extra ;
/* Get the array header information */
ndim = pq_getmsgint ( buf , 4 ) ;
@ -831,16 +867,40 @@ array_recv(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P ( retval ) ;
}
/* Get info about element type, including its receive conversion proc */
system_cache_lookup ( element_type , IOFunc_receive ,
& typlen , & typbyval , & typdelim ,
& typelem , & typreceive , & typalign ) ;
if ( ! OidIsValid ( typreceive ) )
elog ( ERROR , " No binary input function available for type %s " ,
format_type_be ( element_type ) ) ;
fmgr_info ( typreceive , & receiveproc ) ;
/*
* We arrange to look up info about element type , including its receive
* conversion proc , only once per series of calls , assuming the element
* type doesn ' t change underneath us .
*/
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
if ( my_extra = = NULL )
{
fcinfo - > flinfo - > fn_extra = MemoryContextAlloc ( fcinfo - > flinfo - > fn_mcxt ,
sizeof ( ArrayMetaState ) ) ;
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
my_extra - > element_type = InvalidOid ;
}
if ( my_extra - > element_type ! = element_type )
{
/* Get info about element type, including its receive proc */
get_type_io_data ( element_type , IOFunc_receive ,
& my_extra - > typlen , & my_extra - > typbyval ,
& my_extra - > typalign , & my_extra - > typdelim ,
& my_extra - > typelem , & my_extra - > typiofunc ) ;
if ( ! OidIsValid ( my_extra - > typiofunc ) )
elog ( ERROR , " No binary input function available for type %s " ,
format_type_be ( element_type ) ) ;
fmgr_info_cxt ( my_extra - > typiofunc , & my_extra - > proc ,
fcinfo - > flinfo - > fn_mcxt ) ;
my_extra - > element_type = element_type ;
}
typlen = my_extra - > typlen ;
typbyval = my_extra - > typbyval ;
typalign = my_extra - > typalign ;
typelem = my_extra - > typelem ;
dataPtr = ReadArrayBinary ( buf , nitems , & receiveproc , typelem ,
dataPtr = ReadArrayBinary ( buf , nitems , & my_extra - > proc , typelem ,
typlen , typbyval , typalign ,
& nbytes ) ;
nbytes + = ARR_OVERHEAD ( ndim ) ;
@ -965,26 +1025,51 @@ array_send(PG_FUNCTION_ARGS)
Oid element_type ;
int typlen ;
bool typbyval ;
char typdelim ;
Oid typsend ,
typelem ;
FmgrInfo sendproc ;
char typalign ;
Oid typelem ;
char * p ;
int nitems ,
i ;
int ndim ,
* dim ;
StringInfoData buf ;
ArrayMetaState * my_extra ;
/* Get information about the element type and the array dimensions */
element_type = ARR_ELEMTYPE ( v ) ;
system_cache_lookup ( element_type , IOFunc_send , & typlen , & typbyval ,
& typdelim , & typelem , & typsend , & typalign ) ;
if ( ! OidIsValid ( typsend ) )
elog ( ERROR , " No binary output function available for type %s " ,
format_type_be ( element_type ) ) ;
fmgr_info ( typsend , & sendproc ) ;
/*
* We arrange to look up info about element type , including its send
* conversion proc , only once per series of calls , assuming the element
* type doesn ' t change underneath us .
*/
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
if ( my_extra = = NULL )
{
fcinfo - > flinfo - > fn_extra = MemoryContextAlloc ( fcinfo - > flinfo - > fn_mcxt ,
sizeof ( ArrayMetaState ) ) ;
my_extra = ( ArrayMetaState * ) fcinfo - > flinfo - > fn_extra ;
my_extra - > element_type = InvalidOid ;
}
if ( my_extra - > element_type ! = element_type )
{
/* Get info about element type, including its send proc */
get_type_io_data ( element_type , IOFunc_send ,
& my_extra - > typlen , & my_extra - > typbyval ,
& my_extra - > typalign , & my_extra - > typdelim ,
& my_extra - > typelem , & my_extra - > typiofunc ) ;
if ( ! OidIsValid ( my_extra - > typiofunc ) )
elog ( ERROR , " No binary output function available for type %s " ,
format_type_be ( element_type ) ) ;
fmgr_info_cxt ( my_extra - > typiofunc , & my_extra - > proc ,
fcinfo - > flinfo - > fn_mcxt ) ;
my_extra - > element_type = element_type ;
}
typlen = my_extra - > typlen ;
typbyval = my_extra - > typbyval ;
typalign = my_extra - > typalign ;
typelem = my_extra - > typelem ;
ndim = ARR_NDIM ( v ) ;
dim = ARR_DIMS ( v ) ;
@ -1011,7 +1096,7 @@ array_send(PG_FUNCTION_ARGS)
itemvalue = fetch_att ( p , typbyval , typlen ) ;
outputbytes = DatumGetByteaP ( FunctionCall2 ( & send proc,
outputbytes = DatumGetByteaP ( FunctionCall2 ( & my_extra - > proc ,
itemvalue ,
ObjectIdGetDatum ( typelem ) ) ) ;
/* We assume the result will not have been toasted */
@ -1476,6 +1561,26 @@ array_set(ArrayType *array,
array = DatumGetArrayTypeP ( PointerGetDatum ( array ) ) ;
ndim = ARR_NDIM ( array ) ;
/*
* if number of dims is zero , i . e . an empty array , create an array
* with nSubscripts dimensions , and set the lower bounds to the supplied
* subscripts
*/
if ( ndim = = 0 )
{
Oid elmtype = ARR_ELEMTYPE ( array ) ;
for ( i = 0 ; i < nSubscripts ; i + + )
{
dim [ i ] = 1 ;
lb [ i ] = indx [ i ] ;
}
return construct_md_array ( & dataValue , nSubscripts , dim , lb , elmtype ,
elmlen , elmbyval , elmalign ) ;
}
if ( ndim ! = nSubscripts | | ndim < = 0 | | ndim > MAXDIM )
elog ( ERROR , " Invalid array subscripts " ) ;
@ -1632,6 +1737,31 @@ array_set_slice(ArrayType *array,
/* note: we assume srcArray contains no toasted elements */
ndim = ARR_NDIM ( array ) ;
/*
* if number of dims is zero , i . e . an empty array , create an array
* with nSubscripts dimensions , and set the upper and lower bounds
* to the supplied subscripts
*/
if ( ndim = = 0 )
{
Datum * dvalues ;
int nelems ;
Oid elmtype = ARR_ELEMTYPE ( array ) ;
deconstruct_array ( srcArray , elmtype , elmlen , elmbyval , elmalign ,
& dvalues , & nelems ) ;
for ( i = 0 ; i < nSubscripts ; i + + )
{
dim [ i ] = 1 + upperIndx [ i ] - lowerIndx [ i ] ;
lb [ i ] = lowerIndx [ i ] ;
}
return construct_md_array ( dvalues , nSubscripts , dim , lb , elmtype ,
elmlen , elmbyval , elmalign ) ;
}
if ( ndim < nSubscripts | | ndim < = 0 | | ndim > MAXDIM )
elog ( ERROR , " Invalid array subscripts " ) ;
@ -1807,10 +1937,14 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
int typlen ;
bool typbyval ;
char typalign ;
char typdelim ;
Oid typelem ;
Oid proc ;
char * s ;
typedef struct {
ArrayMetaState inp_extra ;
ArrayMetaState ret_extra ;
} am_extra ;
am_extra * my_extra ;
ArrayMetaState * inp_extra ;
ArrayMetaState * ret_extra ;
/* Get input array */
if ( fcinfo - > nargs < 1 )
@ -1829,11 +1963,51 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
if ( nitems < = 0 )
PG_RETURN_ARRAYTYPE_P ( v ) ;
/* Lookup source and result types. Unneeded variables are reused. */
system_cache_lookup ( inpType , IOFunc_input , & inp_typlen , & inp_typbyval ,
& typdelim , & typelem , & proc , & inp_typalign ) ;
system_cache_lookup ( retType , IOFunc_input , & typlen , & typbyval ,
& typdelim , & typelem , & proc , & typalign ) ;
/*
* We arrange to look up info about input and return element types only
* once per series of calls , assuming the element type doesn ' t change
* underneath us .
*/
my_extra = ( am_extra * ) fcinfo - > flinfo - > fn_extra ;
if ( my_extra = = NULL )
{
fcinfo - > flinfo - > fn_extra = MemoryContextAlloc ( fcinfo - > flinfo - > fn_mcxt ,
sizeof ( am_extra ) ) ;
my_extra = ( am_extra * ) fcinfo - > flinfo - > fn_extra ;
inp_extra = & my_extra - > inp_extra ;
inp_extra - > element_type = InvalidOid ;
ret_extra = & my_extra - > ret_extra ;
ret_extra - > element_type = InvalidOid ;
}
else
{
inp_extra = & my_extra - > inp_extra ;
ret_extra = & my_extra - > ret_extra ;
}
if ( inp_extra - > element_type ! = inpType )
{
get_typlenbyvalalign ( inpType ,
& inp_extra - > typlen ,
& inp_extra - > typbyval ,
& inp_extra - > typalign ) ;
inp_extra - > element_type = inpType ;
}
inp_typlen = inp_extra - > typlen ;
inp_typbyval = inp_extra - > typbyval ;
inp_typalign = inp_extra - > typalign ;
if ( ret_extra - > element_type ! = retType )
{
get_typlenbyvalalign ( retType ,
& ret_extra - > typlen ,
& ret_extra - > typbyval ,
& ret_extra - > typalign ) ;
ret_extra - > element_type = retType ;
}
typlen = ret_extra - > typlen ;
typbyval = ret_extra - > typbyval ;
typalign = ret_extra - > typalign ;
/* Allocate temporary array for new values */
values = ( Datum * ) palloc ( nitems * sizeof ( Datum ) ) ;
@ -2049,8 +2223,6 @@ deconstruct_array(ArrayType *array,
* compares two arrays for equality
* result :
* returns true if the arrays are equal , false otherwise .
*
* XXX bitwise equality is pretty bogus . . .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
Datum
@ -2058,12 +2230,101 @@ array_eq(PG_FUNCTION_ARGS)
{
ArrayType * array1 = PG_GETARG_ARRAYTYPE_P ( 0 ) ;
ArrayType * array2 = PG_GETARG_ARRAYTYPE_P ( 1 ) ;
char * p1 = ( char * ) ARR_DATA_PTR ( array1 ) ;
char * p2 = ( char * ) ARR_DATA_PTR ( array2 ) ;
int ndims1 = ARR_NDIM ( array1 ) ;
int ndims2 = ARR_NDIM ( array2 ) ;
int * dims1 = ARR_DIMS ( array1 ) ;
int * dims2 = ARR_DIMS ( array2 ) ;
int nitems1 = ArrayGetNItems ( ndims1 , dims1 ) ;
int nitems2 = ArrayGetNItems ( ndims2 , dims2 ) ;
Oid element_type = ARR_ELEMTYPE ( array1 ) ;
FmgrInfo * ae_fmgr_info = fcinfo - > flinfo ;
bool result = true ;
int typlen ;
bool typbyval ;
char typalign ;
int i ;
ArrayMetaState * my_extra ;
FunctionCallInfoData locfcinfo ;
if ( ARR_SIZE ( array1 ) ! = ARR_SIZE ( array2 ) )
result = false ;
else if ( memcmp ( array1 , array2 , ARR_SIZE ( array1 ) ) ! = 0 )
if ( element_type ! = ARR_ELEMTYPE ( array2 ) )
elog ( ERROR , " cannot compare arrays of different element types " ) ;
/* fast path if the arrays do not have the same number of elements */
if ( nitems1 ! = nitems2 )
result = false ;
else
{
/*
* We arrange to look up the equality function only once per series of
* calls , assuming the element type doesn ' t change underneath us .
*/
my_extra = ( ArrayMetaState * ) ae_fmgr_info - > fn_extra ;
if ( my_extra = = NULL )
{
ae_fmgr_info - > fn_extra = MemoryContextAlloc ( ae_fmgr_info - > fn_mcxt ,
sizeof ( ArrayMetaState ) ) ;
my_extra = ( ArrayMetaState * ) ae_fmgr_info - > fn_extra ;
my_extra - > element_type = InvalidOid ;
}
if ( my_extra - > element_type ! = element_type )
{
Oid opfuncid = equality_oper_funcid ( element_type ) ;
get_typlenbyvalalign ( element_type ,
& my_extra - > typlen ,
& my_extra - > typbyval ,
& my_extra - > typalign ) ;
fmgr_info_cxt ( opfuncid , & my_extra - > proc ,
ae_fmgr_info - > fn_mcxt ) ;
my_extra - > element_type = element_type ;
}
typlen = my_extra - > typlen ;
typbyval = my_extra - > typbyval ;
typalign = my_extra - > typalign ;
/*
* apply the operator to each pair of array elements .
*/
MemSet ( & locfcinfo , 0 , sizeof ( locfcinfo ) ) ;
locfcinfo . flinfo = & my_extra - > proc ;
locfcinfo . nargs = 2 ;
/* Loop over source data */
for ( i = 0 ; i < nitems1 ; i + + )
{
Datum elt1 ;
Datum elt2 ;
bool oprresult ;
/* Get element pair */
elt1 = fetch_att ( p1 , typbyval , typlen ) ;
elt2 = fetch_att ( p2 , typbyval , typlen ) ;
p1 = att_addlength ( p1 , typlen , PointerGetDatum ( p1 ) ) ;
p1 = ( char * ) att_align ( p1 , typalign ) ;
p2 = att_addlength ( p2 , typlen , PointerGetDatum ( p2 ) ) ;
p2 = ( char * ) att_align ( p2 , typalign ) ;
/*
* Apply the operator to the element pair
*/
locfcinfo . arg [ 0 ] = elt1 ;
locfcinfo . arg [ 1 ] = elt2 ;
locfcinfo . argnull [ 0 ] = false ;
locfcinfo . argnull [ 1 ] = false ;
locfcinfo . isnull = false ;
oprresult = DatumGetBool ( FunctionCallInvoke ( & locfcinfo ) ) ;
if ( ! oprresult )
{
result = false ;
break ;
}
}
}
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY ( array1 , 0 ) ;
@ -2073,53 +2334,171 @@ array_eq(PG_FUNCTION_ARGS)
}
/***************************************************************************/
/******************| Support Routines |*****************/
/***************************************************************************/
/*-----------------------------------------------------------------------------
* array - array bool operators :
* Given two arrays , iterate comparison operators
* over the array . Uses logic similar to text comparison
* functions , except element - by - element instead of
* character - by - character .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
Datum
array_ne ( PG_FUNCTION_ARGS )
{
PG_RETURN_BOOL ( ! DatumGetBool ( array_eq ( fcinfo ) ) ) ;
}
static void
system_cache_lookup ( Oid element_type ,
IOFuncSelector which_func ,
int * typlen ,
bool * typbyval ,
char * typdelim ,
Oid * typelem ,
Oid * proc ,
char * typalign )
Datum
array_lt ( PG_FUNCTION_ARGS )
{
PG_RETURN_BOOL ( array_cmp ( fcinfo ) < 0 ) ;
}
Datum
array_gt ( PG_FUNCTION_ARGS )
{
PG_RETURN_BOOL ( array_cmp ( fcinfo ) > 0 ) ;
}
Datum
array_le ( PG_FUNCTION_ARGS )
{
PG_RETURN_BOOL ( array_cmp ( fcinfo ) < = 0 ) ;
}
Datum
array_ge ( PG_FUNCTION_ARGS )
{
HeapTuple typeTuple ;
Form_pg_type typeStruct ;
typeTuple = SearchSysCache ( TYPEOID ,
ObjectIdGetDatum ( element_type ) ,
0 , 0 , 0 ) ;
if ( ! HeapTupleIsValid ( typeTuple ) )
elog ( ERROR , " cache lookup failed for type %u " , element_type ) ;
typeStruct = ( Form_pg_type ) GETSTRUCT ( typeTuple ) ;
* typlen = typeStruct - > typlen ;
* typbyval = typeStruct - > typbyval ;
* typdelim = typeStruct - > typdelim ;
* typelem = typeStruct - > typelem ;
* typalign = typeStruct - > typalign ;
switch ( which_func )
{
case IOFunc_input :
* proc = typeStruct - > typinput ;
break ;
case IOFunc_output :
* proc = typeStruct - > typoutput ;
break ;
case IOFunc_receive :
* proc = typeStruct - > typreceive ;
break ;
case IOFunc_send :
* proc = typeStruct - > typsend ;
break ;
}
ReleaseSysCache ( typeTuple ) ;
PG_RETURN_BOOL ( array_cmp ( fcinfo ) > = 0 ) ;
}
Datum
btarraycmp ( PG_FUNCTION_ARGS )
{
PG_RETURN_INT32 ( array_cmp ( fcinfo ) ) ;
}
/*
* array_cmp ( )
* Internal comparison function for arrays .
*
* Returns - 1 , 0 or 1
*/
static int
array_cmp ( FunctionCallInfo fcinfo )
{
ArrayType * array1 = PG_GETARG_ARRAYTYPE_P ( 0 ) ;
ArrayType * array2 = PG_GETARG_ARRAYTYPE_P ( 1 ) ;
FmgrInfo * ac_fmgr_info = fcinfo - > flinfo ;
Datum opresult ;
int result = 0 ;
Oid element_type = InvalidOid ;
int typlen ;
bool typbyval ;
char typalign ;
Datum * dvalues1 ;
int nelems1 ;
Datum * dvalues2 ;
int nelems2 ;
int min_nelems ;
int i ;
typedef struct
{
Oid element_type ;
int16 typlen ;
bool typbyval ;
char typalign ;
FmgrInfo eqproc ;
FmgrInfo ordproc ;
} ac_extra ;
ac_extra * my_extra ;
element_type = ARR_ELEMTYPE ( array1 ) ;
if ( element_type ! = ARR_ELEMTYPE ( array2 ) )
elog ( ERROR , " cannot compare arrays of different element types " ) ;
/*
* We arrange to look up the element type info and related functions
* only once per series of calls , assuming the element type doesn ' t
* change underneath us .
*/
my_extra = ( ac_extra * ) ac_fmgr_info - > fn_extra ;
if ( my_extra = = NULL )
{
ac_fmgr_info - > fn_extra = MemoryContextAlloc ( ac_fmgr_info - > fn_mcxt ,
sizeof ( ac_extra ) ) ;
my_extra = ( ac_extra * ) ac_fmgr_info - > fn_extra ;
my_extra - > element_type = InvalidOid ;
}
if ( my_extra - > element_type ! = element_type )
{
Oid eqfuncid = equality_oper_funcid ( element_type ) ;
Oid ordfuncid = ordering_oper_funcid ( element_type ) ;
get_typlenbyvalalign ( element_type ,
& my_extra - > typlen ,
& my_extra - > typbyval ,
& my_extra - > typalign ) ;
fmgr_info_cxt ( eqfuncid , & my_extra - > eqproc ,
ac_fmgr_info - > fn_mcxt ) ;
fmgr_info_cxt ( ordfuncid , & my_extra - > ordproc ,
ac_fmgr_info - > fn_mcxt ) ;
my_extra - > element_type = element_type ;
}
typlen = my_extra - > typlen ;
typbyval = my_extra - > typbyval ;
typalign = my_extra - > typalign ;
/* extract a C array of arg array datums */
deconstruct_array ( array1 , element_type , typlen , typbyval , typalign ,
& dvalues1 , & nelems1 ) ;
deconstruct_array ( array2 , element_type , typlen , typbyval , typalign ,
& dvalues2 , & nelems2 ) ;
min_nelems = Min ( nelems1 , nelems2 ) ;
for ( i = 0 ; i < min_nelems ; i + + )
{
/* are they equal */
opresult = FunctionCall2 ( & my_extra - > eqproc ,
dvalues1 [ i ] , dvalues2 [ i ] ) ;
if ( ! DatumGetBool ( opresult ) )
{
/* nope, see if arg1 is less than arg2 */
opresult = FunctionCall2 ( & my_extra - > ordproc ,
dvalues1 [ i ] , dvalues2 [ i ] ) ;
if ( DatumGetBool ( opresult ) )
{
/* arg1 is less than arg2 */
result = - 1 ;
break ;
}
else
{
/* arg1 is greater than arg2 */
result = 1 ;
break ;
}
}
}
if ( ( result = = 0 ) & & ( nelems1 ! = nelems2 ) )
result = ( nelems1 < nelems2 ) ? - 1 : 1 ;
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY ( array1 , 0 ) ;
PG_FREE_IF_COPY ( array2 , 1 ) ;
return result ;
}
/***************************************************************************/
/******************| Support Routines |*****************/
/***************************************************************************/
/*
* Fetch array element at pointer , converted correctly to a Datum
*/
@ -2423,6 +2802,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
if ( tgt_elem_type = = InvalidOid )
elog ( ERROR , " Target type is not an array " ) ;
/*
* We don ' t deal with domain constraints yet , so bail out .
* This isn ' t currently a problem , because we also don ' t
* support arrays of domain type elements either . But in the
* future we might . At that point consideration should be given
* to removing the check below and adding a domain constraints
* check to the coercion .
*/
if ( getBaseType ( tgt_elem_type ) ! = tgt_elem_type )
elog ( ERROR , " array coercion to domain type elements not " \
" currently supported " ) ;
if ( ! find_coercion_pathway ( tgt_elem_type , src_elem_type ,
COERCION_EXPLICIT , & funcId ) )
{
@ -2439,10 +2830,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
}
/*
* If it ' s binary - compatible , return the array unmodified .
* If it ' s binary - compatible , modify the element type in the array header ,
* but otherwise leave the array as we received it .
*/
if ( my_extra - > coerce_finfo . fn_oid = = InvalidOid )
PG_RETURN_ARRAYTYPE_P ( src ) ;
{
ArrayType * result = DatumGetArrayTypePCopy ( PG_GETARG_DATUM ( 0 ) ) ;
ARR_ELEMTYPE ( result ) = my_extra - > desttype ;
PG_RETURN_ARRAYTYPE_P ( result ) ;
}
/*
* Use array_map to apply the function to each array element .
@ -2454,3 +2851,121 @@ array_type_coerce(PG_FUNCTION_ARGS)
return array_map ( & locfcinfo , my_extra - > srctype , my_extra - > desttype ) ;
}
/*
* accumArrayResult - accumulate one ( more ) Datum for an array result
*
* astate is working state ( NULL on first call )
* rcontext is where to keep working state
*/
ArrayBuildState *
accumArrayResult ( ArrayBuildState * astate ,
Datum dvalue , bool disnull ,
Oid element_type ,
MemoryContext rcontext )
{
MemoryContext arr_context ,
oldcontext ;
if ( astate = = NULL )
{
/* First time through --- initialize */
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate ( rcontext ,
" accumArrayResult " ,
ALLOCSET_DEFAULT_MINSIZE ,
ALLOCSET_DEFAULT_INITSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
oldcontext = MemoryContextSwitchTo ( arr_context ) ;
astate = ( ArrayBuildState * ) palloc ( sizeof ( ArrayBuildState ) ) ;
astate - > mcontext = arr_context ;
astate - > dvalues = ( Datum * )
palloc ( ARRAY_ELEMS_CHUNKSIZE * sizeof ( Datum ) ) ;
astate - > nelems = 0 ;
astate - > element_type = element_type ;
get_typlenbyvalalign ( element_type ,
& astate - > typlen ,
& astate - > typbyval ,
& astate - > typalign ) ;
}
else
{
oldcontext = MemoryContextSwitchTo ( astate - > mcontext ) ;
Assert ( astate - > element_type = = element_type ) ;
/* enlarge dvalues[] if needed */
if ( ( astate - > nelems % ARRAY_ELEMS_CHUNKSIZE ) = = 0 )
astate - > dvalues = ( Datum * )
repalloc ( astate - > dvalues ,
( astate - > nelems + ARRAY_ELEMS_CHUNKSIZE ) * sizeof ( Datum ) ) ;
}
if ( disnull )
elog ( ERROR , " NULL elements not allowed in Arrays " ) ;
/* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
astate - > dvalues [ astate - > nelems + + ] =
datumCopy ( dvalue , astate - > typbyval , astate - > typlen ) ;
MemoryContextSwitchTo ( oldcontext ) ;
return astate ;
}
/*
* makeArrayResult - produce 1 - D final result of accumArrayResult
*
* astate is working state ( not NULL )
* rcontext is where to construct result
*/
Datum
makeArrayResult ( ArrayBuildState * astate ,
MemoryContext rcontext )
{
int dims [ 1 ] ;
int lbs [ 1 ] ;
dims [ 0 ] = astate - > nelems ;
lbs [ 0 ] = 1 ;
return makeMdArrayResult ( astate , 1 , dims , lbs , rcontext ) ;
}
/*
* makeMdArrayResult - produce multi - D final result of accumArrayResult
*
* beware : no check that specified dimensions match the number of values
* accumulated .
*
* astate is working state ( not NULL )
* rcontext is where to construct result
*/
Datum
makeMdArrayResult ( ArrayBuildState * astate ,
int ndims ,
int * dims ,
int * lbs ,
MemoryContext rcontext )
{
ArrayType * result ;
MemoryContext oldcontext ;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo ( rcontext ) ;
result = construct_md_array ( astate - > dvalues ,
ndims ,
dims ,
lbs ,
astate - > element_type ,
astate - > typlen ,
astate - > typbyval ,
astate - > typalign ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/* Clean up all the junk */
MemoryContextDelete ( astate - > mcontext ) ;
return PointerGetDatum ( result ) ;
}