use std::sync::Arc;
use anyhow::Result;
use reblessive::tree::Stk;
use crate::catalog::providers::TableProvider;
use crate::catalog::{DatabaseId, NamespaceId, Permission, Record, table_select_permission};
use crate::ctx::FrozenContext;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::exe::FlowResultExt as _;
use crate::expr::{Cond, Expr};
use crate::iam::Action;
use crate::idx::trees::KnnCondFilter;
use crate::idx::trees::gate::{BoxGateFut, CachedTableSelect, CandidateCondition, TableSelectGate};
use crate::legacy::expr_compute;
use crate::val::{RecordId, TableName};
fn candidate_doc(rid: &Arc<RecordId>, record: &Arc<Record>) -> CursorDoc {
CursorDoc {
rid: Some(Arc::clone(rid)),
ir: None,
doc: Arc::clone(record).into(),
fields_computed: false,
}
}
enum ResolvedTableSelect {
None,
Full,
Specific(Expr),
}
impl ResolvedTableSelect {
fn resolve(permission: &Permission) -> Self {
match permission {
Permission::None => Self::None,
Permission::Full => Self::Full,
Permission::Specific(expr) => Self::Specific(expr.clone()),
}
}
}
pub(crate) struct LegacyTableSelect<'a> {
ctx: &'a FrozenContext,
opt: &'a Options,
permission: ResolvedTableSelect,
}
impl TableSelectGate for LegacyTableSelect<'_> {
fn allows_every_doc(&self) -> Option<bool> {
match self.permission {
ResolvedTableSelect::None => Some(false),
ResolvedTableSelect::Full => Some(true),
ResolvedTableSelect::Specific(_) => None,
}
}
fn allows_doc<'a>(
&'a self,
stk: &'a mut Stk,
rid: &'a Arc<RecordId>,
record: &'a Arc<Record>,
) -> BoxGateFut<'a> {
Box::pin(async move {
match &self.permission {
ResolvedTableSelect::None => Ok(false),
ResolvedTableSelect::Full => Ok(true),
ResolvedTableSelect::Specific(e) => {
let cursor_doc = candidate_doc(rid, record);
let opt_no_perms = self.opt.new_for_permission_predicate();
Ok(stk
.run(|stk| expr_compute(e, stk, self.ctx, &opt_no_perms, Some(&cursor_doc)))
.await
.catch_return()?
.is_truthy())
}
}
})
}
}
pub(crate) struct LegacyCondition<'a> {
ctx: &'a FrozenContext,
opt: &'a Options,
cond: Arc<Cond>,
}
impl<'a> LegacyCondition<'a> {
pub(crate) fn new(ctx: &'a FrozenContext, opt: &'a Options, cond: Arc<Cond>) -> Self {
Self {
ctx,
opt,
cond,
}
}
}
impl CandidateCondition for LegacyCondition<'_> {
fn matches<'a>(
&'a self,
stk: &'a mut Stk,
rid: &'a Arc<RecordId>,
record: &'a Arc<Record>,
) -> BoxGateFut<'a> {
Box::pin(async move {
let cursor_doc = candidate_doc(rid, record);
Ok(stk
.run(|stk| expr_compute(&self.cond.0, stk, self.ctx, self.opt, Some(&cursor_doc)))
.await
.catch_return()?
.is_truthy())
})
}
}
pub(crate) struct PhysicalCondition {
phys: Arc<dyn crate::exec::PhysicalExpr>,
exec_ctx: crate::exec::ExecutionContext,
}
impl PhysicalCondition {
pub(crate) fn new(
phys: Arc<dyn crate::exec::PhysicalExpr>,
exec_ctx: crate::exec::ExecutionContext,
) -> Self {
Self {
phys,
exec_ctx,
}
}
}
impl CandidateCondition for PhysicalCondition {
fn matches<'a>(
&'a self,
_stk: &'a mut Stk,
_rid: &'a std::sync::Arc<RecordId>,
record: &'a std::sync::Arc<Record>,
) -> BoxGateFut<'a> {
Box::pin(async move {
let value = record.data.clone();
let eval_ctx =
crate::exec::EvalContext::from_exec_ctx(&self.exec_ctx).with_value_and_doc(&value);
Ok(self.phys.evaluate(eval_ctx).await.catch_return()?.is_truthy())
})
}
}
pub(crate) async fn knn_cond_filter<'a>(
ctx: &'a FrozenContext,
opt: &'a Options,
ns: NamespaceId,
db: DatabaseId,
tb: &TableName,
cond: Option<Arc<Cond>>,
) -> Result<Option<KnnCondFilter<'a>>> {
let Some(cond) = cond else {
return Ok(None);
};
let table = ctx.tx().get_tb(ns, db, tb, None).await?;
let select_gate = if ctx.check_perms(opt, Action::View)? {
CachedTableSelect::Gate(Arc::new(LegacyTableSelect {
ctx,
opt,
permission: ResolvedTableSelect::resolve(table_select_permission(table.as_deref())),
}))
} else {
CachedTableSelect::Skip
};
Ok(Some(KnnCondFilter {
select_gate,
cond: Some(Arc::new(LegacyCondition::new(ctx, opt, cond))),
metrics: None,
version: None,
}))
}