use std::sync::Arc;
use futures::stream;
use surrealdb_types::ToSql;
use crate::catalog::providers::TableProvider;
use crate::exec::context::{ContextLevel, ExecutionContext};
use crate::exec::physical_expr::{EvalContext, PhysicalExpr};
use crate::exec::{
AccessMode, CardinalityHint, ExecOperator, FlowResult, OperatorMetrics, ValueBatch,
ValueBatchStream,
};
use crate::iam::{Action, ResourceKind};
use crate::kvs::index::index_building_info;
use crate::val::{TableName, Value};
#[derive(Debug)]
pub struct IndexInfoPlan {
pub index: Arc<dyn PhysicalExpr>,
pub table: Arc<dyn PhysicalExpr>,
pub structured: bool,
pub(crate) metrics: Arc<OperatorMetrics>,
}
impl IndexInfoPlan {
pub(crate) fn new(
index: Arc<dyn PhysicalExpr>,
table: Arc<dyn PhysicalExpr>,
structured: bool,
) -> Self {
Self {
index,
table,
structured,
metrics: Arc::new(OperatorMetrics::new()),
}
}
}
impl ExecOperator for IndexInfoPlan {
fn name(&self) -> &'static str {
"InfoIndex"
}
fn attrs(&self) -> Vec<(String, String)> {
vec![
("index".to_string(), self.index.to_sql()),
("table".to_string(), self.table.to_sql()),
("structured".to_string(), self.structured.to_string()),
]
}
fn required_context(&self) -> ContextLevel {
self.index.required_context().max(self.table.required_context()).max(ContextLevel::Database)
}
fn access_mode(&self) -> AccessMode {
self.index.access_mode().combine(self.table.access_mode())
}
fn cardinality_hint(&self) -> CardinalityHint {
CardinalityHint::AtMostOne
}
fn metrics(&self) -> Option<&OperatorMetrics> {
Some(self.metrics.as_ref())
}
fn expressions(&self) -> Vec<(&str, &Arc<dyn PhysicalExpr>)> {
vec![("index", &self.index), ("table", &self.table)]
}
fn execute(&self, ctx: &ExecutionContext) -> FlowResult<ValueBatchStream> {
let index = Arc::clone(&self.index);
let table = Arc::clone(&self.table);
let ctx = ctx.clone();
Ok(Box::pin(stream::once(async move {
let value = execute_index_info(&ctx, &*index, &*table).await?;
Ok(ValueBatch {
values: vec![value],
})
})))
}
fn is_scalar(&self) -> bool {
true
}
}
async fn execute_index_info(
ctx: &ExecutionContext,
index_expr: &dyn PhysicalExpr,
table_expr: &dyn PhysicalExpr,
) -> crate::expr::FlowResult<Value> {
let root = ctx.root();
let opt = root
.options
.as_ref()
.ok_or_else(|| anyhow::anyhow!("Options not available in execution context"))?;
ctx.is_allowed(Action::View, ResourceKind::Actor, crate::expr::Base::Db)?;
let eval_ctx = EvalContext::from_exec_ctx(ctx);
let index_value = index_expr.evaluate(eval_ctx.clone()).await?;
let table_value = table_expr.evaluate(eval_ctx).await?;
let index = index_value.coerce_to::<String>().map_err(|e| anyhow::anyhow!("{e}"))?;
let table =
TableName::new(table_value.coerce_to::<String>().map_err(|e| anyhow::anyhow!("{e}"))?);
let frozen_ctx = ctx.ctx();
let (ns, db) = frozen_ctx.expect_ns_db_ids(opt).await?;
let txn = ctx.txn();
let ix = txn.expect_tb_index(ns, db, &table, &index).await?;
Ok(index_building_info(&txn, ns, db, &ix).await?)
}