]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/array_userfuncs.c
In a non-hashed Agg node, reset the "aggcontext" at group boundaries, instead
[postgresql] / src / backend / utils / adt / array_userfuncs.c
1 /*-------------------------------------------------------------------------
2  *
3  * array_userfuncs.c
4  *        Misc user-visible array support functions
5  *
6  * Copyright (c) 2003-2009, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.32 2009/07/23 20:45:27 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "nodes/execnodes.h"
16 #include "utils/array.h"
17 #include "utils/builtins.h"
18 #include "utils/lsyscache.h"
19
20
21 /*-----------------------------------------------------------------------------
22  * array_push :
23  *              push an element onto either end of a one-dimensional array
24  *----------------------------------------------------------------------------
25  */
26 Datum
27 array_push(PG_FUNCTION_ARGS)
28 {
29         ArrayType  *v;
30         Datum           newelem;
31         bool            isNull;
32         int                *dimv,
33                            *lb;
34         ArrayType  *result;
35         int                     indx;
36         Oid                     element_type;
37         int16           typlen;
38         bool            typbyval;
39         char            typalign;
40         Oid                     arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
41         Oid                     arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
42         Oid                     arg0_elemid;
43         Oid                     arg1_elemid;
44         ArrayMetaState *my_extra;
45
46         if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
47                 ereport(ERROR,
48                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
49                                  errmsg("could not determine input data types")));
50
51         arg0_elemid = get_element_type(arg0_typeid);
52         arg1_elemid = get_element_type(arg1_typeid);
53
54         if (arg0_elemid != InvalidOid)
55         {
56                 if (PG_ARGISNULL(0))
57                         v = construct_empty_array(arg0_elemid);
58                 else
59                         v = PG_GETARG_ARRAYTYPE_P(0);
60                 isNull = PG_ARGISNULL(1);
61                 if (isNull)
62                         newelem = (Datum) 0;
63                 else
64                         newelem = PG_GETARG_DATUM(1);
65         }
66         else if (arg1_elemid != InvalidOid)
67         {
68                 if (PG_ARGISNULL(1))
69                         v = construct_empty_array(arg1_elemid);
70                 else
71                         v = PG_GETARG_ARRAYTYPE_P(1);
72                 isNull = PG_ARGISNULL(0);
73                 if (isNull)
74                         newelem = (Datum) 0;
75                 else
76                         newelem = PG_GETARG_DATUM(0);
77         }
78         else
79         {
80                 /* Shouldn't get here given proper type checking in parser */
81                 ereport(ERROR,
82                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
83                                  errmsg("neither input type is an array")));
84                 PG_RETURN_NULL();               /* keep compiler quiet */
85         }
86
87         element_type = ARR_ELEMTYPE(v);
88
89         if (ARR_NDIM(v) == 1)
90         {
91                 lb = ARR_LBOUND(v);
92                 dimv = ARR_DIMS(v);
93
94                 if (arg0_elemid != InvalidOid)
95                 {
96                         /* append newelem */
97                         int                     ub = dimv[0] + lb[0] - 1;
98
99                         indx = ub + 1;
100                         /* overflow? */
101                         if (indx < ub)
102                                 ereport(ERROR,
103                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
104                                                  errmsg("integer out of range")));
105                 }
106                 else
107                 {
108                         /* prepend newelem */
109                         indx = lb[0] - 1;
110                         /* overflow? */
111                         if (indx > lb[0])
112                                 ereport(ERROR,
113                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
114                                                  errmsg("integer out of range")));
115                 }
116         }
117         else if (ARR_NDIM(v) == 0)
118                 indx = 1;
119         else
120                 ereport(ERROR,
121                                 (errcode(ERRCODE_DATA_EXCEPTION),
122                                  errmsg("argument must be empty or one-dimensional array")));
123
124         /*
125          * We arrange to look up info about element type only once per series of
126          * calls, assuming the element type doesn't change underneath us.
127          */
128         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
129         if (my_extra == NULL)
130         {
131                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
132                                                                                                           sizeof(ArrayMetaState));
133                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
134                 my_extra->element_type = ~element_type;
135         }
136
137         if (my_extra->element_type != element_type)
138         {
139                 /* Get info about element type */
140                 get_typlenbyvalalign(element_type,
141                                                          &my_extra->typlen,
142                                                          &my_extra->typbyval,
143                                                          &my_extra->typalign);
144                 my_extra->element_type = element_type;
145         }
146         typlen = my_extra->typlen;
147         typbyval = my_extra->typbyval;
148         typalign = my_extra->typalign;
149
150         result = array_set(v, 1, &indx, newelem, isNull,
151                                            -1, typlen, typbyval, typalign);
152
153         /*
154          * Readjust result's LB to match the input's.  This does nothing in the
155          * append case, but it's the simplest way to implement the prepend case.
156          */
157         if (ARR_NDIM(v) == 1)
158                 ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
159
160         PG_RETURN_ARRAYTYPE_P(result);
161 }
162
163 /*-----------------------------------------------------------------------------
164  * array_cat :
165  *              concatenate two nD arrays to form an nD array, or
166  *              push an (n-1)D array onto the end of an nD array
167  *----------------------------------------------------------------------------
168  */
169 Datum
170 array_cat(PG_FUNCTION_ARGS)
171 {
172         ArrayType  *v1,
173                            *v2;
174         ArrayType  *result;
175         int                *dims,
176                            *lbs,
177                                 ndims,
178                                 nitems,
179                                 ndatabytes,
180                                 nbytes;
181         int                *dims1,
182                            *lbs1,
183                                 ndims1,
184                                 nitems1,
185                                 ndatabytes1;
186         int                *dims2,
187                            *lbs2,
188                                 ndims2,
189                                 nitems2,
190                                 ndatabytes2;
191         int                     i;
192         char       *dat1,
193                            *dat2;
194         bits8      *bitmap1,
195                            *bitmap2;
196         Oid                     element_type;
197         Oid                     element_type1;
198         Oid                     element_type2;
199         int32           dataoffset;
200
201         /* Concatenating a null array is a no-op, just return the other input */
202         if (PG_ARGISNULL(0))
203         {
204                 if (PG_ARGISNULL(1))
205                         PG_RETURN_NULL();
206                 result = PG_GETARG_ARRAYTYPE_P(1);
207                 PG_RETURN_ARRAYTYPE_P(result);
208         }
209         if (PG_ARGISNULL(1))
210         {
211                 result = PG_GETARG_ARRAYTYPE_P(0);
212                 PG_RETURN_ARRAYTYPE_P(result);
213         }
214
215         v1 = PG_GETARG_ARRAYTYPE_P(0);
216         v2 = PG_GETARG_ARRAYTYPE_P(1);
217
218         element_type1 = ARR_ELEMTYPE(v1);
219         element_type2 = ARR_ELEMTYPE(v2);
220
221         /* Check we have matching element types */
222         if (element_type1 != element_type2)
223                 ereport(ERROR,
224                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
225                                  errmsg("cannot concatenate incompatible arrays"),
226                                  errdetail("Arrays with element types %s and %s are not "
227                                                    "compatible for concatenation.",
228                                                    format_type_be(element_type1),
229                                                    format_type_be(element_type2))));
230
231         /* OK, use it */
232         element_type = element_type1;
233
234         /*----------
235          * We must have one of the following combinations of inputs:
236          * 1) one empty array, and one non-empty array
237          * 2) both arrays empty
238          * 3) two arrays with ndims1 == ndims2
239          * 4) ndims1 == ndims2 - 1
240          * 5) ndims1 == ndims2 + 1
241          *----------
242          */
243         ndims1 = ARR_NDIM(v1);
244         ndims2 = ARR_NDIM(v2);
245
246         /*
247          * short circuit - if one input array is empty, and the other is not, we
248          * return the non-empty one as the result
249          *
250          * if both are empty, return the first one
251          */
252         if (ndims1 == 0 && ndims2 > 0)
253                 PG_RETURN_ARRAYTYPE_P(v2);
254
255         if (ndims2 == 0)
256                 PG_RETURN_ARRAYTYPE_P(v1);
257
258         /* the rest fall under rule 3, 4, or 5 */
259         if (ndims1 != ndims2 &&
260                 ndims1 != ndims2 - 1 &&
261                 ndims1 != ndims2 + 1)
262                 ereport(ERROR,
263                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
264                                  errmsg("cannot concatenate incompatible arrays"),
265                                  errdetail("Arrays of %d and %d dimensions are not "
266                                                    "compatible for concatenation.",
267                                                    ndims1, ndims2)));
268
269         /* get argument array details */
270         lbs1 = ARR_LBOUND(v1);
271         lbs2 = ARR_LBOUND(v2);
272         dims1 = ARR_DIMS(v1);
273         dims2 = ARR_DIMS(v2);
274         dat1 = ARR_DATA_PTR(v1);
275         dat2 = ARR_DATA_PTR(v2);
276         bitmap1 = ARR_NULLBITMAP(v1);
277         bitmap2 = ARR_NULLBITMAP(v2);
278         nitems1 = ArrayGetNItems(ndims1, dims1);
279         nitems2 = ArrayGetNItems(ndims2, dims2);
280         ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
281         ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
282
283         if (ndims1 == ndims2)
284         {
285                 /*
286                  * resulting array is made up of the elements (possibly arrays
287                  * themselves) of the input argument arrays
288                  */
289                 ndims = ndims1;
290                 dims = (int *) palloc(ndims * sizeof(int));
291                 lbs = (int *) palloc(ndims * sizeof(int));
292
293                 dims[0] = dims1[0] + dims2[0];
294                 lbs[0] = lbs1[0];
295
296                 for (i = 1; i < ndims; i++)
297                 {
298                         if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
299                                 ereport(ERROR,
300                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
301                                                  errmsg("cannot concatenate incompatible arrays"),
302                                         errdetail("Arrays with differing element dimensions are "
303                                                           "not compatible for concatenation.")));
304
305                         dims[i] = dims1[i];
306                         lbs[i] = lbs1[i];
307                 }
308         }
309         else if (ndims1 == ndims2 - 1)
310         {
311                 /*
312                  * resulting array has the second argument as the outer array, with
313                  * the first argument inserted at the front of the outer dimension
314                  */
315                 ndims = ndims2;
316                 dims = (int *) palloc(ndims * sizeof(int));
317                 lbs = (int *) palloc(ndims * sizeof(int));
318                 memcpy(dims, dims2, ndims * sizeof(int));
319                 memcpy(lbs, lbs2, ndims * sizeof(int));
320
321                 /* increment number of elements in outer array */
322                 dims[0] += 1;
323
324                 /* make sure the added element matches our existing elements */
325                 for (i = 0; i < ndims1; i++)
326                 {
327                         if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
328                                 ereport(ERROR,
329                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
330                                                  errmsg("cannot concatenate incompatible arrays"),
331                                                  errdetail("Arrays with differing dimensions are not "
332                                                                    "compatible for concatenation.")));
333                 }
334         }
335         else
336         {
337                 /*
338                  * (ndims1 == ndims2 + 1)
339                  *
340                  * resulting array has the first argument as the outer array, with the
341                  * second argument appended to the end of the outer dimension
342                  */
343                 ndims = ndims1;
344                 dims = (int *) palloc(ndims * sizeof(int));
345                 lbs = (int *) palloc(ndims * sizeof(int));
346                 memcpy(dims, dims1, ndims * sizeof(int));
347                 memcpy(lbs, lbs1, ndims * sizeof(int));
348
349                 /* increment number of elements in outer array */
350                 dims[0] += 1;
351
352                 /* make sure the added element matches our existing elements */
353                 for (i = 0; i < ndims2; i++)
354                 {
355                         if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
356                                 ereport(ERROR,
357                                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
358                                                  errmsg("cannot concatenate incompatible arrays"),
359                                                  errdetail("Arrays with differing dimensions are not "
360                                                                    "compatible for concatenation.")));
361                 }
362         }
363
364         /* Do this mainly for overflow checking */
365         nitems = ArrayGetNItems(ndims, dims);
366
367         /* build the result array */
368         ndatabytes = ndatabytes1 + ndatabytes2;
369         if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
370         {
371                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
372                 nbytes = ndatabytes + dataoffset;
373         }
374         else
375         {
376                 dataoffset = 0;                 /* marker for no null bitmap */
377                 nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
378         }
379         result = (ArrayType *) palloc(nbytes);
380         SET_VARSIZE(result, nbytes);
381         result->ndim = ndims;
382         result->dataoffset = dataoffset;
383         result->elemtype = element_type;
384         memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
385         memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
386         /* data area is arg1 then arg2 */
387         memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
388         memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
389         /* handle the null bitmap if needed */
390         if (ARR_HASNULL(result))
391         {
392                 array_bitmap_copy(ARR_NULLBITMAP(result), 0,
393                                                   bitmap1, 0,
394                                                   nitems1);
395                 array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
396                                                   bitmap2, 0,
397                                                   nitems2);
398         }
399
400         PG_RETURN_ARRAYTYPE_P(result);
401 }
402
403
404 /*
405  * used by text_to_array() in varlena.c
406  */
407 ArrayType *
408 create_singleton_array(FunctionCallInfo fcinfo,
409                                            Oid element_type,
410                                            Datum element,
411                                            int ndims)
412 {
413         Datum           dvalues[1];
414         int16           typlen;
415         bool            typbyval;
416         char            typalign;
417         int                     dims[MAXDIM];
418         int                     lbs[MAXDIM];
419         int                     i;
420         ArrayMetaState *my_extra;
421
422         if (ndims < 1)
423                 ereport(ERROR,
424                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
425                                  errmsg("invalid number of dimensions: %d", ndims)));
426         if (ndims > MAXDIM)
427                 ereport(ERROR,
428                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
429                                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
430                                                 ndims, MAXDIM)));
431
432         dvalues[0] = element;
433
434         for (i = 0; i < ndims; i++)
435         {
436                 dims[i] = 1;
437                 lbs[i] = 1;
438         }
439
440         /*
441          * We arrange to look up info about element type only once per series of
442          * calls, assuming the element type doesn't change underneath us.
443          */
444         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
445         if (my_extra == NULL)
446         {
447                 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
448                                                                                                           sizeof(ArrayMetaState));
449                 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
450                 my_extra->element_type = ~element_type;
451         }
452
453         if (my_extra->element_type != element_type)
454         {
455                 /* Get info about element type */
456                 get_typlenbyvalalign(element_type,
457                                                          &my_extra->typlen,
458                                                          &my_extra->typbyval,
459                                                          &my_extra->typalign);
460                 my_extra->element_type = element_type;
461         }
462         typlen = my_extra->typlen;
463         typbyval = my_extra->typbyval;
464         typalign = my_extra->typalign;
465
466         return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
467                                                           typlen, typbyval, typalign);
468 }
469
470
471 /*
472  * ARRAY_AGG aggregate function
473  */
474 Datum
475 array_agg_transfn(PG_FUNCTION_ARGS)
476 {
477         Oid                     arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
478         MemoryContext aggcontext;
479         ArrayBuildState *state;
480         Datum           elem;
481
482         if (arg1_typeid == InvalidOid)
483                 ereport(ERROR,
484                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
485                                  errmsg("could not determine input data type")));
486
487         if (fcinfo->context && IsA(fcinfo->context, AggState))
488                 aggcontext = ((AggState *) fcinfo->context)->aggcontext;
489         else if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
490                 aggcontext = ((WindowAggState *) fcinfo->context)->wincontext;
491         else
492         {
493                 /* cannot be called directly because of internal-type argument */
494                 elog(ERROR, "array_agg_transfn called in non-aggregate context");
495                 aggcontext = NULL;              /* keep compiler quiet */
496         }
497
498         state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
499         elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
500         state = accumArrayResult(state,
501                                                          elem,
502                                                          PG_ARGISNULL(1),
503                                                          arg1_typeid,
504                                                          aggcontext);
505
506         /*
507          * The transition type for array_agg() is declared to be "internal", which
508          * is a pass-by-value type the same size as a pointer.  So we can safely
509          * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
510          */
511         PG_RETURN_POINTER(state);
512 }
513
514 Datum
515 array_agg_finalfn(PG_FUNCTION_ARGS)
516 {
517         Datum           result;
518         ArrayBuildState *state;
519         int                     dims[1];
520         int                     lbs[1];
521
522         /*
523          * Test for null before Asserting we are in right context.      This is to
524          * avoid possible Assert failure in 8.4beta installations, where it is
525          * possible for users to create NULL constants of type internal.
526          */
527         if (PG_ARGISNULL(0))
528                 PG_RETURN_NULL();               /* returns null iff no input values */
529
530         /* cannot be called directly because of internal-type argument */
531         Assert(fcinfo->context &&
532                    (IsA(fcinfo->context, AggState) ||
533                         IsA(fcinfo->context, WindowAggState)));
534
535         state = (ArrayBuildState *) PG_GETARG_POINTER(0);
536
537         dims[0] = state->nelems;
538         lbs[0] = 1;
539
540         /*
541          * Make the result.  We cannot release the ArrayBuildState because
542          * sometimes aggregate final functions are re-executed.  Rather, it
543          * is nodeAgg.c's responsibility to reset the aggcontext when it's
544          * safe to do so.
545          */
546         result = makeMdArrayResult(state, 1, dims, lbs,
547                                                            CurrentMemoryContext,
548                                                            false);
549
550         PG_RETURN_DATUM(result);
551 }