4 * Copyright (c) 2009-2014, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5 * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
10 #include "libpq/pqformat.h"
11 #include "utils/array.h"
12 #include "utils/builtins.h"
13 #include "utils/lsyscache.h"
14 #include "utils/memutils.h"
15 #if PG_VERSION_NUM >= 90300
16 #include "access/tupmacs.h"
21 PG_FUNCTION_INFO_V1(dbms_stats_array_recv);
23 extern Datum dbms_stats_array_recv(PG_FUNCTION_ARGS);
25 static void ReadArrayBinary(StringInfo buf, int nitems,
26 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
27 int typlen, bool typbyval, char typalign,
28 Datum *values, bool *nulls,
29 bool *hasnulls, int32 *nbytes);
30 static void CopyArrayEls(ArrayType *array,
31 Datum *values, bool *nulls, int nitems,
32 int typlen, bool typbyval, char typalign,
34 static int ArrayCastAndSet(Datum src,
35 int typlen, bool typbyval, char typalign,
39 * recv function for use-defined type dbms_stats.anyarray. Receive string
40 * representation of anyarray object, and convert it into binary data.
43 dbms_stats_array_recv(PG_FUNCTION_ARGS)
45 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
63 ArrayMetaState *my_extra;
65 /* Get the array header information */
66 ndim = pq_getmsgint(buf, 4);
67 if (ndim < 0) /* we do allow zero-dimension arrays */
69 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
70 errmsg("invalid number of dimensions: %d", ndim)));
73 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
74 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
77 flags = pq_getmsgint(buf, 4);
78 if (flags != 0 && flags != 1)
80 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
81 errmsg("invalid array flags")));
83 element_type = pq_getmsgint(buf, sizeof(Oid));
85 for (i = 0; i < ndim; i++)
89 dim[i] = pq_getmsgint(buf, 4);
90 lBound[i] = pq_getmsgint(buf, 4);
92 ub = lBound[i] + dim[i] - 1;
96 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
97 errmsg("integer out of range")));
100 /* This checks for overflow of array dimensions */
101 nitems = ArrayGetNItems(ndim, dim);
104 * We arrange to look up info about element type, including its receive
105 * conversion proc, only once per series of calls, assuming the element
106 * type doesn't change underneath us.
108 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
109 if (my_extra == NULL)
111 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
112 sizeof(ArrayMetaState));
113 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
114 my_extra->element_type = ~element_type;
117 if (my_extra->element_type != element_type)
119 /* Get info about element type, including its receive proc */
120 get_type_io_data(element_type, IOFunc_receive,
121 &my_extra->typlen, &my_extra->typbyval,
122 &my_extra->typalign, &my_extra->typdelim,
123 &my_extra->typioparam, &my_extra->typiofunc);
124 if (!OidIsValid(my_extra->typiofunc))
126 (errcode(ERRCODE_UNDEFINED_FUNCTION),
127 errmsg("no binary input function available for type \"%s\"",
128 format_type_be(element_type))));
129 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
130 fcinfo->flinfo->fn_mcxt);
131 my_extra->element_type = element_type;
136 /* Return empty array ... but not till we've validated element_type */
137 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
140 typlen = my_extra->typlen;
141 typbyval = my_extra->typbyval;
142 typalign = my_extra->typalign;
143 typioparam = my_extra->typioparam;
145 dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
146 nullsPtr = (bool *) palloc(nitems * sizeof(bool));
147 ReadArrayBinary(buf, nitems,
148 &my_extra->proc, typioparam, 0,
149 typlen, typbyval, typalign,
154 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
155 nbytes += dataoffset;
159 dataoffset = 0; /* marker for no null bitmap */
160 nbytes += ARR_OVERHEAD_NONULLS(ndim);
162 retval = (ArrayType *) palloc(nbytes);
163 SET_VARSIZE(retval, nbytes);
165 retval->dataoffset = dataoffset;
166 retval->elemtype = element_type;
167 memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
168 memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
171 dataPtr, nullsPtr, nitems,
172 typlen, typbyval, typalign,
178 PG_RETURN_ARRAYTYPE_P(retval);
182 ReadArrayBinary(StringInfo buf,
184 FmgrInfo *receiveproc,
199 for (i = 0; i < nitems; i++)
202 StringInfoData elem_buf;
205 /* Get and check the item length */
206 itemlen = pq_getmsgint(buf, 4);
207 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
209 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
210 errmsg("insufficient data left in message")));
214 /* -1 length means NULL */
215 values[i] = ReceiveFunctionCall(receiveproc, NULL,
222 * Rather than copying data around, we just set up a phony StringInfo
223 * pointing to the correct portion of the input buffer. We assume we
224 * can scribble on the input buffer so as to maintain the convention
225 * that StringInfos have a trailing null.
227 elem_buf.data = &buf->data[buf->cursor];
228 elem_buf.maxlen = itemlen + 1;
229 elem_buf.len = itemlen;
232 buf->cursor += itemlen;
234 csave = buf->data[buf->cursor];
235 buf->data[buf->cursor] = '\0';
237 /* Now call the element's receiveproc */
238 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
242 /* Trouble if it didn't eat the whole buffer */
243 if (elem_buf.cursor != itemlen)
245 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
246 errmsg("improper binary format in array element %d",
249 buf->data[buf->cursor] = csave;
253 * Check for nulls, compute total data space needed
257 for (i = 0; i < nitems; i++)
263 /* let's just make sure data is not toasted */
265 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
266 totbytes = att_addlength_datum(totbytes, typlen, values[i]);
267 totbytes = att_align_nominal(totbytes, typalign);
268 /* check for overflow of total request */
269 if (!AllocSizeIsValid(totbytes))
271 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
272 errmsg("array size exceeds the maximum allowed (%d)",
273 (int) MaxAllocSize)));
281 CopyArrayEls(ArrayType *array,
290 char *p = ARR_DATA_PTR(array);
291 bits8 *bitmap = ARR_NULLBITMAP(array);
299 for (i = 0; i < nitems; i++)
301 if (nulls && nulls[i])
303 if (!bitmap) /* shouldn't happen */
304 elog(ERROR, "null array element where not supported");
305 /* bitmap bit stays 0 */
310 p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
312 pfree(DatumGetPointer(values[i]));
317 if (bitmask == 0x100)
326 if (bitmap && bitmask != 1)
331 ArrayCastAndSet(Datum src,
342 store_att_byval(dest, src, typlen);
344 memmove(dest, DatumGetPointer(src), typlen);
345 inc = att_align_nominal(typlen, typalign);
350 inc = att_addlength_datum(0, typlen, src);
351 memmove(dest, DatumGetPointer(src), inc);
352 inc = att_align_nominal(inc, typalign);
359 void test_dump(int *passed, int *total);
361 * Unit test entry point for dump.c. This will be called by PG_init()
362 * function, after initialization for this extension is completed .
363 * This funciton should add the numbers of tests passed and the total number of
364 * tests to parameter passed and total respectively.
367 test_dump(int *passed, int *total)
369 int local_passed = 0;
372 elog(WARNING, "==========");
376 elog(WARNING, "%s %d/%d passed", __FUNCTION__, local_passed, local_total);
377 *passed += local_passed;
378 *total += local_total;