1 /*-------------------------------------------------------------------------
4 * Misc user-visible array support functions
6 * Copyright (c) 2003, PostgreSQL Global Development Group
9 * $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.8 2003/08/17 23:43:26 tgl Exp $
11 *-------------------------------------------------------------------------
15 #include "catalog/pg_proc.h"
16 #include "catalog/pg_type.h"
17 #include "utils/array.h"
18 #include "utils/builtins.h"
19 #include "utils/lsyscache.h"
20 #include "utils/syscache.h"
22 /*-----------------------------------------------------------------------------
24 * push an element onto either end of a one-dimensional array
25 *----------------------------------------------------------------------------
28 array_push(PG_FUNCTION_ARGS)
41 Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
42 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
45 ArrayMetaState *my_extra;
47 if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
49 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
50 errmsg("could not determine input data types")));
52 arg0_elemid = get_element_type(arg0_typeid);
53 arg1_elemid = get_element_type(arg1_typeid);
55 if (arg0_elemid != InvalidOid)
57 v = PG_GETARG_ARRAYTYPE_P(0);
58 element_type = ARR_ELEMTYPE(v);
59 newelem = PG_GETARG_DATUM(1);
61 else if (arg1_elemid != InvalidOid)
63 v = PG_GETARG_ARRAYTYPE_P(1);
64 element_type = ARR_ELEMTYPE(v);
65 newelem = PG_GETARG_DATUM(0);
69 /* Shouldn't get here given proper type checking in parser */
71 (errcode(ERRCODE_DATATYPE_MISMATCH),
72 errmsg("neither input type is an array")));
73 PG_RETURN_NULL(); /* keep compiler quiet */
81 if (arg0_elemid != InvalidOid)
84 int ub = dimv[0] + lb[0] - 1;
94 else if (ARR_NDIM(v) == 0)
98 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
99 errmsg("input must be empty or one-dimensional array")));
102 * We arrange to look up info about element type only once per series
103 * of calls, assuming the element type doesn't change underneath us.
105 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
106 if (my_extra == NULL)
108 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
109 sizeof(ArrayMetaState));
110 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
111 my_extra->element_type = InvalidOid;
114 if (my_extra->element_type != element_type)
116 /* Get info about element type */
117 get_typlenbyvalalign(element_type,
120 &my_extra->typalign);
121 my_extra->element_type = element_type;
123 typlen = my_extra->typlen;
124 typbyval = my_extra->typbyval;
125 typalign = my_extra->typalign;
127 result = array_set(v, 1, &indx, newelem, -1,
128 typlen, typbyval, typalign, &isNull);
130 PG_RETURN_ARRAYTYPE_P(result);
133 /*-----------------------------------------------------------------------------
135 * concatenate two nD arrays to form an nD array, or
136 * push an (n-1)D array onto the end of an nD array
137 *----------------------------------------------------------------------------
140 array_cat(PG_FUNCTION_ARGS)
165 v1 = PG_GETARG_ARRAYTYPE_P(0);
166 v2 = PG_GETARG_ARRAYTYPE_P(1);
169 * We must have one of the following combinations of inputs:
170 * 1) one empty array, and one non-empty array
171 * 2) both arrays empty
172 * 3) two arrays with ndims1 == ndims2
173 * 4) ndims1 == ndims2 - 1
174 * 5) ndims1 == ndims2 + 1
177 ndims1 = ARR_NDIM(v1);
178 ndims2 = ARR_NDIM(v2);
181 * short circuit - if one input array is empty, and the other is not,
182 * we return the non-empty one as the result
184 * if both are empty, return the first one
186 if (ndims1 == 0 && ndims2 > 0)
187 PG_RETURN_ARRAYTYPE_P(v2);
190 PG_RETURN_ARRAYTYPE_P(v1);
192 /* the rest fall under rule 3, 4, or 5 */
193 if (ndims1 != ndims2 &&
194 ndims1 != ndims2 - 1 &&
195 ndims1 != ndims2 + 1)
197 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
198 errmsg("cannot concatenate incompatible arrays"),
199 errdetail("Arrays of %d and %d dimensions are not "
200 "compatible for concatenation.",
203 element_type1 = ARR_ELEMTYPE(v1);
204 element_type2 = ARR_ELEMTYPE(v2);
206 /* Check we have matching element types */
207 if (element_type1 != element_type2)
209 (errcode(ERRCODE_DATATYPE_MISMATCH),
210 errmsg("cannot concatenate incompatible arrays"),
211 errdetail("Arrays with element types %s and %s are not "
212 "compatible for concatenation.",
213 format_type_be(element_type1),
214 format_type_be(element_type2))));
217 element_type = element_type1;
219 /* get argument array details */
220 lbs1 = ARR_LBOUND(v1);
221 lbs2 = ARR_LBOUND(v2);
222 dims1 = ARR_DIMS(v1);
223 dims2 = ARR_DIMS(v2);
224 dat1 = ARR_DATA_PTR(v1);
225 dat2 = ARR_DATA_PTR(v2);
226 ndatabytes1 = ARR_SIZE(v1) - ARR_OVERHEAD(ndims1);
227 ndatabytes2 = ARR_SIZE(v2) - ARR_OVERHEAD(ndims2);
229 if (ndims1 == ndims2)
232 * resulting array is made up of the elements (possibly arrays
233 * themselves) of the input argument arrays
236 dims = (int *) palloc(ndims * sizeof(int));
237 lbs = (int *) palloc(ndims * sizeof(int));
239 dims[0] = dims1[0] + dims2[0];
242 for (i = 1; i < ndims; i++)
244 if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
246 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
247 errmsg("cannot concatenate incompatible arrays"),
248 errdetail("Arrays with differing element dimensions are "
249 "not compatible for concatenation.")));
255 else if (ndims1 == ndims2 - 1)
258 * resulting array has the second argument as the outer array,
259 * with the first argument appended to the front of the outer
263 dims = (int *) palloc(ndims * sizeof(int));
264 lbs = (int *) palloc(ndims * sizeof(int));
265 memcpy(dims, dims2, ndims * sizeof(int));
266 memcpy(lbs, lbs2, ndims * sizeof(int));
268 /* increment number of elements in outer array */
271 /* decrement outer array lower bound */
274 /* make sure the added element matches our existing elements */
275 for (i = 0; i < ndims1; i++)
277 if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
279 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
280 errmsg("cannot concatenate incompatible arrays"),
281 errdetail("Arrays with differing dimensions are not "
282 "compatible for concatenation.")));
288 * (ndims1 == ndims2 + 1)
290 * resulting array has the first argument as the outer array, with
291 * the second argument appended to the end of the outer dimension
294 dims = (int *) palloc(ndims * sizeof(int));
295 lbs = (int *) palloc(ndims * sizeof(int));
296 memcpy(dims, dims1, ndims * sizeof(int));
297 memcpy(lbs, lbs1, ndims * sizeof(int));
299 /* increment number of elements in outer array */
302 /* make sure the added element matches our existing elements */
303 for (i = 0; i < ndims2; i++)
305 if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
307 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
308 errmsg("cannot concatenate incompatible arrays"),
309 errdetail("Arrays with differing dimensions are not "
310 "compatible for concatenation.")));
314 /* build the result array */
315 ndatabytes = ndatabytes1 + ndatabytes2;
316 nbytes = ndatabytes + ARR_OVERHEAD(ndims);
317 result = (ArrayType *) palloc(nbytes);
319 result->size = nbytes;
320 result->ndim = ndims;
322 result->elemtype = element_type;
323 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
324 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
325 /* data area is arg1 then arg2 */
326 memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
327 memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
329 PG_RETURN_ARRAYTYPE_P(result);
334 * used by text_to_array() in varlena.c
337 create_singleton_array(FunctionCallInfo fcinfo,
349 ArrayMetaState *my_extra;
351 if (element_type == 0)
353 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
354 errmsg("invalid array element type: %u", element_type)));
357 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
358 errmsg("invalid number of dimensions: %d", ndims)));
361 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
362 errmsg("number of array dimensions exceeds the maximum allowed, %d",
365 dvalues[0] = element;
367 for (i = 0; i < ndims; i++)
374 * We arrange to look up info about element type only once per series
375 * of calls, assuming the element type doesn't change underneath us.
377 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
378 if (my_extra == NULL)
380 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
381 sizeof(ArrayMetaState));
382 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
383 my_extra->element_type = InvalidOid;
386 if (my_extra->element_type != element_type)
388 /* Get info about element type */
389 get_typlenbyvalalign(element_type,
392 &my_extra->typalign);
393 my_extra->element_type = element_type;
395 typlen = my_extra->typlen;
396 typbyval = my_extra->typbyval;
397 typalign = my_extra->typalign;
399 return construct_md_array(dvalues, ndims, dims, lbs, element_type,
400 typlen, typbyval, typalign);