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)
124 {
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 ++endptr;
165 if (*endptr != '\0')
166 ereport(ERROR, (
167 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
168 errmsg("invalid input syntax for cdata: \"%s\"", orig),
169 errhint("garbage found after number: \"%s\"", endptr)
170 ));
172 if (typmod > 0)
173 data->cf = typmod;
174 else
175 data->cf = 0;
177 PG_RETURN_CDATA_P(data);
178 } /* cdata_in */
180 Datum
181 cdata_out(PG_FUNCTION_ARGS)
182 {
183 cdata_t *data;
185 char cd_str[1024];
186 char *result;
188 if (PG_NARGS() != 1)
189 ereport(ERROR, (
190 errmsg("cdata_out() expects one argument"),
191 errhint("Usage: cdata_out(cdata)")
192 ));
194 data = PG_GETARG_CDATA_P(0);
196 snprintf(cd_str, sizeof(cd_str), "%g (%s U:%i/%i)",
197 data->value, CF_TO_STR(data->cf),
198 data->undef_num, data->val_num);
200 result = pstrdup(cd_str);
201 PG_RETURN_CSTRING(result);
202 } /* cdata_out */
204 Datum
205 cdata_typmodin(PG_FUNCTION_ARGS)
206 {
207 ArrayType *tm_array;
209 Datum *elem_values;
210 int n;
211 char *cf_str;
212 int32 typmod = CF_AVG;
214 if (PG_NARGS() != 1)
215 ereport(ERROR, (
216 errmsg("cdata_typmodin() expects one argument"),
217 errhint("Usage: cdata_typmodin(array)")
218 ));
220 tm_array = PG_GETARG_ARRAYTYPE_P(0);
222 if (ARR_ELEMTYPE(tm_array) != CSTRINGOID)
223 ereport(ERROR, (
224 errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
225 errmsg("typmod array must be type cstring[]")
226 ));
228 if (ARR_NDIM(tm_array) != 1)
229 ereport(ERROR, (
230 errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
231 errmsg("typmod array must be one-dimensional")
232 ));
234 deconstruct_array(tm_array, CSTRINGOID,
235 /* elmlen = */ -2, /* elmbyval = */ false, /* elmalign = */ 'c',
236 &elem_values, /* nullsp = */ NULL, &n);
238 if (n != 1)
239 ereport(ERROR, (
240 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
241 errmsg("cdata typmod array must have one element")
242 ));
244 cf_str = DatumGetCString(elem_values[0]);
245 if (! strcasecmp(cf_str, "AVG"))
246 typmod = CF_AVG;
247 else if (! strcasecmp(cf_str, "MIN"))
248 typmod = CF_MIN;
249 else if (! strcasecmp(cf_str, "MAX"))
250 typmod = CF_MAX;
251 else
252 ereport(ERROR, (
253 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
254 errmsg("invalid cdata typmod: %s", cf_str)
255 ));
257 PG_RETURN_INT32(typmod);
258 } /* cdata_typmodin */
260 Datum
261 cdata_typmodout(PG_FUNCTION_ARGS)
262 {
263 int32 typmod;
264 char tm_str[32];
265 char *result;
267 if (PG_NARGS() != 1)
268 ereport(ERROR, (
269 errmsg("cdata_typmodout() expects one argument"),
270 errhint("Usage: cdata_typmodout(typmod)")
271 ));
273 typmod = PG_GETARG_INT32(0);
274 snprintf(tm_str, sizeof(tm_str), "('%s')", CF_TO_STR(typmod));
275 tm_str[sizeof(tm_str) - 1] = '\0';
276 result = pstrdup(tm_str);
277 PG_RETURN_CSTRING(result);
278 } /* cdata_typmodout */
280 Datum
281 cdata_to_cdata(PG_FUNCTION_ARGS)
282 {
283 cdata_t *data;
284 int32 typmod;
286 if (PG_NARGS() != 3)
287 ereport(ERROR, (
288 errmsg("cdata_to_cdata() "
289 "expects three arguments"),
290 errhint("Usage: cdata_to_cdata"
291 "(cdata, typmod, is_explicit)")
292 ));
294 data = PG_GETARG_CDATA_P(0);
295 typmod = PG_GETARG_INT32(1);
297 if ((data->cf >= 0) && (data->cf != typmod) && (data->val_num > 1))
298 ereport(ERROR, (
299 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
300 errmsg("invalid cast: cannot cast cdata "
301 "with different typmod (yet)")
302 ));
304 data->cf = typmod;
305 PG_RETURN_CDATA_P(data);
306 } /* cdata_to_cdata */
308 Datum
309 int32_to_cdata(PG_FUNCTION_ARGS)
310 {
311 int32 i_val;
312 int32 typmod;
314 cdata_t *data;
316 if (PG_NARGS() != 3)
317 ereport(ERROR, (
318 errmsg("int32_to_cdata() expects three arguments"),
319 errhint("Usage: int32_to_cdata"
320 "(integer, typmod, is_explicit)")
321 ));
323 i_val = PG_GETARG_INT32(0);
324 typmod = PG_GETARG_INT32(1);
326 data = (cdata_t *)palloc0(sizeof(*data));
328 data->value = (float8)i_val;
329 data->undef_num = 0;
330 data->val_num = 1;
332 if (typmod >= 0)
333 data->cf = typmod;
334 else
335 data->cf = CF_AVG;
337 PG_RETURN_CDATA_P(data);
338 } /* int32_to_cdata */
340 Datum
341 cdata_to_float8(PG_FUNCTION_ARGS)
342 {
343 cdata_t *data;
345 if (PG_NARGS() != 1)
346 ereport(ERROR, (
347 errmsg("cdata_to_float8() expects one argument"),
348 errhint("Usage: cdata_to_float8(cdata)")
349 ));
351 data = PG_GETARG_CDATA_P(0);
352 PG_RETURN_FLOAT8(data->value);
353 } /* cdata_to_float8 */
355 Datum
356 cdata_update(PG_FUNCTION_ARGS)
357 {
358 cdata_t *data;
359 cdata_t *update;
361 if (PG_NARGS() != 2)
362 ereport(ERROR, (
363 errmsg("cdata_update() expects two arguments"),
364 errhint("Usage: cdata_update(cdata, cdata)")
365 ));
367 data = PG_GETARG_CDATA_P(0);
368 update = PG_GETARG_CDATA_P(1);
370 if (! data)
371 PG_RETURN_CDATA_P(update);
373 if (! update)
374 PG_RETURN_CDATA_P(data);
376 if ((data->cf != update->cf) && (update->val_num > 1))
377 ereport(ERROR, (
378 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
379 errmsg("invalid update value: incompatible "
380 "consolidation function")
381 ));
383 switch (data->cf) {
384 case CF_AVG:
385 data->value = (data->value * (data->val_num - data->undef_num))
386 + (update->value * (update->val_num - update->undef_num));
387 data->value /= (data->val_num - data->undef_num)
388 + (update->val_num - update->undef_num);
389 break;
390 case CF_MIN:
391 data->value = (data->value <= update->value)
392 ? data->value : update->value;
393 break;
394 case CF_MAX:
395 data->value = (data->value >= update->value)
396 ? data->value : update->value;
397 break;
398 default:
399 ereport(ERROR, (
400 errcode(ERRCODE_DATA_CORRUPTED),
401 errmsg("unknown consolidation function %d",
402 data->cf)
403 ));
404 break;
405 }
407 data->undef_num += update->undef_num;
408 data->val_num += update->val_num;
409 PG_RETURN_CDATA_P(data);
410 } /* cdata_update */
412 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */