i_panic("unknown group-by function %d", group_by->func);
}
+/* Handle string modifiers */
+static inline const char *
+label_by_mod_str(const struct stats_metric_settings_group_by *group_by,
+ const char *value)
+{
+ if ((group_by->mod & STATS_METRICS_GROUPBY_DOMAIN) != 0) {
+ const char *domain = strrchr(value, '@');
+ if (domain != NULL)
+ value = domain+1;
+ else
+ value = "";
+ }
+ if ((group_by->mod & STATS_METRICS_GROUPBY_UPPERCASE) != 0)
+ value = t_str_ucase(value);
+ if ((group_by->mod & STATS_METRICS_GROUPBY_LOWERCASE) != 0)
+ value = t_str_lcase(value);
+ return value;
+}
+
static const char *
stats_metric_group_by_value_label(const struct event_field *field,
const struct stats_metric_settings_group_by *group_by,
{
switch (value->type) {
case METRIC_VALUE_TYPE_STR:
- return field->value.str;
+ return label_by_mod_str(group_by, field->value.str);
case METRIC_VALUE_TYPE_INT:
return dec2str(field->value.intmax);
case METRIC_VALUE_TYPE_IP:
return TRUE;
}
+static bool
+parse_metric_group_by_mod(pool_t pool ATTR_UNUSED,
+ struct stats_metric_settings_group_by *group_by,
+ const char *const *params, const char **error_r)
+{
+ for (; *params != NULL; params++) {
+ if (strcmp(*params, "domain") == 0)
+ group_by->mod |= STATS_METRICS_GROUPBY_DOMAIN;
+ else if (strcmp(*params, "uppercase") == 0)
+ group_by->mod |= STATS_METRICS_GROUPBY_UPPERCASE;
+ else if (strcmp(*params, "lowercase") == 0)
+ group_by->mod |= STATS_METRICS_GROUPBY_LOWERCASE;
+ else {
+ *error_r = t_strdup_printf("Unknown modifier '%s' for '%s'",
+ *params, group_by->field);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
static bool parse_metric_group_by(struct stats_metric_settings *set,
pool_t pool, const char **error_r)
{
} else if (strcmp(params[1], "discrete") == 0) {
/* <field>:discrete */
group_by.func = STATS_METRIC_GROUPBY_DISCRETE;
- if (params[2] != NULL) {
- *error_r = "group_by 'discrete' aggregate function "
- "does not take any args";
+ if (!parse_metric_group_by_mod(pool, &group_by, ¶ms[2], error_r))
return FALSE;
- }
} else if (strcmp(params[1], "exponential") == 0) {
/* <field>:exponential:<min mag>:<max mag>:<base> */
if (!parse_metric_group_by_exp(pool, &group_by, ¶ms[2], error_r))
STATS_METRIC_GROUPBY_QUANTIZED,
};
+/* A modifier for discrete group by.
+ *
+ * Implemented as bit field, as we allow multiple modifiers.
+ */
+enum stats_metric_group_by_modifier {
+ /* extract domain from user@domain */
+ STATS_METRICS_GROUPBY_DOMAIN = BIT(0),
+ STATS_METRICS_GROUPBY_UPPERCASE = BIT(1),
+ STATS_METRICS_GROUPBY_LOWERCASE = BIT(2),
+};
/*
* A range covering a stats bucket. The the interval is half closed - the
* minimum is excluded and the maximum is included. In other words: (min, max].
struct stats_metric_settings_group_by {
const char *field;
enum stats_metric_group_by_func func;
+ enum stats_metric_group_by_modifier mod;
unsigned int num_ranges;
struct stats_metric_settings_bucket_range *ranges;
};