Code

CData: Declared CData_update() as not strict.
[postrr.git] / src / cdata.c
1 /*
2  * PostRR - src/cdata.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 /*
29  * A PostgreSQL data-type providing consolidated data points.
30  */
32 #include "postrr.h"
34 #include <errno.h>
35 #include <string.h>
37 #include <postgres.h>
38 #include <fmgr.h>
40 /* Postgres utilities */
41 #include <catalog/pg_type.h>
42 #include <utils/array.h>
44 enum {
45         CF_AVG = 0,
46         CF_MIN = 1,
47         CF_MAX = 2
48 };
50 #define CF_TO_STR(cf) \
51         (((cf) == CF_AVG) \
52                 ? "AVG" \
53                 : ((cf) == CF_MIN) \
54                         ? "MIN" \
55                         : ((cf) == CF_MAX) \
56                                 ? "MAX" : "UNKNOWN")
58 /*
59  * data type
60  */
62 struct cdata {
63         float8 value;
64         int32 undef_num;
65         int32 val_num;
66         int32 cf;
67 };
69 /*
70  * prototypes for PostgreSQL functions
71  */
73 PG_FUNCTION_INFO_V1(cdata_validate);
75 PG_FUNCTION_INFO_V1(cdata_in);
76 PG_FUNCTION_INFO_V1(cdata_out);
77 PG_FUNCTION_INFO_V1(cdata_typmodin);
78 PG_FUNCTION_INFO_V1(cdata_typmodout);
80 PG_FUNCTION_INFO_V1(cdata_to_cdata);
81 PG_FUNCTION_INFO_V1(int32_to_cdata);
82 PG_FUNCTION_INFO_V1(cdata_to_float8);
84 PG_FUNCTION_INFO_V1(cdata_update);
86 /*
87  * public API
88  */
90 Datum
91 cdata_validate(PG_FUNCTION_ARGS)
92 {
93         char   type_info[1024];
94         char  *result;
95         size_t req_len;
96         size_t len;
98         if (PG_NARGS() != 1)
99                 ereport(ERROR, (
100                                         errmsg("cdata_validate() expect one argument"),
101                                         errhint("Usage cdata_validate(expected_size)")
102                                 ));
104         req_len = (size_t)PG_GETARG_UINT32(0);
105         len = sizeof(cdata_t);
107         if (req_len != len)
108                 ereport(ERROR, (
109                                         errmsg("length of the cdata type "
110                                                 "does not match the expected length"),
111                                         errhint("Please report a bug against PostRR")
112                                 ));
114         snprintf(type_info, sizeof(type_info),
115                         "cdata validated successfully; type length = %zu", len);
116         type_info[sizeof(type_info) - 1] = '\0';
118         result = pstrdup(type_info);
119         PG_RETURN_CSTRING(result);
120 } /* cdata_validate */
122 Datum
123 cdata_in(PG_FUNCTION_ARGS)
125         cdata_t *data;
126         int32 typmod;
128         char *val_str, *orig;
129         char *endptr = NULL;
131         if (PG_NARGS() != 3)
132                 ereport(ERROR, (
133                                         errmsg("cdata_in() expects three arguments"),
134                                         errhint("Usage: cdata_in(col_name, oid, typmod)")
135                                 ));
137         data = (cdata_t *)palloc0(sizeof(*data));
139         val_str = PG_GETARG_CSTRING(0);
140         typmod  = PG_GETARG_INT32(2);
142         orig = val_str;
143         while ((*val_str != '\0') && isspace((int)*val_str))
144                 ++val_str;
146         if (*val_str == '\0')
147                 ereport(ERROR, (
148                                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
149                                         errmsg("invalid input syntax for cdata: \"%s\"", orig)
150                                 ));
152         errno = 0;
153         data->value   = strtod(val_str, &endptr);
154         data->val_num = 1;
156         if ((endptr == val_str) || errno)
157                 ereport(ERROR, (
158                                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
159                                         errmsg("invalid input syntax for cdata: \"%s\"", orig)
160                                 ));
162         while ((*endptr != '\0') && isspace((int)*endptr))
163                 ereport(ERROR, (
164                                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
165                                         errmsg("invalid input syntax for cdata: \"%s\"", orig)
166                                 ));
168         if (typmod > 0)
169                 data->cf = typmod;
170         else
171                 data->cf = 0;
173         PG_RETURN_CDATA_P(data);
174 } /* cdata_in */
176 Datum
177 cdata_out(PG_FUNCTION_ARGS)
179         cdata_t *data;
181         char  cd_str[1024];
182         char *result;
184         if (PG_NARGS() != 1)
185                 ereport(ERROR, (
186                                         errmsg("cdata_out() expects one argument"),
187                                         errhint("Usage: cdata_out(cdata)")
188                                 ));
190         data = PG_GETARG_CDATA_P(0);
192         snprintf(cd_str, sizeof(cd_str), "%g (%s U:%i/%i)",
193                         data->value, CF_TO_STR(data->cf),
194                         data->undef_num, data->val_num);
196         result = pstrdup(cd_str);
197         PG_RETURN_CSTRING(result);
198 } /* cdata_out */
200 Datum
201 cdata_typmodin(PG_FUNCTION_ARGS)
203         ArrayType *tm_array;
205         Datum *elem_values;
206         int    n;
207         char  *cf_str;
208         int32  typmod = CF_AVG;
210         if (PG_NARGS() != 1)
211                 ereport(ERROR, (
212                                         errmsg("cdata_typmodin() expects one argument"),
213                                         errhint("Usage: cdata_typmodin(array)")
214                                 ));
216         tm_array = PG_GETARG_ARRAYTYPE_P(0);
218         if (ARR_ELEMTYPE(tm_array) != CSTRINGOID)
219                 ereport(ERROR, (
220                                         errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
221                                         errmsg("typmod array must be type cstring[]")
222                                 ));
224         if (ARR_NDIM(tm_array) != 1)
225                 ereport(ERROR, (
226                                         errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
227                                         errmsg("typmod array must be one-dimensional")
228                                 ));
230         deconstruct_array(tm_array, CSTRINGOID,
231                         /* elmlen = */ -2, /* elmbyval = */ false, /* elmalign = */ 'c',
232                         &elem_values, /* nullsp = */ NULL, &n);
234         if (n != 1)
235                 ereport(ERROR, (
236                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
237                                         errmsg("cdata typmod array must have one element")
238                                 ));
240         cf_str = DatumGetCString(elem_values[0]);
241         if (! strcasecmp(cf_str, "AVG"))
242                 typmod = CF_AVG;
243         else if (! strcasecmp(cf_str, "MIN"))
244                 typmod = CF_MIN;
245         else if (! strcasecmp(cf_str, "MAX"))
246                 typmod = CF_MAX;
247         else
248                 ereport(ERROR, (
249                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
250                                         errmsg("invalid cdata typmod: %s", cf_str)
251                                 ));
253         PG_RETURN_INT32(typmod);
254 } /* cdata_typmodin */
256 Datum
257 cdata_typmodout(PG_FUNCTION_ARGS)
259         int32 typmod;
260         char  tm_str[32];
261         char *result;
263         if (PG_NARGS() != 1)
264                 ereport(ERROR, (
265                                         errmsg("cdata_typmodout() expects one argument"),
266                                         errhint("Usage: cdata_typmodout(typmod)")
267                                 ));
269         typmod = PG_GETARG_INT32(0);
270         snprintf(tm_str, sizeof(tm_str), "('%s')", CF_TO_STR(typmod));
271         tm_str[sizeof(tm_str) - 1] = '\0';
272         result = pstrdup(tm_str);
273         PG_RETURN_CSTRING(result);
274 } /* cdata_typmodout */
276 Datum
277 cdata_to_cdata(PG_FUNCTION_ARGS)
279         cdata_t *data;
280         int32 typmod;
282         if (PG_NARGS() != 3)
283                 ereport(ERROR, (
284                                         errmsg("cdata_to_cdata() "
285                                                 "expects three arguments"),
286                                         errhint("Usage: cdata_to_cdata"
287                                                 "(cdata, typmod, is_explicit)")
288                                 ));
290         data   = PG_GETARG_CDATA_P(0);
291         typmod = PG_GETARG_INT32(1);
293         if ((data->cf >= 0) && (data->cf != typmod) && (data->val_num > 1))
294                 ereport(ERROR, (
295                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
296                                         errmsg("invalid cast: cannot cast cdata "
297                                                 "with different typmod (yet)")
298                                 ));
300         data->cf = typmod;
301         PG_RETURN_CDATA_P(data);
302 } /* cdata_to_cdata */
304 Datum
305 int32_to_cdata(PG_FUNCTION_ARGS)
307         int32 i_val;
308         int32 typmod;
310         cdata_t *data;
312         if (PG_NARGS() != 3)
313                 ereport(ERROR, (
314                                         errmsg("int32_to_cdata() expects three arguments"),
315                                         errhint("Usage: int32_to_cdata"
316                                                 "(integer, typmod, is_explicit)")
317                                 ));
319         i_val  = PG_GETARG_INT32(0);
320         typmod = PG_GETARG_INT32(1);
322         data = (cdata_t *)palloc0(sizeof(*data));
324         data->value     = (float8)i_val;
325         data->undef_num = 0;
326         data->val_num   = 1;
328         if (typmod >= 0)
329                 data->cf = typmod;
330         else
331                 data->cf = CF_AVG;
333         PG_RETURN_CDATA_P(data);
334 } /* int32_to_cdata */
336 Datum
337 cdata_to_float8(PG_FUNCTION_ARGS)
339         cdata_t *data;
341         if (PG_NARGS() != 1)
342                 ereport(ERROR, (
343                                         errmsg("cdata_to_float8() expects one argument"),
344                                         errhint("Usage: cdata_to_float8(cdata)")
345                                 ));
347         data = PG_GETARG_CDATA_P(0);
348         PG_RETURN_FLOAT8(data->value);
349 } /* cdata_to_float8 */
351 Datum
352 cdata_update(PG_FUNCTION_ARGS)
354         cdata_t *data;
355         cdata_t *update;
357         if (PG_NARGS() != 2)
358                 ereport(ERROR, (
359                                         errmsg("cdata_update() expects two arguments"),
360                                         errhint("Usage: cdata_update(cdata, cdata)")
361                                 ));
363         data   = PG_GETARG_CDATA_P(0);
364         update = PG_GETARG_CDATA_P(1);
366         if (! data)
367                 PG_RETURN_CDATA_P(update);
369         if ((data->cf != update->cf) && (update->val_num > 1))
370                 ereport(ERROR, (
371                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
372                                         errmsg("invalid update value: incompatible "
373                                                 "consolidation function")
374                                 ));
376         switch (data->cf) {
377                 case CF_AVG:
378                         data->value = (data->value * (data->val_num - data->undef_num))
379                                 + (update->value * (update->val_num - update->undef_num));
380                         data->value /= (data->val_num - data->undef_num)
381                                         +  (update->val_num - update->undef_num);
382                         break;
383                 case CF_MIN:
384                         data->value = (data->value <= update->value)
385                                 ? data->value : update->value;
386                         break;
387                 case CF_MAX:
388                         data->value = (data->value >= update->value)
389                                 ? data->value : update->value;
390                         break;
391                 default:
392                         ereport(ERROR, (
393                                                 errcode(ERRCODE_DATA_CORRUPTED),
394                                                 errmsg("unknown consolidation function %d",
395                                                         data->cf)
396                                         ));
397                         break;
398         }
400         data->undef_num += update->undef_num;
401         data->val_num   += update->val_num;
402         PG_RETURN_CDATA_P(data);
403 } /* cdata_update */
405 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */