<commit_before>#include "mysql_engine/pinba_mysql.h"
#include "mysql_engine/view_conf.h"

#include <meow/str_ref_algo.hpp>
#include <meow/convert/number_from_string.hpp>

#include "pinba/globals.h"
#include "pinba/report_by_request.h"
#include "pinba/report_by_timer.h"
#include "pinba/report_by_packet.h"

////////////////////////////////////////////////////////////////////////////////////////////////
namespace { namespace aux {
////////////////////////////////////////////////////////////////////////////////////////////////

	struct pinba_view_conf___internal_t : public pinba_view_conf_t
	{
		report_conf___by_packet_t   by_packet_conf;
		report_conf___by_request_t  by_request_conf;
		report_conf___by_timer_t    by_timer_conf;
	};

////////////////////////////////////////////////////////////////////////////////////////////////

	static pinba_error_t parse_aggregation_window(pinba_view_conf_t *vcf, str_ref aggregation_spec)
	{
		auto const time_window_v = meow::split_ex(aggregation_spec, ",");
		if (time_window_v.size() == 0)
			return ff::fmt_err("time_window must be set");

		auto const time_window_s = time_window_v[0];

		if (time_window_s == "default_history_time")
		{
			vcf->time_window = P_CTX_->settings.time_window;
			vcf->tick_count  = P_CTX_->settings.tick_count;
		}
		else
		{
			uint32_t time_window;
			if (!meow::number_from_string(&time_window, time_window_s))
				return ff::fmt_err("bad seconds_spec: '{0}', expected integer number of seconds", time_window_s);

			vcf->time_window = time_window * d_second;
			vcf->tick_count  = time_window; // i.e. ticks are always 1 second wide
		}

		return {};
	}


	static pinba_error_t parse_keys(pinba_view_conf_t *vcf, str_ref key_spec)
	{
		if (key_spec == "no_keys")
			return {};

		auto const keys_v = meow::split_ex(key_spec, ",");

		for (auto const& key_s : keys_v)
		{
			if (key_s.empty())
				continue;

			vcf->keys.push_back(key_s);
		}

		return {};
	}


	static pinba_error_t parse_histogram_and_percentiles(pinba_view_conf_t *vcf, str_ref histogram_spec)
	{
		if (histogram_spec == "no_percentiles")
			return {};

		bool hv_present = false;

		auto const pct_v = meow::split_ex(histogram_spec, ",");

		for (auto const& pct_s : pct_v)
		{
			if (meow::prefix_compare(pct_s, "hv=")) // 3 chars
			{
				auto const hv_range_s  = meow::sub_str_ref(pct_s, 3, pct_s.size());
				auto const hv_values_v = meow::split_ex(hv_range_s, ":");

				if (hv_values_v.size() != 3)
					return ff::fmt_err("3 integer parts expected");

				// LOG_DEBUG(P_L_, "hv_values_v = {{ {0}, {1}, {2} }", hv_values_v[0], hv_values_v[1], hv_values_v[2]);

				// hv=<hv_lower_time_ms>:<hv_upper_time_ms>:<hv_bucket_count>
				uint32_t hv_lower_ms;
				if (!meow::number_from_string(&hv_lower_ms, hv_values_v[0]))
					return ff::fmt_err("can't parse hv_lower_ms from '{0}'", pct_s);

				uint32_t hv_upper_ms;
				if (!meow::number_from_string(&hv_upper_ms, hv_values_v[1]))
					return ff::fmt_err("can't parse hv_upper_ms from '{0}'", pct_s);

				uint32_t hv_bucket_count;
				if (!meow::number_from_string(&hv_bucket_count, hv_values_v[2]))
					return ff::fmt_err("can't parse hv_bucket_count from '{0}'", pct_s);

				// TODO: add separate validation step (and store raw values here, no division by hv_bucket_count!)

				if (hv_upper_ms <= hv_lower_ms)
					return ff::fmt_err("histogram_spec: hv_upper_ms must be >= hv_lower_ms, in '{0}'", pct_s);

				if (hv_bucket_count == 0)
					return ff::fmt_err("histogram_spec: hv_bucket_count must be >= 0, in '{0}'", pct_s);

				hv_present = true;

				vcf->hv_bucket_count = hv_bucket_count;
				vcf->hv_bucket_d     = (hv_upper_ms - hv_lower_ms) * d_millisecond / hv_bucket_count;
			}
			else if (meow::prefix_compare(pct_s, "percentiles=")) // 12 chars
			{
				auto const pct_spec      = meow::sub_str_ref(pct_s, 12, pct_s.size());
				auto const pct_values_v  = meow::split_ex(pct_spec, ":");

				for (auto const& percentile_s : pct_values_v)
				{
					uint32_t pv;
					if (!meow::number_from_string(&pv, percentile_s))
						return ff::fmt_err("bad percentile_spec: expected integers split by ':', got '{0}'", pct_spec);

					vcf->percentiles.push_back(pv);
				}
			}
			else if (meow::prefix_compare(pct_s, "p")) // p<number>
			{
				uint32_t pv;
				if (!meow::number_from_string(&pv, pct_s))
					return ff::fmt_err("bad percentile_spec: expected integers split by ':', got '{0}'", pct_s);

				vcf->percentiles.push_back(pv);
			}
			else
			{
				return ff::fmt_err("unexpected token '{0}'", pct_s);
			}
		}

		// TODO: should make histogram setup optional (i.e. have sensible defaults)
		if (!hv_present)
			return ff::fmt_err("hv=<time_lower>:<time_upper>:<n_buckets> must be present");

		// makes no sense to have histogram without percentiles specified
		bool const pct_present = (vcf->percentiles.size() > 0);
		if (!pct_present)
			return ff::fmt_err("percentiles=<integer>[:<integer>[...]] or p<numer>[,p<number>[...]] must be present");

		return {};
	}


	static pinba_error_t parse_filters(pinba_view_conf_t *vcf, str_ref filters_spec)
	{
		if (filters_spec == "no_filters")
			return {};<commit_msg>fixed old percentiles format (p<number>,p<number>) parsing<commit_after>#include "mysql_engine/pinba_mysql.h"
#include "mysql_engine/view_conf.h"

#include <meow/str_ref_algo.hpp>
#include <meow/convert/number_from_string.hpp>

#include "pinba/globals.h"
#include "pinba/report_by_request.h"
#include "pinba/report_by_timer.h"
#include "pinba/report_by_packet.h"

////////////////////////////////////////////////////////////////////////////////////////////////
namespace { namespace aux {
////////////////////////////////////////////////////////////////////////////////////////////////

	struct pinba_view_conf___internal_t : public pinba_view_conf_t
	{
		report_conf___by_packet_t   by_packet_conf;
		report_conf___by_request_t  by_request_conf;
		report_conf___by_timer_t    by_timer_conf;
	};

////////////////////////////////////////////////////////////////////////////////////////////////

	static pinba_error_t parse_aggregation_window(pinba_view_conf_t *vcf, str_ref aggregation_spec)
	{
		auto const time_window_v = meow::split_ex(aggregation_spec, ",");
		if (time_window_v.size() == 0)
			return ff::fmt_err("time_window must be set");

		auto const time_window_s = time_window_v[0];

		if (time_window_s == "default_history_time")
		{
			vcf->time_window = P_CTX_->settings.time_window;
			vcf->tick_count  = P_CTX_->settings.tick_count;
		}
		else
		{
			uint32_t time_window;
			if (!meow::number_from_string(&time_window, time_window_s))
				return ff::fmt_err("bad seconds_spec: '{0}', expected integer number of seconds", time_window_s);

			vcf->time_window = time_window * d_second;
			vcf->tick_count  = time_window; // i.e. ticks are always 1 second wide
		}

		return {};
	}


	static pinba_error_t parse_keys(pinba_view_conf_t *vcf, str_ref key_spec)
	{
		if (key_spec == "no_keys")
			return {};

		auto const keys_v = meow::split_ex(key_spec, ",");

		for (auto const& key_s : keys_v)
		{
			if (key_s.empty())
				continue;

			vcf->keys.push_back(key_s);
		}

		return {};
	}


	static pinba_error_t parse_histogram_and_percentiles(pinba_view_conf_t *vcf, str_ref histogram_spec)
	{
		if (histogram_spec == "no_percentiles")
			return {};

		bool hv_present = false;

		auto const pct_v = meow::split_ex(histogram_spec, ",");

		for (auto const& pct_s : pct_v)
		{
			if (meow::prefix_compare(pct_s, "hv=")) // 3 chars
			{
				auto const hv_range_s  = meow::sub_str_ref(pct_s, 3, pct_s.size());
				auto const hv_values_v = meow::split_ex(hv_range_s, ":");

				if (hv_values_v.size() != 3)
					return ff::fmt_err("3 integer parts expected");

				// LOG_DEBUG(P_L_, "hv_values_v = {{ {0}, {1}, {2} }", hv_values_v[0], hv_values_v[1], hv_values_v[2]);

				// hv=<hv_lower_time_ms>:<hv_upper_time_ms>:<hv_bucket_count>
				uint32_t hv_lower_ms;
				if (!meow::number_from_string(&hv_lower_ms, hv_values_v[0]))
					return ff::fmt_err("can't parse hv_lower_ms from '{0}'", pct_s);

				uint32_t hv_upper_ms;
				if (!meow::number_from_string(&hv_upper_ms, hv_values_v[1]))
					return ff::fmt_err("can't parse hv_upper_ms from '{0}'", pct_s);

				uint32_t hv_bucket_count;
				if (!meow::number_from_string(&hv_bucket_count, hv_values_v[2]))
					return ff::fmt_err("can't parse hv_bucket_count from '{0}'", pct_s);

				// TODO: add separate validation step (and store raw values here, no division by hv_bucket_count!)

				if (hv_upper_ms <= hv_lower_ms)
					return ff::fmt_err("histogram_spec: hv_upper_ms must be >= hv_lower_ms, in '{0}'", pct_s);

				if (hv_bucket_count == 0)
					return ff::fmt_err("histogram_spec: hv_bucket_count must be >= 0, in '{0}'", pct_s);

				hv_present = true;

				vcf->hv_bucket_count = hv_bucket_count;
				vcf->hv_bucket_d     = (hv_upper_ms - hv_lower_ms) * d_millisecond / hv_bucket_count;
			}
			else if (meow::prefix_compare(pct_s, "percentiles=")) // 12 chars
			{
				auto const pct_spec      = meow::sub_str_ref(pct_s, 12, pct_s.size());
				auto const pct_values_v  = meow::split_ex(pct_spec, ":");

				for (auto const& percentile_s : pct_values_v)
				{
					uint32_t pv;
					if (!meow::number_from_string(&pv, percentile_s))
						return ff::fmt_err("bad percentile_spec: expected integers split by ':', got '{0}'", pct_spec);

					vcf->percentiles.push_back(pv);
				}
			}
			else if (meow::prefix_compare(pct_s, "p")) // p<number>
			{
				auto const pct_spec = meow::sub_str_ref(pct_s, 1, pct_s.size());

				uint32_t pv;
				if (!meow::number_from_string(&pv, pct_spec))
					return ff::fmt_err("bad percentile_spec: expected 'p<integer>', got '{0}'", pct_s);

				vcf->percentiles.push_back(pv);
			}
			else
			{
				return ff::fmt_err("unexpected token '{0}'", pct_s);
			}
		}

		// TODO: should make histogram setup optional (i.e. have sensible defaults)
		if (!hv_present)
			return ff::fmt_err("hv=<time_lower>:<time_upper>:<n_buckets> must be present");

		// makes no sense to have histogram without percentiles specified
		bool const pct_present = (vcf->percentiles.size() > 0);
		if (!pct_present)
			return ff::fmt_err("percentiles=<integer>[:<integer>[...]] or p<numer>[,p<number>[...]] must be present");

		return {};
	}


	static pinba_error_t parse_filters(pinba_view_conf_t *vcf, str_ref filters_spec)
	{
		if (filters_spec == "no_filters")
			return {};
{'fraction_non_alphanumeric': 0.18816636250120064, 'fraction_numerical': 0.008548650465853425, 'mean_word_length': 3.3275145469659186, 'pattern_counts': {'":': 4, '<': 34, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4642403', 'n_tokens_mistral': 4469, 'n_tokens_neox': 3817, 'n_words': 735}