/* *ArraySortCachedInfo *Usedforcachingcatalogdatainarray_sort
*/ typedefstruct ArraySortCachedInfo
{
ArrayMetaState array_meta; /* metadata for array_create_iterator */
Oid elem_lt_opr; /* "<" operator for element type */
Oid elem_gt_opr; /* ">" operator for element type */
Oid array_type; /* pg_type OID of array type */
} ArraySortCachedInfo;
static Datum array_position_common(FunctionCallInfo fcinfo);
/* If first time through, create datatype cache struct */
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; if (my_extra == NULL)
{
my_extra = (ArrayMetaState *)
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState));
my_extra->element_type = InvalidOid;
fcinfo->flinfo->fn_extra = my_extra;
}
/* Figure out which context we want the result in */ if (!AggCheckCallContext(fcinfo, &resultcxt))
resultcxt = CurrentMemoryContext;
/* Now collect the array value */ if (!PG_ARGISNULL(argno))
{
MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
MemoryContextSwitchTo(oldcxt);
} else
{ /* We have to look up the array type and element type */
Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
if (!OidIsValid(arr_typeid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
element_type = get_element_type(arr_typeid); if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("input data type is not an array")));
/*----------------------------------------------------------------------------- *array_append: *pushanelementontotheendofaone-dimensionalarray *----------------------------------------------------------------------------
*/
Datum
array_append(PG_FUNCTION_ARGS)
{
ExpandedArrayHeader *eah;
Datum newelem; bool isNull;
Datum result; int *dimv,
*lb; int indx;
ArrayMetaState *my_extra;
/* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */ if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
} elseif (eah->ndims == 0)
indx = 1; else
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("argument must be empty or one-dimensional array")));
/* Perform element insertion */
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (arg && IsA(arg, Param) &&
arg->paramkind == PARAM_EXTERN &&
arg->paramid == req->paramid)
ret = (Node *) arg;
}
PG_RETURN_POINTER(ret);
}
/*----------------------------------------------------------------------------- *array_prepend: *pushanelementontothefrontofaone-dimensionalarray *----------------------------------------------------------------------------
*/
Datum
array_prepend(PG_FUNCTION_ARGS)
{
ExpandedArrayHeader *eah;
Datum newelem; bool isNull;
Datum result; int *lb; int indx; int lb0;
ArrayMetaState *my_extra;
/* Readjust result's LB to match the input's, as expected for prepend */
Assert(result == EOHPGetRWDatum(&eah->hdr)); if (eah->ndims == 1)
{ /* This is ok whether we've deconstructed or not */
eah->lbound[0] = lb0;
}
if (arg && IsA(arg, Param) &&
arg->paramkind == PARAM_EXTERN &&
arg->paramid == req->paramid)
ret = (Node *) arg;
}
PG_RETURN_POINTER(ret);
}
/*----------------------------------------------------------------------------- *array_cat: *concatenatetwonDarraystoformannDarray,or *pushan(n-1)DarrayontotheendofannDarray *----------------------------------------------------------------------------
*/
Datum
array_cat(PG_FUNCTION_ARGS)
{
ArrayType *v1,
*v2;
ArrayType *result; int *dims,
*lbs,
ndims,
nitems,
ndatabytes,
nbytes; int *dims1,
*lbs1,
ndims1,
nitems1,
ndatabytes1; int *dims2,
*lbs2,
ndims2,
nitems2,
ndatabytes2; int i; char *dat1,
*dat2;
bits8 *bitmap1,
*bitmap2;
Oid element_type;
Oid element_type1;
Oid element_type2;
int32 dataoffset;
/* Concatenating a null array is a no-op, just return the other input */ if (PG_ARGISNULL(0))
{ if (PG_ARGISNULL(1))
PG_RETURN_NULL();
result = PG_GETARG_ARRAYTYPE_P(1);
PG_RETURN_ARRAYTYPE_P(result);
} if (PG_ARGISNULL(1))
{
result = PG_GETARG_ARRAYTYPE_P(0);
PG_RETURN_ARRAYTYPE_P(result);
}
/* Check we have matching element types */ if (element_type1 != element_type2)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot concatenate incompatible arrays"),
errdetail("Arrays with element types %s and %s are not " "compatible for concatenation.",
format_type_be(element_type1),
format_type_be(element_type2))));
/* the rest fall under rule 3, 4, or 5 */ if (ndims1 != ndims2 &&
ndims1 != ndims2 - 1 &&
ndims1 != ndims2 + 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot concatenate incompatible arrays"),
errdetail("Arrays of %d and %d dimensions are not " "compatible for concatenation.",
ndims1, ndims2)));
for (i = 1; i < ndims; i++)
{ if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot concatenate incompatible arrays"),
errdetail("Arrays with differing element dimensions are " "not compatible for concatenation.")));
/* increment number of elements in outer array */
dims[0] += 1;
/* make sure the added element matches our existing elements */ for (i = 0; i < ndims1; i++)
{ if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot concatenate incompatible arrays"),
errdetail("Arrays with differing dimensions are not " "compatible for concatenation.")));
}
} else
{ /* *(ndims1==ndims2+1) * *resultingarrayhasthefirstargumentastheouterarray,withthe *secondargumentappendedtotheendoftheouterdimension
*/
ndims = ndims1;
dims = (int *) palloc(ndims * sizeof(int));
lbs = (int *) palloc(ndims * sizeof(int));
memcpy(dims, dims1, ndims * sizeof(int));
memcpy(lbs, lbs1, ndims * sizeof(int));
/* increment number of elements in outer array */
dims[0] += 1;
/* make sure the added element matches our existing elements */ for (i = 0; i < ndims2; i++)
{ if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot concatenate incompatible arrays"),
errdetail("Arrays with differing dimensions are not " "compatible for concatenation.")));
}
}
/* Do this mainly for overflow checking */
nitems = ArrayGetNItems(ndims, dims);
ArrayCheckBounds(ndims, dims, lbs);
/* build the result array */
ndatabytes = ndatabytes1 + ndatabytes2; if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
{
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
nbytes = ndatabytes + dataoffset;
} else
{
dataoffset = 0; /* marker for no null bitmap */
nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
}
result = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(result, nbytes);
result->ndim = ndims;
result->dataoffset = dataoffset;
result->elemtype = element_type;
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int)); /* data area is arg1 then arg2 */
memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2); /* handle the null bitmap if needed */ if (ARR_HASNULL(result))
{
array_bitmap_copy(ARR_NULLBITMAP(result), 0,
bitmap1, 0,
nitems1);
array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
bitmap2, 0,
nitems2);
}
PG_RETURN_ARRAYTYPE_P(result);
}
/* *ARRAY_AGG(anynonarray)aggregatefunction
*/
Datum
array_agg_transfn(PG_FUNCTION_ARGS)
{
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
MemoryContext aggcontext;
ArrayBuildState *state;
Datum elem;
if (arg1_typeid == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
if (!AggCheckCallContext(fcinfo, &aggcontext))
{ /* cannot be called directly because of internal-type argument */
elog(ERROR, "array_agg_transfn called in non-aggregate context");
}
if (PG_ARGISNULL(0))
state = initArrayResult(arg1_typeid, aggcontext, false); else
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
if (state2 == NULL)
{ /* *NULLstate2iseasy,justreturnstate1,whichweknowisalready *intheagg_context
*/ if (state1 == NULL)
PG_RETURN_NULL();
PG_RETURN_POINTER(state1);
}
if (state1 == NULL)
{ /* We must copy state2's data into the agg_context */
state1 = initArrayResultWithSize(state2->element_type, agg_context, false, state2->alen);
old_context = MemoryContextSwitchTo(agg_context);
for (int i = 0; i < state2->nelems; i++)
{ if (!state2->dnulls[i])
state1->dvalues[i] = datumCopy(state2->dvalues[i],
state1->typbyval,
state1->typlen); else
state1->dvalues[i] = (Datum) 0;
}
PG_RETURN_POINTER(state1);
} elseif (state2->nelems > 0)
{ /* We only need to combine the two states if state2 has any elements */ int reqsize = state1->nelems + state2->nelems;
MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
/* Enlarge state1 arrays if needed */ if (state1->alen < reqsize)
{ /* Use a power of 2 size rather than allocating just reqsize */
state1->alen = pg_nextpower2_32(reqsize);
state1->dvalues = (Datum *) repalloc(state1->dvalues,
state1->alen * sizeof(Datum));
state1->dnulls = (bool *) repalloc(state1->dnulls,
state1->alen * sizeof(bool));
}
/* Copy in the state2 elements to the end of the state1 arrays */ for (int i = 0; i < state2->nelems; i++)
{ if (!state2->dnulls[i])
state1->dvalues[i + state1->nelems] =
datumCopy(state2->dvalues[i],
state1->typbyval,
state1->typlen); else
state1->dvalues[i + state1->nelems] = (Datum) 0;
}
/* Create output ArrayBuildState with the needed number of elements */
result = initArrayResultWithSize(element_type, CurrentMemoryContext, false, nelems);
result->nelems = nelems;
if (!AggCheckCallContext(fcinfo, &aggcontext))
{ /* cannot be called directly because of internal-type argument */
elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
}
if (PG_ARGISNULL(0))
state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false); else
state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
state = accumArrayResultArr(state,
PG_GETARG_DATUM(1),
PG_ARGISNULL(1),
arg1_typeid,
aggcontext);
/* We only need to combine the two states if state2 has any items */ if (state2->nitems > 0)
{
MemoryContext oldContext; int reqsize; int newnitems; int i;
/* *Checkthestatesarecompatiblewitheachother.Ensureweusethe *sameerrormessagesthatarelistedinaccumArrayResultArrsothat *thesameerrorisshownaswouldhavebeenifwe'dnotusedthe *combinefunctionfortheaggregation.
*/ if (state1->ndims != state2->ndims)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot accumulate arrays of different dimensionality")));
/* Check dimensions match ignoring the first dimension. */ for (i = 1; i < state1->ndims; i++)
{ if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("cannot accumulate arrays of different dimensionality")));
}
/* Types should match already. */
Assert(state1->array_type == state2->array_type);
Assert(state1->element_type == state2->element_type);
/* Calculate new sizes, guarding against overflow. */ if (pg_add_s32_overflow(state1->nbytes, state2->nbytes, &reqsize) ||
pg_add_s32_overflow(state1->nitems, state2->nitems, &newnitems))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%zu)",
MaxArraySize)));
/* *Ifthere'snotenoughspaceinstate1thenwe'llneedtoreallocate *more.
*/ if (state1->abytes < reqsize)
{ /* use a power of 2 size rather than allocating just reqsize */
state1->abytes = pg_nextpower2_32(reqsize);
state1->data = (char *) repalloc(state1->data, state1->abytes);
}
/* Combine the null bitmaps, if present. */ if (state1->nullbitmap || state2->nullbitmap)
{ if (state1->nullbitmap == NULL)
{ /* *Firstinputwithnulls;wemustretrospectivelyhandleany *previousinputsbymarkingalltheiritemsnon-null.
*/
state1->aitems = pg_nextpower2_32(Max(256, newnitems));
state1->nullbitmap = (bits8 *) palloc((state1->aitems + 7) / 8);
array_bitmap_copy(state1->nullbitmap, 0,
NULL, 0,
state1->nitems);
} elseif (newnitems > state1->aitems)
{
state1->aitems = pg_nextpower2_32(newnitems);
state1->nullbitmap = (bits8 *)
repalloc(state1->nullbitmap, (state1->aitems + 7) / 8);
} /* This will do the right thing if state2->nullbitmap is NULL: */
array_bitmap_copy(state1->nullbitmap, state1->nitems,
state2->nullbitmap, 0,
state2->nitems);
}
/* Finally, combine the data and adjust sizes. */
memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
state1->nbytes += state2->nbytes;
state1->nitems += state2->nitems;
state1->dims[0] += state2->dims[0]; /* remaining dims already match, per test above */
Datum
array_agg_array_deserialize(PG_FUNCTION_ARGS)
{
bytea *sstate;
ArrayBuildStateArr *result;
StringInfoData buf;
Oid element_type;
Oid array_type; int nbytes; constchar *temp;
/* cannot be called directly because of internal-type argument */
Assert(AggCheckCallContext(fcinfo, NULL));
Datum
array_position_start(PG_FUNCTION_ARGS)
{ return array_position_common(fcinfo);
}
/* *array_position_common *Commoncodeforarray_positionandarray_position_start * *Theseareseparatewrappersforthesakeofopr_sanityregressiontest. *Theyarenotstrictsowehavetotestfornullinputsexplicitly.
*/ static Datum
array_position_common(FunctionCallInfo fcinfo)
{
ArrayType *array;
Oid collation = PG_GET_COLLATION();
Oid element_type;
Datum searched_element,
value; bool isnull; int position,
position_min; bool found = false;
TypeCacheEntry *typentry;
ArrayMetaState *my_extra; bool null_search;
ArrayIterator array_iterator;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
array = PG_GETARG_ARRAYTYPE_P(0);
/* *Werefusetosearchforelementsinmulti-dimensionalarrays,sincewe *havenogoodwaytoreporttheelement'slocationinthearray.
*/ if (ARR_NDIM(array) > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("searching for elements in multidimensional arrays is not supported")));
/* Searching in an empty array is well-defined, though: it always fails */ if (ARR_NDIM(array) < 1)
PG_RETURN_NULL();
if (PG_ARGISNULL(1))
{ /* fast return when the array doesn't have nulls */ if (!array_contains_nulls(array))
PG_RETURN_NULL();
searched_element = (Datum) 0;
null_search = true;
} else
{
searched_element = PG_GETARG_DATUM(1);
null_search = false;
}
element_type = ARR_ELEMTYPE(array);
position = (ARR_LBOUND(array))[0] - 1;
/* figure out where to start */ if (PG_NARGS() == 3)
{ if (PG_ARGISNULL(2))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("initial position must not be null")));
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))));
/* Examine each array element until we find a match. */
array_iterator = array_create_iterator(array, 0, my_extra); while (array_iterate(array_iterator, &value, &isnull))
{
position++;
/* skip initial elements if caller requested so */ if (position < position_min) continue;
/* *Can'tlookatthearrayelement'svalueifit'snull;butifwe *searchfornull,wehaveahitandaredone.
*/ if (isnull || null_search)
{ if (isnull && null_search)
{
found = true; break;
} else continue;
}
/* not nulls, so run the operator */ if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
searched_element, value)))
{
found = true; break;
}
}
/*----------------------------------------------------------------------------- *array_positions: *returnanarrayofpositionsofavalueinanarray. * *ISNOTDISTINCTFROMsemanticsareusedforcomparisons.ReturnsNULLwhen *theinputarrayisNULL.Whenthevalueisnotfoundinthearray,returns *anemptyarray. * *Thisisnotstrictsowehavetotestfornullinputsexplicitly. *-----------------------------------------------------------------------------
*/
Datum
array_positions(PG_FUNCTION_ARGS)
{
ArrayType *array;
Oid collation = PG_GET_COLLATION();
Oid element_type;
Datum searched_element,
value; bool isnull; int position;
TypeCacheEntry *typentry;
ArrayMetaState *my_extra; bool null_search;
ArrayIterator array_iterator;
ArrayBuildState *astate = NULL;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
array = PG_GETARG_ARRAYTYPE_P(0);
/* *Werefusetosearchforelementsinmulti-dimensionalarrays,sincewe *havenogoodwaytoreporttheelement'slocationinthearray.
*/ if (ARR_NDIM(array) > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("searching for elements in multidimensional arrays is not supported")));
/* Searching in an empty array is well-defined, though: it always fails */ if (ARR_NDIM(array) < 1)
PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
if (PG_ARGISNULL(1))
{ /* fast return when the array doesn't have nulls */ if (!array_contains_nulls(array))
PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
searched_element = (Datum) 0;
null_search = true;
} else
{
searched_element = PG_GETARG_DATUM(1);
null_search = false;
}
element_type = ARR_ELEMTYPE(array);
position = (ARR_LBOUND(array))[0] - 1;
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))));
/* *Accumulateeacharraypositionifftheelementmatchesthegiven *element.
*/
array_iterator = array_create_iterator(array, 0, my_extra); while (array_iterate(array_iterator, &value, &isnull))
{
position += 1;
/* *Can'tlookatthearrayelement'svalueifit'snull;butifwe *searchfornull,wehaveahit.
*/ if (isnull || null_search)
{ if (isnull && null_search)
astate =
accumArrayResult(astate, Int32GetDatum(position), false,
INT4OID, CurrentMemoryContext);
continue;
}
/* not nulls, so run the operator */ if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
searched_element, value)))
astate =
accumArrayResult(astate, Int32GetDatum(position), false,
INT4OID, CurrentMemoryContext);
}
/* Set up dimensions of the result */
memcpy(rdims, dims, ndim * sizeof(int));
memcpy(rlbs, lbs, ndim * sizeof(int));
rdims[0] = n; if (!keep_lb)
rlbs[0] = 1;
result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
PG_RETURN_ARRAYTYPE_P(result);
}
/* *array_sample * *Returnsanarrayofnrandomlychosenfirst-dimensionelements *fromtheinputarray.
*/
Datum
array_sample(PG_FUNCTION_ARGS)
{
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0); int n = PG_GETARG_INT32(1);
ArrayType *result;
Oid elmtyp;
TypeCacheEntry *typentry; int nitem;
/* Quick exit if we don't need to sort */ if (ndim < 1 || dims[0] < 2) return array;
/* Set up cache area if we didn't already */
cache_info = (ArraySortCachedInfo *) fcinfo->flinfo->fn_extra; if (cache_info == NULL)
{
cache_info = (ArraySortCachedInfo *)
MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, sizeof(ArraySortCachedInfo));
fcinfo->flinfo->fn_extra = cache_info;
}
/* Fetch and cache required data if we don't have it */
elmtyp = ARR_ELEMTYPE(array); if (elmtyp != cache_info->array_meta.element_type)
{
TypeCacheEntry *typentry;
/* Identify the sort operator to use */ if (ndim == 1)
{ /* Need to sort the element type */
sort_typ = elmtyp;
sort_opr = (descending ? cache_info->elem_gt_opr : cache_info->elem_lt_opr);
} else
{ /* Otherwise we're sorting arrays */
sort_typ = cache_info->array_type; if (!OidIsValid(sort_typ))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(elmtyp)))); /* We know what operators to use for arrays */
sort_opr = (descending ? ARRAY_GT_OP : ARRAY_LT_OP);
}
/* *Failifwedon'tknowhowtosort.Theerrormessageischosento *matchwhatarray_lt()/array_gt()willsayinthemultidimensionalcase.
*/ if (!OidIsValid(sort_opr))
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not identify a comparison function for type %s",
format_type_be(elmtyp)));
/* Put the things to be sorted (elements or sub-arrays) into a tuplesort */
tuplesortstate = tuplesort_begin_datum(sort_typ,
sort_opr,
collation,
nulls_first,
work_mem,
NULL,
TUPLESORT_NONE);
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.