From 0e369b5129e64580e444e7d7ef4033a9d2995a28 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Tue, 29 Jul 2014 20:49:24 +0200 Subject: [PATCH] store_lookup: Added conditional for accessing store-object fields. Fields are "last-update", "age", "interval", and "backend". "backend" only supports the lookup of objects provided by a named backend but no conditional checks. --- src/core/store-private.h | 7 ++ src/core/store_lookup.c | 125 +++++++++++++++++++++++++++++--- src/include/core/store.h | 26 ++++++- t/unit/core/store_lookup_test.c | 119 +++++++++++++++++++++++++++--- 4 files changed, 254 insertions(+), 23 deletions(-) diff --git a/src/core/store-private.h b/src/core/store-private.h index 0df3c7b..3f07067 100644 --- a/src/core/store-private.h +++ b/src/core/store-private.h @@ -112,6 +112,13 @@ typedef struct { } attr_cond_t; #define ATTR_C(obj) ((attr_cond_t *)(obj)) +typedef struct { + sdb_store_cond_t super; + int field; + sdb_store_expr_t *expr; +} obj_cond_t; +#define OBJ_C(obj) ((obj_cond_t *)(obj)) + /* * matchers */ diff --git a/src/core/store_lookup.c b/src/core/store_lookup.c index 476e0e7..c692b8c 100644 --- a/src/core/store_lookup.c +++ b/src/core/store_lookup.c @@ -128,6 +128,61 @@ attr_cmp(sdb_store_obj_t *obj, sdb_store_cond_t *cond, return status; } /* attr_cmp */ +static int +obj_cmp(sdb_store_obj_t *obj, sdb_store_cond_t *cond, + sdb_store_matcher_t __attribute__((unused)) *filter) +{ + sdb_data_t obj_value = SDB_DATA_INIT; + sdb_data_t value = SDB_DATA_INIT; + int status; + + switch (OBJ_C(cond)->field) { + case SDB_FIELD_LAST_UPDATE: + obj_value.type = SDB_TYPE_DATETIME; + obj_value.data.datetime = obj->last_update; + break; + case SDB_FIELD_AGE: + obj_value.type = SDB_TYPE_DATETIME; + obj_value.data.datetime = sdb_gettime() - obj->last_update; + break; + case SDB_FIELD_INTERVAL: + obj_value.type = SDB_TYPE_DATETIME; + obj_value.data.datetime = obj->interval; + break; + case SDB_FIELD_BACKEND: + obj_value.type = SDB_TYPE_STRING; + obj_value.data.string = NULL; /* handled separately */ + break; + default: + return INT_MAX; + } + + if (sdb_store_expr_eval(OBJ_C(cond)->expr, &value)) + return INT_MAX; + + if (obj_value.type != value.type) { + sdb_data_free_datum(&value); + return INT_MAX; + } + else if (OBJ_C(cond)->field == SDB_FIELD_BACKEND) { + /* this implementation is not actually a conditional but rather checks + * for equality (or rather, existence) only */ + size_t i; + status = INT_MAX; + for (i = 0; i < obj->backends_num; ++i) { + if (! strcasecmp(obj->backends[i], value.data.string)) { + status = 0; + break; + } + } + } + else { + status = sdb_data_cmp(&obj_value, &value); + } + sdb_data_free_datum(&value); + return status; +} /* obj_cmp */ + /* * matcher implementations */ @@ -356,6 +411,32 @@ static sdb_type_t attr_cond_type = { /* destroy = */ attr_cond_destroy, }; +static int +obj_cond_init(sdb_object_t *obj, va_list ap) +{ + int field = va_arg(ap, int); + sdb_store_expr_t *expr = va_arg(ap, sdb_store_expr_t *); + + SDB_STORE_COND(obj)->cmp = obj_cmp; + + OBJ_C(obj)->field = field; + OBJ_C(obj)->expr = expr; + sdb_object_ref(SDB_OBJ(expr)); + return 0; +} /* obj_cond_init */ + +static void +obj_cond_destroy(sdb_object_t *obj) +{ + sdb_object_deref(SDB_OBJ(OBJ_C(obj)->expr)); +} /* obj_cond_destroy */ + +static sdb_type_t obj_cond_type = { + /* size = */ sizeof(obj_cond_t), + /* init = */ obj_cond_init, + /* destroy = */ obj_cond_destroy, +}; + /* * private matcher types */ @@ -495,21 +576,34 @@ cond_matcher_destroy(sdb_object_t *obj) static char * cond_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen) { + const char *type, *id; + sdb_data_t value = SDB_DATA_INIT; + char value_str[buflen]; + sdb_store_expr_t *expr; + if (COND_M(m)->cond->cmp == attr_cmp) { - sdb_data_t value = SDB_DATA_INIT; - char value_str[buflen]; - if (sdb_store_expr_eval(ATTR_C(COND_M(m)->cond)->expr, &value)) - snprintf(value_str, sizeof(value_str), "ERR"); - else if (sdb_data_format(&value, value_str, sizeof(value_str), - SDB_SINGLE_QUOTED) < 0) - snprintf(value_str, sizeof(value_str), "ERR"); - snprintf(buf, buflen, "ATTR[%s]{ %s %s }", - ATTR_C(COND_M(m)->cond)->name, MATCHER_SYM(m->type), - value_str); - sdb_data_free_datum(&value); + type = "ATTR"; + id = ATTR_C(COND_M(m)->cond)->name; + expr = ATTR_C(COND_M(m)->cond)->expr; } - else + else if (COND_M(m)->cond->cmp == obj_cmp) { + type = "OBJ"; + id = SDB_FIELD_TO_NAME(OBJ_C(COND_M(m)->cond)->field); + expr = OBJ_C(COND_M(m)->cond)->expr; + } + else { snprintf(buf, buflen, ""); + return buf; + } + + if (sdb_store_expr_eval(expr, &value)) + snprintf(value_str, sizeof(value_str), "ERR"); + else if (sdb_data_format(&value, value_str, sizeof(value_str), + SDB_SINGLE_QUOTED) < 0) + snprintf(value_str, sizeof(value_str), "ERR"); + snprintf(buf, buflen, "%s[%s]{ %s %s }", type, id, + MATCHER_SYM(m->type), value_str); + sdb_data_free_datum(&value); return buf; } /* cond_tostring */ @@ -696,6 +790,13 @@ sdb_store_attr_cond(const char *name, sdb_store_expr_t *expr) name, expr)); } /* sdb_store_attr_cond */ +sdb_store_cond_t * +sdb_store_obj_cond(int field, sdb_store_expr_t *expr) +{ + return SDB_STORE_COND(sdb_object_create("obj-cond", obj_cond_type, + field, expr)); +} /* sdb_store_obj_cond */ + sdb_store_matcher_t * sdb_store_name_matcher(int type, const char *name, _Bool re) { diff --git a/src/include/core/store.h b/src/include/core/store.h index 578fb51..a9788f4 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -53,7 +53,6 @@ enum { : ((t) == SDB_SERVICE) ? "service" \ : ((t) == SDB_ATTRIBUTE) ? "attribute" : "unknown") - /* * sdb_store_obj_t represents the super-class of any object stored in the * database. It inherits from sdb_object_t and may safely be cast to a generic @@ -62,6 +61,22 @@ enum { struct sdb_store_obj; typedef struct sdb_store_obj sdb_store_obj_t; +/* + * Queryable fields of a stored object. + */ +enum { + SDB_FIELD_LAST_UPDATE = 1, /* datetime */ + SDB_FIELD_AGE, /* datetime */ + SDB_FIELD_INTERVAL, /* datetime */ + SDB_FIELD_BACKEND, /* string */ +}; + +#define SDB_FIELD_TO_NAME(f) \ + (((f) == SDB_FIELD_LAST_UPDATE) ? "last-update" \ + : ((f) == SDB_FIELD_AGE) ? "age" \ + : ((f) == SDB_FIELD_INTERVAL) ? "interval" \ + : ((f) == SDB_FIELD_BACKEND) ? "backend" : "unknown") + /* * sdb_store_clear: * Clear the entire store and remove all stored objects. @@ -224,6 +239,15 @@ typedef struct sdb_store_cond sdb_store_cond_t; sdb_store_cond_t * sdb_store_attr_cond(const char *name, sdb_store_expr_t *expr); +/* + * sdb_store_obj_cond: + * Creates a conditional based on queryable object fields. The respective + * field of *any* object type is compared against the value the expression + * evaluates to. + */ +sdb_store_cond_t * +sdb_store_obj_cond(int field, sdb_store_expr_t *expr); + /* * Store matchers may be used to lookup hosts from the store based on their * various attributes. Service and attribute matchers are applied to a host's diff --git a/t/unit/core/store_lookup_test.c b/t/unit/core/store_lookup_test.c index 1adece5..89ef451 100644 --- a/t/unit/core/store_lookup_test.c +++ b/t/unit/core/store_lookup_test.c @@ -248,7 +248,7 @@ START_TEST(test_store_match_attr) } END_TEST -START_TEST(test_store_cond) +START_TEST(test_attr_cond) { sdb_store_obj_t *obj; @@ -287,13 +287,13 @@ START_TEST(test_store_cond) struct { sdb_store_matcher_t *(*matcher)(sdb_store_cond_t *); - int *expected; + int expected; } tests[] = { - { sdb_store_lt_matcher, &golden_data[i].expected_lt }, - { sdb_store_le_matcher, &golden_data[i].expected_le }, - { sdb_store_eq_matcher, &golden_data[i].expected_eq }, - { sdb_store_ge_matcher, &golden_data[i].expected_ge }, - { sdb_store_gt_matcher, &golden_data[i].expected_gt }, + { sdb_store_lt_matcher, golden_data[i].expected_lt }, + { sdb_store_le_matcher, golden_data[i].expected_le }, + { sdb_store_eq_matcher, golden_data[i].expected_eq }, + { sdb_store_ge_matcher, golden_data[i].expected_ge }, + { sdb_store_gt_matcher, golden_data[i].expected_gt }, }; sdb_data_format(&golden_data[i].value, @@ -319,11 +319,11 @@ START_TEST(test_store_cond) "sdb_store__matcher() = NULL; expected: "); status = sdb_store_matcher_matches(m, obj, /* filter */ NULL); - fail_unless(status == *tests[j].expected, + fail_unless(status == tests[j].expected, "sdb_store_matcher_matches(%s, , NULL) = %d; " "expected: %d", sdb_store_matcher_tostring(m, m_str, sizeof(m_str)), - status, *tests[j].expected); + status, tests[j].expected); sdb_object_deref(SDB_OBJ(m)); } @@ -335,6 +335,104 @@ START_TEST(test_store_cond) } END_TEST +START_TEST(test_obj_cond) +{ + struct { + const char *host; + int field; + const sdb_data_t value; + int expected_lt, expected_le, expected_eq, expected_ge, expected_gt; + } golden_data[] = { + /* last-update = 1 for all objects */ + { "a", SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 1 } }, 0, 1, 1, 1, 0 }, + { "a", SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 2 } }, 1, 1, 0, 0, 0 }, + { "a", SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 0 } }, 0, 0, 0, 1, 1 }, + /* age > 0 for all objects */ + { "a", SDB_FIELD_AGE, + { SDB_TYPE_DATETIME, { .datetime = 0 } }, 0, 0, 0, 1, 1 }, + /* interval = 0 for all objects */ + { "a", SDB_FIELD_INTERVAL, + { SDB_TYPE_DATETIME, { .datetime = 0 } }, 0, 1, 1, 1, 0 }, + { "a", SDB_FIELD_INTERVAL, + { SDB_TYPE_DATETIME, { .datetime = 1 } }, 1, 1, 0, 0, 0 }, + /* type mismatch */ + { "a", SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_INTEGER, { .integer = 0 } }, 0, 0, 0, 0, 0 }, + { "a", SDB_FIELD_AGE, + { SDB_TYPE_INTEGER, { .integer = 0 } }, 0, 0, 0, 0, 0 }, + { "a", SDB_FIELD_INTERVAL, + { SDB_TYPE_INTEGER, { .integer = 0 } }, 0, 0, 0, 0, 0 }, + { "a", SDB_FIELD_BACKEND, + { SDB_TYPE_INTEGER, { .integer = 0 } }, 0, 0, 0, 0, 0 }, + }; + + int status; + size_t i; + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) { + sdb_store_obj_t *obj; + sdb_store_expr_t *expr; + sdb_store_cond_t *c; + char buf[1024]; + size_t j; + + struct { + sdb_store_matcher_t *(*matcher)(sdb_store_cond_t *); + int expected; + } tests[] = { + { sdb_store_lt_matcher, golden_data[i].expected_lt }, + { sdb_store_le_matcher, golden_data[i].expected_le }, + { sdb_store_eq_matcher, golden_data[i].expected_eq }, + { sdb_store_ge_matcher, golden_data[i].expected_ge }, + { sdb_store_gt_matcher, golden_data[i].expected_gt }, + }; + + obj = sdb_store_get_host(golden_data[i].host); + fail_unless(obj != NULL, + "sdb_store_get_host(%s) = NULL; expected: ", + golden_data[i].host); + + sdb_data_format(&golden_data[i].value, + buf, sizeof(buf), SDB_UNQUOTED); + + expr = sdb_store_expr_constvalue(&golden_data[i].value); + fail_unless(expr != NULL, + "sdb_store_expr_constvalue(%s) = NULL; expected: ", + buf); + + c = sdb_store_obj_cond(golden_data[i].field, expr); + sdb_object_deref(SDB_OBJ(expr)); + fail_unless(c != NULL, + "sdb_store_obj_cond(%d, expr{%s}) = NULL; expected: ", + golden_data[i].field, buf); + + for (j = 0; j < SDB_STATIC_ARRAY_LEN(tests); ++j) { + sdb_store_matcher_t *m; + char m_str[1024]; + + m = tests[j].matcher(c); + fail_unless(m != NULL, + "sdb_store__matcher() = NULL; expected: "); + + status = sdb_store_matcher_matches(m, obj, /* filter */ NULL); + fail_unless(status == tests[j].expected, + "sdb_store_matcher_matches(%s, , NULL) = %d; " + "expected: %d", + sdb_store_matcher_tostring(m, m_str, sizeof(m_str)), + status, tests[j].expected); + + sdb_object_deref(SDB_OBJ(m)); + } + + sdb_object_deref(SDB_OBJ(c)); + sdb_object_deref(SDB_OBJ(obj)); + } +} +END_TEST + START_TEST(test_store_match_op) { sdb_store_obj_t *obj; @@ -659,7 +757,8 @@ core_store_lookup_suite(void) tcase_add_checked_fixture(tc, populate, sdb_store_clear); tcase_add_test(tc, test_store_match_name); tcase_add_test(tc, test_store_match_attr); - tcase_add_test(tc, test_store_cond); + tcase_add_test(tc, test_attr_cond); + tcase_add_test(tc, test_obj_cond); tcase_add_test(tc, test_store_match_op); tcase_add_test(tc, test_parse_cmp); tcase_add_test(tc, test_scan); -- 2.30.2