Properly access a buffer's LSN using existing access macros instead of abusing
[PostgreSQL.git] / src / backend / utils / adt / array_userfuncs.c
blob9f34f8ec7b6e1c28ee34f125e3c90e77987550a8
1 /*-------------------------------------------------------------------------
3 * array_userfuncs.c
4 * Misc user-visible array support functions
6 * Copyright (c) 2003-2008, PostgreSQL Global Development Group
8 * IDENTIFICATION
9 * $PostgreSQL$
11 *-------------------------------------------------------------------------
13 #include "postgres.h"
15 #include "utils/array.h"
16 #include "utils/builtins.h"
17 #include "utils/lsyscache.h"
20 /*-----------------------------------------------------------------------------
21 * array_push :
22 * push an element onto either end of a one-dimensional array
23 *----------------------------------------------------------------------------
25 Datum
26 array_push(PG_FUNCTION_ARGS)
28 ArrayType *v;
29 Datum newelem;
30 bool isNull;
31 int *dimv,
32 *lb;
33 ArrayType *result;
34 int indx;
35 Oid element_type;
36 int16 typlen;
37 bool typbyval;
38 char typalign;
39 Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
40 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
41 Oid arg0_elemid;
42 Oid arg1_elemid;
43 ArrayMetaState *my_extra;
45 if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
46 ereport(ERROR,
47 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
48 errmsg("could not determine input data types")));
50 arg0_elemid = get_element_type(arg0_typeid);
51 arg1_elemid = get_element_type(arg1_typeid);
53 if (arg0_elemid != InvalidOid)
55 if (PG_ARGISNULL(0))
56 v = construct_empty_array(arg0_elemid);
57 else
58 v = PG_GETARG_ARRAYTYPE_P(0);
59 isNull = PG_ARGISNULL(1);
60 if (isNull)
61 newelem = (Datum) 0;
62 else
63 newelem = PG_GETARG_DATUM(1);
65 else if (arg1_elemid != InvalidOid)
67 if (PG_ARGISNULL(1))
68 v = construct_empty_array(arg1_elemid);
69 else
70 v = PG_GETARG_ARRAYTYPE_P(1);
71 isNull = PG_ARGISNULL(0);
72 if (isNull)
73 newelem = (Datum) 0;
74 else
75 newelem = PG_GETARG_DATUM(0);
77 else
79 /* Shouldn't get here given proper type checking in parser */
80 ereport(ERROR,
81 (errcode(ERRCODE_DATATYPE_MISMATCH),
82 errmsg("neither input type is an array")));
83 PG_RETURN_NULL(); /* keep compiler quiet */
86 element_type = ARR_ELEMTYPE(v);
88 if (ARR_NDIM(v) == 1)
90 lb = ARR_LBOUND(v);
91 dimv = ARR_DIMS(v);
93 if (arg0_elemid != InvalidOid)
95 /* append newelem */
96 int ub = dimv[0] + lb[0] - 1;
98 indx = ub + 1;
99 /* overflow? */
100 if (indx < ub)
101 ereport(ERROR,
102 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
103 errmsg("integer out of range")));
105 else
107 /* prepend newelem */
108 indx = lb[0] - 1;
109 /* overflow? */
110 if (indx > lb[0])
111 ereport(ERROR,
112 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
113 errmsg("integer out of range")));
116 else if (ARR_NDIM(v) == 0)
117 indx = 1;
118 else
119 ereport(ERROR,
120 (errcode(ERRCODE_DATA_EXCEPTION),
121 errmsg("argument must be empty or one-dimensional array")));
124 * We arrange to look up info about element type only once per series of
125 * calls, assuming the element type doesn't change underneath us.
127 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
128 if (my_extra == NULL)
130 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
131 sizeof(ArrayMetaState));
132 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
133 my_extra->element_type = ~element_type;
136 if (my_extra->element_type != element_type)
138 /* Get info about element type */
139 get_typlenbyvalalign(element_type,
140 &my_extra->typlen,
141 &my_extra->typbyval,
142 &my_extra->typalign);
143 my_extra->element_type = element_type;
145 typlen = my_extra->typlen;
146 typbyval = my_extra->typbyval;
147 typalign = my_extra->typalign;
149 result = array_set(v, 1, &indx, newelem, isNull,
150 -1, typlen, typbyval, typalign);
153 * Readjust result's LB to match the input's. This does nothing in the
154 * append case, but it's the simplest way to implement the prepend case.
156 if (ARR_NDIM(v) == 1)
157 ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
159 PG_RETURN_ARRAYTYPE_P(result);
162 /*-----------------------------------------------------------------------------
163 * array_cat :
164 * concatenate two nD arrays to form an nD array, or
165 * push an (n-1)D array onto the end of an nD array
166 *----------------------------------------------------------------------------
168 Datum
169 array_cat(PG_FUNCTION_ARGS)
171 ArrayType *v1,
172 *v2;
173 ArrayType *result;
174 int *dims,
175 *lbs,
176 ndims,
177 nitems,
178 ndatabytes,
179 nbytes;
180 int *dims1,
181 *lbs1,
182 ndims1,
183 nitems1,
184 ndatabytes1;
185 int *dims2,
186 *lbs2,
187 ndims2,
188 nitems2,
189 ndatabytes2;
190 int i;
191 char *dat1,
192 *dat2;
193 bits8 *bitmap1,
194 *bitmap2;
195 Oid element_type;
196 Oid element_type1;
197 Oid element_type2;
198 int32 dataoffset;
200 /* Concatenating a null array is a no-op, just return the other input */
201 if (PG_ARGISNULL(0))
203 if (PG_ARGISNULL(1))
204 PG_RETURN_NULL();
205 result = PG_GETARG_ARRAYTYPE_P(1);
206 PG_RETURN_ARRAYTYPE_P(result);
208 if (PG_ARGISNULL(1))
210 result = PG_GETARG_ARRAYTYPE_P(0);
211 PG_RETURN_ARRAYTYPE_P(result);
214 v1 = PG_GETARG_ARRAYTYPE_P(0);
215 v2 = PG_GETARG_ARRAYTYPE_P(1);
217 element_type1 = ARR_ELEMTYPE(v1);
218 element_type2 = ARR_ELEMTYPE(v2);
220 /* Check we have matching element types */
221 if (element_type1 != element_type2)
222 ereport(ERROR,
223 (errcode(ERRCODE_DATATYPE_MISMATCH),
224 errmsg("cannot concatenate incompatible arrays"),
225 errdetail("Arrays with element types %s and %s are not "
226 "compatible for concatenation.",
227 format_type_be(element_type1),
228 format_type_be(element_type2))));
230 /* OK, use it */
231 element_type = element_type1;
233 /*----------
234 * We must have one of the following combinations of inputs:
235 * 1) one empty array, and one non-empty array
236 * 2) both arrays empty
237 * 3) two arrays with ndims1 == ndims2
238 * 4) ndims1 == ndims2 - 1
239 * 5) ndims1 == ndims2 + 1
240 *----------
242 ndims1 = ARR_NDIM(v1);
243 ndims2 = ARR_NDIM(v2);
246 * short circuit - if one input array is empty, and the other is not, we
247 * return the non-empty one as the result
249 * if both are empty, return the first one
251 if (ndims1 == 0 && ndims2 > 0)
252 PG_RETURN_ARRAYTYPE_P(v2);
254 if (ndims2 == 0)
255 PG_RETURN_ARRAYTYPE_P(v1);
257 /* the rest fall under rule 3, 4, or 5 */
258 if (ndims1 != ndims2 &&
259 ndims1 != ndims2 - 1 &&
260 ndims1 != ndims2 + 1)
261 ereport(ERROR,
262 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
263 errmsg("cannot concatenate incompatible arrays"),
264 errdetail("Arrays of %d and %d dimensions are not "
265 "compatible for concatenation.",
266 ndims1, ndims2)));
268 /* get argument array details */
269 lbs1 = ARR_LBOUND(v1);
270 lbs2 = ARR_LBOUND(v2);
271 dims1 = ARR_DIMS(v1);
272 dims2 = ARR_DIMS(v2);
273 dat1 = ARR_DATA_PTR(v1);
274 dat2 = ARR_DATA_PTR(v2);
275 bitmap1 = ARR_NULLBITMAP(v1);
276 bitmap2 = ARR_NULLBITMAP(v2);
277 nitems1 = ArrayGetNItems(ndims1, dims1);
278 nitems2 = ArrayGetNItems(ndims2, dims2);
279 ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
280 ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
282 if (ndims1 == ndims2)
285 * resulting array is made up of the elements (possibly arrays
286 * themselves) of the input argument arrays
288 ndims = ndims1;
289 dims = (int *) palloc(ndims * sizeof(int));
290 lbs = (int *) palloc(ndims * sizeof(int));
292 dims[0] = dims1[0] + dims2[0];
293 lbs[0] = lbs1[0];
295 for (i = 1; i < ndims; i++)
297 if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
298 ereport(ERROR,
299 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
300 errmsg("cannot concatenate incompatible arrays"),
301 errdetail("Arrays with differing element dimensions are "
302 "not compatible for concatenation.")));
304 dims[i] = dims1[i];
305 lbs[i] = lbs1[i];
308 else if (ndims1 == ndims2 - 1)
311 * resulting array has the second argument as the outer array, with
312 * the first argument inserted at the front of the outer dimension
314 ndims = ndims2;
315 dims = (int *) palloc(ndims * sizeof(int));
316 lbs = (int *) palloc(ndims * sizeof(int));
317 memcpy(dims, dims2, ndims * sizeof(int));
318 memcpy(lbs, lbs2, ndims * sizeof(int));
320 /* increment number of elements in outer array */
321 dims[0] += 1;
323 /* make sure the added element matches our existing elements */
324 for (i = 0; i < ndims1; i++)
326 if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
327 ereport(ERROR,
328 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
329 errmsg("cannot concatenate incompatible arrays"),
330 errdetail("Arrays with differing dimensions are not "
331 "compatible for concatenation.")));
334 else
337 * (ndims1 == ndims2 + 1)
339 * resulting array has the first argument as the outer array, with the
340 * second argument appended to the end of the outer dimension
342 ndims = ndims1;
343 dims = (int *) palloc(ndims * sizeof(int));
344 lbs = (int *) palloc(ndims * sizeof(int));
345 memcpy(dims, dims1, ndims * sizeof(int));
346 memcpy(lbs, lbs1, ndims * sizeof(int));
348 /* increment number of elements in outer array */
349 dims[0] += 1;
351 /* make sure the added element matches our existing elements */
352 for (i = 0; i < ndims2; i++)
354 if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
355 ereport(ERROR,
356 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
357 errmsg("cannot concatenate incompatible arrays"),
358 errdetail("Arrays with differing dimensions are not "
359 "compatible for concatenation.")));
363 /* Do this mainly for overflow checking */
364 nitems = ArrayGetNItems(ndims, dims);
366 /* build the result array */
367 ndatabytes = ndatabytes1 + ndatabytes2;
368 if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
370 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
371 nbytes = ndatabytes + dataoffset;
373 else
375 dataoffset = 0; /* marker for no null bitmap */
376 nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
378 result = (ArrayType *) palloc(nbytes);
379 SET_VARSIZE(result, nbytes);
380 result->ndim = ndims;
381 result->dataoffset = dataoffset;
382 result->elemtype = element_type;
383 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
384 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
385 /* data area is arg1 then arg2 */
386 memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
387 memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
388 /* handle the null bitmap if needed */
389 if (ARR_HASNULL(result))
391 array_bitmap_copy(ARR_NULLBITMAP(result), 0,
392 bitmap1, 0,
393 nitems1);
394 array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
395 bitmap2, 0,
396 nitems2);
399 PG_RETURN_ARRAYTYPE_P(result);
404 * used by text_to_array() in varlena.c
406 ArrayType *
407 create_singleton_array(FunctionCallInfo fcinfo,
408 Oid element_type,
409 Datum element,
410 int ndims)
412 Datum dvalues[1];
413 int16 typlen;
414 bool typbyval;
415 char typalign;
416 int dims[MAXDIM];
417 int lbs[MAXDIM];
418 int i;
419 ArrayMetaState *my_extra;
421 if (ndims < 1)
422 ereport(ERROR,
423 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
424 errmsg("invalid number of dimensions: %d", ndims)));
425 if (ndims > MAXDIM)
426 ereport(ERROR,
427 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
428 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
429 ndims, MAXDIM)));
431 dvalues[0] = element;
433 for (i = 0; i < ndims; i++)
435 dims[i] = 1;
436 lbs[i] = 1;
440 * We arrange to look up info about element type only once per series of
441 * calls, assuming the element type doesn't change underneath us.
443 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
444 if (my_extra == NULL)
446 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
447 sizeof(ArrayMetaState));
448 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
449 my_extra->element_type = ~element_type;
452 if (my_extra->element_type != element_type)
454 /* Get info about element type */
455 get_typlenbyvalalign(element_type,
456 &my_extra->typlen,
457 &my_extra->typbyval,
458 &my_extra->typalign);
459 my_extra->element_type = element_type;
461 typlen = my_extra->typlen;
462 typbyval = my_extra->typbyval;
463 typalign = my_extra->typalign;
465 return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
466 typlen, typbyval, typalign);