OSDN Git Service

Back out array mega-patch.
[pg-rex/syncrep.git] / 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, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.3 2003/06/25 21:30:32 momjian Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "catalog/pg_proc.h"
16 #include "catalog/pg_type.h"
17 #include "utils/array.h"
18 #include "utils/lsyscache.h"
19 #include "utils/syscache.h"
20
21
22 /*-----------------------------------------------------------------------------
23  * singleton_array :
24  *              Form a multi-dimensional array given one starting element.
25  *
26  * - first argument is the datum with which to build the array
27  * - second argument is the number of dimensions the array should have;
28  *     defaults to 1 if no second argument is provided
29  *----------------------------------------------------------------------------
30  */
31 Datum
32 singleton_array(PG_FUNCTION_ARGS)
33 {
34         Oid                     elem_type = get_fn_expr_argtype(fcinfo, 0);
35         int                     ndims;
36
37         if (elem_type == InvalidOid)
38                 elog(ERROR, "Cannot determine input datatype");
39
40         if (PG_NARGS() == 2)
41                 ndims = PG_GETARG_INT32(1);
42         else
43                 ndims = 1;
44
45         PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
46                                                                                                  PG_GETARG_DATUM(0),
47                                                                                                  ndims));
48 }
49
50 /*-----------------------------------------------------------------------------
51  * array_push :
52  *              push an element onto either end of a one-dimensional array
53  *----------------------------------------------------------------------------
54  */
55 Datum
56 array_push(PG_FUNCTION_ARGS)
57 {
58         ArrayType  *v;
59         Datum           newelem;
60         int                *dimv,
61                            *lb;
62         ArrayType  *result;
63         int                     indx;
64         bool            isNull;
65         Oid                     element_type;
66         int16           typlen;
67         bool            typbyval;
68         char            typalign;
69         Oid                     arg0_typeid = get_fn_expr_argtype(fcinfo, 0);
70         Oid                     arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
71         Oid                     arg0_elemid;
72         Oid                     arg1_elemid;
73
74         if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
75                 elog(ERROR, "array_push: cannot determine input data types");
76         arg0_elemid = get_element_type(arg0_typeid);
77         arg1_elemid = get_element_type(arg1_typeid);
78
79         if (arg0_elemid != InvalidOid)
80         {
81                 v = PG_GETARG_ARRAYTYPE_P(0);
82                 element_type = ARR_ELEMTYPE(v);
83                 newelem = PG_GETARG_DATUM(1);
84         }
85         else if (arg1_elemid != InvalidOid)
86         {
87                 v = PG_GETARG_ARRAYTYPE_P(1);
88                 element_type = ARR_ELEMTYPE(v);
89                 newelem = PG_GETARG_DATUM(0);
90         }
91         else
92         {
93                 /* Shouldn't get here given proper type checking in parser */
94                 elog(ERROR, "array_push: neither input type is an array");
95                 PG_RETURN_NULL();               /* keep compiler quiet */
96         }
97
98         /* Sanity check: do we have a one-dimensional array */
99         if (ARR_NDIM(v) != 1)
100                 elog(ERROR, "Arrays greater than one-dimension are not supported");
101
102         lb = ARR_LBOUND(v);
103         dimv = ARR_DIMS(v);
104         if (arg0_elemid != InvalidOid)
105         {
106                 /* append newelem */
107                 int     ub = dimv[0] + lb[0] - 1;
108                 indx = ub + 1;
109         }
110         else
111         {
112                 /* prepend newelem */
113                 indx = lb[0] - 1;
114         }
115
116         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
117
118         result = array_set(v, 1, &indx, newelem, -1,
119                                            typlen, typbyval, typalign, &isNull);
120
121         PG_RETURN_ARRAYTYPE_P(result);
122 }
123
124 /*-----------------------------------------------------------------------------
125  * array_cat :
126  *              concatenate two nD arrays to form an (n+1)D array, or
127  *              push an (n-1)D array onto the end of an nD array
128  *----------------------------------------------------------------------------
129  */
130 Datum
131 array_cat(PG_FUNCTION_ARGS)
132 {
133         ArrayType  *v1, *v2;
134         int                *dims, *lbs, ndims, ndatabytes, nbytes;
135         int                *dims1, *lbs1, ndims1, ndatabytes1;
136         int                *dims2, *lbs2, ndims2, ndatabytes2;
137         char       *dat1, *dat2;
138         Oid                     element_type;
139         Oid                     element_type1;
140         Oid                     element_type2;
141         ArrayType  *result;
142
143         v1 = PG_GETARG_ARRAYTYPE_P(0);
144         v2 = PG_GETARG_ARRAYTYPE_P(1);
145
146         /*
147          * We must have one of the following combinations of inputs:
148          * 1) two arrays with ndims1 == ndims2
149          * 2) ndims1 == ndims2 - 1
150          * 3) ndims1 == ndims2 + 1
151          */
152         ndims1 = ARR_NDIM(v1);
153         ndims2 = ARR_NDIM(v2);
154
155         if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
156                 elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
157                                         "%d dimensions", ndims1, ndims2);
158
159         element_type1 = ARR_ELEMTYPE(v1);
160         element_type2 = ARR_ELEMTYPE(v2);
161
162         /* Do we have a matching element types */
163         if (element_type1 != element_type2)
164                 elog(ERROR, "Cannot concatenate incompatible arrays with element "
165                                         "type %u and %u", element_type1, element_type2);
166
167         /* OK, use it */
168         element_type = element_type1;
169
170         /* get argument array details */
171         lbs1 = ARR_LBOUND(v1);
172         lbs2 = ARR_LBOUND(v2);
173         dims1 = ARR_DIMS(v1);
174         dims2 = ARR_DIMS(v2);
175         dat1 = ARR_DATA_PTR(v1);
176         dat2 = ARR_DATA_PTR(v2);
177         ndatabytes1 = ARR_SIZE(v1) - ARR_OVERHEAD(ndims1);
178         ndatabytes2 = ARR_SIZE(v2) - ARR_OVERHEAD(ndims2);
179
180         if (ndims1 == ndims2)
181         {
182                 /*
183                  * resulting array has two element outer array made up of input
184                  * argument arrays
185                  */
186                 int             i;
187
188                 ndims = ndims1 + 1;
189                 dims = (int *) palloc(ndims * sizeof(int));
190                 lbs = (int *) palloc(ndims * sizeof(int));
191
192                 dims[0] = 2;    /* outer array made up of two input arrays */
193                 lbs[0] = 1;             /* start lower bound at 1 */
194
195                 for (i = 0; i < ndims1; i++)
196                 {
197                         if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
198                                 elog(ERROR, "Cannot concatenate arrays with differing dimensions");
199
200                         dims[i + 1] = dims1[i];
201                         lbs[i + 1] = lbs1[i];
202                 }
203         }
204         else if (ndims1 == ndims2 - 1)
205         {
206                 /*
207                  * resulting array has the second argument as the outer array,
208                  * with the first argument appended to the front of the outer
209                  * dimension
210                  */
211                 int             i;
212
213                 ndims = ndims2;
214                 dims = dims2;
215                 lbs = lbs2;
216
217                 /* increment number of elements in outer array */
218                 dims[0] += 1;
219
220                 /* make sure the added element matches our existing elements */
221                 for (i = 0; i < ndims1; i++)
222                 {
223                         if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
224                                 elog(ERROR, "Cannot concatenate arrays with differing dimensions");
225                 }
226         }
227         else /* (ndims1 == ndims2 + 1) */
228         {
229                 /*
230                  * resulting array has the first argument as the outer array,
231                  * with the second argument appended to the end of the outer
232                  * dimension
233                  */
234                 int             i;
235
236                 ndims = ndims1;
237                 dims = dims1;
238                 lbs = lbs1;
239
240                 /* increment number of elements in outer array */
241                 dims[0] += 1;
242
243                 /* make sure the added element matches our existing elements */
244                 for (i = 0; i < ndims2; i++)
245                 {
246                         if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
247                                 elog(ERROR, "Cannot concatenate arrays with differing dimensions");
248                 }
249         }
250
251         /* build the result array */
252         ndatabytes = ndatabytes1 + ndatabytes2;
253         nbytes = ndatabytes + ARR_OVERHEAD(ndims);
254         result = (ArrayType *) palloc(nbytes);
255
256         result->size = nbytes;
257         result->ndim = ndims;
258         result->flags = 0;
259         result->elemtype = element_type;
260         memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
261         memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
262         /* data area is arg1 then arg2 */
263         memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
264         memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
265
266         PG_RETURN_ARRAYTYPE_P(result);
267 }
268
269 /*----------------------------------------------------------------------------
270  * array_accum :
271  *              accumulator to build a 1-D array from input values -- this can be used
272  *              to create custom aggregates.
273  *
274  * This function is not marked strict, so we have to be careful about nulls.
275  *----------------------------------------------------------------------------
276  */
277 Datum
278 array_accum(PG_FUNCTION_ARGS)
279 {
280         /* return NULL if both arguments are NULL */
281         if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
282                 PG_RETURN_NULL();
283
284         /* create a new 1-D array from the new element if the array is NULL */
285         if (PG_ARGISNULL(0))
286         {
287                 Oid                     tgt_type = get_fn_expr_rettype(fcinfo);
288                 Oid                     tgt_elem_type;
289
290                 if (tgt_type == InvalidOid)
291                         elog(ERROR, "Cannot determine target array type");
292                 tgt_elem_type = get_element_type(tgt_type);
293                 if (tgt_elem_type == InvalidOid)
294                         elog(ERROR, "Target type is not an array");
295
296                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
297                                                                                                          PG_GETARG_DATUM(1),
298                                                                                                          1));
299         }
300
301         /* return the array if the new element is NULL */
302         if (PG_ARGISNULL(1))
303                 PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
304
305         /*
306          * Otherwise this is equivalent to array_push.  We hack the call a little
307          * so that array_push can see the fn_expr information.
308          */
309         return array_push(fcinfo);
310 }
311
312 /*-----------------------------------------------------------------------------
313  * array_assign :
314  *              assign an element of an array to a new value and return the
315  *              redefined array
316  *----------------------------------------------------------------------------
317  */
318 Datum
319 array_assign(PG_FUNCTION_ARGS)
320 {
321         ArrayType  *v;
322         int                     idx_to_chg;
323         Datum           newelem;
324         int                *dimv,
325                            *lb, ub;
326         ArrayType  *result;
327         bool            isNull;
328         Oid                     element_type;
329         int16           typlen;
330         bool            typbyval;
331         char            typalign;
332
333         v = PG_GETARG_ARRAYTYPE_P(0);
334         idx_to_chg = PG_GETARG_INT32(1);
335         newelem = PG_GETARG_DATUM(2);
336
337         /* Sanity check: do we have a one-dimensional array */
338         if (ARR_NDIM(v) != 1)
339                 elog(ERROR, "Arrays greater than one-dimension are not supported");
340
341         lb = ARR_LBOUND(v);
342         dimv = ARR_DIMS(v);
343         ub = dimv[0] + lb[0] - 1;
344         if (idx_to_chg < lb[0] || idx_to_chg > ub)
345                 elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
346
347         element_type = ARR_ELEMTYPE(v);
348         /* Sanity check: do we have a non-zero element type */
349         if (element_type == 0)
350                 elog(ERROR, "Invalid array element type: %u", element_type);
351
352         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
353
354         result = array_set(v, 1, &idx_to_chg, newelem, -1,
355                                            typlen, typbyval, typalign, &isNull);
356
357         PG_RETURN_ARRAYTYPE_P(result);
358 }
359
360 /*-----------------------------------------------------------------------------
361  * array_subscript :
362  *              return specific element of an array
363  *----------------------------------------------------------------------------
364  */
365 Datum
366 array_subscript(PG_FUNCTION_ARGS)
367 {
368         ArrayType  *v;
369         int                     idx;
370         int                *dimv,
371                            *lb, ub;
372         Datum           result;
373         bool            isNull;
374         Oid                     element_type;
375         int16           typlen;
376         bool            typbyval;
377         char            typalign;
378
379         v = PG_GETARG_ARRAYTYPE_P(0);
380         idx = PG_GETARG_INT32(1);
381
382         /* Sanity check: do we have a one-dimensional array */
383         if (ARR_NDIM(v) != 1)
384                 elog(ERROR, "Arrays greater than one-dimension are not supported");
385
386         lb = ARR_LBOUND(v);
387         dimv = ARR_DIMS(v);
388         ub = dimv[0] + lb[0] - 1;
389         if (idx < lb[0] || idx > ub)
390                 elog(ERROR, "Cannot return nonexistent array element: %d", idx);
391
392         element_type = ARR_ELEMTYPE(v);
393         /* Sanity check: do we have a non-zero element type */
394         if (element_type == 0)
395                 elog(ERROR, "Invalid array element type: %u", element_type);
396
397         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
398
399         result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
400
401         PG_RETURN_DATUM(result);
402 }
403
404 /*
405  * actually does the work for singleton_array(), and array_accum() if it is
406  * given a null input array.
407  */
408 ArrayType *
409 create_singleton_array(Oid element_type, Datum element, int ndims)
410 {
411         Datum   dvalues[1];
412         int16   typlen;
413         bool    typbyval;
414         char    typalign;
415         int             dims[MAXDIM];
416         int             lbs[MAXDIM];
417         int             i;
418
419         if (element_type == 0)
420                 elog(ERROR, "Invalid array element type: %u", element_type);
421         if (ndims < 1 || ndims > MAXDIM)
422                 elog(ERROR, "Invalid number of dimensions %d", ndims);
423
424         dvalues[0] = element;
425
426         for (i = 0; i < ndims; i++)
427         {
428                 dims[i] = 1;
429                 lbs[i] = 1;
430         }
431
432         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
433
434         return construct_md_array(dvalues, ndims, dims, lbs, element_type,
435                                                           typlen, typbyval, typalign);
436 }