diff --git a/src/metric-engine/src/engine/read.rs b/src/metric-engine/src/engine/read.rs index 0537d17c73..d714ed4a23 100644 --- a/src/metric-engine/src/engine/read.rs +++ b/src/metric-engine/src/engine/read.rs @@ -25,7 +25,8 @@ use store_api::storage::{RegionId, ScanRequest, SequenceNumber}; use crate::engine::MetricEngineInner; use crate::error::{ - InvalidMetadataSnafu, LogicalRegionNotFoundSnafu, MitoReadOperationSnafu, Result, + InvalidMetadataSnafu, InvalidRequestSnafu, LogicalRegionNotFoundSnafu, MitoReadOperationSnafu, + Result, }; use crate::metrics::MITO_OPERATION_ELAPSED; use crate::utils; @@ -192,8 +193,16 @@ impl MetricEngineInner { .await?; let projected_logical_names = origin_projection .iter() - .map(|i| all_logical_columns[*i].clone()) - .collect::>(); + .map(|&index| { + all_logical_columns + .get(index) + .map(String::as_str) + .with_context(|| InvalidRequestSnafu { + region_id: logical_region_id, + reason: format!("projection index {index} is out of bounds"), + }) + }) + .collect::>>()?; // generate physical projection let mut physical_projection = Vec::with_capacity(origin_projection.len()); @@ -206,7 +215,7 @@ impl MetricEngineInner { for name in projected_logical_names { // Safety: logical columns is a strict subset of physical columns - physical_projection.push(physical_metadata.column_index_by_name(&name).unwrap()); + physical_projection.push(physical_metadata.column_index_by_name(name).unwrap()); } Ok(physical_projection) @@ -302,6 +311,8 @@ impl MetricEngineInner { #[cfg(test)] mod test { + use common_error::ext::ErrorExt; + use common_error::status_code::StatusCode; use store_api::region_request::RegionRequest; use super::*; @@ -309,6 +320,33 @@ mod test { TestEnv, alter_logical_region_add_tag_columns, create_logical_region_request, }; + #[tokio::test] + async fn test_invalid_logical_projection() { + let env = TestEnv::new().await; + env.init_metric_region().await; + + let logical_region_id = env.default_logical_region_id(); + let invalid_index = usize::MAX; + let request = ScanRequest { + projection_input: Some(vec![invalid_index].into()), + ..Default::default() + }; + + let error = + match RegionEngine::handle_query(&env.metric(), logical_region_id, request).await { + Ok(_) => panic!("invalid logical projection unexpectedly succeeded"), + Err(error) => error, + }; + + assert_eq!(error.status_code(), StatusCode::InvalidArguments); + assert!( + error.to_string().contains(&format!( + "projection index {invalid_index} is out of bounds" + )), + "unexpected error: {error}" + ); + } + #[tokio::test] async fn test_transform_scan_req() { let env = TestEnv::new().await;