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);
83 PG_FUNCTION_INFO_V1(cdata_update);
85 /*
86 * public API
87 */
89 Datum
90 cdata_validate(PG_FUNCTION_ARGS)
91 {
92 char type_info[1024];
93 char *result;
94 size_t req_len;
95 size_t len;
97 if (PG_NARGS() != 1)
98 ereport(ERROR, (
99 errmsg("cdata_validate() expect one argument"),
100 errhint("Usage cdata_validate(expected_size)")
101 ));
103 req_len = (size_t)PG_GETARG_UINT32(0);
104 len = sizeof(cdata_t);
106 if (req_len != len)
107 ereport(ERROR, (
108 errmsg("length of the cdata type "
109 "does not match the expected length"),
110 errhint("Please report a bug against PostRR")
111 ));
113 snprintf(type_info, sizeof(type_info),
114 "cdata validated successfully; type length = %zu", len);
115 type_info[sizeof(type_info) - 1] = '\0';
117 result = pstrdup(type_info);
118 PG_RETURN_CSTRING(result);
119 } /* cdata_validate */
121 Datum
122 cdata_in(PG_FUNCTION_ARGS)
123 {
124 cdata_t *data;
125 int32 typmod;
127 char *val_str, *orig;
128 char *endptr = NULL;
130 if (PG_NARGS() != 3)
131 ereport(ERROR, (
132 errmsg("cdata_in() expects three arguments"),
133 errhint("Usage: cdata_in(col_name, oid, typmod)")
134 ));
136 data = (cdata_t *)palloc0(sizeof(*data));
138 val_str = PG_GETARG_CSTRING(0);
139 typmod = PG_GETARG_INT32(2);
141 orig = val_str;
142 while ((*val_str != '\0') && isspace((int)*val_str))
143 ++val_str;
145 if (*val_str == '\0')
146 ereport(ERROR, (
147 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
148 errmsg("invalid input syntax for cdata: \"%s\"", orig)
149 ));
151 errno = 0;
152 data->value = strtod(val_str, &endptr);
153 data->val_num = 1;
155 if ((endptr == val_str) || errno)
156 ereport(ERROR, (
157 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
158 errmsg("invalid input syntax for cdata: \"%s\"", orig)
159 ));
161 while ((*endptr != '\0') && isspace((int)*endptr))
162 ereport(ERROR, (
163 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
164 errmsg("invalid input syntax for cdata: \"%s\"", orig)
165 ));
167 if (typmod > 0)
168 data->cf = typmod;
169 else
170 data->cf = 0;
172 PG_RETURN_CDATA_P(data);
173 } /* cdata_in */
175 Datum
176 cdata_out(PG_FUNCTION_ARGS)
177 {
178 cdata_t *data;
180 char cd_str[1024];
181 char *result;
183 if (PG_NARGS() != 1)
184 ereport(ERROR, (
185 errmsg("cdata_out() expects one argument"),
186 errhint("Usage: cdata_out(cdata)")
187 ));
189 data = PG_GETARG_CDATA_P(0);
191 snprintf(cd_str, sizeof(cd_str), "%g (U:%i/%i)",
192 data->value, data->undef_num, data->val_num);
194 result = pstrdup(cd_str);
195 PG_RETURN_CSTRING(result);
196 } /* cdata_out */
198 Datum
199 cdata_typmodin(PG_FUNCTION_ARGS)
200 {
201 ArrayType *tm_array;
203 Datum *elem_values;
204 int n;
205 char *cf_str;
206 int32 typmod = CF_AVG;
208 if (PG_NARGS() != 1)
209 ereport(ERROR, (
210 errmsg("cdata_typmodin() expects one argument"),
211 errhint("Usage: cdata_typmodin(array)")
212 ));
214 tm_array = PG_GETARG_ARRAYTYPE_P(0);
216 if (ARR_ELEMTYPE(tm_array) != CSTRINGOID)
217 ereport(ERROR, (
218 errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
219 errmsg("typmod array must be type cstring[]")
220 ));
222 if (ARR_NDIM(tm_array) != 1)
223 ereport(ERROR, (
224 errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
225 errmsg("typmod array must be one-dimensional")
226 ));
228 deconstruct_array(tm_array, CSTRINGOID,
229 /* elmlen = */ -2, /* elmbyval = */ false, /* elmalign = */ 'c',
230 &elem_values, /* nullsp = */ NULL, &n);
232 if (n != 1)
233 ereport(ERROR, (
234 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
235 errmsg("cdata typmod array must have one element")
236 ));
238 cf_str = DatumGetCString(elem_values[0]);
239 if (! strcasecmp(cf_str, "AVG"))
240 typmod = CF_AVG;
241 else if (! strcasecmp(cf_str, "MIN"))
242 typmod = CF_MIN;
243 else if (! strcasecmp(cf_str, "MAX"))
244 typmod = CF_MAX;
245 else
246 ereport(ERROR, (
247 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
248 errmsg("invalid cdata typmod: %s", cf_str)
249 ));
251 PG_RETURN_INT32(typmod);
252 } /* cdata_typmodin */
254 Datum
255 cdata_typmodout(PG_FUNCTION_ARGS)
256 {
257 int32 typmod;
258 char tm_str[32];
259 char *result;
261 if (PG_NARGS() != 1)
262 ereport(ERROR, (
263 errmsg("cdata_typmodout() expects one argument"),
264 errhint("Usage: cdata_typmodout(typmod)")
265 ));
267 typmod = PG_GETARG_INT32(0);
268 snprintf(tm_str, sizeof(tm_str), "('%s')", CF_TO_STR(typmod));
269 tm_str[sizeof(tm_str) - 1] = '\0';
270 result = pstrdup(tm_str);
271 PG_RETURN_CSTRING(result);
272 } /* cdata_typmodout */
274 Datum
275 cdata_to_cdata(PG_FUNCTION_ARGS)
276 {
277 cdata_t *data;
278 int32 typmod;
280 if (PG_NARGS() != 3)
281 ereport(ERROR, (
282 errmsg("cdata_to_cdata() "
283 "expects three arguments"),
284 errhint("Usage: cdata_to_cdata"
285 "(cdata, typmod, is_explicit)")
286 ));
288 data = PG_GETARG_CDATA_P(0);
289 typmod = PG_GETARG_INT32(1);
291 if ((data->cf >= 0) && (data->cf != typmod) && (data->val_num > 1))
292 ereport(ERROR, (
293 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
294 errmsg("invalid cast: cannot cast cdata "
295 "with different typmod (yet)")
296 ));
298 data->cf = typmod;
299 PG_RETURN_CDATA_P(data);
300 } /* cdata_to_cdata */
302 Datum
303 int32_to_cdata(PG_FUNCTION_ARGS)
304 {
305 int32 i_val;
306 int32 typmod;
308 cdata_t *data;
310 if (PG_NARGS() != 3)
311 ereport(ERROR, (
312 errmsg("int32_to_cdata() expects three arguments"),
313 errhint("Usage: int32_to_cdata"
314 "(integer, typmod, is_explicit)")
315 ));
317 i_val = PG_GETARG_INT32(0);
318 typmod = PG_GETARG_INT32(1);
320 data = (cdata_t *)palloc0(sizeof(*data));
322 data->value = (float8)i_val;
323 data->undef_num = 0;
324 data->val_num = 1;
326 if (typmod >= 0)
327 data->cf = typmod;
328 else
329 data->cf = CF_AVG;
331 PG_RETURN_CDATA_P(data);
332 } /* int32_to_cdata */
334 Datum
335 cdata_update(PG_FUNCTION_ARGS)
336 {
337 cdata_t *data;
338 cdata_t *update;
340 if (PG_NARGS() != 2)
341 ereport(ERROR, (
342 errmsg("cdata_update() expects two arguments"),
343 errhint("Usage: cdata_update(cdata, cdata)")
344 ));
346 data = PG_GETARG_CDATA_P(0);
347 update = PG_GETARG_CDATA_P(1);
349 if ((data->cf != update->cf) && (update->val_num > 1))
350 ereport(ERROR, (
351 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
352 errmsg("invalid update value: incompatible "
353 "consolidation function")
354 ));
356 switch (data->cf) {
357 case CF_AVG:
358 data->value = (data->value * (data->val_num - data->undef_num))
359 + (update->value * (update->val_num - update->undef_num));
360 data->value /= (data->val_num - data->undef_num)
361 + (update->val_num - update->undef_num);
362 break;
363 case CF_MIN:
364 data->value = (data->value <= update->value)
365 ? data->value : update->value;
366 break;
367 case CF_MAX:
368 data->value = (data->value >= update->value)
369 ? data->value : update->value;
370 break;
371 default:
372 ereport(ERROR, (
373 errcode(ERRCODE_DATA_CORRUPTED),
374 errmsg("unknown consolidation function %d",
375 data->cf)
376 ));
377 break;
378 }
380 data->undef_num += update->undef_num;
381 data->val_num += update->val_num;
382 PG_RETURN_CDATA_P(data);
383 } /* cdata_update */
385 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */