}
switch (field->value_type) {
case EVENT_FIELD_VALUE_TYPE_STR:
+ if (field->value.str[0] == '\0') {
+ /* field was removed */
+ return FALSE;
+ }
return wildcard_match_icase(field->value.str, wanted_field->value.str);
case EVENT_FIELD_VALUE_TYPE_INTMAX:
return field->value.intmax == wanted_field->value.intmax;
/* Add key=value field to the event. If a key already exists, it's replaced.
Child events automatically inherit key=values from their parents at the
time the event is sent. So changing a key in parent will change the values
- in the child events as well. Returns the event parameter. */
+ in the child events as well, unless the key has been overwritten in the
+ child event. Setting the value to "" is the same as event_field_clear().
+ Returns the event parameter. */
struct event *
event_add_str(struct event *event, const char *key, const char *value);
struct event *
terminates with key=NULL. Returns the event parameter. */
struct event *
event_add_fields(struct event *event, const struct event_add_field *fields);
+/* Mark a field as nonexistent. If a parent event has the field set, this
+ allows removing it from the child event. Using an event filter with e.g.
+ "key=*" won't match this field anymore, although it's still visible in
+ event_find_field*() and event_get_fields(). This is the same as using
+ event_add_str() with value="". */
+void event_field_clear(struct event *event, const char *key);
/* Returns the parent event, or NULL if it doesn't exist. */
struct event *event_get_parent(struct event *event);
--- /dev/null
+/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "ioloop.h"
+#include "event-filter.h"
+
+static void test_event_filter_clear_parent_fields(void)
+{
+ struct event_filter *filter;
+ struct event_filter_field filter_fields[] = {
+ { .key = "", .value = "*" },
+ { .key = NULL, .value = NULL }
+ };
+ const struct event_filter_query query = {
+ .fields = filter_fields,
+ };
+ const struct failure_context failure_ctx = {
+ .type = LOG_TYPE_DEBUG
+ };
+ const char *keys[] = { "str", "int" };
+
+ test_begin("event filter: clear parent fields");
+
+ struct event *parent = event_create(NULL);
+ event_add_str(parent, "str", "parent_str");
+ event_add_int(parent, "int", 0);
+
+ struct event *child = event_create(NULL);
+ event_field_clear(child, "str");
+ event_field_clear(child, "int");
+
+ for (unsigned int i = 0; i < N_ELEMENTS(keys); i++) {
+ filter_fields[0].key = keys[i];
+ filter = event_filter_create();
+ event_filter_add(filter, &query);
+
+ test_assert_idx(event_filter_match(filter, parent, &failure_ctx), i);
+ test_assert_idx(!event_filter_match(filter, child, &failure_ctx), i);
+ event_filter_unref(&filter);
+ }
+
+ event_unref(&parent);
+ event_unref(&child);
+ test_end();
+}
+
+void test_event_filter(void)
+{
+ test_event_filter_clear_parent_fields();
+}