mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Create real array comparison functions (that use the element datatype's
comparison functions), replacing the highly bogus bitwise array_eq. Create a btree index opclass for ANYARRAY --- it is now possible to create indexes on array columns. Arrange to cache the results of catalog lookups across multiple array operations, instead of repeating the lookups on every call. Add string_to_array and array_to_string functions. Remove singleton_array, array_accum, array_assign, and array_subscript functions, since these were for proof-of-concept and not intended to become supported functions. Minor adjustments to behavior in some corner cases with empty or zero-dimensional arrays. Joe Conway (with some editorializing by Tom Lane).
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.90 2003/06/25 21:30:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.91 2003/06/27 00:33:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -34,7 +34,7 @@ static const char *getid(const char *s, char *n);
|
||||
static void putid(char *p, const char *s);
|
||||
static Acl *allocacl(int n);
|
||||
static const char *aclparse(const char *s, AclItem *aip);
|
||||
static bool aclitemeq(const AclItem *a1, const AclItem *a2);
|
||||
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
|
||||
static Acl *recursive_revoke(Acl *acl, AclId grantee,
|
||||
AclMode revoke_privs, DropBehavior behavior);
|
||||
|
||||
@@ -415,18 +415,33 @@ aclitemout(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* aclitemeq
|
||||
* Two AclItems are considered equal iff they have the same
|
||||
* aclitem_match
|
||||
* Two AclItems are considered to match iff they have the same
|
||||
* grantee and grantor; the privileges are ignored.
|
||||
*/
|
||||
static bool
|
||||
aclitemeq(const AclItem *a1, const AclItem *a2)
|
||||
aclitem_match(const AclItem *a1, const AclItem *a2)
|
||||
{
|
||||
return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
|
||||
a1->ai_grantee == a2->ai_grantee &&
|
||||
a1->ai_grantor == a2->ai_grantor;
|
||||
}
|
||||
|
||||
/*
|
||||
* aclitem equality operator
|
||||
*/
|
||||
Datum
|
||||
aclitem_eq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
AclItem *a1 = PG_GETARG_ACLITEM_P(0);
|
||||
AclItem *a2 = PG_GETARG_ACLITEM_P(1);
|
||||
bool result;
|
||||
|
||||
result = a1->ai_privs == a2->ai_privs &&
|
||||
a1->ai_grantee == a2->ai_grantee &&
|
||||
a1->ai_grantor == a2->ai_grantor;
|
||||
PG_RETURN_BOOL(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* acldefault() --- create an ACL describing default access permissions
|
||||
@@ -535,7 +550,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
|
||||
|
||||
for (dst = 0; dst < num; ++dst)
|
||||
{
|
||||
if (aclitemeq(mod_aip, old_aip + dst))
|
||||
if (aclitem_match(mod_aip, old_aip + dst))
|
||||
{
|
||||
/* found a match, so modify existing item */
|
||||
new_acl = allocacl(num);
|
||||
@@ -685,8 +700,10 @@ aclremove(PG_FUNCTION_ARGS)
|
||||
old_aip = ACL_DAT(old_acl);
|
||||
|
||||
/* Search for the matching entry */
|
||||
for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
|
||||
;
|
||||
for (dst = 0;
|
||||
dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
|
||||
++dst)
|
||||
/* continue */ ;
|
||||
|
||||
if (dst >= old_num)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.3 2003/06/25 21:30:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.4 2003/06/27 00:33:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -18,35 +18,6 @@
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* singleton_array :
|
||||
* Form a multi-dimensional array given one starting element.
|
||||
*
|
||||
* - first argument is the datum with which to build the array
|
||||
* - second argument is the number of dimensions the array should have;
|
||||
* defaults to 1 if no second argument is provided
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
Datum
|
||||
singleton_array(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid elem_type = get_fn_expr_argtype(fcinfo, 0);
|
||||
int ndims;
|
||||
|
||||
if (elem_type == InvalidOid)
|
||||
elog(ERROR, "Cannot determine input datatype");
|
||||
|
||||
if (PG_NARGS() == 2)
|
||||
ndims = PG_GETARG_INT32(1);
|
||||
else
|
||||
ndims = 1;
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
|
||||
PG_GETARG_DATUM(0),
|
||||
ndims));
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* array_push :
|
||||
* push an element onto either end of a one-dimensional array
|
||||
@@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
|
||||
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
|
||||
Oid arg0_elemid;
|
||||
Oid arg1_elemid;
|
||||
ArrayMetaState *my_extra;
|
||||
|
||||
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
|
||||
elog(ERROR, "array_push: cannot determine input data types");
|
||||
@@ -95,25 +67,54 @@ array_push(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NULL(); /* keep compiler quiet */
|
||||
}
|
||||
|
||||
/* Sanity check: do we have a one-dimensional array */
|
||||
if (ARR_NDIM(v) != 1)
|
||||
elog(ERROR, "Arrays greater than one-dimension are not supported");
|
||||
|
||||
lb = ARR_LBOUND(v);
|
||||
dimv = ARR_DIMS(v);
|
||||
if (arg0_elemid != InvalidOid)
|
||||
if (ARR_NDIM(v) == 1)
|
||||
{
|
||||
/* append newelem */
|
||||
int ub = dimv[0] + lb[0] - 1;
|
||||
indx = ub + 1;
|
||||
lb = ARR_LBOUND(v);
|
||||
dimv = ARR_DIMS(v);
|
||||
|
||||
if (arg0_elemid != InvalidOid)
|
||||
{
|
||||
/* append newelem */
|
||||
int ub = dimv[0] + lb[0] - 1;
|
||||
indx = ub + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* prepend newelem */
|
||||
indx = lb[0] - 1;
|
||||
}
|
||||
}
|
||||
else if (ARR_NDIM(v) == 0)
|
||||
indx = 1;
|
||||
else
|
||||
elog(ERROR, "only empty and one-dimensional arrays are supported");
|
||||
|
||||
|
||||
/*
|
||||
* We arrange to look up info about element type 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)
|
||||
{
|
||||
/* prepend newelem */
|
||||
indx = lb[0] - 1;
|
||||
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
|
||||
sizeof(ArrayMetaState));
|
||||
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
|
||||
my_extra->element_type = InvalidOid;
|
||||
}
|
||||
|
||||
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
|
||||
if (my_extra->element_type != element_type)
|
||||
{
|
||||
/* Get info about element type */
|
||||
get_typlenbyvalalign(element_type,
|
||||
&my_extra->typlen,
|
||||
&my_extra->typbyval,
|
||||
&my_extra->typalign);
|
||||
my_extra->element_type = element_type;
|
||||
}
|
||||
typlen = my_extra->typlen;
|
||||
typbyval = my_extra->typbyval;
|
||||
typalign = my_extra->typalign;
|
||||
|
||||
result = array_set(v, 1, &indx, newelem, -1,
|
||||
typlen, typbyval, typalign, &isNull);
|
||||
@@ -145,13 +146,28 @@ array_cat(PG_FUNCTION_ARGS)
|
||||
|
||||
/*
|
||||
* We must have one of the following combinations of inputs:
|
||||
* 1) two arrays with ndims1 == ndims2
|
||||
* 2) ndims1 == ndims2 - 1
|
||||
* 3) ndims1 == ndims2 + 1
|
||||
* 1) one empty array, and one non-empty array
|
||||
* 2) both arrays empty
|
||||
* 3) two arrays with ndims1 == ndims2
|
||||
* 4) ndims1 == ndims2 - 1
|
||||
* 5) ndims1 == ndims2 + 1
|
||||
*/
|
||||
ndims1 = ARR_NDIM(v1);
|
||||
ndims2 = ARR_NDIM(v2);
|
||||
|
||||
/*
|
||||
* short circuit - if one input array is empty, and the other is not,
|
||||
* we return the non-empty one as the result
|
||||
*
|
||||
* if both are empty, return the first one
|
||||
*/
|
||||
if (ndims1 == 0 && ndims2 > 0)
|
||||
PG_RETURN_ARRAYTYPE_P(v2);
|
||||
|
||||
if (ndims2 == 0)
|
||||
PG_RETURN_ARRAYTYPE_P(v1);
|
||||
|
||||
/* the rest fall into combo 2, 3, or 4 */
|
||||
if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
|
||||
elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
|
||||
"%d dimensions", ndims1, ndims2);
|
||||
@@ -266,147 +282,15 @@ array_cat(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_ARRAYTYPE_P(result);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* array_accum :
|
||||
* accumulator to build a 1-D array from input values -- this can be used
|
||||
* to create custom aggregates.
|
||||
*
|
||||
* This function is not marked strict, so we have to be careful about nulls.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
Datum
|
||||
array_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
/* return NULL if both arguments are NULL */
|
||||
if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
/* create a new 1-D array from the new element if the array is NULL */
|
||||
if (PG_ARGISNULL(0))
|
||||
{
|
||||
Oid tgt_type = get_fn_expr_rettype(fcinfo);
|
||||
Oid tgt_elem_type;
|
||||
|
||||
if (tgt_type == InvalidOid)
|
||||
elog(ERROR, "Cannot determine target array type");
|
||||
tgt_elem_type = get_element_type(tgt_type);
|
||||
if (tgt_elem_type == InvalidOid)
|
||||
elog(ERROR, "Target type is not an array");
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
|
||||
PG_GETARG_DATUM(1),
|
||||
1));
|
||||
}
|
||||
|
||||
/* return the array if the new element is NULL */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
|
||||
|
||||
/*
|
||||
* Otherwise this is equivalent to array_push. We hack the call a little
|
||||
* so that array_push can see the fn_expr information.
|
||||
*/
|
||||
return array_push(fcinfo);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* array_assign :
|
||||
* assign an element of an array to a new value and return the
|
||||
* redefined array
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
Datum
|
||||
array_assign(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *v;
|
||||
int idx_to_chg;
|
||||
Datum newelem;
|
||||
int *dimv,
|
||||
*lb, ub;
|
||||
ArrayType *result;
|
||||
bool isNull;
|
||||
Oid element_type;
|
||||
int16 typlen;
|
||||
bool typbyval;
|
||||
char typalign;
|
||||
|
||||
v = PG_GETARG_ARRAYTYPE_P(0);
|
||||
idx_to_chg = PG_GETARG_INT32(1);
|
||||
newelem = PG_GETARG_DATUM(2);
|
||||
|
||||
/* Sanity check: do we have a one-dimensional array */
|
||||
if (ARR_NDIM(v) != 1)
|
||||
elog(ERROR, "Arrays greater than one-dimension are not supported");
|
||||
|
||||
lb = ARR_LBOUND(v);
|
||||
dimv = ARR_DIMS(v);
|
||||
ub = dimv[0] + lb[0] - 1;
|
||||
if (idx_to_chg < lb[0] || idx_to_chg > ub)
|
||||
elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
|
||||
|
||||
element_type = ARR_ELEMTYPE(v);
|
||||
/* Sanity check: do we have a non-zero element type */
|
||||
if (element_type == 0)
|
||||
elog(ERROR, "Invalid array element type: %u", element_type);
|
||||
|
||||
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
|
||||
|
||||
result = array_set(v, 1, &idx_to_chg, newelem, -1,
|
||||
typlen, typbyval, typalign, &isNull);
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(result);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* array_subscript :
|
||||
* return specific element of an array
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
Datum
|
||||
array_subscript(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *v;
|
||||
int idx;
|
||||
int *dimv,
|
||||
*lb, ub;
|
||||
Datum result;
|
||||
bool isNull;
|
||||
Oid element_type;
|
||||
int16 typlen;
|
||||
bool typbyval;
|
||||
char typalign;
|
||||
|
||||
v = PG_GETARG_ARRAYTYPE_P(0);
|
||||
idx = PG_GETARG_INT32(1);
|
||||
|
||||
/* Sanity check: do we have a one-dimensional array */
|
||||
if (ARR_NDIM(v) != 1)
|
||||
elog(ERROR, "Arrays greater than one-dimension are not supported");
|
||||
|
||||
lb = ARR_LBOUND(v);
|
||||
dimv = ARR_DIMS(v);
|
||||
ub = dimv[0] + lb[0] - 1;
|
||||
if (idx < lb[0] || idx > ub)
|
||||
elog(ERROR, "Cannot return nonexistent array element: %d", idx);
|
||||
|
||||
element_type = ARR_ELEMTYPE(v);
|
||||
/* Sanity check: do we have a non-zero element type */
|
||||
if (element_type == 0)
|
||||
elog(ERROR, "Invalid array element type: %u", element_type);
|
||||
|
||||
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
|
||||
|
||||
result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
|
||||
|
||||
PG_RETURN_DATUM(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* actually does the work for singleton_array(), and array_accum() if it is
|
||||
* given a null input array.
|
||||
* used by text_to_array() in varlena.c
|
||||
*/
|
||||
ArrayType *
|
||||
create_singleton_array(Oid element_type, Datum element, int ndims)
|
||||
create_singleton_array(FunctionCallInfo fcinfo,
|
||||
Oid element_type,
|
||||
Datum element,
|
||||
int ndims)
|
||||
{
|
||||
Datum dvalues[1];
|
||||
int16 typlen;
|
||||
@@ -415,6 +299,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
|
||||
int dims[MAXDIM];
|
||||
int lbs[MAXDIM];
|
||||
int i;
|
||||
ArrayMetaState *my_extra;
|
||||
|
||||
if (element_type == 0)
|
||||
elog(ERROR, "Invalid array element type: %u", element_type);
|
||||
@@ -429,7 +314,31 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
|
||||
lbs[i] = 1;
|
||||
}
|
||||
|
||||
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
|
||||
/*
|
||||
* We arrange to look up info about element type 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 */
|
||||
get_typlenbyvalalign(element_type,
|
||||
&my_extra->typlen,
|
||||
&my_extra->typbyval,
|
||||
&my_extra->typalign);
|
||||
my_extra->element_type = element_type;
|
||||
}
|
||||
typlen = my_extra->typlen;
|
||||
typbyval = my_extra->typbyval;
|
||||
typalign = my_extra->typalign;
|
||||
|
||||
return construct_md_array(dvalues, ndims, dims, lbs, element_type,
|
||||
typlen, typbyval, typalign);
|
||||
|
||||
@@ -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, &inputproc, 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(&outputproc,
|
||||
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;
|
||||
}
|
||||
|
||||
dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
|
||||
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, &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(&sendproc,
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* 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)));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 |*****************/
|
||||
/***************************************************************************/
|
||||
|
||||
static void
|
||||
system_cache_lookup(Oid element_type,
|
||||
IOFuncSelector which_func,
|
||||
int *typlen,
|
||||
bool *typbyval,
|
||||
char *typdelim,
|
||||
Oid *typelem,
|
||||
Oid *proc,
|
||||
char *typalign)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.100 2003/06/25 21:30:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.101 2003/06/27 00:33:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -19,11 +19,14 @@
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "access/tuptoaster.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "libpq/crypt.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/pg_locale.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
typedef struct varlena unknown;
|
||||
@@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
|
||||
if (fldnum == 1) /* first field - just return the input
|
||||
* string */
|
||||
PG_RETURN_TEXT_P(inputstring);
|
||||
else
|
||||
/* otherwise return an empty string */
|
||||
else /* otherwise return an empty string */
|
||||
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
||||
}
|
||||
|
||||
@@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
|
||||
if (fldnum == 1) /* first field - just return the input
|
||||
* string */
|
||||
PG_RETURN_TEXT_P(inputstring);
|
||||
else
|
||||
/* otherwise return an empty string */
|
||||
else /* otherwise return an empty string */
|
||||
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
||||
}
|
||||
else if ((start_posn != 0) && (end_posn == 0))
|
||||
@@ -2028,6 +2029,176 @@ split_text(PG_FUNCTION_ARGS)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* text_to_array
|
||||
* parse input string
|
||||
* return text array of elements
|
||||
* based on provided field separator
|
||||
*/
|
||||
Datum
|
||||
text_to_array(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *inputstring = PG_GETARG_TEXT_P(0);
|
||||
int inputstring_len = TEXTLEN(inputstring);
|
||||
text *fldsep = PG_GETARG_TEXT_P(1);
|
||||
int fldsep_len = TEXTLEN(fldsep);
|
||||
int fldnum;
|
||||
int start_posn = 0;
|
||||
int end_posn = 0;
|
||||
text *result_text = NULL;
|
||||
ArrayBuildState *astate = NULL;
|
||||
MemoryContext oldcontext = CurrentMemoryContext;
|
||||
|
||||
/* return NULL for empty input string */
|
||||
if (inputstring_len < 1)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
/* empty field separator
|
||||
* return one element, 1D, array using the input string */
|
||||
if (fldsep_len < 1)
|
||||
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
|
||||
CStringGetDatum(inputstring), 1));
|
||||
|
||||
/* start with end position holding the initial start position */
|
||||
end_posn = 0;
|
||||
for (fldnum=1;;fldnum++) /* field number is 1 based */
|
||||
{
|
||||
Datum dvalue;
|
||||
bool disnull = false;
|
||||
|
||||
start_posn = end_posn;
|
||||
end_posn = text_position(PointerGetDatum(inputstring),
|
||||
PointerGetDatum(fldsep),
|
||||
fldnum);
|
||||
|
||||
if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */
|
||||
{
|
||||
if (fldnum == 1)
|
||||
{
|
||||
/* first element
|
||||
* return one element, 1D, array using the input string */
|
||||
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
|
||||
CStringGetDatum(inputstring), 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise create array and exit */
|
||||
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
|
||||
}
|
||||
}
|
||||
else if ((start_posn != 0) && (end_posn == 0))
|
||||
{
|
||||
/* last field requested */
|
||||
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
|
||||
}
|
||||
else if ((start_posn == 0) && (end_posn != 0))
|
||||
{
|
||||
/* first field requested */
|
||||
result_text = LEFT(inputstring, fldsep);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* prior to last field requested */
|
||||
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
|
||||
}
|
||||
|
||||
/* stash away current value */
|
||||
dvalue = PointerGetDatum(result_text);
|
||||
astate = accumArrayResult(astate, dvalue,
|
||||
disnull, TEXTOID, oldcontext);
|
||||
|
||||
}
|
||||
|
||||
/* never reached -- keep compiler quiet */
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
/*
|
||||
* array_to_text
|
||||
* concatenate Cstring representation of input array elements
|
||||
* using provided field separator
|
||||
*/
|
||||
Datum
|
||||
array_to_text(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
|
||||
char *fldsep = PG_TEXTARG_GET_STR(1);
|
||||
int nitems, *dims, ndims;
|
||||
char *p;
|
||||
Oid element_type;
|
||||
int typlen;
|
||||
bool typbyval;
|
||||
char typalign;
|
||||
Oid typelem;
|
||||
StringInfo result_str = makeStringInfo();
|
||||
int i;
|
||||
ArrayMetaState *my_extra;
|
||||
|
||||
p = ARR_DATA_PTR(v);
|
||||
ndims = ARR_NDIM(v);
|
||||
dims = ARR_DIMS(v);
|
||||
nitems = ArrayGetNItems(ndims, dims);
|
||||
|
||||
/* if there are no elements, return an empty string */
|
||||
if (nitems == 0)
|
||||
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
||||
|
||||
element_type = ARR_ELEMTYPE(v);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
typelem = my_extra->typelem;
|
||||
|
||||
for (i = 0; i < nitems; i++)
|
||||
{
|
||||
Datum itemvalue;
|
||||
char *value;
|
||||
|
||||
itemvalue = fetch_att(p, typbyval, typlen);
|
||||
|
||||
value = DatumGetCString(FunctionCall3(&my_extra->proc,
|
||||
itemvalue,
|
||||
ObjectIdGetDatum(typelem),
|
||||
Int32GetDatum(-1)));
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfo(result_str, "%s%s", fldsep, value);
|
||||
else
|
||||
appendStringInfo(result_str, "%s", value);
|
||||
|
||||
p = att_addlength(p, typlen, PointerGetDatum(p));
|
||||
p = (char *) att_align(p, typalign);
|
||||
}
|
||||
|
||||
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
|
||||
}
|
||||
|
||||
#define HEXBASE 16
|
||||
/*
|
||||
* Convert a int32 to a string containing a base 16 (hex) representation of
|
||||
|
||||
Reference in New Issue
Block a user