use serde_json::{Map, Value as JsonValue};
use crate::control::security::identity::AuthenticatedIdentity;
use crate::control::server::response_shape::types::{DdlColType, ShapedRows};
use crate::control::state::SharedState;
use crate::types::DatabaseId;
use super::super::result::{DdlError, DdlResult};
pub fn explain_tiers(
state: &SharedState,
identity: &AuthenticatedIdentity,
database_id: DatabaseId,
parts: &[&str],
) -> Result<Vec<DdlResult>, DdlError> {
if parts.len() < 4 || !parts[2].eq_ignore_ascii_case("ON") {
return Err(err(
"42601",
"syntax: EXPLAIN TIERS ON <collection> [RANGE <start_ms> <end_ms>]",
));
}
let collection = parts[3].to_lowercase();
let tenant_id = identity.tenant_id.as_u64();
let policy = state
.retention_policy_registry
.get_for_collection(database_id.as_u64(), tenant_id, &collection)
.ok_or_else(|| {
err(
"42704",
&format!("no retention policy found for '{collection}'"),
)
})?;
if !policy.auto_tier {
return Err(err(
"42809",
&format!("AUTO_TIER is not enabled on '{collection}'"),
));
}
let time_range = if parts.len() >= 7 && parts[4].eq_ignore_ascii_case("RANGE") {
let start = parts[5]
.parse::<i64>()
.map_err(|_| err("42601", &format!("invalid RANGE start: {}", parts[5])))?;
let end = parts[6]
.parse::<i64>()
.map_err(|_| err("42601", &format!("invalid RANGE end: {}", parts[6])))?;
(start, end)
} else {
(0i64, i64::MAX)
};
let explanation =
crate::control::planner::auto_tier::explain_tier_selection(&policy, time_range);
let columns = vec!["plan".to_string()];
let column_types = vec![DdlColType::Text];
let mut rows = Vec::new();
for line in explanation.lines() {
let mut row = Map::new();
row.insert("plan".to_string(), JsonValue::String(line.to_string()));
rows.push(row);
}
Ok(vec![DdlResult::Rows(ShapedRows {
columns,
column_types,
rows,
notice: None,
})])
}
fn err(sqlstate: &str, message: &str) -> DdlError {
DdlError {
sqlstate: sqlstate.to_string(),
message: message.to_string(),
}
}