/*----------------------------------------------------------------------------- * array_prepend : * push an element onto the front of a one-dimensional array *---------------------------------------------------------------------------- */ Datum array_prepend(PG_FUNCTION_ARGS) { ExpandedArrayHeader *eah; Datum newelem; bool isNull; Datum result; int *lb; int indx; int lb0; ArrayMetaState *my_extra; isNull = PG_ARGISNULL(0); if (isNull) newelem = (Datum) 0; else newelem = PG_GETARG_DATUM(0); eah = fetch_array_arg_replace_nulls(fcinfo, 1); if (eah->ndims == 1) { /* prepend newelem */ lb = eah->lbound; indx = lb[0] - 1; lb0 = lb[0]; /* overflow? */ if (indx > lb[0]) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); } else if (eah->ndims == 0) { indx = 1; lb0 = 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; result = array_set_element(EOHPGetRWDatum(&eah->hdr), 1, &indx, newelem, isNull, -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign); /* 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; } PG_RETURN_DATUM(result); }
/*----------------------------------------------------------------------------- * array_append : * push an element onto the end of a one-dimensional array *---------------------------------------------------------------------------- */ Datum array_append(PG_FUNCTION_ARGS) { ExpandedArrayHeader *eah; Datum newelem; bool isNull; Datum result; int *dimv, *lb; int indx; ArrayMetaState *my_extra; eah = fetch_array_arg_replace_nulls(fcinfo, 0); isNull = PG_ARGISNULL(1); if (isNull) newelem = (Datum) 0; else newelem = PG_GETARG_DATUM(1); if (eah->ndims == 1) { /* append newelem */ int ub; lb = eah->lbound; dimv = eah->dims; ub = dimv[0] + lb[0] - 1; indx = ub + 1; /* overflow? */ if (indx < ub) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); } else if (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; result = array_set_element(EOHPGetRWDatum(&eah->hdr), 1, &indx, newelem, isNull, -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign); PG_RETURN_DATUM(result); }
/* * Transfer ownership of an expanded object to a new___ parent memory context. * The object must be referenced by a R/W pointer, and what we return is * always its "standard" R/W pointer, which is certain to have the same * lifespan as the object itself. (The passed-in pointer might not, and * in any case wouldn't provide a unique identifier if it's not that one.) */ Datum TransferExpandedObject(Datum d, MemoryContext new_parent) { ExpandedObjectHeader *eohptr = DatumGetEOHP(d); /* Assert caller gave a R/W pointer */ Assert(VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d))); /* Transfer ownership */ MemoryContextSetParent(eohptr->eoh_context, new_parent); /* Return the object's standard read-write pointer */ return EOHPGetRWDatum(eohptr); }
/* * expand_array: convert an array Datum into an expanded array * * The expanded object will be a child of parentcontext. * * Some callers can provide cache space to avoid repeated lookups of element * type data across calls; if so, pass a metacache pointer, making sure that * metacache->element_type is initialized to InvalidOid before first call. * If no cross-call caching is required, pass NULL for metacache. */ Datum expand_array(Datum arraydatum, MemoryContext parentcontext, ArrayMetaState *metacache) { ArrayType *array; ExpandedArrayHeader *eah; MemoryContext objcxt; MemoryContext oldcxt; ArrayMetaState fakecache; /* * Allocate private context for expanded object. We start by assuming * that the array won't be very large; but if it does grow a lot, don't * constrain aset.c's large-context behavior. */ objcxt = AllocSetContextCreate(parentcontext, "expanded array", ALLOCSET_START_SMALL_SIZES); /* Set up expanded array header */ eah = (ExpandedArrayHeader *) MemoryContextAlloc(objcxt, sizeof(ExpandedArrayHeader)); EOH_init_header(&eah->hdr, &EA_methods, objcxt); eah->ea_magic = EA_MAGIC; /* If the source is an expanded array, we may be able to optimize */ if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum))) { ExpandedArrayHeader *oldeah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum); Assert(oldeah->ea_magic == EA_MAGIC); /* * Update caller's cache if provided; we don't need it this time, but * next call might be for a non-expanded source array. Furthermore, * if the caller didn't provide a cache area, use some local storage * to cache anyway, thereby avoiding a catalog lookup in the case * where we fall through to the flat-copy code path. */ if (metacache == NULL) metacache = &fakecache; metacache->element_type = oldeah->element_type; metacache->typlen = oldeah->typlen; metacache->typbyval = oldeah->typbyval; metacache->typalign = oldeah->typalign; /* * If element type is pass-by-value and we have a Datum-array * representation, just copy the source's metadata and Datum/isnull * arrays. The original flat array, if present at all, adds no * additional information so we need not copy it. */ if (oldeah->typbyval && oldeah->dvalues != NULL) { copy_byval_expanded_array(eah, oldeah); /* return a R/W pointer to the expanded array */ return EOHPGetRWDatum(&eah->hdr); } /* * Otherwise, either we have only a flat representation or the * elements are pass-by-reference. In either case, the best thing * seems to be to copy the source as a flat representation and then * deconstruct that later if necessary. For the pass-by-ref case, we * could perhaps save some cycles with custom code that generates the * deconstructed representation in parallel with copying the values, * but it would be a lot of extra code for fairly marginal gain. So, * fall through into the flat-source code path. */ } /* * Detoast and copy source array into private context, as a flat array. * * Note that this coding risks leaking some memory in the private context * if we have to fetch data from a TOAST table; however, experimentation * says that the leak is minimal. Doing it this way saves a copy step, * which seems worthwhile, especially if the array is large enough to need * external storage. */ oldcxt = MemoryContextSwitchTo(objcxt); array = DatumGetArrayTypePCopy(arraydatum); MemoryContextSwitchTo(oldcxt); eah->ndims = ARR_NDIM(array); /* note these pointers point into the fvalue header! */ eah->dims = ARR_DIMS(array); eah->lbound = ARR_LBOUND(array); /* Save array's element-type data for possible use later */ eah->element_type = ARR_ELEMTYPE(array); if (metacache && metacache->element_type == eah->element_type) { /* We have a valid cache of representational data */ eah->typlen = metacache->typlen; eah->typbyval = metacache->typbyval; eah->typalign = metacache->typalign; } else { /* No, so look it up */ get_typlenbyvalalign(eah->element_type, &eah->typlen, &eah->typbyval, &eah->typalign); /* Update cache if provided */ if (metacache) { metacache->element_type = eah->element_type; metacache->typlen = eah->typlen; metacache->typbyval = eah->typbyval; metacache->typalign = eah->typalign; } } /* we don't make a deconstructed representation now */ eah->dvalues = NULL; eah->dnulls = NULL; eah->dvalueslen = 0; eah->nelems = 0; eah->flat_size = 0; /* remember we have a flat representation */ eah->fvalue = array; eah->fstartptr = ARR_DATA_PTR(array); eah->fendptr = ((char *) array) + ARR_SIZE(array); /* return a R/W pointer to the expanded array */ return EOHPGetRWDatum(&eah->hdr); }