Code

data: Support some arithmetic expressions on mismatching types.
[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         switch (op) {
212                 case SDB_DATA_CONCAT:
213                         return data_concat(d1, d2, res);
214                 case SDB_DATA_ADD:
215                         if (d1->type != d2->type)
216                                 return -1;
217                         switch (d1->type) {
218                                 case SDB_TYPE_INTEGER:
219                                         res->data.integer = d1->data.integer + d2->data.integer;
220                                         break;
221                                 case SDB_TYPE_DECIMAL:
222                                         res->data.decimal = d1->data.decimal + d2->data.decimal;
223                                         break;
224                                 case SDB_TYPE_DATETIME:
225                                         res->data.datetime = d1->data.datetime + d2->data.datetime;
226                                         break;
227                                 default:
228                                         return -1;
229                         }
230                         break;
231                 case SDB_DATA_SUB:
232                         if (d1->type != d2->type)
233                                 return -1;
234                         switch (d1->type) {
235                                 case SDB_TYPE_INTEGER:
236                                         res->data.integer = d1->data.integer - d2->data.integer;
237                                         break;
238                                 case SDB_TYPE_DECIMAL:
239                                         res->data.decimal = d1->data.decimal - d2->data.decimal;
240                                         break;
241                                 case SDB_TYPE_DATETIME:
242                                         res->data.datetime = d1->data.datetime - d2->data.datetime;
243                                         break;
244                                 default:
245                                         return -1;
246                         }
247                         break;
248                 case SDB_DATA_MUL:
249                         switch (d1->type) {
250                                 case SDB_TYPE_INTEGER:
251                                         if (d2->type == SDB_TYPE_INTEGER)
252                                                 res->data.integer = d1->data.integer
253                                                         * d2->data.integer;
254                                         else if (d2->type == SDB_TYPE_DATETIME) {
255                                                 res->data.datetime = (sdb_time_t)d1->data.integer
256                                                         * d2->data.datetime;
257                                                 res->type = SDB_TYPE_DATETIME;
258                                                 return 0;
259                                         }
260                                         else
261                                                 return -1;
262                                         break;
263                                 case SDB_TYPE_DECIMAL:
264                                         if (d2->type == SDB_TYPE_DECIMAL)
265                                                 res->data.decimal = d1->data.decimal
266                                                         * d2->data.decimal;
267                                         else if (d2->type == SDB_TYPE_DATETIME) {
268                                                 double tmp = d1->data.decimal
269                                                         * (double)d2->data.datetime;
270                                                 res->data.datetime = (sdb_time_t)tmp;
271                                                 res->type = SDB_TYPE_DATETIME;
272                                                 return 0;
273                                         }
274                                         else
275                                                 return -1;
276                                         break;
277                                 case SDB_TYPE_DATETIME:
278                                         if (d2->type == SDB_TYPE_DATETIME)
279                                                 res->data.datetime = d1->data.datetime
280                                                         * d2->data.datetime;
281                                         else if (d2->type == SDB_TYPE_INTEGER) {
282                                                 res->data.datetime = d1->data.datetime
283                                                         * (sdb_time_t)d2->data.integer;
284                                                 res->type = SDB_TYPE_DATETIME;
285                                                 return 0;
286                                         }
287                                         else if (d2->type == SDB_TYPE_DECIMAL) {
288                                                 double tmp = (double)d1->data.datetime
289                                                         * d2->data.decimal;
290                                                 res->data.datetime = (sdb_time_t)tmp;
291                                                 res->type = SDB_TYPE_DATETIME;
292                                                 return 0;
293                                         }
294                                         else
295                                                 return -1;
296                                         break;
297                                 default:
298                                         return -1;
299                         }
300                         break;
301                 case SDB_DATA_DIV:
302                         switch (d1->type) {
303                                 case SDB_TYPE_INTEGER:
304                                         if (d2->type != SDB_TYPE_INTEGER)
305                                                 return -1;
306                                         res->data.integer = d1->data.integer / d2->data.integer;
307                                         break;
308                                 case SDB_TYPE_DECIMAL:
309                                         if (d2->type != SDB_TYPE_DECIMAL)
310                                                 return -1;
311                                         res->data.decimal = d1->data.decimal / d2->data.decimal;
312                                         break;
313                                 case SDB_TYPE_DATETIME:
314                                         if (d2->type == SDB_TYPE_DATETIME)
315                                                 res->data.datetime = d1->data.datetime
316                                                         / d2->data.datetime;
317                                         else if (d2->type == SDB_TYPE_INTEGER) {
318                                                 res->data.datetime = d1->data.datetime
319                                                         / (sdb_time_t)d2->data.integer;
320                                                 res->type = SDB_TYPE_DATETIME;
321                                                 return 0;
322                                         }
323                                         else if (d2->type == SDB_TYPE_DECIMAL) {
324                                                 double tmp = (double)d1->data.datetime
325                                                         / d2->data.decimal;
326                                                 res->data.datetime = (sdb_time_t)tmp;
327                                                 res->type = SDB_TYPE_DATETIME;
328                                                 return 0;
329                                         }
330                                         else
331                                                 return -1;
332                                         break;
333                                 default:
334                                         return -1;
335                         }
336                         break;
337                 case SDB_DATA_MOD:
338                         switch (d1->type) {
339                                 case SDB_TYPE_INTEGER:
340                                         if (d2->type != SDB_TYPE_INTEGER)
341                                                 return -1;
342                                         res->data.integer = d1->data.integer % d2->data.integer;
343                                         break;
344                                 case SDB_TYPE_DECIMAL:
345                                         if (d2->type != SDB_TYPE_DECIMAL)
346                                                 return -1;
347                                         res->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
348                                         break;
349                                 case SDB_TYPE_DATETIME:
350                                         if (d2->type == SDB_TYPE_DATETIME)
351                                                 res->data.datetime = d1->data.datetime
352                                                         % d2->data.datetime;
353                                         else if (d2->type == SDB_TYPE_INTEGER) {
354                                                 res->data.datetime = d1->data.datetime
355                                                         % (sdb_time_t)d2->data.integer;
356                                                 res->type = SDB_TYPE_DATETIME;
357                                                 return 0;
358                                         }
359                                         else if (d2->type == SDB_TYPE_DECIMAL) {
360                                                 double tmp = fmod((double)d1->data.datetime,
361                                                         d2->data.decimal);
362                                                 res->data.datetime = (sdb_time_t)tmp;
363                                                 res->type = SDB_TYPE_DATETIME;
364                                                 return 0;
365                                         }
366                                         else
367                                                 return -1;
368                                         break;
369                                 default:
370                                         return -1;
371                         }
372                         break;
373                 default:
374                         return -1;
375         }
377         res->type = d1->type;
378         return 0;
379 } /* sdb_data_expr_eval */
381 size_t
382 sdb_data_strlen(const sdb_data_t *datum)
384         if (! datum)
385                 return 0;
387         switch (datum->type) {
388                 case SDB_TYPE_INTEGER:
389                         /* log(64) */
390                         return 20;
391                 case SDB_TYPE_DECIMAL:
392                         /* XXX: -0xN.NNNNNNp+NNN */
393                         return 42;
394                 case SDB_TYPE_STRING:
395                         if (! datum->data.string)
396                                 return 6; /* "NULL" */
397                         /* in the worst case, each character needs to be escaped */
398                         return 2 * strlen(datum->data.string) + 2;
399                 case SDB_TYPE_DATETIME:
400                         /* "YYYY-MM-DD HH:MM:SS +zzzz" */
401                         return 27;
402                 case SDB_TYPE_BINARY:
403                         /* "\xNN" */
404                         return 4 * datum->data.binary.length + 2;
405         }
406         return 0;
407 } /* sdb_data_strlen */
409 int
410 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
412         char tmp[sdb_data_strlen(datum) + 1];
413         char *data = NULL;
414         int ret = -1;
416         size_t i, pos;
418         if ((! datum) || (! buf))
419                 return -1;
421         switch (datum->type) {
422                 case SDB_TYPE_INTEGER:
423                         ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
424                         break;
425                 case SDB_TYPE_DECIMAL:
426                         ret = snprintf(buf, buflen, "%a", datum->data.decimal);
427                         break;
428                 case SDB_TYPE_STRING:
429                         if (! datum->data.string)
430                                 data = "NULL";
431                         else {
432                                 pos = 0;
433                                 for (i = 0; i < strlen(datum->data.string); ++i) {
434                                         char byte = datum->data.string[i];
436                                         if ((byte == '\\') || (byte == '"')) {
437                                                 tmp[pos] = '\\';
438                                                 ++pos;
439                                         }
440                                         tmp[pos] = byte;
441                                         ++pos;
442                                 }
443                                 tmp[pos] = '\0';
444                                 data = tmp;
445                         }
446                         break;
447                 case SDB_TYPE_DATETIME:
448                         if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
449                                                 datum->data.datetime))
450                                 return -1;
451                         tmp[sizeof(tmp) - 1] = '\0';
452                         data = tmp;
453                         break;
454                 case SDB_TYPE_BINARY:
455                         pos = 0;
456                         for (i = 0; i < datum->data.binary.length; ++i) {
457                                 int byte = (int)datum->data.binary.datum[i];
458                                 char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
459                                         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
461                                 tmp[pos] = '\\';
462                                 tmp[pos + 1] = 'x';
463                                 pos += 2;
465                                 if (byte > 0xf) {
466                                         tmp[pos] = hex[byte >> 4];
467                                         ++pos;
468                                 }
469                                 tmp[pos] = hex[byte & 0xf];
470                                 ++pos;
471                         }
472                         tmp[pos] = '\0';
473                         data = tmp;
474                         break;
475         }
477         if (data) {
478                 if (quoted == SDB_UNQUOTED)
479                         ret = snprintf(buf, buflen, "%s", data);
480                 else if (quoted == SDB_SINGLE_QUOTED)
481                         ret = snprintf(buf, buflen, "'%s'", data);
482                 else
483                         ret = snprintf(buf, buflen, "\"%s\"", data);
484         }
485         buf[buflen - 1] = '\0';
486         return ret;
487 } /* sdb_data_format */
489 int
490 sdb_data_parse(char *str, int type, sdb_data_t *data)
492         sdb_data_t tmp;
494         char *endptr = NULL;
496         errno = 0;
497         switch (type) {
498                 case SDB_TYPE_INTEGER:
499                         tmp.data.integer = strtoll(str, &endptr, 0);
500                         break;
501                 case SDB_TYPE_DECIMAL:
502                         tmp.data.decimal = strtod(str, &endptr);
503                         break;
504                 case SDB_TYPE_STRING:
505                         tmp.data.string = str;
506                         break;
507                 case SDB_TYPE_DATETIME:
508                         {
509                                 double datetime = strtod(str, &endptr);
510                                 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
511                         }
512                         break;
513                 case SDB_TYPE_BINARY:
514                         /* we don't support any binary information containing 0-bytes */
515                         tmp.data.binary.length = strlen(str);
516                         tmp.data.binary.datum = (unsigned char *)str;
517                         break;
518                 default:
519                         errno = EINVAL;
520                         return -1;
521         }
523         if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
524                         || (type == SDB_TYPE_DATETIME)) {
525                 if (errno || (str == endptr)) {
526                         char errbuf[1024];
527                         sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
528                                         "'%s' as numeric value (type %i): %s", str, type,
529                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
530                         return -1;
531                 }
532                 else if (endptr && (*endptr != '\0'))
533                         sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
534                                         "number while parsing numeric value (type %i): %s.",
535                                         type, endptr);
536         }
538         if (data) {
539                 *data = tmp;
540                 data->type = type;
541         }
542         return 0;
543 } /* sdb_data_parse */
545 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */