use std::ops::Bound;
use std::sync::Arc;
use super::common::evaluate_bound_key;
use crate::catalog::{DatabaseId, NamespaceId};
use crate::exec::{ControlFlowExt, ExecutionContext, PhysicalExpr};
use crate::expr::{ControlFlow, Dir};
pub(crate) use crate::key::graph::DecodedGraph;
use crate::kvs::KVKey;
use crate::val::{RecordId, TableName};
#[derive(Debug, Clone)]
pub struct EdgeTableSpec {
pub table: TableName,
pub range_start: Bound<Arc<dyn PhysicalExpr>>,
pub range_end: Bound<Arc<dyn PhysicalExpr>>,
}
pub(crate) async fn compute_graph_ranges(
ns_id: NamespaceId,
db_id: DatabaseId,
rid: &RecordId,
dir: Dir,
edge_tables: &[EdgeTableSpec],
ctx: &ExecutionContext,
) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ControlFlow> {
if edge_tables.is_empty() {
let beg = crate::key::graph::egprefix(ns_id, db_id, &rid.table, &rid.key, dir)
.context("Failed to create graph prefix")?;
let end = crate::key::graph::egsuffix(ns_id, db_id, &rid.table, &rid.key, dir)
.context("Failed to create graph suffix")?;
Ok(vec![(beg, end)])
} else {
let mut ranges = Vec::with_capacity(edge_tables.len());
for spec in edge_tables {
let beg =
eval_graph_bound(ns_id, db_id, rid, dir, &spec.table, &spec.range_start, true, ctx)
.await?;
let end =
eval_graph_bound(ns_id, db_id, rid, dir, &spec.table, &spec.range_end, false, ctx)
.await?;
ranges.push((beg, end));
}
Ok(ranges)
}
}
#[allow(clippy::too_many_arguments)]
async fn eval_graph_bound(
ns_id: NamespaceId,
db_id: DatabaseId,
rid: &RecordId,
dir: Dir,
edge_table: &TableName,
bound: &Bound<Arc<dyn PhysicalExpr>>,
is_start: bool,
ctx: &ExecutionContext,
) -> Result<Vec<u8>, ControlFlow> {
match bound {
Bound::Unbounded => {
if is_start {
crate::key::graph::ftprefix(ns_id, db_id, &rid.table, &rid.key, dir, edge_table)
.context("Failed to create graph table prefix")
} else {
crate::key::graph::ftsuffix(ns_id, db_id, &rid.table, &rid.key, dir, edge_table)
.context("Failed to create graph table suffix")
}
}
Bound::Included(expr) => {
let fk = evaluate_bound_key(expr, ctx).await?;
let mut key = encode_graph_key(ns_id, db_id, rid, dir, edge_table, fk)?;
if !is_start {
key.push(0xff);
}
Ok(key)
}
Bound::Excluded(expr) => {
let fk = evaluate_bound_key(expr, ctx).await?;
let mut key = encode_graph_key(ns_id, db_id, rid, dir, edge_table, fk)?;
if is_start {
key.push(0xff);
}
Ok(key)
}
}
}
fn encode_graph_key(
ns_id: NamespaceId,
db_id: DatabaseId,
rid: &RecordId,
dir: Dir,
edge_table: &TableName,
fk: crate::val::RecordIdKey,
) -> Result<Vec<u8>, ControlFlow> {
crate::key::graph::new(
ns_id,
db_id,
&rid.table,
&rid.key,
dir,
&RecordId {
table: edge_table.clone(),
key: fk,
},
)
.encode_key()
.context("Failed to encode graph range key")
}
pub(crate) fn decode_graph_edge(key: &[u8]) -> Result<DecodedGraph, ControlFlow> {
crate::key::graph::Graph::decode_key(key).context("Failed to decode graph key")
}