diff --git a/oximeter/db/src/lib.rs b/oximeter/db/src/lib.rs index f632a531af..24f7d8c2d0 100644 --- a/oximeter/db/src/lib.rs +++ b/oximeter/db/src/lib.rs @@ -281,31 +281,8 @@ mod tests { use super::*; use crate::model::DbFieldList; use crate::model::DbTimeseriesSchema; - use std::convert::TryFrom; use uuid::Uuid; - #[test] - fn test_timeseries_name() { - let name = TimeseriesName::try_from("foo:bar").unwrap(); - assert_eq!(format!("{}", name), "foo:bar"); - } - - #[test] - fn test_timeseries_name_from_str() { - assert!(TimeseriesName::try_from("a:b").is_ok()); - assert!(TimeseriesName::try_from("a_a:b_b").is_ok()); - assert!(TimeseriesName::try_from("a0:b0").is_ok()); - assert!(TimeseriesName::try_from("a_0:b_0").is_ok()); - - assert!(TimeseriesName::try_from("_:b").is_err()); - assert!(TimeseriesName::try_from("a_:b").is_err()); - assert!(TimeseriesName::try_from("0:b").is_err()); - assert!(TimeseriesName::try_from(":b").is_err()); - assert!(TimeseriesName::try_from("a:").is_err()); - assert!(TimeseriesName::try_from("123").is_err()); - assert!(TimeseriesName::try_from("no:no:no").is_err()); - } - // Validates that the timeseries_key stability for a sample is stable. #[test] fn test_timeseries_key_sample_stability() { diff --git a/oximeter/db/src/sql/mod.rs b/oximeter/db/src/sql/mod.rs index 9aea3bae55..1f84e208d2 100644 --- a/oximeter/db/src/sql/mod.rs +++ b/oximeter/db/src/sql/mod.rs @@ -756,6 +756,23 @@ impl RestrictedQuery { } } + // Verify that the identifier is a single, concrete timeseries name. + fn extract_timeseries_name( + from: &[Ident], + ) -> Result, OxdbError> { + if from.len() != 1 { + return unsupported!( + "Query must select from single named \ + timeseries, with no database" + ); + } + from[0] + .value + .parse() + .map(|n| indexmap::indexset! { n }) + .map_err(|_| MetricsError::InvalidTimeseriesName.into()) + } + // Process a single "table factor", the in `FROM ` to // extract the names of the timeseries it refers to. // @@ -770,17 +787,7 @@ impl RestrictedQuery { "Table functions and hints are not supported" ); } - if name.0.len() != 1 { - return unsupported!( - "Query must select from single named \ - table, with no database" - ); - } - let timeseries_name = name.0[0] - .value - .parse() - .map(|n| indexmap::indexset! { n }) - .map_err(|_| MetricsError::InvalidTimeseriesName)?; + let timeseries_name = Self::extract_timeseries_name(&name.0)?; // Rewrite the quote style to be backticks, so that the // resulting actual query translates into a valid identifier for // ClickHouse, naming the CTE's well generate later. diff --git a/oximeter/oximeter/src/schema.rs b/oximeter/oximeter/src/schema.rs index b6953fda52..2a577fc8f1 100644 --- a/oximeter/oximeter/src/schema.rs +++ b/oximeter/oximeter/src/schema.rs @@ -261,7 +261,7 @@ impl PartialEq for TimeseriesSchema { // // That describes the target/metric name, and the timeseries is two of those, joined with ':'. const TIMESERIES_NAME_REGEX: &str = - "(([a-z]+[a-z0-9]*)(_([a-z0-9]+))*):(([a-z]+[a-z0-9]*)(_([a-z0-9]+))*)"; + "^(([a-z]+[a-z0-9]*)(_([a-z0-9]+))*):(([a-z]+[a-z0-9]*)(_([a-z0-9]+))*)$"; /// A set of timeseries schema, useful for testing changes to targets or /// metrics. @@ -502,6 +502,7 @@ mod tests { assert!(TimeseriesName::try_from(":b").is_err()); assert!(TimeseriesName::try_from("a:").is_err()); assert!(TimeseriesName::try_from("123").is_err()); + assert!(TimeseriesName::try_from("x.a:b").is_err()); } #[derive(Target)]