Code

data: Support some arithmetic expressions on mismatching types.
authorSebastian Harl <sh@tokkee.org>
Fri, 1 Aug 2014 06:59:50 +0000 (08:59 +0200)
committerSebastian Harl <sh@tokkee.org>
Fri, 1 Aug 2014 06:59:50 +0000 (08:59 +0200)
The following cases are now supported:
 - <integer> or <decimal> <mul> <datetime>
 - <datetime> <mul> or <div> or <mod> <integer> or <decimal>

Think of <datetime> having a time unit like seconds. It's perfectly fine to
multiply with or divide seconds by a number. However, since we don't have any
type having the unit Hertz, we cannot support numbers divided by <datetime>.

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

index cdafac30f284701482b583bd2f14f85e508b6526..fc646f0b5456c5842900223e759698866699b7f3 100644 (file)
@@ -208,13 +208,12 @@ int
 sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
                sdb_data_t *res)
 {
-       if (d1->type != d2->type)
-               return -1;
-
        switch (op) {
                case SDB_DATA_CONCAT:
                        return data_concat(d1, d2, res);
                case SDB_DATA_ADD:
+                       if (d1->type != d2->type)
+                               return -1;
                        switch (d1->type) {
                                case SDB_TYPE_INTEGER:
                                        res->data.integer = d1->data.integer + d2->data.integer;
@@ -230,6 +229,8 @@ sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
                        }
                        break;
                case SDB_DATA_SUB:
+                       if (d1->type != d2->type)
+                               return -1;
                        switch (d1->type) {
                                case SDB_TYPE_INTEGER:
                                        res->data.integer = d1->data.integer - d2->data.integer;
@@ -247,13 +248,51 @@ sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
                case SDB_DATA_MUL:
                        switch (d1->type) {
                                case SDB_TYPE_INTEGER:
-                                       res->data.integer = d1->data.integer * d2->data.integer;
+                                       if (d2->type == SDB_TYPE_INTEGER)
+                                               res->data.integer = d1->data.integer
+                                                       * d2->data.integer;
+                                       else if (d2->type == SDB_TYPE_DATETIME) {
+                                               res->data.datetime = (sdb_time_t)d1->data.integer
+                                                       * d2->data.datetime;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else
+                                               return -1;
                                        break;
                                case SDB_TYPE_DECIMAL:
-                                       res->data.decimal = d1->data.decimal * d2->data.decimal;
+                                       if (d2->type == SDB_TYPE_DECIMAL)
+                                               res->data.decimal = d1->data.decimal
+                                                       * d2->data.decimal;
+                                       else if (d2->type == SDB_TYPE_DATETIME) {
+                                               double tmp = d1->data.decimal
+                                                       * (double)d2->data.datetime;
+                                               res->data.datetime = (sdb_time_t)tmp;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else
+                                               return -1;
                                        break;
                                case SDB_TYPE_DATETIME:
-                                       res->data.datetime = d1->data.datetime * d2->data.datetime;
+                                       if (d2->type == SDB_TYPE_DATETIME)
+                                               res->data.datetime = d1->data.datetime
+                                                       * d2->data.datetime;
+                                       else if (d2->type == SDB_TYPE_INTEGER) {
+                                               res->data.datetime = d1->data.datetime
+                                                       * (sdb_time_t)d2->data.integer;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else if (d2->type == SDB_TYPE_DECIMAL) {
+                                               double tmp = (double)d1->data.datetime
+                                                       * d2->data.decimal;
+                                               res->data.datetime = (sdb_time_t)tmp;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else
+                                               return -1;
                                        break;
                                default:
                                        return -1;
@@ -262,13 +301,34 @@ sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
                case SDB_DATA_DIV:
                        switch (d1->type) {
                                case SDB_TYPE_INTEGER:
+                                       if (d2->type != SDB_TYPE_INTEGER)
+                                               return -1;
                                        res->data.integer = d1->data.integer / d2->data.integer;
                                        break;
                                case SDB_TYPE_DECIMAL:
+                                       if (d2->type != SDB_TYPE_DECIMAL)
+                                               return -1;
                                        res->data.decimal = d1->data.decimal / d2->data.decimal;
                                        break;
                                case SDB_TYPE_DATETIME:
-                                       res->data.datetime = d1->data.datetime / d2->data.datetime;
+                                       if (d2->type == SDB_TYPE_DATETIME)
+                                               res->data.datetime = d1->data.datetime
+                                                       / d2->data.datetime;
+                                       else if (d2->type == SDB_TYPE_INTEGER) {
+                                               res->data.datetime = d1->data.datetime
+                                                       / (sdb_time_t)d2->data.integer;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else if (d2->type == SDB_TYPE_DECIMAL) {
+                                               double tmp = (double)d1->data.datetime
+                                                       / d2->data.decimal;
+                                               res->data.datetime = (sdb_time_t)tmp;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else
+                                               return -1;
                                        break;
                                default:
                                        return -1;
@@ -277,13 +337,34 @@ sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
                case SDB_DATA_MOD:
                        switch (d1->type) {
                                case SDB_TYPE_INTEGER:
+                                       if (d2->type != SDB_TYPE_INTEGER)
+                                               return -1;
                                        res->data.integer = d1->data.integer % d2->data.integer;
                                        break;
                                case SDB_TYPE_DECIMAL:
+                                       if (d2->type != SDB_TYPE_DECIMAL)
+                                               return -1;
                                        res->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
                                        break;
                                case SDB_TYPE_DATETIME:
-                                       res->data.datetime = d1->data.datetime % d2->data.datetime;
+                                       if (d2->type == SDB_TYPE_DATETIME)
+                                               res->data.datetime = d1->data.datetime
+                                                       % d2->data.datetime;
+                                       else if (d2->type == SDB_TYPE_INTEGER) {
+                                               res->data.datetime = d1->data.datetime
+                                                       % (sdb_time_t)d2->data.integer;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else if (d2->type == SDB_TYPE_DECIMAL) {
+                                               double tmp = fmod((double)d1->data.datetime,
+                                                       d2->data.decimal);
+                                               res->data.datetime = (sdb_time_t)tmp;
+                                               res->type = SDB_TYPE_DATETIME;
+                                               return 0;
+                                       }
+                                       else
+                                               return -1;
                                        break;
                                default:
                                        return -1;
index 2e9b90bcfd55af660d3cb576b1c5e3e21427730e..8f9b282777925330c3f4ed9669febbb73b846760 100644 (file)
@@ -143,9 +143,14 @@ enum {
 
 /*
  * sdb_data_expr_eval:
- * Evaluate a simple arithmetic expression on two data points. The data-type
- * of d1 and d2 have to be the same. String and binary data only support
- * concatenation and all other data types only support the other operators.
+ * Evaluate a simple arithmetic expression on two data points. String and
+ * binary data only support concatenation and all other data types only
+ * support the other operators.
+ *
+ * The data-types of d1 and d2 have to be the same, except for the following
+ * cases:
+ *  - <integer> or <decimal> <mul> <datetime>
+ *  - <datetime> <mul> or <div> or <mod> <integer> or <decimal>
  *
  * Returns:
  *  - 0 on success
index 0fd61a57539ff7e322be0356666a5375add663ee..606eae53b1cf69415eb2076a8c868be0ae890f40 100644 (file)
@@ -421,6 +421,51 @@ START_TEST(test_expr_eval)
                                { .binary = { 6, (unsigned char *)"a\0ab\0b" } },
                        },
                },
+               /* supported type-mismatches */
+               {
+                       /* int * datetime */
+                       { SDB_TYPE_INTEGER,  { .integer  = 20 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 2 } },
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+                       { SDB_TYPE_DATETIME, { .datetime = 40 } },
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+               },
+               {
+                       /* datetime * int, datetime / int, datetime % int */
+                       { SDB_TYPE_DATETIME, { .datetime = 20 } },
+                       { SDB_TYPE_INTEGER,  { .integer  = 2 } },
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+                       { SDB_TYPE_DATETIME, { .datetime = 40 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 10 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 0 } },
+                       SDB_DATA_INIT,
+               },
+               {
+                       /* float * datetime */
+                       { SDB_TYPE_DECIMAL,  { .decimal  = 20.0 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 2 } },
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+                       { SDB_TYPE_DATETIME, { .datetime = 40 } },
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+               },
+               {
+                       /* datetime * float, datetime / float, datetime % float */
+                       { SDB_TYPE_DATETIME, { .datetime = 20 } },
+                       { SDB_TYPE_DECIMAL,  { .decimal  = 2.0 } },
+                       SDB_DATA_INIT,
+                       SDB_DATA_INIT,
+                       { SDB_TYPE_DATETIME, { .datetime = 40 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 10 } },
+                       { SDB_TYPE_DATETIME, { .datetime = 0 } },
+                       SDB_DATA_INIT,
+               },
        };
 
        size_t i;