Code

data: Added sdb_data_expr_eval().
[sysdb.git] / src / core / data.c
1 /*
2  * SysDB - src/core/data.c
3  * Copyright (C) 2014 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 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
34 #include "core/data.h"
35 #include "utils/error.h"
37 #include <errno.h>
39 #include <inttypes.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
45 #include <math.h>
47 /*
48  * private helper functions
49  */
51 static int
52 data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res)
53 {
54         unsigned char *new;
55         unsigned char *s1, *s2;
56         size_t len1, len2;
58         if (d1->type == SDB_TYPE_STRING) {
59                 s1 = (unsigned char *)d1->data.string;
60                 s2 = (unsigned char *)d2->data.string;
61                 len1 = s1 ? strlen((char *)s1) : 0;
62                 len2 = s2 ? strlen((char *)s2) : 0;
63         }
64         else if (d1->type == SDB_TYPE_BINARY) {
65                 s1 = d1->data.binary.datum;
66                 s2 = d2->data.binary.datum;
67                 len1 = d1->data.binary.length;
68                 len2 = d2->data.binary.length;
69         }
70         else
71                 return -1;
73         if (s1 || s2) {
74                 new = malloc(len1 + len2 + 1);
75                 if (! new)
76                         return -1;
77         }
78         else
79                 new = NULL;
81         if (len1)
82                 memcpy(new, s1, len1);
83         if (len2)
84                 memcpy(new + len1, s2, len2);
85         if (new)
86                 new[len1 + len2] = '\0';
88         res->type = d1->type;
89         if (res->type == SDB_TYPE_STRING) {
90                 res->data.string = (char *)new;
91         }
92         else {
93                 res->data.binary.datum = new;
94                 res->data.binary.length = len1 + len2;
95         }
96         return 0;
97 } /* data_concat */
99 /*
100  * public API
101  */
103 int
104 sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src)
106         sdb_data_t tmp;
108         if ((! dst) || (! src))
109                 return -1;
111         tmp = *src;
112         switch (src->type) {
113                 case SDB_TYPE_STRING:
114                         tmp.data.string = strdup(src->data.string);
115                         if (! tmp.data.string)
116                                 return -1;
117                         break;
118                 case SDB_TYPE_BINARY:
119                         tmp.data.binary.datum = malloc(src->data.binary.length);
120                         if (! tmp.data.binary.datum)
121                                 return -1;
122                         memcpy(tmp.data.binary.datum, src->data.binary.datum,
123                                         src->data.binary.length);
124                         break;
125         }
127         sdb_data_free_datum(dst);
128         *dst = tmp;
129         return 0;
130 } /* sdb_data_copy */
132 void
133 sdb_data_free_datum(sdb_data_t *datum)
135         if (! datum)
136                 return;
138         switch (datum->type) {
139                 case SDB_TYPE_STRING:
140                         if (datum->data.string)
141                                 free(datum->data.string);
142                         datum->data.string = NULL;
143                         break;
144                 case SDB_TYPE_BINARY:
145                         if (datum->data.binary.datum)
146                                 free(datum->data.binary.datum);
147                         datum->data.binary.datum = NULL;
148                         datum->data.binary.length = 0;
149                         break;
150         }
151 } /* sdb_data_free_datum */
153 int
154 sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2)
156 #define CMP_NULL(a, b) \
157         do { \
158                 if (!(a) && !(b)) return 0; \
159                 if (!(a)) return -1; \
160                 if (!(b)) return 1; \
161         } while (0)
163         CMP_NULL(d1, d2);
165         if (d1->type != d2->type)
166                 return -1;
168         switch (d1->type) {
169                 case SDB_TYPE_INTEGER:
170                         return SDB_CMP(d1->data.integer, d2->data.integer);
171                 case SDB_TYPE_DECIMAL:
172                         return SDB_CMP(d1->data.decimal, d2->data.decimal);
173                 case SDB_TYPE_STRING:
174                         CMP_NULL(d1->data.string, d2->data.string);
175                         return strcasecmp(d1->data.string, d2->data.string);
176                 case SDB_TYPE_DATETIME:
177                         return SDB_CMP(d1->data.datetime, d2->data.datetime);
178                 case SDB_TYPE_BINARY:
179                 {
180                         int diff;
182                         CMP_NULL(d1->data.binary.datum, d2->data.binary.datum);
184                         /* on a common prefix, the shorter datum sorts less */
185                         if (d1->data.binary.length < d2->data.binary.length) {
186                                 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
187                                                 d1->data.binary.length);
188                                 diff = diff ? diff : -1;
189                         }
190                         else if (d1->data.binary.length > d2->data.binary.length) {
191                                 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
192                                                 d2->data.binary.length);
193                                 diff = diff ? diff : 1;
194                         }
195                         else
196                                 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
197                                                 d1->data.binary.length);
199                         return diff;
200                 }
201                 default:
202                         return -1;
203         }
204 #undef CMP_NULL
205 } /* sdb_data_cmp */
207 int
208 sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
209                 sdb_data_t *res)
211         if (d1->type != d2->type)
212                 return -1;
214         switch (op) {
215                 case SDB_DATA_CONCAT:
216                         return data_concat(d1, d2, res);
217                 case SDB_DATA_ADD:
218                         switch (d1->type) {
219                                 case SDB_TYPE_INTEGER:
220                                         res->data.integer = d1->data.integer + d2->data.integer;
221                                         break;
222                                 case SDB_TYPE_DECIMAL:
223                                         res->data.decimal = d1->data.decimal + d2->data.decimal;
224                                         break;
225                                 case SDB_TYPE_DATETIME:
226                                         res->data.datetime = d1->data.datetime + d2->data.datetime;
227                                         break;
228                                 default:
229                                         return -1;
230                         }
231                         break;
232                 case SDB_DATA_SUB:
233                         switch (d1->type) {
234                                 case SDB_TYPE_INTEGER:
235                                         res->data.integer = d1->data.integer - d2->data.integer;
236                                         break;
237                                 case SDB_TYPE_DECIMAL:
238                                         res->data.decimal = d1->data.decimal - d2->data.decimal;
239                                         break;
240                                 case SDB_TYPE_DATETIME:
241                                         res->data.datetime = d1->data.datetime - d2->data.datetime;
242                                         break;
243                                 default:
244                                         return -1;
245                         }
246                         break;
247                 case SDB_DATA_MUL:
248                         switch (d1->type) {
249                                 case SDB_TYPE_INTEGER:
250                                         res->data.integer = d1->data.integer * d2->data.integer;
251                                         break;
252                                 case SDB_TYPE_DECIMAL:
253                                         res->data.decimal = d1->data.decimal * d2->data.decimal;
254                                         break;
255                                 case SDB_TYPE_DATETIME:
256                                         res->data.datetime = d1->data.datetime * d2->data.datetime;
257                                         break;
258                                 default:
259                                         return -1;
260                         }
261                         break;
262                 case SDB_DATA_DIV:
263                         switch (d1->type) {
264                                 case SDB_TYPE_INTEGER:
265                                         res->data.integer = d1->data.integer / d2->data.integer;
266                                         break;
267                                 case SDB_TYPE_DECIMAL:
268                                         res->data.decimal = d1->data.decimal / d2->data.decimal;
269                                         break;
270                                 case SDB_TYPE_DATETIME:
271                                         res->data.datetime = d1->data.datetime / d2->data.datetime;
272                                         break;
273                                 default:
274                                         return -1;
275                         }
276                         break;
277                 case SDB_DATA_MOD:
278                         switch (d1->type) {
279                                 case SDB_TYPE_INTEGER:
280                                         res->data.integer = d1->data.integer % d2->data.integer;
281                                         break;
282                                 case SDB_TYPE_DECIMAL:
283                                         res->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
284                                         break;
285                                 case SDB_TYPE_DATETIME:
286                                         res->data.datetime = d1->data.datetime % d2->data.datetime;
287                                         break;
288                                 default:
289                                         return -1;
290                         }
291                         break;
292                 default:
293                         return -1;
294         }
296         res->type = d1->type;
297         return 0;
298 } /* sdb_data_expr_eval */
300 size_t
301 sdb_data_strlen(const sdb_data_t *datum)
303         if (! datum)
304                 return 0;
306         switch (datum->type) {
307                 case SDB_TYPE_INTEGER:
308                         /* log(64) */
309                         return 20;
310                 case SDB_TYPE_DECIMAL:
311                         /* XXX: -0xN.NNNNNNp+NNN */
312                         return 42;
313                 case SDB_TYPE_STRING:
314                         if (! datum->data.string)
315                                 return 6; /* "NULL" */
316                         /* in the worst case, each character needs to be escaped */
317                         return 2 * strlen(datum->data.string) + 2;
318                 case SDB_TYPE_DATETIME:
319                         /* "YYYY-MM-DD HH:MM:SS +zzzz" */
320                         return 27;
321                 case SDB_TYPE_BINARY:
322                         /* "\xNN" */
323                         return 4 * datum->data.binary.length + 2;
324         }
325         return 0;
326 } /* sdb_data_strlen */
328 int
329 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
331         char tmp[sdb_data_strlen(datum) + 1];
332         char *data = NULL;
333         int ret = -1;
335         size_t i, pos;
337         if ((! datum) || (! buf))
338                 return -1;
340         switch (datum->type) {
341                 case SDB_TYPE_INTEGER:
342                         ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
343                         break;
344                 case SDB_TYPE_DECIMAL:
345                         ret = snprintf(buf, buflen, "%a", datum->data.decimal);
346                         break;
347                 case SDB_TYPE_STRING:
348                         if (! datum->data.string)
349                                 data = "NULL";
350                         else {
351                                 pos = 0;
352                                 for (i = 0; i < strlen(datum->data.string); ++i) {
353                                         char byte = datum->data.string[i];
355                                         if ((byte == '\\') || (byte == '"')) {
356                                                 tmp[pos] = '\\';
357                                                 ++pos;
358                                         }
359                                         tmp[pos] = byte;
360                                         ++pos;
361                                 }
362                                 tmp[pos] = '\0';
363                                 data = tmp;
364                         }
365                         break;
366                 case SDB_TYPE_DATETIME:
367                         if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
368                                                 datum->data.datetime))
369                                 return -1;
370                         tmp[sizeof(tmp) - 1] = '\0';
371                         data = tmp;
372                         break;
373                 case SDB_TYPE_BINARY:
374                         pos = 0;
375                         for (i = 0; i < datum->data.binary.length; ++i) {
376                                 int byte = (int)datum->data.binary.datum[i];
377                                 char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
378                                         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
380                                 tmp[pos] = '\\';
381                                 tmp[pos + 1] = 'x';
382                                 pos += 2;
384                                 if (byte > 0xf) {
385                                         tmp[pos] = hex[byte >> 4];
386                                         ++pos;
387                                 }
388                                 tmp[pos] = hex[byte & 0xf];
389                                 ++pos;
390                         }
391                         tmp[pos] = '\0';
392                         data = tmp;
393                         break;
394         }
396         if (data) {
397                 if (quoted == SDB_UNQUOTED)
398                         ret = snprintf(buf, buflen, "%s", data);
399                 else if (quoted == SDB_SINGLE_QUOTED)
400                         ret = snprintf(buf, buflen, "'%s'", data);
401                 else
402                         ret = snprintf(buf, buflen, "\"%s\"", data);
403         }
404         buf[buflen - 1] = '\0';
405         return ret;
406 } /* sdb_data_format */
408 int
409 sdb_data_parse(char *str, int type, sdb_data_t *data)
411         sdb_data_t tmp;
413         char *endptr = NULL;
415         errno = 0;
416         switch (type) {
417                 case SDB_TYPE_INTEGER:
418                         tmp.data.integer = strtoll(str, &endptr, 0);
419                         break;
420                 case SDB_TYPE_DECIMAL:
421                         tmp.data.decimal = strtod(str, &endptr);
422                         break;
423                 case SDB_TYPE_STRING:
424                         tmp.data.string = str;
425                         break;
426                 case SDB_TYPE_DATETIME:
427                         {
428                                 double datetime = strtod(str, &endptr);
429                                 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
430                         }
431                         break;
432                 case SDB_TYPE_BINARY:
433                         /* we don't support any binary information containing 0-bytes */
434                         tmp.data.binary.length = strlen(str);
435                         tmp.data.binary.datum = (unsigned char *)str;
436                         break;
437                 default:
438                         errno = EINVAL;
439                         return -1;
440         }
442         if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
443                         || (type == SDB_TYPE_DATETIME)) {
444                 if (errno || (str == endptr)) {
445                         char errbuf[1024];
446                         sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
447                                         "'%s' as numeric value (type %i): %s", str, type,
448                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
449                         return -1;
450                 }
451                 else if (endptr && (*endptr != '\0'))
452                         sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
453                                         "number while parsing numeric value (type %i): %s.",
454                                         type, endptr);
455         }
457         if (data) {
458                 *data = tmp;
459                 data->type = type;
460         }
461         return 0;
462 } /* sdb_data_parse */
464 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */