Code

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