Code

CData: Use float8 rather than double.
[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);
82 /*
83  * public API
84  */
86 Datum
87 cdata_validate(PG_FUNCTION_ARGS)
88 {
89         char   type_info[1024];
90         char  *result;
91         size_t req_len;
92         size_t len;
94         if (PG_NARGS() != 1)
95                 ereport(ERROR, (
96                                         errmsg("cdata_validate() expect one argument"),
97                                         errhint("Usage cdata_validate(expected_size)")
98                                 ));
100         req_len = (size_t)PG_GETARG_UINT32(0);
101         len = sizeof(cdata_t);
103         if (req_len != len)
104                 ereport(ERROR, (
105                                         errmsg("length of the cdata type "
106                                                 "does not match the expected length"),
107                                         errhint("Please report a bug against PostRR")
108                                 ));
110         snprintf(type_info, sizeof(type_info),
111                         "cdata validated successfully; type length = %zu", len);
112         type_info[sizeof(type_info) - 1] = '\0';
114         result = pstrdup(type_info);
115         PG_RETURN_CSTRING(result);
116 } /* cdata_validate */
118 Datum
119 cdata_in(PG_FUNCTION_ARGS)
121         cdata_t *data;
122         int32 typmod;
124         char *val_str, *orig;
125         char *endptr = NULL;
127         if (PG_NARGS() != 3)
128                 ereport(ERROR, (
129                                         errmsg("cdata_in() expects three arguments"),
130                                         errhint("Usage: cdata_in(col_name, oid, typmod)")
131                                 ));
133         data = (cdata_t *)palloc0(sizeof(*data));
135         val_str = PG_GETARG_CSTRING(0);
136         typmod  = PG_GETARG_INT32(2);
138         orig = val_str;
139         while ((*val_str != '\0') && isspace((int)*val_str))
140                 ++val_str;
142         if (*val_str == '\0')
143                 ereport(ERROR, (
144                                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
145                                         errmsg("invalid input syntax for cdata: \"%s\"", orig)
146                                 ));
148         errno = 0;
149         data->value   = strtod(val_str, &endptr);
150         data->val_num = 1;
152         if ((endptr == val_str) || errno)
153                 ereport(ERROR, (
154                                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
155                                         errmsg("invalid input syntax for cdata: \"%s\"", orig)
156                                 ));
158         while ((*endptr != '\0') && isspace((int)*endptr))
159                 ereport(ERROR, (
160                                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
161                                         errmsg("invalid input syntax for cdata: \"%s\"", orig)
162                                 ));
164         if (typmod > 0)
165                 data->cf = typmod;
166         else
167                 data->cf = 0;
169         PG_RETURN_CDATA_P(data);
170 } /* cdata_in */
172 Datum
173 cdata_out(PG_FUNCTION_ARGS)
175         cdata_t *data;
177         char  cd_str[1024];
178         char *result;
180         if (PG_NARGS() != 1)
181                 ereport(ERROR, (
182                                         errmsg("cdata_out() expects one argument"),
183                                         errhint("Usage: cdata_out(cdata)")
184                                 ));
186         data = PG_GETARG_CDATA_P(0);
188         snprintf(cd_str, sizeof(cd_str), "%g (U:%i/%i)",
189                         data->value, data->undef_num, data->val_num);
191         result = pstrdup(cd_str);
192         PG_RETURN_CSTRING(result);
193 } /* cdata_out */
195 Datum
196 cdata_typmodin(PG_FUNCTION_ARGS)
198         ArrayType *tm_array;
200         Datum *elem_values;
201         int    n;
202         char  *cf_str;
203         int32  typmod = CF_AVG;
205         if (PG_NARGS() != 1)
206                 ereport(ERROR, (
207                                         errmsg("cdata_typmodin() expects one argument"),
208                                         errhint("Usage: cdata_typmodin(array)")
209                                 ));
211         tm_array = PG_GETARG_ARRAYTYPE_P(0);
213         if (ARR_ELEMTYPE(tm_array) != CSTRINGOID)
214                 ereport(ERROR, (
215                                         errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
216                                         errmsg("typmod array must be type cstring[]")
217                                 ));
219         if (ARR_NDIM(tm_array) != 1)
220                 ereport(ERROR, (
221                                         errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
222                                         errmsg("typmod array must be one-dimensional")
223                                 ));
225         deconstruct_array(tm_array, CSTRINGOID,
226                         /* elmlen = */ -2, /* elmbyval = */ false, /* elmalign = */ 'c',
227                         &elem_values, /* nullsp = */ NULL, &n);
229         if (n != 1)
230                 ereport(ERROR, (
231                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
232                                         errmsg("cdata typmod array must have one element")
233                                 ));
235         cf_str = DatumGetCString(elem_values[0]);
236         if (! strcasecmp(cf_str, "AVG"))
237                 typmod = CF_AVG;
238         else if (! strcasecmp(cf_str, "MIN"))
239                 typmod = CF_MIN;
240         else if (! strcasecmp(cf_str, "MAX"))
241                 typmod = CF_MAX;
242         else
243                 ereport(ERROR, (
244                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
245                                         errmsg("invalid cdata typmod: %s", cf_str)
246                                 ));
248         PG_RETURN_INT32(typmod);
249 } /* cdata_typmodin */
251 Datum
252 cdata_typmodout(PG_FUNCTION_ARGS)
254         int32 typmod;
255         char  tm_str[32];
256         char *result;
258         if (PG_NARGS() != 1)
259                 ereport(ERROR, (
260                                         errmsg("cdata_typmodout() expects one argument"),
261                                         errhint("Usage: cdata_typmodout(typmod)")
262                                 ));
264         typmod = PG_GETARG_INT32(0);
265         snprintf(tm_str, sizeof(tm_str), "('%s')", CF_TO_STR(typmod));
266         tm_str[sizeof(tm_str) - 1] = '\0';
267         result = pstrdup(tm_str);
268         PG_RETURN_CSTRING(result);
269 } /* cdata_typmodout */
271 Datum
272 cdata_to_cdata(PG_FUNCTION_ARGS)
274         cdata_t *data;
275         int32 typmod;
277         if (PG_NARGS() != 3)
278                 ereport(ERROR, (
279                                         errmsg("cdata_to_cdata() "
280                                                 "expects three arguments"),
281                                         errhint("Usage: cdata_to_cdata"
282                                                 "(cdata, typmod, is_explicit)")
283                                 ));
285         data   = PG_GETARG_CDATA_P(0);
286         typmod = PG_GETARG_INT32(1);
288         if ((data->cf >= 0) && (data->cf != typmod) && (data->val_num > 1))
289                 ereport(ERROR, (
290                                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
291                                         errmsg("invalid cast: cannot cast cdata "
292                                                 "with different typmod (yet)")
293                                 ));
295         data->cf = typmod;
296         PG_RETURN_CDATA_P(data);
297 } /* cdata_to_cdata */
299 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */