OSDN Git Service

Fix a misspelling in regression test
[pgdbmsstats/pg_dbms_stats.git] / dump.c
1 /*
2  * dump.c
3  *
4  * Copyright (c) 2009-2017, 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
7  */
8 #include "postgres.h"
9
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"
17 #endif
18
19
20
21 PG_FUNCTION_INFO_V1(dbms_stats_array_recv);
22
23 extern Datum dbms_stats_array_recv(PG_FUNCTION_ARGS);
24
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 CopyAnyArrayEls(ArrayType *array,
31                          Datum *values, bool *nulls, int nitems,
32                          int typlen, bool typbyval, char typalign,
33                          bool freedata);
34 static int ArrayCastAndSet(Datum src,
35                                 int typlen, bool typbyval, char typalign,
36                                 char *dest);
37
38 /*
39  * recv function for use-defined type dbms_stats.anyarray.  Receive string
40  * representation of anyarray object, and convert it into binary data.
41  */
42 Datum
43 dbms_stats_array_recv(PG_FUNCTION_ARGS)
44 {
45         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
46         Oid                     element_type;
47         int                     typlen;
48         bool            typbyval;
49         char            typalign;
50         Oid                     typioparam;
51         int                     i,
52                                 nitems;
53         Datum      *dataPtr;
54         bool       *nullsPtr;
55         bool            hasnulls;
56         int32           nbytes;
57         int32           dataoffset;
58         ArrayType  *retval;
59         int                     ndim,
60                                 flags,
61                                 dim[MAXDIM],
62                                 lBound[MAXDIM];
63         ArrayMetaState *my_extra;
64
65         /* Get the array header information */
66         ndim = pq_getmsgint(buf, 4);
67         if (ndim < 0)                           /* we do allow zero-dimension arrays */
68                 ereport(ERROR,
69                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
70                                  errmsg("invalid number of dimensions: %d", ndim)));
71         if (ndim > MAXDIM)
72                 ereport(ERROR,
73                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
74                                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
75                                                 ndim, MAXDIM)));
76
77         flags = pq_getmsgint(buf, 4);
78         if (flags != 0 && flags != 1)
79                 ereport(ERROR,
80                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
81                                  errmsg("invalid array flags")));
82
83         element_type = pq_getmsgint(buf, sizeof(Oid));
84
85         for (i = 0; i < ndim; i++)
86         {
87                 int ub;
88
89                 dim[i] = pq_getmsgint(buf, 4);
90                 lBound[i] = pq_getmsgint(buf, 4);
91
92                 ub = lBound[i] + dim[i] - 1;
93                 /* overflow? */
94                 if (lBound[i] > ub)
95                         ereport(ERROR,
96                                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
97                                          errmsg("integer out of range")));
98         }
99
100         /* This checks for overflow of array dimensions */
101         nitems = ArrayGetNItems(ndim, dim);
102
103         /*
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.
107          */
108         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
109         if (my_extra == NULL)
110         {
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;
115         }
116
117         if (my_extra->element_type != element_type)
118         {
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))
125                         ereport(ERROR,
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;
132         }
133
134         if (nitems == 0)
135         {
136                 /* Return empty array ... but not till we've validated element_type */
137                 PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
138         }
139
140         typlen = my_extra->typlen;
141         typbyval = my_extra->typbyval;
142         typalign = my_extra->typalign;
143         typioparam = my_extra->typioparam;
144
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,
150                                         dataPtr, nullsPtr,
151                                         &hasnulls, &nbytes);
152         if (hasnulls)
153         {
154                 dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
155                 nbytes += dataoffset;
156         }
157         else
158         {
159                 dataoffset = 0;                 /* marker for no null bitmap */
160                 nbytes += ARR_OVERHEAD_NONULLS(ndim);
161         }
162         retval = (ArrayType *) palloc(nbytes);
163         SET_VARSIZE(retval, nbytes);
164         retval->ndim = ndim;
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));
169
170         CopyAnyArrayEls(retval,
171                                         dataPtr, nullsPtr, nitems,
172                                         typlen, typbyval, typalign,
173                                         true);
174
175         pfree(dataPtr);
176         pfree(nullsPtr);
177
178         PG_RETURN_ARRAYTYPE_P(retval);
179 }
180
181 static void
182 ReadArrayBinary(StringInfo buf,
183                                 int nitems,
184                                 FmgrInfo *receiveproc,
185                                 Oid typioparam,
186                                 int32 typmod,
187                                 int typlen,
188                                 bool typbyval,
189                                 char typalign,
190                                 Datum *values,
191                                 bool *nulls,
192                                 bool *hasnulls,
193                                 int32 *nbytes)
194 {
195         int                     i;
196         bool            hasnull;
197         int32           totbytes;
198
199         for (i = 0; i < nitems; i++)
200         {
201                 int                     itemlen;
202                 StringInfoData elem_buf;
203                 char            csave;
204
205                 /* Get and check the item length */
206                 itemlen = pq_getmsgint(buf, 4);
207                 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
208                         ereport(ERROR,
209                                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
210                                          errmsg("insufficient data left in message")));
211
212                 if (itemlen == -1)
213                 {
214                         /* -1 length means NULL */
215                         values[i] = ReceiveFunctionCall(receiveproc, NULL,
216                                                                                         typioparam, typmod);
217                         nulls[i] = true;
218                         continue;
219                 }
220
221                 /*
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.
226                  */
227                 elem_buf.data = &buf->data[buf->cursor];
228                 elem_buf.maxlen = itemlen + 1;
229                 elem_buf.len = itemlen;
230                 elem_buf.cursor = 0;
231
232                 buf->cursor += itemlen;
233
234                 csave = buf->data[buf->cursor];
235                 buf->data[buf->cursor] = '\0';
236
237                 /* Now call the element's receiveproc */
238                 values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
239                                                                                 typioparam, typmod);
240                 nulls[i] = false;
241
242                 /* Trouble if it didn't eat the whole buffer */
243                 if (elem_buf.cursor != itemlen)
244                         ereport(ERROR,
245                                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
246                                          errmsg("improper binary format in array element %d",
247                                                         i + 1)));
248
249                 buf->data[buf->cursor] = csave;
250         }
251
252         /*
253          * Check for nulls, compute total data space needed
254          */
255         hasnull = false;
256         totbytes = 0;
257         for (i = 0; i < nitems; i++)
258         {
259                 if (nulls[i])
260                         hasnull = true;
261                 else
262                 {
263                         /* let's just make sure data is not toasted */
264                         if (typlen == -1)
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))
270                                 ereport(ERROR,
271                                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
272                                                  errmsg("array size exceeds the maximum allowed (%d)",
273                                                                 (int) MaxAllocSize)));
274                 }
275         }
276         *hasnulls = hasnull;
277         *nbytes = totbytes;
278 }
279
280 static void
281 CopyAnyArrayEls(ArrayType *array,
282                                 Datum *values,
283                                 bool *nulls,
284                                 int nitems,
285                                 int typlen,
286                                 bool typbyval,
287                                 char typalign,
288                                 bool freedata)
289 {
290         char       *p = ARR_DATA_PTR(array);
291         bits8      *bitmap = ARR_NULLBITMAP(array);
292         int                     bitval = 0;
293         int                     bitmask = 1;
294         int                     i;
295
296         if (typbyval)
297                 freedata = false;
298
299         for (i = 0; i < nitems; i++)
300         {
301                 if (nulls && nulls[i])
302                 {
303                         if (!bitmap)            /* shouldn't happen */
304                                 elog(ERROR, "null array element where not supported");
305                         /* bitmap bit stays 0 */
306                 }
307                 else
308                 {
309                         bitval |= bitmask;
310                         p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
311                         if (freedata)
312                                 pfree(DatumGetPointer(values[i]));
313                 }
314                 if (bitmap)
315                 {
316                         bitmask <<= 1;
317                         if (bitmask == 0x100)
318                         {
319                                 *bitmap++ = bitval;
320                                 bitval = 0;
321                                 bitmask = 1;
322                         }
323                 }
324         }
325
326         if (bitmap && bitmask != 1)
327                 *bitmap = bitval;
328 }
329
330 static int
331 ArrayCastAndSet(Datum src,
332                                 int typlen,
333                                 bool typbyval,
334                                 char typalign,
335                                 char *dest)
336 {
337         int                     inc;
338
339         if (typlen > 0)
340         {
341                 if (typbyval)
342                         store_att_byval(dest, src, typlen);
343                 else
344                         memmove(dest, DatumGetPointer(src), typlen);
345                 inc = att_align_nominal(typlen, typalign);
346         }
347         else
348         {
349                 Assert(!typbyval);
350                 inc = att_addlength_datum(0, typlen, src);
351                 memmove(dest, DatumGetPointer(src), inc);
352                 inc = att_align_nominal(inc, typalign);
353         }
354
355         return inc;
356 }
357
358 #ifdef UNIT_TEST
359 void test_dump(int *passed, int *total);
360 /*
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.
365  */
366 void
367 test_dump(int *passed, int *total)
368 {
369         int local_passed = 0;
370         int local_total = 0;
371
372         elog(WARNING, "==========");
373
374         /* Do tests here */
375
376         elog(WARNING, "%s %d/%d passed", __FUNCTION__, local_passed, local_total);
377         *passed += local_passed;
378         *total += local_total;
379 }
380 #endif