Code

data: Let sdb_data_cmp() support all array 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 <assert.h>
39 #include <errno.h>
41 #include <inttypes.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
47 #include <math.h>
49 /*
50  * Operator support maxtrix.
51  * <type1> <op> <type2> -> op_matrix[<op>][<type1>][<type2>]
52  */
54 /* add, sub, mul, div, mod, concat */
56 /* integer, decimal, string, datetime, binary, regex */
58 static int op_matrix[6][6][6] = {
59         /* SDB_DATA_ADD */
60         {
61                 { SDB_TYPE_INTEGER, -1, -1, -1, -1, -1 },
62                 { -1, SDB_TYPE_DECIMAL, -1, -1, -1, -1 },
63                 { -1, -1, -1, -1, -1, -1 },
64                 { -1, -1, -1, SDB_TYPE_DATETIME, -1, -1 },
65                 { -1, -1, -1, -1, -1, -1 },
66                 { -1, -1, -1, -1, -1, -1 },
67         },
69         /* SDB_DATA_SUB */
70         {
71                 { SDB_TYPE_INTEGER, -1, -1, -1, -1, -1 },
72                 { -1, SDB_TYPE_DECIMAL, -1, -1, -1, -1 },
73                 { -1, -1, -1, -1, -1, -1 },
74                 { -1, -1, -1, SDB_TYPE_DATETIME, -1, -1 },
75                 { -1, -1, -1, -1, -1, -1 },
76                 { -1, -1, -1, -1, -1, -1 },
77         },
79         /* SDB_DATA_MUL */
80         {
81                 { SDB_TYPE_INTEGER, -1, -1, SDB_TYPE_DATETIME, -1, -1 },
82                 { -1, SDB_TYPE_DECIMAL, -1, SDB_TYPE_DATETIME, -1, -1 },
83                 { -1, -1, -1, -1, -1, -1 },
84                 { SDB_TYPE_DATETIME, SDB_TYPE_DATETIME, -1, SDB_TYPE_DATETIME, -1, -1 },
85                 { -1, -1, -1, -1, -1, -1 },
86                 { -1, -1, -1, -1, -1, -1 },
87         },
89         /* SDB_DATA_DIV */
90         {
91                 { SDB_TYPE_INTEGER, -1, -1, -1, -1, -1 },
92                 { -1, SDB_TYPE_DECIMAL, -1, -1, -1, -1 },
93                 { -1, -1, -1, -1, -1, -1 },
94                 { SDB_TYPE_DATETIME, SDB_TYPE_DATETIME, -1, SDB_TYPE_DATETIME, -1, -1 },
95                 { -1, -1, -1, -1, -1, -1 },
96                 { -1, -1, -1, -1, -1, -1 },
97         },
99         /* SDB_DATA_MOD */
100         {
101                 { SDB_TYPE_INTEGER, -1, -1, -1, -1, -1 },
102                 { -1, SDB_TYPE_DECIMAL, -1, -1, -1, -1 },
103                 { -1, -1, -1, -1, -1, -1 },
104                 { SDB_TYPE_DATETIME, SDB_TYPE_DATETIME, -1, SDB_TYPE_DATETIME, -1, -1 },
105                 { -1, -1, -1, -1, -1, -1 },
106                 { -1, -1, -1, -1, -1, -1 },
107         },
109         /* SDB_DATA_CONCAT */
110         {
111                 { -1, -1, -1, -1, -1, -1 },
112                 { -1, -1, -1, -1, -1, -1 },
113                 { -1, -1, SDB_TYPE_STRING, -1, -1, -1 },
114                 { -1, -1, -1, -1, -1, -1 },
115                 { -1, -1, -1, -1, SDB_TYPE_BINARY, -1 },
116                 { -1, -1, -1, -1, -1, -1 },
117         },
118 };
120 /*
121  * private helper functions
122  */
124 /* this function supports in-place copies */
125 static int
126 copy_array_values(sdb_data_t *dst, const sdb_data_t *src, size_t elem_size)
128         int type = src->type & 0xff;
130         if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)) {
131                 if (dst != src)
132                         memcpy(dst->data.array.values, src->data.array.values,
133                                         src->data.array.length * elem_size);
134         }
135         else if (type == SDB_TYPE_STRING) {
136                 char **s = src->data.array.values;
137                 char **d = dst->data.array.values;
138                 size_t i;
140                 for (i = 0; i < src->data.array.length; ++i) {
141                         d[i] = strdup(s[i]);
142                         if (! d[i])
143                                 return -1;
144                 }
145         }
146         else {
147                 /* TODO */
148                 errno = ENOTSUP;
149                 return -1;
150         }
151         return 0;
152 } /* copy_array_values */
154 static void
155 free_array_values(sdb_data_t *datum)
157         int type = datum->type & 0xff;
159         if (type == SDB_TYPE_STRING) {
160                 char **v = datum->data.array.values;
161                 size_t i;
163                 for (i = 0; i < datum->data.array.length; ++i) {
164                         if (v[i])
165                                 free(v[i]);
166                         v[i] = NULL;
167                 }
168         }
169 } /* free_array_values */
171 /* compare two arrays element-by-element returning how the first non-equal
172  * elements compare to each other */
173 static int
174 array_cmp(const sdb_data_t *a1, const sdb_data_t *a2)
176         int type = a1->type & 0xff;
177         size_t len, i;
179         assert((a1->type == a2->type) && (a1->type & SDB_TYPE_ARRAY));
181         len = SDB_MIN(a1->data.array.length, a2->data.array.length);
183         if (type == SDB_TYPE_INTEGER) {
184                 int64_t *v1 = a1->data.array.values;
185                 int64_t *v2 = a2->data.array.values;
187                 for (i = 0; i < len; ++i)
188                         if (v1[i] != v2[i])
189                                 return SDB_CMP(v1[i], v2[i]);
190         }
191         else if (type == SDB_TYPE_DECIMAL) {
192                 double *v1 = a1->data.array.values;
193                 double *v2 = a2->data.array.values;
195                 for (i = 0; i < len; ++i)
196                         if (v1[i] != v2[i])
197                                 return SDB_CMP(v1[i], v2[i]);
198         }
199         else if (type == SDB_TYPE_STRING) {
200                 char **v1 = a1->data.array.values;
201                 char **v2 = a2->data.array.values;
203                 for (i = 0; i < len; ++i) {
204                         int diff = strcasecmp(v1[i], v2[i]);
205                         if (diff)
206                                 return diff;
207                 }
208         }
209         else if (type == SDB_TYPE_DATETIME) {
210                 sdb_time_t *v1 = a1->data.array.values;
211                 sdb_time_t *v2 = a2->data.array.values;
213                 for (i = 0; i < len; ++i)
214                         if (v1[i] != v2[i])
215                                 return SDB_CMP(v1[i], v2[i]);
216         }
217         else if (type == SDB_TYPE_BINARY) {
218                 struct {
219                         size_t length;
220                         unsigned char *datum;
221                 } *v1 = a1->data.array.values;
222                 struct {
223                         size_t length;
224                         unsigned char *datum;
225                 } *v2 = a2->data.array.values;
227                 for (i = 0; i < len; ++i) {
228                         int diff;
230                         /* on a common prefix, the shorter datum sorts less */
231                         if (v1[i].length < v2[i].length) {
232                                 diff = memcmp(v1[i].datum, v2[i].datum, v1[i].length);
233                                 diff = diff ? diff : -1;
234                         }
235                         else if (v1[i].length > v2[i].length) {
236                                 diff = memcmp(v1[i].datum, v2[i].datum, v2[i].length);
237                                 diff = diff ? diff : 1;
238                         }
239                         else
240                                 diff = memcmp(v1[i].datum, v2[i].datum, v1[i].length);
242                         if (diff)
243                                 return diff;
244                 }
245         }
246         else if (type == SDB_TYPE_REGEX) {
247                 struct {
248                         char *raw;
249                         regex_t regex;
250                 } *v1 = a1->data.array.values;
251                 struct {
252                         char *raw;
253                         regex_t regex;
254                 } *v2 = a2->data.array.values;
256                 for (i = 0; i < len; ++i) {
257                         int diff = strcasecmp(v1[i].raw, v2[i].raw);
258                         if (diff)
259                                 return diff;
260                 }
261         }
262         else {
263                 errno = EINVAL;
264                 /* but fall through to ensure stable sorting: */
265         }
266         return SDB_CMP(a1->data.array.length, a2->data.array.length);
267 } /* array_cmp */
269 /* Calculate the linear function 'd1 + n * d2'. */
270 static int
271 data_lin(const sdb_data_t *d1, int n, const sdb_data_t *d2, sdb_data_t *res)
273         if (d1->type != d2->type)
274                 return -1;
276         if (d1->type == SDB_TYPE_INTEGER)
277                 res->data.integer = d1->data.integer + (int64_t)n * d2->data.integer;
278         else if (d1->type == SDB_TYPE_DECIMAL)
279                 res->data.decimal = d1->data.decimal + (double)n * d2->data.decimal;
280         else if (d1->type ==  SDB_TYPE_DATETIME)
281                 res->data.datetime = d1->data.datetime + (sdb_time_t)n * d2->data.datetime;
282         else
283                 return -1;
284         res->type = d1->type;
285         return 0;
286 } /* data_lin */
288 /* Multiply d1 with d2. */
289 static int
290 data_mul(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res)
292         if (d1->type == SDB_TYPE_INTEGER) {
293                 if (d2->type == SDB_TYPE_INTEGER)
294                         res->data.integer = d1->data.integer * d2->data.integer;
295                 else if (d2->type == SDB_TYPE_DATETIME) {
296                         res->data.datetime = (sdb_time_t)d1->data.integer
297                                 * d2->data.datetime;
298                         res->type = SDB_TYPE_DATETIME;
299                         return 0;
300                 }
301                 else
302                         return -1;
303         }
304         else if (d1->type == SDB_TYPE_DECIMAL) {
305                 if (d2->type == SDB_TYPE_DECIMAL)
306                         res->data.decimal = d1->data.decimal * d2->data.decimal;
307                 else if (d2->type == SDB_TYPE_DATETIME) {
308                         res->data.datetime = (sdb_time_t)(d1->data.decimal
309                                         * (double)d2->data.datetime);
310                         res->type = SDB_TYPE_DATETIME;
311                         return 0;
312                 }
313                 else
314                         return -1;
315         }
316         else if (d1->type == SDB_TYPE_DATETIME) {
317                 if (d2->type == SDB_TYPE_DATETIME)
318                         res->data.datetime = d1->data.datetime
319                                 * d2->data.datetime;
320                 else if (d2->type == SDB_TYPE_INTEGER)
321                         res->data.datetime = d1->data.datetime
322                                 * (sdb_time_t)d2->data.integer;
323                 else if (d2->type == SDB_TYPE_DECIMAL)
324                         res->data.datetime = (sdb_time_t)((double)d1->data.datetime
325                                         * d2->data.decimal);
326                 else
327                         return -1;
328         }
329         else
330                 return -1;
332         res->type = d1->type;
333         return 0;
334 } /* data_mul */
336 /* Device d1 by d2 and return the result and the remainder. */
337 static int
338 data_div(const sdb_data_t *d1, const sdb_data_t *d2,
339                 sdb_data_t *res, sdb_data_t *rem)
341         if (d1->type == SDB_TYPE_INTEGER) {
342                 if (d2->type != SDB_TYPE_INTEGER)
343                         return -1;
344                 if (res)
345                         res->data.integer = d1->data.integer / d2->data.integer;
346                 if (rem)
347                         rem->data.integer = d1->data.integer % d2->data.integer;
348         }
349         else if (d1->type == SDB_TYPE_DECIMAL) {
350                 if (d2->type != SDB_TYPE_DECIMAL)
351                         return -1;
352                 if (res)
353                         res->data.decimal = d1->data.decimal / d2->data.decimal;
354                 if (rem)
355                         rem->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
356         }
357         else if (d1->type == SDB_TYPE_DATETIME) {
358                 if (d2->type == SDB_TYPE_DECIMAL) {
359                         if (res)
360                                 res->data.datetime = (sdb_time_t)((double)d1->data.datetime
361                                                 / d2->data.decimal);
362                         if (rem) {
363                                 double tmp = fmod((double)d1->data.datetime, d2->data.decimal);
364                                 rem->data.datetime = (sdb_time_t)tmp;
365                         }
366                 }
367                 else {
368                         sdb_time_t a, b;
369                         if (d2->type == SDB_TYPE_DATETIME) {
370                                 a = d1->data.datetime;
371                                 b = d2->data.datetime;
372                         }
373                         else if (d2->type == SDB_TYPE_INTEGER) {
374                                 a = d1->data.datetime;
375                                 b = (sdb_time_t)d2->data.integer;
376                         }
377                         else
378                                 return -1;
379                         if (res)
380                                 res->data.datetime = a / b;
381                         if (rem)
382                                 rem->data.datetime = a % b;
383                 }
384         }
385         else
386                 return -1;
388         if (res)
389                 res->type = d1->type;
390         if (rem)
391                 rem->type = d1->type;
392         return 0;
393 } /* data_div */
395 /* Concatenate d1 and d2. */
396 static int
397 data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res)
399         unsigned char *new;
400         const unsigned char *s1, *s2;
401         size_t len1, len2, array1_len = 0, array2_len = 0;
403         if ((d1->type & 0xff) != (d2->type & 0xff))
404                 return -1;
406         if ((d1->type & SDB_TYPE_ARRAY) || (d2->type & SDB_TYPE_ARRAY)) {
407                 size_t elem_size = sdb_data_sizeof(d1->type & 0xff);
408                 if (d1->type & SDB_TYPE_ARRAY) {
409                         s1 = (const unsigned char *)d1->data.array.values;
410                         array1_len = d1->data.array.length;
411                 }
412                 else {
413                         /* As per C99, section 6.7.2.1, paragraph 14:
414                          * "A pointer to a union object, suitably converted, points to
415                          * each of its members" */
416                         s1 = (const unsigned char *)&d1->data;
417                         array1_len = 1;
418                 }
419                 if (d2->type & SDB_TYPE_ARRAY) {
420                         s2 = (const unsigned char *)d2->data.array.values;
421                         array2_len = d2->data.array.length;
422                 }
423                 else {
424                         s2 = (const unsigned char *)&d2->data;
425                         array2_len = 1;
426                 }
427                 len1 = array1_len * elem_size;
428                 len2 = array2_len * elem_size;
429         }
430         else if (d1->type == SDB_TYPE_STRING) {
431                 s1 = (unsigned char *)d1->data.string;
432                 s2 = (unsigned char *)d2->data.string;
433                 len1 = s1 ? strlen((const char *)s1) : 0;
434                 len2 = s2 ? strlen((const char *)s2) : 0;
435         }
436         else if (d1->type == SDB_TYPE_BINARY) {
437                 s1 = d1->data.binary.datum;
438                 s2 = d2->data.binary.datum;
439                 len1 = d1->data.binary.length;
440                 len2 = d2->data.binary.length;
441         }
442         else
443                 return -1;
445         new = malloc(len1 + len2 + 1);
446         if (! new)
447                 return -1;
449         if (len1)
450                 memcpy(new, s1, len1);
451         if (len2)
452                 memcpy(new + len1, s2, len2);
453         new[len1 + len2] = '\0';
455         /* element types match and if either datum is an array,
456          * the result is an array as well */
457         res->type = d1->type | d2->type;
458         if (res->type == SDB_TYPE_STRING) {
459                 res->data.string = (char *)new;
460         }
461         else if (res->type == SDB_TYPE_BINARY) {
462                 res->data.binary.datum = new;
463                 res->data.binary.length = len1 + len2;
464         }
465         else if (res->type & SDB_TYPE_ARRAY) {
466                 res->data.array.values = new;
467                 res->data.array.length = array1_len + array2_len;
468                 if (copy_array_values(res, res, sdb_data_sizeof(res->type & 0xff))) {
469                         /* this leaks already copied values but there's not much we can
470                          * do and this should only happen if we're in trouble anyway */
471                         free(new);
472                         res->data.array.values = NULL;
473                         res->data.array.length = 0;
474                         return -1;
475                 }
476         }
477         return 0;
478 } /* data_concat */
480 /*
481  * public API
482  */
484 const sdb_data_t SDB_DATA_NULL = SDB_DATA_INIT;
486 int
487 sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src)
489         sdb_data_t tmp;
491         if ((! dst) || (! src))
492                 return -1;
494         tmp = *src;
495         if (src->type == SDB_TYPE_STRING) {
496                 if (src->data.string) {
497                         tmp.data.string = strdup(src->data.string);
498                         if (! tmp.data.string)
499                                 return -1;
500                 }
501         }
502         else if (src->type == SDB_TYPE_BINARY) {
503                 if (src->data.binary.datum) {
504                         tmp.data.binary.datum = malloc(src->data.binary.length);
505                         if (! tmp.data.binary.datum)
506                                 return -1;
507                         memcpy(tmp.data.binary.datum, src->data.binary.datum,
508                                         src->data.binary.length);
509                 }
510         }
511         else if (src->type == SDB_TYPE_REGEX) {
512                 if (src->data.re.raw) {
513                         tmp.data.re.raw = strdup(src->data.re.raw);
514                         if (! tmp.data.re.raw)
515                                 return -1;
516                         /* we need to recompile because the regex might point to
517                          * dynamically allocated memory */
518                         if (regcomp(&tmp.data.re.regex, tmp.data.re.raw,
519                                                 REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
520                                 free(tmp.data.re.raw);
521                                 return -1;
522                         }
523                 }
524                 else
525                         memset(&tmp.data.re.regex, 0, sizeof(tmp.data.re.regex));
526         }
527         else if (src->type & SDB_TYPE_ARRAY) {
528                 if (src->data.array.values) {
529                         size_t elem_size = sdb_data_sizeof(src->type & 0xff);
530                         tmp.data.array.values = calloc(src->data.array.length, elem_size);
531                         if (! tmp.data.array.values)
532                                 return -1;
533                         if (copy_array_values(&tmp, src, elem_size)) {
534                                 sdb_data_free_datum(&tmp);
535                                 return -1;
536                         }
537                 }
538         }
540         sdb_data_free_datum(dst);
541         *dst = tmp;
542         return 0;
543 } /* sdb_data_copy */
545 void
546 sdb_data_free_datum(sdb_data_t *datum)
548         if (! datum)
549                 return;
551         if (datum->type == SDB_TYPE_STRING) {
552                 if (datum->data.string)
553                         free(datum->data.string);
554                 datum->data.string = NULL;
555         }
556         else if (datum->type == SDB_TYPE_BINARY) {
557                 if (datum->data.binary.datum)
558                         free(datum->data.binary.datum);
559                 datum->data.binary.datum = NULL;
560                 datum->data.binary.length = 0;
561         }
562         else if (datum->type == SDB_TYPE_REGEX) {
563                 if (datum->data.re.raw) {
564                         free(datum->data.re.raw);
565                         regfree(&datum->data.re.regex);
566                 }
567                 datum->data.re.raw = NULL;
568                 memset(&datum->data.re.regex, 0, sizeof(datum->data.re.regex));
569         }
570         else if (datum->type & SDB_TYPE_ARRAY) {
571                 free_array_values(datum);
572                 if (datum->data.array.values)
573                         free(datum->data.array.values);
574                 datum->data.array.values = NULL;
575                 datum->data.array.length = 0;
576         }
577 } /* sdb_data_free_datum */
579 int
580 sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2)
582 #define CMP_NULL(a, b) \
583         do { \
584                 if (!(a) && !(b)) return 0; \
585                 if (!(a)) return -1; \
586                 if (!(b)) return 1; \
587         } while (0)
589         CMP_NULL(d1, d2);
591         if (d1->type != d2->type)
592                 return SDB_CMP(d1->type, d2->type);
594         if (d1->type == SDB_TYPE_INTEGER)
595                 return SDB_CMP(d1->data.integer, d2->data.integer);
596         else if (d1->type == SDB_TYPE_DECIMAL)
597                 return SDB_CMP(d1->data.decimal, d2->data.decimal);
598         else if (d1->type == SDB_TYPE_STRING) {
599                 CMP_NULL(d1->data.string, d2->data.string);
600                 return strcasecmp(d1->data.string, d2->data.string);
601         }
602         else if (d1->type == SDB_TYPE_DATETIME)
603                 return SDB_CMP(d1->data.datetime, d2->data.datetime);
604         else if (d1->type == SDB_TYPE_BINARY) {
605                 int diff;
607                 CMP_NULL(d1->data.binary.datum, d2->data.binary.datum);
609                 /* on a common prefix, the shorter datum sorts less */
610                 if (d1->data.binary.length < d2->data.binary.length) {
611                         diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
612                                         d1->data.binary.length);
613                         diff = diff ? diff : -1;
614                 }
615                 else if (d1->data.binary.length > d2->data.binary.length) {
616                         diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
617                                         d2->data.binary.length);
618                         diff = diff ? diff : 1;
619                 }
620                 else
621                         diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
622                                         d1->data.binary.length);
624                 return diff;
625         }
626         else if (d1->type == SDB_TYPE_REGEX) {
627                 CMP_NULL(d1->data.re.raw, d2->data.re.raw);
628                 return strcmp(d1->data.re.raw, d2->data.re.raw);
629         }
630         else if (d1->type & SDB_TYPE_ARRAY) {
631                 CMP_NULL(d1->data.array.values, d2->data.array.values);
632                 return array_cmp(d1, d2);
633         }
634         return -1;
635 } /* sdb_data_cmp */
637 int
638 sdb_data_strcmp(const sdb_data_t *d1, const sdb_data_t *d2)
640         char d1_str[sdb_data_strlen(d1) + 1];
641         char d2_str[sdb_data_strlen(d2) + 1];
643         if ((d1->type & SDB_TYPE_ARRAY) || (d2->type & SDB_TYPE_ARRAY)) {
644                 /* TODO */
645                 errno = ENOTSUP;
646                 return -1;
647         }
649         if (sdb_data_isnull(d1))
650                 d1 = NULL;
651         if (sdb_data_isnull(d2))
652                 d2 = NULL;
654         CMP_NULL(d1, d2);
656         if (sdb_data_format(d1, d1_str, sizeof(d1_str), SDB_UNQUOTED) < 0)
657                 return SDB_CMP(sizeof(d1_str), sizeof(d2_str));
658         if (sdb_data_format(d2, d2_str, sizeof(d2_str), SDB_UNQUOTED) < 0)
659                 return SDB_CMP(sizeof(d1_str), sizeof(d2_str));
661         return strcasecmp(d1_str, d2_str);
662 #undef CMP_NULL
663 } /* sdb_data_strcmp */
665 bool
666 sdb_data_isnull(const sdb_data_t *datum)
668         if (! datum)
669                 return 1;
670         if (datum->type == SDB_TYPE_NULL)
671                 return 1;
672         if ((datum->type == SDB_TYPE_STRING) && (! datum->data.string))
673                 return 1;
674         if ((datum->type == SDB_TYPE_BINARY) && (! datum->data.binary.datum))
675                 return 1;
676         if ((datum->type == SDB_TYPE_REGEX) && (! datum->data.re.raw))
677                 return 1;
678         return 0;
679 } /* sdb_data_isnull */
681 bool
682 sdb_data_inarray(const sdb_data_t *value, const sdb_data_t *array)
684         const void *values;
685         size_t length, i;
686         int type = value->type & 0xff;
688         if (sdb_data_isnull(value) || sdb_data_isnull(array))
689                 return 0;
690         if (! (array->type & SDB_TYPE_ARRAY))
691                 return 0;
692         if ((value->type & 0xff) != (array->type & 0xff))
693                 return 0;
695         if (value->type & SDB_TYPE_ARRAY) {
696                 values = value->data.array.values;
697                 length = value->data.array.length;
698         }
699         else {
700                 values = &value->data;
701                 length = 1;
702         }
704         for (i = 0; i < length; ++i) {
705                 size_t j;
707                 if (type == SDB_TYPE_INTEGER) {
708                         int64_t *v = array->data.array.values;
709                         for (j = 0; j < array->data.array.length; ++j)
710                                 if (((const int64_t *)values)[i] == v[j])
711                                         break;
712                 }
713                 else if (type == SDB_TYPE_DECIMAL) {
714                         double *v = array->data.array.values;
715                         for (j = 0; j < array->data.array.length; ++j)
716                                 if (((const double *)values)[i] == v[j])
717                                         break;
718                 }
719                 else if (type == SDB_TYPE_STRING) {
720                         char **v = array->data.array.values;
721                         for (j = 0; j < array->data.array.length; ++j)
722                                 if (!strcasecmp(((const char * const*)values)[i], v[j]))
723                                         break;
724                 }
725                 else {
726                         /* TODO */
727                         errno = ENOTSUP;
728                         return 0;
729                 }
731                 if (j >= array->data.array.length)
732                         /* value not found */
733                         return 0;
734         }
735         return 1;
736 } /* sdb_data_inarray */
738 int
739 sdb_data_array_get(const sdb_data_t *array, size_t i, sdb_data_t *value)
741         sdb_data_t tmp = SDB_DATA_INIT;
742         int type;
744         if ((! array) || (! (array->type & SDB_TYPE_ARRAY)))
745                 return -1;
746         if (i >= array->data.array.length)
747                 return -1;
749         type = array->type & 0xff;
750         if (type == SDB_TYPE_INTEGER) {
751                 int64_t *v = array->data.array.values;
752                 tmp.data.integer = v[i];
753         }
754         else if (type == SDB_TYPE_DECIMAL) {
755                 double *v = array->data.array.values;
756                 tmp.data.decimal = v[i];
757         }
758         else if (type == SDB_TYPE_STRING) {
759                 char **v = array->data.array.values;
760                 tmp.data.string = v[i];
761         }
762         else {
763                 /* TODO */
764                 errno = ENOTSUP;
765                 return -1;
766         }
768         if (value) {
769                 *value = tmp;
770                 value->type = type;
771         }
772         return 0;
773 } /* sdb_data_array_get */
775 int
776 sdb_data_parse_op(const char *op)
778         if (! strcmp(op, "+"))
779                 return SDB_DATA_ADD;
780         else if (! strcmp(op, "-"))
781                 return SDB_DATA_SUB;
782         else if (! strcmp(op, "*"))
783                 return SDB_DATA_MUL;
784         else if (! strcmp(op, "/"))
785                 return SDB_DATA_DIV;
786         else if (! strcmp(op, "%"))
787                 return SDB_DATA_MOD;
788         else if (! strcmp(op, "||"))
789                 return SDB_DATA_CONCAT;
790         return -1;
791 } /* sdb_data_parse_op */
793 int
794 sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
795                 sdb_data_t *res)
797         if ((! d1) || (! d2) || (! res))
798                 return -1;
799         if (sdb_data_isnull(d1) || sdb_data_isnull(d2)) {
800                 *res = SDB_DATA_NULL;
801                 return 0;
802         }
803         switch (op) {
804                 case SDB_DATA_CONCAT: return data_concat(d1, d2, res);
805                 case SDB_DATA_ADD: return data_lin(d1, 1, d2, res);
806                 case SDB_DATA_SUB: return data_lin(d1, -1, d2, res);
807                 case SDB_DATA_MUL: return data_mul(d1, d2, res);
808                 case SDB_DATA_DIV: return data_div(d1, d2, res, NULL);
809                 case SDB_DATA_MOD: return data_div(d1, d2, NULL, res);
810         }
811         return -1;
812 } /* sdb_data_expr_eval */
814 int
815 sdb_data_expr_type(int op, int type1, int type2)
817         int types_num = (int)SDB_STATIC_ARRAY_LEN(op_matrix[0]);
819         assert(SDB_STATIC_ARRAY_LEN(op_matrix[0])
820                         == SDB_STATIC_ARRAY_LEN(op_matrix[0][0]));
822         if ((op <= 0) || (SDB_STATIC_ARRAY_LEN(op_matrix) < (size_t)op))
823                 return -1;
825         /* arrays only support concat; element type has to match */
826         if ((type1 & SDB_TYPE_ARRAY) || (type2 & SDB_TYPE_ARRAY)) {
827                 if (((type1 & 0xff) != (type2 & 0xff)) || (op != SDB_DATA_CONCAT))
828                         return -1;
829                 return type1 | SDB_TYPE_ARRAY;
830         }
831         if ((type1 < 0) || (types_num < type1)
832                         || (type2 < 0) || (types_num < type2))
833                 return -1;
835         if ((type1 == SDB_TYPE_NULL) || (type2 == SDB_TYPE_NULL))
836                 return SDB_TYPE_NULL;
837         return op_matrix[op - 1][type1 - 1][type2 - 1];
838 } /* sdb_data_expr_type */
840 size_t
841 sdb_data_strlen(const sdb_data_t *datum)
843         if (! datum)
844                 return 0;
846         if (datum->type == SDB_TYPE_INTEGER) {
847                 /* log(64) */
848                 return 20;
849         }
850         else if (datum->type == SDB_TYPE_DECIMAL) {
851                 /* XXX: -d.dddddde+dd or -ddddd.dddddd */
852                 return 42;
853         }
854         else if (datum->type == SDB_TYPE_STRING) {
855                 if (! datum->data.string)
856                         return 6; /* NULL */
857                 /* in the worst case, each character needs to be escaped */
858                 return 2 * strlen(datum->data.string) + 2;
859         }
860         else if (datum->type == SDB_TYPE_DATETIME) {
861                 /* "YYYY-MM-DD HH:MM:SS +zzzz" */
862                 return 27;
863         }
864         else if (datum->type == SDB_TYPE_BINARY) {
865                 if (! datum->data.binary.datum)
866                         return 6; /* NULL */
867                 /* "\xNN" */
868                 return 4 * datum->data.binary.length + 2;
869         }
870         else if (datum->type == SDB_TYPE_REGEX) {
871                 if (! datum->data.re.raw)
872                         return 6; /* NULL */
873                 /* "/.../" */
874                 return strlen(datum->data.re.raw) + 4;
875         }
876         else if (datum->type & SDB_TYPE_ARRAY) {
877                 size_t len = 2; /* [] */
878                 size_t i;
879                 for (i = 0; i < datum->data.array.length; ++i) {
880                         sdb_data_t v = SDB_DATA_INIT;
881                         sdb_data_array_get(datum, i, &v);
882                         len += sdb_data_strlen(&v) + 1;
883                 }
884                 return len;
885         }
886         return 0;
887 } /* sdb_data_strlen */
889 int
890 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
892         char tmp[sdb_data_strlen(datum) + 1];
893         char *data = NULL;
894         bool is_null = 0;
895         int ret = -1;
897         size_t i, pos;
899         if ((! datum) || (! buf) || (! buflen))
900                 return -1;
902         if (datum->type == SDB_TYPE_INTEGER) {
903                 ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
904         }
905         else if (datum->type == SDB_TYPE_DECIMAL) {
906                 if (isnan(datum->data.decimal))
907                         ret = snprintf(buf, buflen, "nan");
908                 else
909                         ret = snprintf(buf, buflen, "%g", datum->data.decimal);
910         }
911         else if (datum->type == SDB_TYPE_STRING) {
912                 if (! datum->data.string)
913                         is_null = 1;
914                 else {
915                         pos = 0;
916                         for (i = 0; i < strlen(datum->data.string); ++i) {
917                                 char byte = datum->data.string[i];
919                                 if ((byte == '\\') || (byte == '"')) {
920                                         tmp[pos] = '\\';
921                                         ++pos;
922                                 }
923                                 tmp[pos] = byte;
924                                 ++pos;
925                         }
926                         tmp[pos] = '\0';
927                         data = tmp;
928                 }
929         }
930         else if (datum->type == SDB_TYPE_DATETIME) {
931                 if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
932                                         datum->data.datetime))
933                         return -1;
934                 tmp[sizeof(tmp) - 1] = '\0';
935                 data = tmp;
936         }
937         else if (datum->type == SDB_TYPE_BINARY) {
938                 pos = 0;
939                 for (i = 0; i < datum->data.binary.length; ++i) {
940                         int byte = (int)datum->data.binary.datum[i];
941                         char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
942                                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
944                         tmp[pos] = '\\';
945                         tmp[pos + 1] = 'x';
946                         pos += 2;
948                         if (byte > 0xf) {
949                                 tmp[pos] = hex[byte >> 4];
950                                 ++pos;
951                         }
952                         tmp[pos] = hex[byte & 0xf];
953                         ++pos;
954                 }
955                 if (datum->data.binary.datum) {
956                         tmp[pos] = '\0';
957                         data = tmp;
958                 }
959                 else
960                         is_null = 1;
961         }
962         else if (datum->type == SDB_TYPE_REGEX) {
963                 if (! datum->data.re.raw)
964                         is_null = 1;
965                 else {
966                         snprintf(tmp, sizeof(tmp), "/%s/", datum->data.re.raw);
967                         data = tmp;
968                 }
969         }
970         else if (datum->type & SDB_TYPE_ARRAY) {
971                 ret = 1;
972                 buf[0] = '[';
973                 for (i = 0; i < datum->data.array.length; ++i) {
974                         sdb_data_t v = SDB_DATA_INIT;
975                         int n;
976                         if ((size_t)ret >= buflen - 1)
977                                 break;
979                         if (ret > 1) {
980                                 buf[ret] = ',';
981                                 buf[ret + 1] = ' ';
982                                 ret += 2;
983                         }
985                         sdb_data_array_get(datum, i, &v);
986                         n = sdb_data_format(&v, buf + ret, buflen - ret, quoted);
987                         if (n > 0)
988                                 ret += n;
989                         else
990                                 break;
991                 }
992                 if ((size_t)ret < buflen - 1) {
993                         buf[ret] = ']';
994                         buf[ret + 1] = '\0';
995                         ++ret;
996                 }
997         }
999         if (is_null) {
1000                 /* never quote NULL */
1001                 strncpy(buf, "NULL", buflen);
1002                 ret = (int)SDB_MIN(buflen, 4);
1003         }
1004         else if (data) {
1005                 if (quoted == SDB_UNQUOTED)
1006                         ret = snprintf(buf, buflen, "%s", data);
1007                 else if (quoted == SDB_SINGLE_QUOTED)
1008                         ret = snprintf(buf, buflen, "'%s'", data);
1009                 else
1010                         ret = snprintf(buf, buflen, "\"%s\"", data);
1011         }
1012         buf[buflen - 1] = '\0';
1013         return ret;
1014 } /* sdb_data_format */
1016 int
1017 sdb_data_parse(char *str, int type, sdb_data_t *data)
1019         sdb_data_t tmp;
1021         char *endptr = NULL;
1023         errno = 0;
1024         if (type == SDB_TYPE_INTEGER) {
1025                 tmp.data.integer = strtoll(str, &endptr, 0);
1026         }
1027         else if (type == SDB_TYPE_DECIMAL) {
1028                 tmp.data.decimal = strtod(str, &endptr);
1029         }
1030         else if (type == SDB_TYPE_STRING) {
1031                 tmp.data.string = str;
1032         }
1033         else if (type == SDB_TYPE_DATETIME) {
1034                 double datetime = strtod(str, &endptr);
1035                 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
1036         }
1037         else if (type == SDB_TYPE_BINARY) {
1038                 /* we don't support any binary information containing 0-bytes */
1039                 tmp.data.binary.length = strlen(str);
1040                 tmp.data.binary.datum = (unsigned char *)str;
1041         }
1042         else if (type == SDB_TYPE_REGEX) {
1043                 tmp.data.re.raw = strdup(str);
1044                 if (! tmp.data.re.raw)
1045                         return -1;
1046                 if (regcomp(&tmp.data.re.regex, tmp.data.re.raw,
1047                                         REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
1048                         sdb_log(SDB_LOG_ERR, "core: Failed to compile regular "
1049                                         "expression '%s'", tmp.data.re.raw);
1050                         free(tmp.data.re.raw);
1051                         return -1;
1052                 }
1053                 if (! data) {
1054                         tmp.type = SDB_TYPE_REGEX;
1055                         sdb_data_free_datum(&tmp);
1056                 }
1057         }
1058         else if (type & SDB_TYPE_ARRAY) {
1059                 /* TODO */
1060                 errno = ENOTSUP;
1061                 return -1;
1062         }
1063         else {
1064                 errno = EINVAL;
1065                 return -1;
1066         }
1068         if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
1069                         || (type == SDB_TYPE_DATETIME)) {
1070                 if (errno || (str == endptr)) {
1071                         char errbuf[1024];
1072                         sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
1073                                         "'%s' as numeric value (type %i): %s", str, type,
1074                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1075                         return -1;
1076                 }
1077                 else if (endptr && (*endptr != '\0'))
1078                         sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
1079                                         "number while parsing numeric value (type %i): %s.",
1080                                         type, endptr);
1081         }
1083         if (data) {
1084                 *data = tmp;
1085                 data->type = type;
1086         }
1087         return 0;
1088 } /* sdb_data_parse */
1090 size_t
1091 sdb_data_sizeof(int type)
1093         sdb_data_t v;
1094         if (type == SDB_TYPE_INTEGER)
1095                 return sizeof(v.data.integer);
1096         else if (type == SDB_TYPE_DECIMAL)
1097                 return sizeof(v.data.decimal);
1098         else if (type == SDB_TYPE_STRING)
1099                 return sizeof(v.data.string);
1100         else if (type == SDB_TYPE_DATETIME)
1101                 return sizeof(v.data.datetime);
1102         else if (type == SDB_TYPE_BINARY)
1103                 return sizeof(v.data.binary);
1104         else if (type == SDB_TYPE_REGEX)
1105                 return sizeof(v.data.re);
1106         return 0;
1107 } /* sdb_data_sizeof */
1109 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */