/* Working state for array_iterate() */ typedefstruct ArrayIteratorData
{ /* basic info about the array, set up during array_create_iterator() */
ArrayType *arr; /* array we're iterating through */
bits8 *nullbitmap; /* its null bitmap, if any */ int nitems; /* total number of elements in array */
int16 typlen; /* element type's length */ bool typbyval; /* element type's byval property */ char typalign; /* element type's align property */
/* information about the requested slice size */ int slice_ndim; /* slice dimension, or 0 if not slicing */ int slice_len; /* number of elements per slice */ int *slice_dims; /* slice dims array */ int *slice_lbound; /* slice lbound array */
Datum *slice_values; /* workspace of length slice_len */ bool *slice_nulls; /* workspace of length slice_len */
/* current position information, updated on each iteration */ char *data_ptr; /* our current position in the array */ int current_item; /* the item # we're at in the array */
} ArrayIteratorData;
staticbool ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound, constchar *origStr, Node *escontext); staticbool ReadDimensionInt(char **srcptr, int *result, constchar *origStr, Node *escontext); staticbool ReadArrayStr(char **srcptr,
FmgrInfo *inputproc, Oid typioparam, int32 typmod, char typdelim, int typlen, bool typbyval, char typalign, int *ndim_p, int *dim, int *nitems_p,
Datum **values_p, bool **nulls_p, constchar *origStr, Node *escontext); static ArrayToken ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim, constchar *origStr, Node *escontext); staticvoid ReadArrayBinary(StringInfo buf, int nitems,
FmgrInfo *receiveproc, Oid typioparam, int32 typmod, int typlen, bool typbyval, char typalign,
Datum *values, bool *nulls, bool *hasnulls, int32 *nbytes); static Datum array_get_element_expanded(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull); static Datum array_set_element_expanded(Datum arraydatum, int nSubscripts, int *indx,
Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign); staticbool array_get_isnull(const bits8 *nullbitmap, int offset); staticvoid array_set_isnull(bits8 *nullbitmap, int offset, bool isNull); static Datum ArrayCast(char *value, bool byval, int len); staticint ArrayCastAndSet(Datum src, int typlen, bool typbyval, char typalign, char *dest); staticchar *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems, int typlen, bool typbyval, char typalign); staticint array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems, int typlen, bool typbyval, char typalign); staticint array_copy(char *destptr, int nitems, char *srcptr, int offset, bits8 *nullbitmap, int typlen, bool typbyval, char typalign); staticint array_slice_size(char *arraydataptr, bits8 *arraynullsptr, int ndim, int *dim, int *lb, int *st, int *endp, int typlen, bool typbyval, char typalign); staticvoid array_extract_slice(ArrayType *newarray, int ndim, int *dim, int *lb, char *arraydataptr, bits8 *arraynullsptr, int *st, int *endp, int typlen, bool typbyval, char typalign); staticvoid array_insert_slice(ArrayType *destArray, ArrayType *origArray,
ArrayType *srcArray, int ndim, int *dim, int *lb, int *st, int *endp, int typlen, bool typbyval, char typalign); staticint array_cmp(FunctionCallInfo fcinfo); static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
Oid elmtype, int dataoffset); 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); staticint width_bucket_array_float8(Datum operand, ArrayType *thresholds); staticint width_bucket_array_fixed(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry); staticint width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
/* *array_in: *convertsanarrayfromtheexternalformatin"string"to *itsinternalformat. * *returnvalue: *theinternalrepresentationoftheinputarray
*/
Datum
array_in(PG_FUNCTION_ARGS)
{ char *string = PG_GETARG_CSTRING(0); /* external form */
Oid element_type = PG_GETARG_OID(1); /* type of an array
* element */
int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
Node *escontext = fcinfo->context; int typlen; bool typbyval; char typalign; char typdelim;
Oid typioparam; char *p; int nitems;
Datum *values; bool *nulls; bool hasnulls;
int32 nbytes;
int32 dataoffset;
ArrayType *retval; int ndim,
dim[MAXDIM],
lBound[MAXDIM];
ArrayMetaState *my_extra;
/* *Startprocessingtheinputstring. * *Iftheinputstringstartswithdimensioninfo,readandusethat. *Otherwise,we'lldeterminethedimensionsduringReadArrayStr.
*/
p = string; if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext)) return (Datum) 0;
if (ndim == 0)
{ /* No array dimensions, so next character should be a left brace */ if (*p != '{')
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", string),
errdetail("Array value must start with \"{\" or dimension information.")));
} else
{ /* If array dimensions are given, expect '=' operator */ if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", string),
errdetail("Missing \"%s\" after array dimensions.",
ASSGN)));
p += strlen(ASSGN); /* Allow whitespace after it */ while (scanner_isspace(*p))
p++;
if (*p != '{')
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", string),
errdetail("Array contents must start with \"{\".")));
}
/* Parse the value part, in the curly braces: { ... } */ if (!ReadArrayStr(&p,
&my_extra->proc, typioparam, typmod,
typdelim,
typlen, typbyval, typalign,
&ndim,
dim,
&nitems,
&values, &nulls,
string,
escontext)) return (Datum) 0;
/* only whitespace is allowed after the closing brace */ while (*p)
{ if (!scanner_isspace(*p++))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", string),
errdetail("Junk after closing right brace.")));
}
/* Empty array? */ if (nitems == 0)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
/* *Checkfornulls,computetotaldataspaceneeded
*/
hasnulls = false;
nbytes = 0; for (int i = 0; i < nitems; i++)
{ if (nulls[i])
hasnulls = true; else
{ /* let's just make sure data is not toasted */ if (typlen == -1)
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
nbytes = att_addlength_datum(nbytes, typlen, values[i]);
nbytes = att_align_nominal(nbytes, typalign); /* check for overflow of total request */ if (!AllocSizeIsValid(nbytes))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxAllocSize)));
}
} if (hasnulls)
{
dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
nbytes += dataoffset;
} else
{
dataoffset = 0; /* marker for no null bitmap */
nbytes += ARR_OVERHEAD_NONULLS(ndim);
}
/* *Dimensioninfotakestheformofoneormore[n]or[m:n]items.This *loopiteratesonceperdimensionitem.
*/
ndim = 0; for (;;)
{ char *q; int ub; int i;
/* *Note:wecurrentlyallowwhitespacebetween,butnotwithin, *dimensionitems.
*/ while (scanner_isspace(*p))
p++; if (*p != '[') break; /* no more dimension items */
p++; if (ndim >= MAXDIM)
ereturn(escontext, false,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions exceeds the maximum allowed (%d)",
MAXDIM)));
q = p; if (!ReadDimensionInt(&p, &i, origStr, escontext)) returnfalse; if (p == q) /* no digits? */
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", origStr),
errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
if (*p == ':')
{ /* [m:n] format */
lBound[ndim] = i;
p++;
q = p; if (!ReadDimensionInt(&p, &ub, origStr, escontext)) returnfalse; if (p == q) /* no digits? */
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", origStr),
errdetail("Missing array dimension value.")));
} else
{ /* [n] format */
lBound[ndim] = 1;
ub = i;
} if (*p != ']')
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", origStr),
errdetail("Missing \"%s\" after array dimensions.", "]")));
p++;
/* *Note:wecouldacceptub=lb-1torepresentazero-length *dimension.However,thatwouldresultinanemptyarray,forwhich *wedon'tkeepanydimensiondata,sothate.g.[1:0]and[101:100] *wouldbeequivalent.Giventhelackoffielddemand,thereseems *littlepointinallowingsuchcases.
*/ if (ub < lBound[ndim])
ereturn(escontext, false,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("upper bound cannot be less than lower bound")));
/* Upper bound of INT_MAX must be disallowed, cf ArrayCheckBounds() */ if (ub == INT_MAX)
ereturn(escontext, false,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array upper bound is too large: %d", ub)));
/* Compute "ub - lBound[ndim] + 1", detecting overflow */ if (pg_sub_s32_overflow(ub, lBound[ndim], &ub) ||
pg_add_s32_overflow(ub, 1, &ub))
ereturn(escontext, false,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
if (errno == ERANGE || l > PG_INT32_MAX || l < PG_INT32_MIN)
ereturn(escontext, false,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array bound is out of integer range")));
*result = (int) l; returntrue;
}
/* *ReadArrayStr: *parsesthearraystringpointedtoby*srcptrandconvertsthevalues *tointernalformat.Determinesthearraydimensionsasitgoes. * *Onentry,*srcptrpointstothestringtoparse(itmustpointtoa'{'). *Onsuccessfulreturn,itisadvancedtopointpasttheclosing'}'. * *Ifdimensionswerespecifiedexplicitly,theyarepassedin*ndim_pand *dim[].Thisfunctionwillcheckthatthearrayvaluesmatchthespecified *dimensions.Ifdimensionswerenotgiven,callermustpass*ndim_p==0 *andinitializeallelementsofdim[]to-1.Thenthisfunctionwill *deducethedimensionsfromthestructureoftheinputandstorethemin **ndim_pandthedim[]array. * *Elementtypeinformation: *inputproc:type-specificinputprocedureforelementdatatype. *typioparam,typmod:auxiliaryvaluestopasstoinputproc. *typdelim:thevaluedelimiter(type-specific). *typlen,typbyval,typalign:storageparametersofelementdatatype. * *Outputs: **ndim_p,dim:dimensionsdeducedfromtheinputstructure. **nitems_p:totalnumberofelements. **values_p[]:palloc'darray,filledwithconverteddatavalues. **nulls_p[]:palloc'darray,filledwithis-nullmarkers. * *'origStr'istheoriginalinputstring,usedonlyinerrormessages. *If*escontextpointstoanErrorSaveContext,detailsofanyerrorare *reportedthere. * *Result: *trueforsuccess,falseforfailure(ifescontextisprovided).
*/ staticbool
ReadArrayStr(char **srcptr,
FmgrInfo *inputproc,
Oid typioparam,
int32 typmod, char typdelim, int typlen, bool typbyval, char typalign, int *ndim_p, int *dim, int *nitems_p,
Datum **values_p, bool **nulls_p, constchar *origStr,
Node *escontext)
{ int ndim = *ndim_p; bool dimensions_specified = (ndim != 0); int maxitems;
Datum *values; bool *nulls;
StringInfoData elembuf; int nest_level; int nitems; bool ndim_frozen; bool expect_delim; int nelems[MAXDIM];
/* Allocate workspace to hold (string representation of) one element */
initStringInfo(&elembuf);
/* Loop below assumes first token is ATOK_LEVEL_START */
Assert(**srcptr == '{');
/* Parse tokens until we reach the matching right brace */
nest_level = 0;
nitems = 0;
ndim_frozen = dimensions_specified;
expect_delim = false; do
{
ArrayToken tok;
tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
switch (tok)
{ case ATOK_LEVEL_START: /* Can't write left brace where delim is expected */ if (expect_delim)
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", origStr),
errdetail("Unexpected \"%c\" character.", '{')));
/* Initialize element counting in the new level */ if (nest_level >= MAXDIM)
ereturn(escontext, false,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions exceeds the maximum allowed (%d)",
MAXDIM)));
nelems[nest_level] = 0;
nest_level++; if (nest_level > ndim)
{ /* Can't increase ndim once it's frozen */ if (ndim_frozen) goto dimension_error;
ndim = nest_level;
} break;
case ATOK_LEVEL_END: /* Can't get here with nest_level == 0 */
Assert(nest_level > 0);
/* *Weallowarightbracetoterminateanemptysub-array, *otherwiseitmustoccurwhereweexpectadelimiter.
*/ if (nelems[nest_level - 1] > 0 && !expect_delim)
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", origStr),
errdetail("Unexpected \"%c\" character.", '}')));
nest_level--; /* Nested sub-arrays count as elements of outer level */ if (nest_level > 0)
nelems[nest_level - 1]++;
/* *Note:ifwehaddimensionalityinfo,thendim[nest_level] *isinitiallynon-negative,andwe'llcheckeachsub-array's *lengthagainstthat.
*/ if (dim[nest_level] < 0)
{ /* Save length of first sub-array of this level */
dim[nest_level] = nelems[nest_level];
} elseif (nelems[nest_level] != dim[nest_level])
{ /* Subsequent sub-arrays must have same length */ goto dimension_error;
}
/* Count the pair of double quotes, if needed */ if (needquote)
overall_length += 2; /* and the comma (or other typdelim delimiter) */
overall_length += 1;
}
/* *Theverylastarrayelementdoesn'thaveatypdelimdelimiterafterit, *butthat'sOK;thatspaceisneededforthetrailing'\0'. * *Nowcounttotalnumberofcurlybracepairsinoutputstring.
*/ for (i = j = 0, k = 1; i < ndim; i++)
{
j += k, k *= dims[i];
}
overall_length += 2 * j;
/* Format explicit dimensions if required */
dims_str[0] = '\0'; if (needdims)
{ char *ptr = dims_str;
if (needdims)
APPENDSTR(dims_str);
APPENDCHAR('{'); for (i = 0; i < ndim; i++)
indx[i] = 0;
j = 0;
k = 0; do
{ for (i = j; i < ndim - 1; i++)
APPENDCHAR('{');
if (needquotes[k])
{
APPENDCHAR('"'); for (tmp = values[k]; *tmp; tmp++)
{ char ch = *tmp;
for (i = ndim - 1; i >= 0; i--)
{ if (++(indx[i]) < dims[i])
{
APPENDCHAR(typdelim); break;
} else
{
indx[i] = 0;
APPENDCHAR('}');
}
}
j = i;
} while (j != -1);
#undef APPENDSTR #undef APPENDCHAR
/* Assert that we calculated the string length accurately */
Assert(overall_length == (p - retval + 1));
pfree(values);
pfree(needquotes);
PG_RETURN_CSTRING(retval);
}
/* *array_recv: *convertsanarrayfromtheexternalbinaryformatto *itsinternalformat. * *returnvalue: *theinternalrepresentationoftheinputarray
*/
Datum
array_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
* element */
int32 typmod = PG_GETARG_INT32(2); /* typmod for array elements */
Oid element_type; int typlen; bool typbyval; char typalign;
Oid typioparam; int i,
nitems;
Datum *dataPtr; bool *nullsPtr; bool hasnulls;
int32 nbytes;
int32 dataoffset;
ArrayType *retval; int ndim,
flags,
dim[MAXDIM],
lBound[MAXDIM];
ArrayMetaState *my_extra;
/* Get the array header information */
ndim = pq_getmsgint(buf, 4); if (ndim < 0) /* we do allow zero-dimension arrays */
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of dimensions: %d", ndim))); if (ndim > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndim, MAXDIM)));
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->typioparam, &my_extra->typiofunc); if (!OidIsValid(my_extra->typiofunc))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("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;
}
if (nitems == 0)
{ /* Return empty array ... but not till we've validated element_type */
PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
}
for (i = 0; i < nitems; i++)
{ int itemlen;
StringInfoData elem_buf;
/* Get and check the item length */
itemlen = pq_getmsgint(buf, 4); if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("insufficient data left in message")));
/* Now call the element's receiveproc */
values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
typioparam, typmod);
nulls[i] = false;
/* Trouble if it didn't eat the whole buffer */ if (elem_buf.cursor != itemlen)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("improper binary format in array element %d",
i + 1)));
}
/* *Checkfornulls,computetotaldataspaceneeded
*/
hasnull = false;
totbytes = 0; for (i = 0; i < nitems; i++)
{ if (nulls[i])
hasnull = true; else
{ /* let's just make sure data is not toasted */ if (typlen == -1)
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
totbytes = att_addlength_datum(totbytes, typlen, values[i]);
totbytes = att_align_nominal(totbytes, typalign); /* check for overflow of total request */ if (!AllocSizeIsValid(totbytes))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxAllocSize)));
}
}
*hasnulls = hasnull;
*nbytes = totbytes;
}
/* *array_send: *takestheinternalrepresentationofanarrayandreturnsabytea *containingthearrayinitsexternalbinaryformat.
*/
Datum
array_send(PG_FUNCTION_ARGS)
{
AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
Oid element_type = AARR_ELEMTYPE(v); int typlen; bool typbyval; char typalign; int nitems,
i; int ndim,
*dim,
*lb;
StringInfoData buf;
array_iter iter;
ArrayMetaState *my_extra;
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->typioparam, &my_extra->typiofunc); if (!OidIsValid(my_extra->typiofunc))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("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;
/* Sanity check: does it look like an array at all? */ if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
PG_RETURN_NULL();
dimv = AARR_DIMS(v);
lb = AARR_LBOUND(v);
p = buf; for (i = 0; i < AARR_NDIM(v); i++)
{
sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
p += strlen(p);
}
PG_RETURN_TEXT_P(cstring_to_text(buf));
}
/* *array_lower: *returnsthelowerdimension,oftheDIMrequested,for *thearraypointedtoby"v",asanint4
*/
Datum
array_lower(PG_FUNCTION_ARGS)
{
AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0); int reqdim = PG_GETARG_INT32(1); int *lb; int result;
/* Sanity check: does it look like an array at all? */ if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
PG_RETURN_NULL();
/* Sanity check: was the requested dim valid */ if (reqdim <= 0 || reqdim > AARR_NDIM(v))
PG_RETURN_NULL();
lb = AARR_LBOUND(v);
result = lb[reqdim - 1];
PG_RETURN_INT32(result);
}
/* *array_upper: *returnstheupperdimension,oftheDIMrequested,for *thearraypointedtoby"v",asanint4
*/
Datum
array_upper(PG_FUNCTION_ARGS)
{
AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0); int reqdim = PG_GETARG_INT32(1); int *dimv,
*lb; int result;
/* Sanity check: does it look like an array at all? */ if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
PG_RETURN_NULL();
/* Sanity check: was the requested dim valid */ if (reqdim <= 0 || reqdim > AARR_NDIM(v))
PG_RETURN_NULL();
lb = AARR_LBOUND(v);
dimv = AARR_DIMS(v);
result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
PG_RETURN_INT32(result);
}
/* *array_length: *returnsthelength,ofthedimensionrequested,for *thearraypointedtoby"v",asanint4
*/
Datum
array_length(PG_FUNCTION_ARGS)
{
AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0); int reqdim = PG_GETARG_INT32(1); int *dimv; int result;
/* Sanity check: does it look like an array at all? */ if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
PG_RETURN_NULL();
/* Sanity check: was the requested dim valid */ if (reqdim <= 0 || reqdim > AARR_NDIM(v))
PG_RETURN_NULL();
/* *Implementationofarray_get_element()foranexpandedarray
*/ static Datum
array_get_element_expanded(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
{
ExpandedArrayHeader *eah; int i,
ndim,
*dim,
*lb,
offset;
Datum *dvalues; bool *dnulls;
if (nSubscripts <= 0 || nSubscripts > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts")));
/* make sure item to be inserted is not toasted */ if (elmlen == -1 && !isNull)
dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
{ /* expanded array: let's do this in a separate function */ return array_set_element_expanded(arraydatum,
nSubscripts,
indx,
dataValue,
isNull,
arraytyplen,
elmlen,
elmbyval,
elmalign);
}
/* detoast input array if necessary */
array = DatumGetArrayTypeP(arraydatum);
ndim = ARR_NDIM(array);
/* *ifnumberofdimsiszero,i.e.anemptyarray,createanarraywith *nSubscriptsdimensions,andsetthelowerboundstothesupplied *subscripts
*/ if (ndim == 0)
{
Oid elmtype = ARR_ELEMTYPE(array);
for (i = 0; i < nSubscripts; i++)
{
dim[i] = 1;
lb[i] = indx[i];
}
/* palloc0 above already marked any inserted positions as nulls */ /* Fix the inserted value */ if (addedafter)
array_set_isnull(newnullbitmap, newnitems - 1, isNull); else
array_set_isnull(newnullbitmap, offset, isNull); /* Fix the copied range(s) */ if (addedbefore)
array_bitmap_copy(newnullbitmap, addedbefore,
oldnullbitmap, 0,
oldnitems); else
{
array_bitmap_copy(newnullbitmap, 0,
oldnullbitmap, 0,
offset); if (addedafter == 0)
array_bitmap_copy(newnullbitmap, offset + 1,
oldnullbitmap, offset + 1,
oldnitems - offset - 1);
}
}
return PointerGetDatum(newarray);
}
/* *Implementationofarray_set_element()foranexpandedarray * *Note:aswithanyoperationonaread/writeexpandedobject,wemust *takepainsnottoleavetheobjectinacorruptstateifwefailpartway *through.
*/ static Datum
array_set_element_expanded(Datum arraydatum, int nSubscripts, int *indx,
Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
{
ExpandedArrayHeader *eah;
Datum *dvalues; bool *dnulls; int i,
ndim,
dim[MAXDIM],
lb[MAXDIM],
offset; bool dimschanged,
newhasnulls; int addedbefore,
addedafter; char *oldValue;
/* Convert to R/W object if not so already */
eah = DatumGetExpandedArray(arraydatum);
/* Sanity-check caller's info against object; we don't use it otherwise */
Assert(arraytyplen == -1);
Assert(elmlen == eah->typlen);
Assert(elmbyval == eah->typbyval);
Assert(elmalign == eah->typalign);
/* *Checksubscripts(thislogicmustmatcharray_set_element).Weassume *theexistingsubscriptspassedArrayCheckBounds,sothatdim[i]+lb[i] *canbecomputedwithoutoverflow.Butwemustbewareofother *overflowsinourcalculationsofnewdim[]values.
*/ if (ndim == 1)
{ if (indx[0] < lb[0])
{ /* addedbefore = lb[0] - indx[0]; */ /* dim[0] += addedbefore; */ if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
lb[0] = indx[0];
dimschanged = true; if (addedbefore > 1)
newhasnulls = true; /* will insert nulls */
} if (indx[0] >= (dim[0] + lb[0]))
{ /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */ /* dim[0] += addedafter; */ if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
pg_add_s32_overflow(addedafter, 1, &addedafter) ||
pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
dimschanged = true; if (addedafter > 1)
newhasnulls = true; /* will insert nulls */
}
} else
{ /* *XXXcurrentlywedonotsupportextendingmulti-dimensionalarrays *duringassignment
*/ for (i = 0; i < ndim; i++)
{ if (indx[i] < lb[i] ||
indx[i] >= (dim[i] + lb[i]))
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("array subscript out of range")));
}
}
/* Check for overflow of the array dimensions */ if (dimschanged)
{
(void) ArrayGetNItems(ndim, dim);
ArrayCheckBounds(ndim, dim, lb);
}
/* Now we can calculate linear offset of target item in array */
offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
/* Physically enlarge existing dvalues/dnulls arrays if needed */ if (dim[0] > eah->dvalueslen)
{ /* We want some extra space if we're enlarging */ int newlen = dim[0] + dim[0] / 8;
/* Flattened value will no longer represent array accurately */
eah->fvalue = NULL; /* And we don't know the flattened size either */
eah->flat_size = 0;
/* Update dimensionality info if needed */ if (dimschanged)
{
eah->ndims = ndim;
memcpy(eah->dims, dim, ndim * sizeof(int));
memcpy(eah->lbound, lb, ndim * sizeof(int));
}
/* Reposition items if needed, and fill addedbefore items with nulls */ if (addedbefore > 0)
{
memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum)); for (i = 0; i < addedbefore; i++)
dvalues[i] = (Datum) 0; if (dnulls)
{
memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool)); for (i = 0; i < addedbefore; i++)
dnulls[i] = true;
}
eah->nelems += addedbefore;
}
/* fill addedafter items with nulls */ if (addedafter > 0)
{ for (i = 0; i < addedafter; i++)
dvalues[eah->nelems + i] = (Datum) 0; if (dnulls)
{ for (i = 0; i < addedafter; i++)
dnulls[eah->nelems + i] = true;
}
eah->nelems += addedafter;
}
/* Grab old element value for pfree'ing, if needed. */ if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
oldValue = (char *) DatumGetPointer(dvalues[offset]); else
oldValue = NULL;
/* And finally we can insert the new element. */
dvalues[offset] = dataValue; if (dnulls)
dnulls[offset] = isNull;
/* *Freeoldelementifneeded;thiskeepsrepeatedelementreplacements *frombloatingthearray'sstorage.Ifthepfreesomehowfails,it *won'tcorruptthearray.
*/ if (oldValue)
{ /* Don't try to pfree a part of the original flat array */ if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
pfree(oldValue);
}
/* Done, return standard TOAST pointer for object */ return EOHPGetRWDatum(&eah->hdr);
}
/* *array_set_slice: *Thisroutinesetsthevalueofarangeofarraylocations(specified *byupperandlowersubscriptvalues)tonewvaluespassedas *anotherarray. * *Thishandlesbothordinaryvarlenaarraysandfixed-lengtharrays. * *Inputs: *arraydatum:theinitialarrayobject(mustn'tbeNULL) *nSubscripts:numberofsubscriptssupplied(mustbesameforupper/lower) *upperIndx[]:theuppersubscriptvalues *lowerIndx[]:thelowersubscriptvalues *upperProvided[]:trueforprovideduppersubscriptvalues *lowerProvided[]:trueforprovidedlowersubscriptvalues *srcArrayDatum:thesourcefortheinsertedvalues *isNull:indicateswhethersrcArrayDatumisNULL *arraytyplen:pg_type.typlenforthearraytype *elmlen:pg_type.typlenforthearray'selementtype *elmbyval:pg_type.typbyvalforthearray'selementtype *elmalign:pg_type.typalignforthearray'selementtype * *Result: *Anewarrayisreturned,justliketheoldexceptforthe *modifiedrange.Theoriginalarrayobjectisnotchanged. * *Omittedupperandlowersubscriptvaluesarereplacedbythecorresponding *arraybound. * *Forone-dimensionalarraysonly,weallowthearraytobeextended *byassigningtopositionsoutsidetheexistingsubscriptrange;any *positionsbetweentheexistingelementsandthenewonesaresettoNULLs. *(XXXTODO:allowacorrespondingbehaviorformultidimensionalarrays) * *NOTE:weassumeitisOKtoscribbleontheprovidedindexarrays *lowerIndx[]andupperIndx[];also,thesearraysmustbeofsizeMAXDIM *evenwhennSubscriptsisless.Thesearegenerallyjusttemporaries. * *NOTE:Forassignments,wethrowanerrorforsillysubscriptsetc, *ratherthanreturningaNULLoremptyarrayasthefetchoperationsdo.
*/
Datum
array_set_slice(Datum arraydatum, int nSubscripts, int *upperIndx, int *lowerIndx, bool *upperProvided, bool *lowerProvided,
Datum srcArrayDatum, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
{
ArrayType *array;
ArrayType *srcArray;
ArrayType *newarray; int i,
ndim,
dim[MAXDIM],
lb[MAXDIM],
span[MAXDIM]; bool newhasnulls; int nitems,
nsrcitems,
olddatasize,
newsize,
olditemsize,
newitemsize,
overheadlen,
oldoverheadlen,
addedbefore,
addedafter,
lenbefore,
lenafter,
itemsbefore,
itemsafter,
nolditems;
/* Currently, assignment from a NULL source array is a no-op */ if (isNull) return arraydatum;
if (arraytyplen > 0)
{ /* *fixed-lengtharrays--notgotroundtodoingthis...
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("updates on slices of fixed-length arrays not implemented")));
}
/* note: we assume srcArray contains no toasted elements */
ndim = ARR_NDIM(array);
/* *ifnumberofdimsiszero,i.e.anemptyarray,createanarraywith *nSubscriptsdimensions,andsettheupperandlowerboundstothe *suppliedsubscripts
*/ if (ndim == 0)
{
Datum *dvalues; bool *dnulls; int nelems;
Oid elmtype = ARR_ELEMTYPE(array);
for (i = 0; i < nSubscripts; i++)
{ if (!upperProvided[i] || !lowerProvided[i])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("array slice subscript must provide both boundaries"),
errdetail("When assigning to a slice of an empty array value," " slice boundaries must be fully specified.")));
/* compute "upperIndx[i] - lowerIndx[i] + 1", detecting overflow */ if (pg_sub_s32_overflow(upperIndx[i], lowerIndx[i], &dim[i]) ||
pg_add_s32_overflow(dim[i], 1, &dim[i]))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
lb[i] = lowerIndx[i];
}
/* complain if too few source items; we ignore extras, however */ if (nelems < ArrayGetNItems(nSubscripts, dim))
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("source array too small")));
/* Loop over source data */
array_iter_setup(&iter, v);
hasnulls = false;
for (i = 0; i < nitems; i++)
{ /* Get source element, checking for NULL */
*transform_source =
array_iter_next(&iter, transform_source_isnull, i,
inp_typlen, inp_typbyval, inp_typalign);
/* Apply the given expression to source element */
values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
if (nulls[i])
hasnulls = true; else
{ /* Ensure data is not toasted */ if (typlen == -1)
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i])); /* Update total result size */
nbytes = att_addlength_datum(nbytes, typlen, values[i]);
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)));
}
}
/* Allocate and fill the result array */ if (hasnulls)
{
dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
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 = retType;
memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
/* *construct_md_array---simplemethodforconstructinganarrayobject *witharbitrarydimensionsandpossibleNULLs * *elems:arrayofDatumitemstobecomethearraycontents *nulls:arrayofis-nullflags(canbeNULLifnonulls) *ndims:numberofdimensions *dims:integerarraywithsizeofeachdimension *lbs:integerarraywithlowerboundofeachdimension *elmtype,elmlen,elmbyval,elmalign:infoforthedatatypeoftheitems * *Apalloc'dndims-Darrayobjectisconstructedandreturned.Notethat *elemvalueswillbecopiedintotheobjectevenifpass-by-reftype. *Alsonotetheresultwillbe0-Dnotndims-Difanydims[i]=0. * *NOTE:itwouldbecleanertolookuptheelmlen/elmbval/elmaligninfo *fromthesystemcatalogs,giventheelmtype.However,thecalleris *inabetterpositiontocachethisinfoacrossmultipleuses,oreven *tohard-wirevaluesiftheelementtypeishard-wired.
*/
ArrayType *
construct_md_array(Datum *elems, bool *nulls, int ndims, int *dims, int *lbs,
Oid elmtype, int elmlen, bool elmbyval, char elmalign)
{
ArrayType *result; bool hasnulls;
int32 nbytes;
int32 dataoffset; int i; int nelems;
if (ndims < 0) /* we do allow zero-dimension arrays */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid number of dimensions: %d", ndims))); if (ndims > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndims, MAXDIM)));
/* This checks for overflow of the array dimensions */
nelems = ArrayGetNItems(ndims, dims);
ArrayCheckBounds(ndims, dims, lbs);
/* if ndims <= 0 or any dims[i] == 0, return empty array */ if (nelems <= 0) return construct_empty_array(elmtype);
/* compute required space */
nbytes = 0;
hasnulls = false; for (i = 0; i < nelems; i++)
{ if (nulls && nulls[i])
{
hasnulls = true; continue;
} /* make sure data is not toasted */ if (elmlen == -1)
elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
nbytes = att_align_nominal(nbytes, elmalign); /* 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)));
}
/* Allocate and initialize result array */ if (hasnulls)
{
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
nbytes += dataoffset;
} else
{
dataoffset = 0; /* marker for no null bitmap */
nbytes += ARR_OVERHEAD_NONULLS(ndims);
}
result = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(result, nbytes);
result->ndim = ndims;
result->dataoffset = dataoffset;
result->elemtype = elmtype;
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
/* check whole bytes of the bitmap byte-at-a-time */ while (nelems >= 8)
{ if (*bitmap != 0xFF) returntrue;
bitmap++;
nelems -= 8;
}
/* check last partial byte */
bitmask = 1; while (nelems > 0)
{ if ((*bitmap & bitmask) == 0) returntrue;
bitmask <<= 1;
nelems--;
}
returnfalse;
}
/* *array_eq: *comparestwoarraysforequality *result: *returnstrueifthearraysareequal,falseotherwise. * *Note:wedonotusearray_cmphere,sinceequalitymaybemeaningfulin *datatypesthatdon'thaveatotalordering(andhencenobtreesupport).
*/
Datum
array_eq(PG_FUNCTION_ARGS)
{
LOCAL_FCINFO(locfcinfo, 2);
AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
Oid collation = PG_GET_COLLATION(); int ndims1 = AARR_NDIM(array1); int ndims2 = AARR_NDIM(array2); int *dims1 = AARR_DIMS(array1); int *dims2 = AARR_DIMS(array2); int *lbs1 = AARR_LBOUND(array1); int *lbs2 = AARR_LBOUND(array2);
Oid element_type = AARR_ELEMTYPE(array1); bool result = true; int nitems;
TypeCacheEntry *typentry; int typlen; bool typbyval; char typalign;
array_iter it1;
array_iter it2; int i;
if (element_type != AARR_ELEMTYPE(array2))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare arrays of different element types")));
/* fast path if the arrays do not have the same dimensionality */ if (ndims1 != ndims2 ||
memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
result = false; else
{ /* *Wearrangetolookuptheequalityfunctiononlyonceperseriesof *calls,assumingtheelementtypedoesn'tchangeunderneathus.The *typcacheisusedsothatwehavenomemoryleakagewhenbeingused *asanindexsupportfunction.
*/
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 = typentry;
}
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
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() *Internalcomparisonfunctionforarrays. * *Returns-1,0or1
*/ staticint
array_cmp(FunctionCallInfo fcinfo)
{
LOCAL_FCINFO(locfcinfo, 2);
AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
Oid collation = PG_GET_COLLATION(); int ndims1 = AARR_NDIM(array1); int ndims2 = AARR_NDIM(array2); int *dims1 = AARR_DIMS(array1); int *dims2 = AARR_DIMS(array2); int nitems1 = ArrayGetNItems(ndims1, dims1); int nitems2 = ArrayGetNItems(ndims2, dims2);
Oid element_type = AARR_ELEMTYPE(array1); int result = 0;
TypeCacheEntry *typentry; int typlen; bool typbyval; char typalign; int min_nitems;
array_iter it1;
array_iter it2; int i;
if (element_type != AARR_ELEMTYPE(array2))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare arrays of different element types")));
/* *Wearrangetolookupthecomparisonfunctiononlyonceperseriesof *calls,assumingtheelementtypedoesn'tchangeunderneathus.The *typcacheisusedsothatwehavenomemoryleakagewhenbeingusedas *anindexsupportfunction.
*/
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; if (typentry == NULL ||
typentry->type_id != element_type)
{
typentry = lookup_type_cache(element_type,
TYPECACHE_CMP_PROC_FINFO); if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify a comparison function for type %s",
format_type_be(element_type))));
fcinfo->flinfo->fn_extra = typentry;
}
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
/* Loop over source data */
min_nitems = Min(nitems1, nitems2);
array_iter_setup(&it1, array1);
array_iter_setup(&it2, array2);
for (i = 0; i < min_nitems; i++)
{
Datum elt1;
Datum elt2; bool isnull1; bool isnull2;
int32 cmpresult;
/* Get elements, checking for NULL */
elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
/* *WeconsidertwoNULLsequal;NULL>not-NULL.
*/ if (isnull1 && isnull2) continue; if (isnull1)
{ /* arg1 is greater than arg2 */
result = 1; break;
} if (isnull2)
{ /* arg1 is less than arg2 */
result = -1; break;
}
/* Compare the pair of elements */
locfcinfo->args[0].value = elt1;
locfcinfo->args[0].isnull = false;
locfcinfo->args[1].value = elt2;
locfcinfo->args[1].isnull = false;
cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
/* We don't expect comparison support functions to return null */
Assert(!locfcinfo->isnull);
if (cmpresult == 0) continue; /* equal */
if (cmpresult < 0)
{ /* arg1 is less than arg2 */
result = -1; break;
} else
{ /* arg1 is greater than arg2 */
result = 1; break;
}
}
/* *Ifarrayscontainsamedata(uptoendofshorterone),apply *additionalrulestosortbydimensionality.Therelativesignificance *ofthedifferentbitsofinformationishistorical;mainlywejustcare *thatwedon'tsay"equal"forarraysofdifferentdimensionality.
*/ if (result == 0)
{ if (nitems1 != nitems2)
result = (nitems1 < nitems2) ? -1 : 1; elseif (ndims1 != ndims2)
result = (ndims1 < ndims2) ? -1 : 1; else
{ for (i = 0; i < ndims1; i++)
{ if (dims1[i] != dims2[i])
{
result = (dims1[i] < dims2[i]) ? -1 : 1; break;
}
} if (result == 0)
{ int *lbound1 = AARR_LBOUND(array1); int *lbound2 = AARR_LBOUND(array2);
for (i = 0; i < ndims1; i++)
{ if (lbound1[i] != lbound2[i])
{
result = (lbound1[i] < lbound2[i]) ? -1 : 1; break;
}
}
}
}
}
Datum
hash_array(PG_FUNCTION_ARGS)
{
LOCAL_FCINFO(locfcinfo, 1);
AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0); int ndims = AARR_NDIM(array); int *dims = AARR_DIMS(array);
Oid element_type = AARR_ELEMTYPE(array);
uint32 result = 1; int nitems;
TypeCacheEntry *typentry; int typlen; bool typbyval; char typalign; int i;
array_iter iter;
/* *Wearrangetolookupthehashfunctiononlyonceperseriesofcalls, *assumingtheelementtypedoesn'tchangeunderneathus.Thetypcache *isusedsothatwehavenomemoryleakagewhenbeingusedasanindex *supportfunction.
*/
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; if (typentry == NULL ||
typentry->type_id != element_type)
{
typentry = lookup_type_cache(element_type,
TYPECACHE_HASH_PROC_FINFO); if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify a hash function for type %s",
format_type_be(element_type))));
/* fill in what we need below */
record_typentry->typlen = typentry->typlen;
record_typentry->typbyval = typentry->typbyval;
record_typentry->typalign = typentry->typalign;
fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
/* *array_contain_compare: *comparestwoarraysforoverlap/containment * *Whenmatchallistrue,returntrueifallmembersofarray1areinarray2. *Whenmatchallisfalse,returntrueifanymembersofarray1areinarray2.
*/ staticbool
array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation, bool matchall, void **fn_extra)
{
LOCAL_FCINFO(locfcinfo, 2); bool result = matchall;
Oid element_type = AARR_ELEMTYPE(array1);
TypeCacheEntry *typentry; int nelems1;
Datum *values2; bool *nulls2; int nelems2; int typlen; bool typbyval; char typalign; int i; int j;
array_iter it1;
if (element_type != AARR_ELEMTYPE(array2))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot compare arrays of different element types")));
/* *Wearrangetolookuptheequalityfunctiononlyonceperseriesof *calls,assumingtheelementtypedoesn'tchangeunderneathus.The *typcacheisusedsothatwehavenomemoryleakagewhenbeingusedas *anindexsupportfunction.
*/
typentry = (TypeCacheEntry *) *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))));
*fn_extra = typentry;
}
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
/* *Sinceweprobablywillneedtoscanarray2multipletimes,it's *worthwhiletousedeconstruct_arrayonit.Wescanarray1thehardway *however,sinceweverylikelywon'tneedtolookatallofit.
*/ if (VARATT_IS_EXPANDED_HEADER(array2))
{ /* This should be safe even if input is read-only */
deconstruct_expanded_array(&(array2->xpn));
values2 = array2->xpn.dvalues;
nulls2 = array2->xpn.dnulls;
nelems2 = array2->xpn.nelems;
} else
deconstruct_array((ArrayType *) array2,
element_type, typlen, typbyval, typalign,
&values2, &nulls2, &nelems2);
if (j < nelems2)
{ /* found a match for elt1 */ if (!matchall)
{
result = true; break;
}
} else
{ /* no match for elt1 */ if (matchall)
{
result = false; break;
}
}
}
return result;
}
Datum
arrayoverlap(PG_FUNCTION_ARGS)
{
AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
Oid collation = PG_GET_COLLATION(); bool result;
result = array_contain_compare(array1, array2, collation, false,
&fcinfo->flinfo->fn_extra);
/* *Iteratethroughthearrayreferencedby'iterator'. * *Aslongasthereisanotherelement(orslice),returnitinto **value/*isnull,andreturntrue.Returnfalsewhennomoredata.
*/ bool
array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
{ /* Done if we have reached the end of the array */ if (iterator->current_item >= iterator->nitems) returnfalse;
if (iterator->slice_ndim == 0)
{ /* *Scalarcase:returnoneelement.
*/ if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
{
*isnull = true;
*value = (Datum) 0;
} else
{ /* non-NULL, so fetch the individual Datum to return */ char *p = iterator->data_ptr;
/* Move our data pointer forward to the next element */
p = att_addlength_pointer(p, iterator->typlen, p);
p = (char *) att_align_nominal(p, iterator->typalign);
iterator->data_ptr = p;
}
} else
{ /* *Slicecase:buildandreturnanarrayoftherequestedsize.
*/
ArrayType *result;
Datum *values = iterator->slice_values; bool *nulls = iterator->slice_nulls; char *p = iterator->data_ptr; int i;
for (i = 0; i < iterator->slice_len; i++)
{ if (array_get_isnull(iterator->nullbitmap,
iterator->current_item++))
{
nulls[i] = true;
values[i] = (Datum) 0;
} else
{
nulls[i] = false;
values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
/* Move our data pointer forward to the next element */
p = att_addlength_pointer(p, iterator->typlen, p);
p = (char *) att_align_nominal(p, iterator->typalign);
}
}
/* *Copynitemsnull-bitmapbitsfromsourcetodestination * *destbitmap:startofdestinationarray'snullbitmap(mustn'tbeNULL) *destoffset:0-basedlinearelementnumberoffirstdestelement *srcbitmap:startofsourcearray'snullbitmap,orNULLifnone *srcoffset:0-basedlinearelementnumberoffirstsourceelement *nitems:numberofbitstocopy(>=0) * *IfsrcbitmapisNULLthenweassumethesourceisall-non-NULLand *fill1'sintothedestinationbitmap.Notethatonlythespecified *bitsinthedestinationmaparechanged,notanybeforeorafter. * *Note:thiscouldcertainlybeoptimizedusingstandardbitbltmethods. *However,it'snotclearthatthetypicalPostgresarrayhasenoughelements *tomakeitworthworryingtoomuch.Forthemoment,KISS.
*/ void
array_bitmap_copy(bits8 *destbitmap, int destoffset, const bits8 *srcbitmap, int srcoffset, int nitems)
{ int destbitmask,
destbitval,
srcbitmask,
srcbitval;
Assert(destbitmap); if (nitems <= 0) return; /* don't risk fetch off end of memory */
destbitmap += destoffset / 8;
destbitmask = 1 << (destoffset % 8);
destbitval = *destbitmap; if (srcbitmap)
{
srcbitmap += srcoffset / 8;
srcbitmask = 1 << (srcoffset % 8);
srcbitval = *srcbitmap; while (nitems-- > 0)
{ if (srcbitval & srcbitmask)
destbitval |= destbitmask; else
destbitval &= ~destbitmask;
destbitmask <<= 1; if (destbitmask == 0x100)
{
*destbitmap++ = destbitval;
destbitmask = 1; if (nitems > 0)
destbitval = *destbitmap;
}
srcbitmask <<= 1; if (srcbitmask == 0x100)
{
srcbitmap++;
srcbitmask = 1; if (nitems > 0)
srcbitval = *srcbitmap;
}
} if (destbitmask != 1)
*destbitmap = destbitval;
} else
{ while (nitems-- > 0)
{
destbitval |= destbitmask;
destbitmask <<= 1; if (destbitmask == 0x100)
{
*destbitmap++ = destbitval;
destbitmask = 1; if (nitems > 0)
destbitval = *destbitmap;
}
} if (destbitmask != 1)
*destbitmap = destbitval;
}
}
/* *Computespaceneededforasliceofanarray * *Weassumethecallerhasverifiedthattheslicecoordinatesarevalid.
*/ staticint
array_slice_size(char *arraydataptr, bits8 *arraynullsptr, int ndim, int *dim, int *lb, int *st, int *endp, int typlen, bool typbyval, char typalign)
{ int src_offset,
span[MAXDIM],
prod[MAXDIM],
dist[MAXDIM],
indx[MAXDIM]; char *ptr; int i,
j,
inc; int count = 0;
mda_get_range(ndim, span, st, endp);
/* Pretty easy for fixed element length without nulls ... */ if (typlen > 0 && !arraynullsptr) return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
/* Else gotta do it the hard way */
src_offset = ArrayGetOffset(ndim, dim, lb, st);
ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
typlen, typbyval, typalign);
mda_get_prod(ndim, dim, prod);
mda_get_offset_values(ndim, dist, prod, span); for (i = 0; i < ndim; i++)
indx[i] = 0;
j = ndim - 1; do
{ if (dist[j])
{
ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
typlen, typbyval, typalign);
src_offset += dist[j];
} if (!array_get_isnull(arraynullsptr, src_offset))
{
inc = att_addlength_pointer(0, typlen, ptr);
inc = att_align_nominal(inc, typalign);
ptr += inc;
count += inc;
}
src_offset++;
} while ((j = mda_next_tuple(ndim, indx, span)) != -1); return count;
}
/* *Extractasliceofanarrayintoconsecutiveelementsinthedestination *array. * *Weassumethecallerhasverifiedthattheslicecoordinatesarevalid, *allocatedenoughstoragefortheresult,andinitializedtheheader *ofthenewarray.
*/ staticvoid
array_extract_slice(ArrayType *newarray, int ndim, int *dim, int *lb, char *arraydataptr,
bits8 *arraynullsptr, int *st, int *endp, int typlen, bool typbyval, char typalign)
{ char *destdataptr = ARR_DATA_PTR(newarray);
bits8 *destnullsptr = ARR_NULLBITMAP(newarray); char *srcdataptr; int src_offset,
dest_offset,
prod[MAXDIM],
span[MAXDIM],
dist[MAXDIM],
indx[MAXDIM]; int i,
j,
inc;
/* *Insertasliceintoanarray. * *ndim/dim[]/lb[]aredimensionsoftheoriginalarray.Anewarraywith *thosesamedimensionsistobeconstructed.destArraymustalready *havebeenallocatedanditsheaderinitialized. * *st[]/endp[]identifytheslicetobereplaced.Elementswithintheslice *volumearetakenfromconsecutiveelementsofthesrcArray;elements *outsideitarecopiedfromorigArray. * *Weassumethecallerhasverifiedthattheslicecoordinatesarevalid.
*/ staticvoid
array_insert_slice(ArrayType *destArray,
ArrayType *origArray,
ArrayType *srcArray, int ndim, int *dim, int *lb, int *st, int *endp, int typlen, bool typbyval, char typalign)
{ char *destPtr = ARR_DATA_PTR(destArray); char *origPtr = ARR_DATA_PTR(origArray); char *srcPtr = ARR_DATA_PTR(srcArray);
bits8 *destBitmap = ARR_NULLBITMAP(destArray);
bits8 *origBitmap = ARR_NULLBITMAP(origArray);
bits8 *srcBitmap = ARR_NULLBITMAP(srcArray); int orignitems = ArrayGetNItems(ARR_NDIM(origArray),
ARR_DIMS(origArray)); int dest_offset,
orig_offset,
src_offset,
prod[MAXDIM],
span[MAXDIM],
dist[MAXDIM],
indx[MAXDIM]; int i,
j,
inc;
dest_offset = ArrayGetOffset(ndim, dim, lb, st); /* copy items before the slice start */
inc = array_copy(destPtr, dest_offset,
origPtr, 0, origBitmap,
typlen, typbyval, typalign);
destPtr += inc;
origPtr += inc; if (destBitmap)
array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
orig_offset = dest_offset;
mda_get_prod(ndim, dim, prod);
mda_get_range(ndim, span, st, endp);
mda_get_offset_values(ndim, dist, prod, span); for (i = 0; i < ndim; i++)
indx[i] = 0;
src_offset = 0;
j = ndim - 1; do
{ /* Copy/advance over elements between here and next part of slice */ if (dist[j])
{
inc = array_copy(destPtr, dist[j],
origPtr, orig_offset, origBitmap,
typlen, typbyval, typalign);
destPtr += inc;
origPtr += inc; if (destBitmap)
array_bitmap_copy(destBitmap, dest_offset,
origBitmap, orig_offset,
dist[j]);
dest_offset += dist[j];
orig_offset += dist[j];
} /* Copy new element at this slice position */
inc = array_copy(destPtr, 1,
srcPtr, src_offset, srcBitmap,
typlen, typbyval, typalign); if (destBitmap)
array_bitmap_copy(destBitmap, dest_offset,
srcBitmap, src_offset, 1);
destPtr += inc;
srcPtr += inc;
dest_offset++;
src_offset++; /* Advance over old element at this slice position */
origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
typlen, typbyval, typalign);
orig_offset++;
} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
/* don't miss any data at the end */
array_copy(destPtr, orignitems - orig_offset,
origPtr, orig_offset, origBitmap,
typlen, typbyval, typalign); if (destBitmap)
array_bitmap_copy(destBitmap, dest_offset,
origBitmap, orig_offset,
orignitems - orig_offset);
}
/* Make a temporary context to hold all the junk */ if (subcontext)
arr_context = AllocSetContextCreate(rcontext, "accumArrayResult",
ALLOCSET_DEFAULT_SIZES);
/* *initArrayResultArr-initializeanemptyArrayBuildStateArr * *array_typeisthearraytype(mustbeavalidvarlenaarraytype) *element_typeisthetypeofthearray'selements(lookupifInvalidOid) *rcontextiswheretokeepworkingstate *subcontextisaflagdeterminingwhethertouseaseparatememorycontext
*/
ArrayBuildStateArr *
initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext, bool subcontext)
{
ArrayBuildStateArr *astate;
MemoryContext arr_context = rcontext; /* by default use the parent ctx */
/* Lookup element type, unless element_type already provided */ if (!OidIsValid(element_type))
{
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("data type %s is not an array type",
format_type_be(array_type))));
}
/* Make a temporary context to hold all the junk */ if (subcontext)
arr_context = AllocSetContextCreate(rcontext, "accumArrayResultArr",
ALLOCSET_DEFAULT_SIZES);
/* Note we initialize all fields to zero */
astate = (ArrayBuildStateArr *)
MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
astate->mcontext = arr_context;
astate->private_cxt = subcontext;
/* Save relevant datatype information */
astate->array_type = array_type;
astate->element_type = element_type;
return astate;
}
/* *accumArrayResultArr-accumulateone(more)sub-arrayforanarrayresult * *astateisworkingstate(canbeNULLonfirstcall) *dvalue/disnullrepresentthenewsub-arraytoappendtothearray *array_typeisthearraytype(mustbeavalidvarlenaarraytype) *rcontextiswheretokeepworkingstate
*/
ArrayBuildStateArr *
accumArrayResultArr(ArrayBuildStateArr *astate,
Datum dvalue, bool disnull,
Oid array_type,
MemoryContext rcontext)
{
ArrayType *arg;
MemoryContext oldcontext; int *dims,
*lbs,
ndims,
nitems,
ndatabytes; char *data; int i; int newnitems;
/* Check that the array doesn't grow too large */
newnitems = astate->nitems + nitems; if (newnitems > MaxArraySize)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%zu)",
MaxArraySize)));
if (astate->ndims == 0)
{ /* First input; check/save the dimensionality info */
/* Should we allow empty inputs and just produce an empty output? */ if (ndims == 0)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot accumulate empty arrays"))); if (ndims + 1 > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndims + 1, MAXDIM)));
/* Allocate at least enough data space for this item */
astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
astate->data = (char *) palloc(astate->abytes);
} else
{ /* Second or later input: must match first input's dimensionality */ if (astate->ndims != ndims + 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot accumulate arrays of different dimensionality"))); for (i = 0; i < ndims; i++)
{ if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot accumulate arrays of different dimensionality")));
}
/* Enlarge data space if needed */ if (astate->nbytes + ndatabytes > astate->abytes)
{
astate->abytes = Max(astate->abytes * 2,
astate->nbytes + ndatabytes);
astate->data = (char *) repalloc(astate->data, astate->abytes);
}
}
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
if (astate->ndims == 0)
{ /* No inputs, return empty array */
result = construct_empty_array(astate->element_type);
} else
{ int dataoffset,
nbytes;
/* Check for overflow of the array dimensions */
(void) ArrayGetNItems(astate->ndims, astate->dims);
ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
/* *makeArrayResultAny-producefinalresultofaccumArrayResultAny * *astateisworkingstate(mustnotbeNULL) *rcontextiswheretoconstructresult *releaseistrueifokaytoreleaseworkingstate
*/
Datum
makeArrayResultAny(ArrayBuildStateAny *astate,
MemoryContext rcontext, bool release)
{
Datum result;
if (astate->scalarstate)
{ /* Must use makeMdArrayResult to support "release" parameter */ int ndims; int dims[1]; int lbs[1];
/* If no elements were presented, we want to create an empty array */
ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
dims[0] = astate->scalarstate->nelems;
lbs[0] = 1;
result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
rcontext, release);
} else
{
result = makeArrayResultArr(astate->arraystate,
rcontext, release);
} return result;
}
Datum
array_larger(PG_FUNCTION_ARGS)
{ if (array_cmp(fcinfo) > 0)
PG_RETURN_DATUM(PG_GETARG_DATUM(0)); else
PG_RETURN_DATUM(PG_GETARG_DATUM(1));
}
Datum
array_smaller(PG_FUNCTION_ARGS)
{ if (array_cmp(fcinfo) < 0)
PG_RETURN_DATUM(PG_GETARG_DATUM(0)); else
PG_RETURN_DATUM(PG_GETARG_DATUM(1));
}
/* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL())
{
AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0); int reqdim = PG_GETARG_INT32(1); int *lb,
*dimv;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* Sanity check: does it look like an array at all? */ if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
SRF_RETURN_DONE(funcctx);
/* Sanity check: was the requested dim valid */ if (reqdim <= 0 || reqdim > AARR_NDIM(v))
SRF_RETURN_DONE(funcctx);
if (fctx->lower <= fctx->upper)
{ if (!fctx->reverse)
SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++)); else
SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
} else /* done when there are no more elements left */
SRF_RETURN_DONE(funcctx);
}
/* *generate_subscripts_nodir *Implementsthe2-argumentversionofgenerate_subscripts
*/
Datum
generate_subscripts_nodir(PG_FUNCTION_ARGS)
{ /* just call the other one -- it can handle both cases */ return generate_subscripts(fcinfo);
}
/* *array_fill_with_lower_bounds *Createandfillarraywithdefinedlowerbounds.
*/
Datum
array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
{
ArrayType *dims;
ArrayType *lbs;
ArrayType *result;
Oid elmtype;
Datum value; bool isnull;
if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension array or low bound array cannot be null")));
static ArrayType *
array_fill_internal(ArrayType *dims, ArrayType *lbs,
Datum value, bool isnull, Oid elmtype,
FunctionCallInfo fcinfo)
{
ArrayType *result; int *dimv; int *lbsv; int ndims; int nitems; int deflbs[MAXDIM];
int16 elmlen; bool elmbyval; char elmalign;
ArrayMetaState *my_extra;
/* *Paramschecks
*/ if (ARR_NDIM(dims) > 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"),
errdetail("Dimension array must be one dimensional.")));
if (array_contains_nulls(dims))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension values cannot be null")));
if (ndims < 0) /* we do allow zero-dimension arrays */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid number of dimensions: %d", ndims))); if (ndims > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndims, MAXDIM)));
if (lbs != NULL)
{ if (ARR_NDIM(lbs) > 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"),
errdetail("Dimension array must be one dimensional.")));
if (array_contains_nulls(lbs))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension values cannot be null")));
if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"),
errdetail("Low bound array has different size than dimensions array.")));
lbsv = (int *) ARR_DATA_PTR(lbs);
} else
{ int i;
for (i = 0; i < MAXDIM; i++)
deflbs[i] = 1;
lbsv = deflbs;
}
/* This checks for overflow of the array dimensions */
nitems = ArrayGetNItems(ndims, dimv);
ArrayCheckBounds(ndims, dimv, lbsv);
/* fast track for empty array */ if (nitems <= 0) return construct_empty_array(elmtype);
if (my_extra->element_type != elmtype)
{ /* Get info about element type */
get_typlenbyvalalign(elmtype,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
my_extra->element_type = elmtype;
}
/* check for overflow of multiplication or total request */ if (totbytes / nbytes != nitems ||
!AllocSizeIsValid(totbytes))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxAllocSize)));
result = create_array_envelope(ndims, dimv, lbsv, totbytes,
elmtype, 0);
p = ARR_DATA_PTR(result); for (i = 0; i < nitems; i++)
p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
} else
{ int nbytes; int dataoffset;
if (VARATT_IS_EXPANDED_HEADER(arr))
{ /* we can just grab the type data from expanded array */
fctx->elmlen = arr->xpn.typlen;
fctx->elmbyval = arr->xpn.typbyval;
fctx->elmalign = arr->xpn.typalign;
} else
get_typlenbyvalalign(AARR_ELEMTYPE(arr),
&fctx->elmlen,
&fctx->elmbyval,
&fctx->elmalign);
/* Return input array unmodified if it is empty */ if (nitems <= 0) return array;
/* *Wecan'tremoveelementsfrommulti-dimensionalarrays,sincethe *resultmightnotberectangular.
*/ if (remove && ndim > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("removing elements from multidimensional arrays is not supported")));
/* *Wearrangetolookuptheequalityfunctiononlyonceperseriesof *calls,assumingtheelementtypedoesn'tchangeunderneathus.
*/
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 = typentry;
}
typlen = typentry->typlen;
typbyval = typentry->typbyval;
typalign = typentry->typalign;
/* *Detoastvaluesiftheyaretoasted.Thereplacementvaluemustbe *detoastedforinsertionintotheresultarray,whiledetoastingthe *searchvalueonlyoncesavescycles.
*/ 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);
/* If all elements were removed return an empty array */ if (nresult == 0)
{
pfree(values);
pfree(nulls); return construct_empty_array(element_type);
}
/* 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), ndim * sizeof(int));
memcpy(ARR_LBOUND(result), ARR_LBOUND(array), 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;
}
/* *Removeanyoccurrencesofanelementfromanarray * *Ifusedonamulti-dimensionalarraythiswillraiseanerror.
*/
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);
/* *Implementswidth_bucket(anyelement,anyarray). * *'thresholds'isanarraycontaininglowerboundvaluesforeachbucket; *thesemustbesortedfromsmallesttolargest,orbogusresultswillbe *produced.IfNthresholdsaresupplied,theoutputisfrom0toN: *0isforinputs<firstthreshold,Nisforinputs>=lastthreshold.
*/
Datum
width_bucket_array(PG_FUNCTION_ARGS)
{
Datum operand = PG_GETARG_DATUM(0);
ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
Oid collation = PG_GET_COLLATION();
Oid element_type = ARR_ELEMTYPE(thresholds); int result;
/* Check input */ if (ARR_NDIM(thresholds) > 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("thresholds must be one-dimensional array")));
if (array_contains_nulls(thresholds))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("thresholds array must not contain NULLs")));
/* We have a dedicated implementation for float8 data */ if (element_type == FLOAT8OID)
result = width_bucket_array_float8(operand, thresholds); else
{
TypeCacheEntry *typentry;
/* Cache information about the input type */
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; if (typentry == NULL ||
typentry->type_id != element_type)
{
typentry = lookup_type_cache(element_type,
TYPECACHE_CMP_PROC_FINFO); if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify a comparison function for type %s",
format_type_be(element_type))));
fcinfo->flinfo->fn_extra = typentry;
}
/* *Wehaveseparateimplementationpathsforfixed-andvariable-width *types,sinceindexingthearrayisalotcheaperinthefirstcase.
*/ if (typentry->typlen > 0)
result = width_bucket_array_fixed(operand, thresholds,
collation, typentry); else
result = width_bucket_array_variable(operand, thresholds,
collation, typentry);
}
/* Find the bucket */
left = 0;
right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds)); while (left < right)
{ int mid = (left + right) / 2; char *ptr; int i;
int32 cmpresult;
/* Locate mid'th array element by advancing from left element */
ptr = thresholds_data; for (i = left; i < mid; i++)
{
ptr = att_addlength_pointer(ptr, typlen, ptr);
ptr = (char *) att_align_nominal(ptr, typalign);
}
/* *TrimthelastNelementsfromanarraybybuildinganappropriateslice. *Onlythefirstdimensionistrimmed.
*/
Datum
trim_array(PG_FUNCTION_ARGS)
{
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); int n = PG_GETARG_INT32(1); int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
int16 elmlen; bool elmbyval; char elmalign; int lower[MAXDIM]; int upper[MAXDIM]; bool lowerProvided[MAXDIM]; bool upperProvided[MAXDIM];
Datum result;
/* Per spec, throw an error if out of bounds */ if (n < 0 || n > array_length)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
errmsg("number of elements to trim must be between 0 and %d",
array_length)));
/* Set all the bounds as unprovided except the first upper bound */
memset(lowerProvided, false, sizeof(lowerProvided));
memset(upperProvided, false, sizeof(upperProvided)); if (ARR_NDIM(v) > 0)
{
upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
upperProvided[0] = true;
}
/* Fetch the needed information about the element type */
get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
/* Get the slice */
result = array_get_slice(PointerGetDatum(v), 1,
upper, lower, upperProvided, lowerProvided,
-1, elmlen, elmbyval, elmalign);
PG_RETURN_DATUM(result);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.993Bemerkung:
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.