Code

data: Added explicit NULL values.
authorSebastian Harl <sh@tokkee.org>
Sun, 19 Oct 2014 12:56:38 +0000 (14:56 +0200)
committerSebastian Harl <sh@tokkee.org>
Sun, 19 Oct 2014 12:56:38 +0000 (14:56 +0200)
… and let all expressions involving NULL values to return NULL.

src/core/data.c
src/include/core/data.h
t/unit/core/data_test.c

index 4840736896f55a03b4cf0716e18e3dec7cb9baec..f489367a42218e35a94bbf64afbff7d6255442fb 100644 (file)
@@ -34,6 +34,8 @@
 #include "core/data.h"
 #include "utils/error.h"
 
+#include <assert.h>
+
 #include <errno.h>
 
 #include <inttypes.h>
@@ -200,21 +202,17 @@ data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res)
        else
                return -1;
 
-       if (s1 && s2) {
-               new = malloc(len1 + len2 + 1);
-               if (! new)
-                       return -1;
+       assert(s1 && s2);
 
-               if (len1)
-                       memcpy(new, s1, len1);
-               if (len2)
-                       memcpy(new + len1, s2, len2);
-               new[len1 + len2] = '\0';
-       }
-       else {
-               len1 = len2 = 0;
-               new = NULL;
-       }
+       new = malloc(len1 + len2 + 1);
+       if (! new)
+               return -1;
+
+       if (len1)
+               memcpy(new, s1, len1);
+       if (len2)
+               memcpy(new + len1, s2, len2);
+       new[len1 + len2] = '\0';
 
        res->type = d1->type;
        if (res->type == SDB_TYPE_STRING) {
@@ -231,6 +229,8 @@ data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res)
  * public API
  */
 
+const sdb_data_t SDB_DATA_NULL = SDB_DATA_INIT;
+
 int
 sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src)
 {
@@ -391,6 +391,8 @@ sdb_data_isnull(const sdb_data_t *datum)
 {
        if (! datum)
                return 1;
+       if (datum->type == SDB_TYPE_NULL)
+               return 1;
        if ((datum->type == SDB_TYPE_STRING) && (! datum->data.string))
                return 1;
        if ((datum->type == SDB_TYPE_BINARY) && (! datum->data.binary.datum))
@@ -424,6 +426,10 @@ sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
 {
        if ((! d1) || (! d2) || (! res))
                return -1;
+       if (sdb_data_isnull(d1) || sdb_data_isnull(d2)) {
+               *res = SDB_DATA_NULL;
+               return 0;
+       }
        switch (op) {
                case SDB_DATA_CONCAT:
                        return data_concat(d1, d2, res);
index ffddf979f5509593417c80d6b3358b0b75e27a34..514c4d524ce2dd04dc463ff6b7e51254dede129f 100644 (file)
@@ -41,7 +41,8 @@ extern "C" {
 #endif
 
 enum {
-       SDB_TYPE_INTEGER = 1,
+       SDB_TYPE_NULL = 0,
+       SDB_TYPE_INTEGER,
        SDB_TYPE_DECIMAL,
        SDB_TYPE_STRING,
        SDB_TYPE_DATETIME,
@@ -78,7 +79,9 @@ typedef struct {
                } re;                 /* SDB_TYPE_REGEX */
        } data;
 } sdb_data_t;
-#define SDB_DATA_INIT { 0, { .integer = 0 } }
+#define SDB_DATA_INIT { SDB_TYPE_NULL, { .integer = 0 } }
+
+extern const sdb_data_t SDB_DATA_NULL;
 
 /*
  * sdb_data_copy:
@@ -135,7 +138,8 @@ sdb_data_strcmp(const sdb_data_t *d1, const sdb_data_t *d2);
 /*
  * sdb_data_isnull:
  * Determine whether a datum is NULL. A datum is considered to be NULL if
- * either datum is NULL or if the string or binary datum is NULL.
+ * either datum is NULL or if the type is SDB_TYPE_NULL or if the string or
+ * binary datum is NULL.
  */
 _Bool
 sdb_data_isnull(const sdb_data_t *datum);
@@ -179,6 +183,8 @@ sdb_data_parse_op(const char *op);
  * support the other operators. The result may be allocated dynamically and
  * has to be freed by the caller (using sdb_data_free_datum).
  *
+ * If any of the data points is a NULL value, the result is also NULL.
+ *
  * The data-types of d1 and d2 have to be the same, except for the following
  * cases:
  *  - <integer> or <decimal> <mul> <datetime>
index 2336c5e98d335714f510797c31f745126bb938ee..9c736d399e291d12eb61c80ae0c3a83772393d2e 100644 (file)
@@ -656,6 +656,8 @@ END_TEST
 
 START_TEST(test_expr_eval)
 {
+       sdb_data_t err = { -1, { .integer = 0 } };
+
        struct {
                sdb_data_t d1;
                sdb_data_t d2;
@@ -674,7 +676,7 @@ START_TEST(test_expr_eval)
                        { SDB_TYPE_INTEGER, { .integer = 221417 } },
                        { SDB_TYPE_INTEGER, { .integer = 100 } },
                        { SDB_TYPE_INTEGER, { .integer = 11 } },
-                       SDB_DATA_INIT,
+                       err,
                },
                {
                        { SDB_TYPE_DECIMAL, { .decimal = 35.0 } },
@@ -684,46 +686,46 @@ START_TEST(test_expr_eval)
                        { SDB_TYPE_DECIMAL, { .decimal = 612.5 } },
                        { SDB_TYPE_DECIMAL, { .decimal = 2.0 } },
                        { SDB_TYPE_DECIMAL, { .decimal = 0.0 } },
-                       SDB_DATA_INIT,
+                       err,
                },
                {
                        { SDB_TYPE_STRING, { .string = NULL } },
                        { SDB_TYPE_STRING, { .string = "" } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       { SDB_TYPE_STRING, { .string = NULL } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                {
                        { SDB_TYPE_STRING, { .string = NULL } },
                        { SDB_TYPE_STRING, { .string = NULL } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       { SDB_TYPE_STRING, { .string = NULL } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                {
                        { SDB_TYPE_STRING, { .string = "" } },
                        { SDB_TYPE_STRING, { .string = NULL } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       { SDB_TYPE_STRING, { .string = NULL } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                {
                        { SDB_TYPE_STRING, { .string = "a" } },
                        { SDB_TYPE_STRING, { .string = "b" } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
+                       err,
+                       err,
+                       err,
                        { SDB_TYPE_STRING, { .string = "ab" } },
                },
                {
@@ -734,37 +736,37 @@ START_TEST(test_expr_eval)
                        { SDB_TYPE_DATETIME, { .datetime = 221957403521 } },
                        { SDB_TYPE_DATETIME, { .datetime = 10001 } },
                        { SDB_TYPE_DATETIME, { .datetime = 0 } },
-                       SDB_DATA_INIT,
+                       err,
                },
                {
                        { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
                        { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                {
                        { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
                        { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                {
                        { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } },
                        { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       { SDB_TYPE_BINARY, { .binary = { 0, NULL } } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                {
                        {
@@ -775,11 +777,11 @@ START_TEST(test_expr_eval)
                                SDB_TYPE_BINARY,
                                { .binary = { 3, (unsigned char *)"b\0b" } },
                        },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
+                       err,
+                       err,
+                       err,
                        {
                                SDB_TYPE_BINARY,
                                { .binary = { 6, (unsigned char *)"a\0ab\0b" } },
@@ -788,57 +790,187 @@ START_TEST(test_expr_eval)
                {
                        { SDB_TYPE_REGEX, { .re = { ".", empty_re } } },
                        { SDB_TYPE_REGEX, { .re = { ".", empty_re } } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
+                       err,
+                       err,
+                       err,
+                       err,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_INTEGER, { .integer = 42 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_INTEGER, { .integer = 42 } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_DECIMAL, { .decimal = 47.11 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_DECIMAL, { .decimal = 47.11 } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_STRING, { .string = "47.11" } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_STRING, { .string = "47.11" } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 4711 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_DATETIME, { .datetime = 4711 } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_BINARY, { .binary = { 1, (unsigned char *)"a" } } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       { SDB_TYPE_REGEX, { .re = { ".", empty_re } } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+               },
+               {
+                       { SDB_TYPE_REGEX, { .re = { ".", empty_re } } },
+                       { SDB_TYPE_NULL, { .integer = 0 } },
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
+                       SDB_DATA_NULL,
                },
                /* supported type-mismatches */
                {
                        /* int * datetime */
                        { SDB_TYPE_INTEGER,  { .integer  = 20 } },
                        { SDB_TYPE_DATETIME, { .datetime = 2 } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
                        { SDB_TYPE_DATETIME, { .datetime = 40 } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
+                       err,
                },
                {
                        /* datetime * int, datetime / int, datetime % int */
                        { SDB_TYPE_DATETIME, { .datetime = 20 } },
                        { SDB_TYPE_INTEGER,  { .integer  = 2 } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
                        { SDB_TYPE_DATETIME, { .datetime = 40 } },
                        { SDB_TYPE_DATETIME, { .datetime = 10 } },
                        { SDB_TYPE_DATETIME, { .datetime = 0 } },
-                       SDB_DATA_INIT,
+                       err,
                },
                {
                        /* float * datetime */
                        { SDB_TYPE_DECIMAL,  { .decimal  = 20.0 } },
                        { SDB_TYPE_DATETIME, { .datetime = 2 } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
                        { SDB_TYPE_DATETIME, { .datetime = 40 } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
+                       err,
                },
                {
                        /* datetime * float, datetime / float, datetime % float */
                        { SDB_TYPE_DATETIME, { .datetime = 20 } },
                        { SDB_TYPE_DECIMAL,  { .decimal  = 2.0 } },
-                       SDB_DATA_INIT,
-                       SDB_DATA_INIT,
+                       err,
+                       err,
                        { SDB_TYPE_DATETIME, { .datetime = 40 } },
                        { SDB_TYPE_DATETIME, { .datetime = 10 } },
                        { SDB_TYPE_DATETIME, { .datetime = 0 } },
-                       SDB_DATA_INIT,
+                       err,
                },
        };
 
@@ -870,13 +1002,22 @@ START_TEST(test_expr_eval)
 
                        check = sdb_data_expr_eval(tests[j].op,
                                        &golden_data[i].d1, &golden_data[i].d2, &res);
-                       fail_unless((check == 0) == (tests[j].expected.type != 0),
+                       fail_unless((check == 0) == (tests[j].expected.type != -1),
                                        "sdb_data_expr_eval(%s, %s, %s) = %d; expected: %d",
                                        SDB_DATA_OP_TO_STRING(tests[j].op), d1_str, d2_str, check,
-                                       tests[j].expected.type == 0 ? -1 : 0);
-                       if (tests[j].expected.type == 0)
+                                       tests[j].expected.type == -1 ? -1 : 0);
+                       if (tests[j].expected.type == -1)
                                continue;
 
+                       if (tests[j].expected.type == SDB_TYPE_NULL) {
+                               fail_unless(res.type == SDB_TYPE_NULL,
+                                               "sdb_data_expr_eval(%s, %s, %s) evaluated to "
+                                               "type %d; expected: SDB_TYPE_NULL",
+                                               SDB_DATA_OP_TO_STRING(tests[j].op),
+                                               d1_str, d2_str, res.type);
+                               continue;
+                       }
+
                        check = sdb_data_cmp(&res, &tests[j].expected);
                        if (check != 0) {
                                char res_str[64] = "", expected_str[64] = "";
@@ -884,9 +1025,11 @@ START_TEST(test_expr_eval)
                                                SDB_DOUBLE_QUOTED);
                                sdb_data_format(&tests[j].expected, expected_str,
                                                sizeof(expected_str), SDB_DOUBLE_QUOTED);
-                               fail("sdb_data_expr_eval(%s, %s, %s) evaluated to %s; "
-                                               "expected: %s", SDB_DATA_OP_TO_STRING(tests[j].op),
-                                               d1_str, d2_str, res_str, expected_str);
+                               fail("sdb_data_expr_eval(%s, %s, %s) evaluated to %s "
+                                               "(type %d); expected: %s (type %d)",
+                                               SDB_DATA_OP_TO_STRING(tests[j].op),
+                                               d1_str, d2_str, res_str, res.type,
+                                               expected_str, tests[j].expected.type);
                        }
 
                        sdb_data_free_datum(&res);