use std::collections::{BTreeMap, BTreeSet};
use std::mem::ManuallyDrop;
use std::sync::Arc;
use lora_compiler::physical::{PhysicalNodeId, PhysicalPlan};
use lora_compiler::CompiledQuery;
use lora_store::{GraphStorage, GraphStorageMut};
use crate::errors::{ExecResult, ExecutorError};
use crate::executor::{GroupValueKey, MutableExecutionContext, MutableExecutor};
use crate::value::{LoraValue, Row};
use super::traits::write_op_input;
use super::{build_streaming, subtree_is_fully_streaming, BufferedRowSource, RowSource};
pub struct MutablePullExecutor<'a, S: GraphStorageMut> {
storage: &'a mut S,
params: BTreeMap<String, LoraValue>,
}
impl<'a, S: GraphStorageMut + GraphStorage> MutablePullExecutor<'a, S> {
pub fn new(storage: &'a mut S, params: BTreeMap<String, LoraValue>) -> Self {
Self { storage, params }
}
pub fn open_compiled(self, compiled: &'a CompiledQuery) -> ExecResult<Box<dyn RowSource + 'a>>
where
S: 'a,
{
if compiled.unions.is_empty() {
return open_mutable_plan_cursor(self.storage, &compiled.physical, self.params);
}
MutableUnionSource::open(self.storage, compiled, self.params)
.map(|source| Box::new(source) as Box<dyn RowSource + 'a>)
}
}
fn open_mutable_plan_cursor<'a, S: GraphStorageMut + GraphStorage + 'a>(
storage: &'a mut S,
plan: &'a PhysicalPlan,
params: BTreeMap<String, LoraValue>,
) -> ExecResult<Box<dyn RowSource + 'a>> {
if let Some(input) = write_op_input(plan, plan.root) {
if subtree_is_fully_streaming(plan, input) {
let cursor = StreamingWriteCursor::open(storage, plan, plan.root, params)?;
let cursor: Box<dyn RowSource + 'a> = Box::new(cursor);
if crate::executor::plan_ends_in_write(plan) {
return Ok(Box::new(DrainSilently {
inner: Some(cursor),
}));
}
return Ok(cursor);
}
}
let mut executor = MutableExecutor::new(MutableExecutionContext { storage, params });
let rows = executor.execute_rows(plan)?;
Ok(Box::new(BufferedRowSource::new(rows)))
}
#[derive(Clone, Copy)]
struct StoragePtr<S> {
ptr: *mut S,
}
impl<S> StoragePtr<S> {
fn from_mut(storage: &mut S) -> Self {
Self {
ptr: storage as *mut S,
}
}
unsafe fn as_ref<'a>(&self) -> &'a S {
unsafe { &*self.ptr }
}
unsafe fn as_mut<'a>(&self) -> &'a mut S {
unsafe { &mut *self.ptr }
}
}
pub struct MutableUnionSource<'a, S: GraphStorageMut + GraphStorage + 'a> {
storage_ptr: StoragePtr<S>,
compiled: &'a CompiledQuery,
params: BTreeMap<String, LoraValue>,
branch_idx: usize,
current: Option<Box<dyn RowSource + 'a>>,
needs_dedup: bool,
seen: BTreeSet<Vec<(String, GroupValueKey)>>,
_phantom: std::marker::PhantomData<&'a mut S>,
}
impl<'a, S: GraphStorageMut + GraphStorage + 'a> MutableUnionSource<'a, S> {
fn open(
storage: &'a mut S,
compiled: &'a CompiledQuery,
params: BTreeMap<String, LoraValue>,
) -> ExecResult<Self> {
let needs_dedup = compiled.unions.iter().any(|branch| !branch.all);
Ok(Self {
storage_ptr: StoragePtr::from_mut(storage),
compiled,
params,
branch_idx: 0,
current: None,
needs_dedup,
seen: BTreeSet::new(),
_phantom: std::marker::PhantomData,
})
}
fn branch_count(&self) -> usize {
self.compiled.unions.len() + 1
}
fn branch_plan(&self, idx: usize) -> &'a PhysicalPlan {
if idx == 0 {
&self.compiled.physical
} else {
&self.compiled.unions[idx - 1].physical
}
}
fn open_branch(&mut self, idx: usize) -> ExecResult<Box<dyn RowSource + 'a>> {
let plan = self.branch_plan(idx);
let storage = unsafe { self.storage_ptr.as_mut() };
open_mutable_plan_cursor(storage, plan, self.params.clone())
}
}
impl<'a, S: GraphStorageMut + GraphStorage + 'a> RowSource for MutableUnionSource<'a, S> {
fn next_row(&mut self) -> ExecResult<Option<Row>> {
loop {
if self.branch_idx >= self.branch_count() {
return Ok(None);
}
if self.current.is_none() {
self.current = Some(self.open_branch(self.branch_idx)?);
}
let Some(current) = self.current.as_mut() else {
return Err(ExecutorError::RuntimeError(
"mutable UNION cursor lost its current branch".into(),
));
};
match current.next_row()? {
Some(row) => {
if self.needs_dedup {
let key = row
.iter_named()
.map(|(_, name, val)| {
(name.into_owned(), GroupValueKey::from_value(val))
})
.collect();
if !self.seen.insert(key) {
continue;
}
}
return Ok(Some(row));
}
None => {
self.current.take();
self.branch_idx += 1;
}
}
}
}
}
pub struct StreamingWriteCursor<'a, S: GraphStorageMut + GraphStorage + 'a> {
upstream: ManuallyDrop<Box<dyn RowSource + 'a>>,
storage_ptr: StoragePtr<S>,
plan: &'a PhysicalPlan,
write_op_node: PhysicalNodeId,
params: BTreeMap<String, LoraValue>,
_phantom: std::marker::PhantomData<&'a mut S>,
}
impl<'a, S: GraphStorageMut + GraphStorage + 'a> StreamingWriteCursor<'a, S> {
pub(crate) fn open(
storage: &'a mut S,
plan: &'a PhysicalPlan,
write_op_node: PhysicalNodeId,
params: BTreeMap<String, LoraValue>,
) -> ExecResult<Self> {
let input = match write_op_input(plan, write_op_node) {
Some(i) => i,
None => {
return Err(ExecutorError::RuntimeError(format!(
"StreamingWriteCursor::open called with non-write node {write_op_node:?}"
)));
}
};
let storage_ptr = StoragePtr::from_mut(storage);
let storage_ref: &'a S = unsafe { storage_ptr.as_ref() };
let upstream = build_streaming(plan, input, storage_ref, Arc::new(params.clone()))?;
Ok(Self {
upstream: ManuallyDrop::new(upstream),
storage_ptr,
plan,
write_op_node,
params,
_phantom: std::marker::PhantomData,
})
}
}
impl<'a, S: GraphStorageMut + GraphStorage + 'a> RowSource for StreamingWriteCursor<'a, S> {
fn next_row(&mut self) -> ExecResult<Option<Row>> {
let mut row = match self.upstream.next_row()? {
Some(r) => r,
None => return Ok(None),
};
let storage_mut: &mut S = unsafe { self.storage_ptr.as_mut() };
let mut exec = MutableExecutor::new(MutableExecutionContext {
storage: storage_mut,
params: self.params.clone(),
});
exec.defer_existence_checks(crate::executor::plan_defers_existence(self.plan));
let op = &self.plan.nodes[self.write_op_node];
exec.apply_write_op(op, &mut row)?;
exec.check_pending_existence()?;
let row = exec.hydrate_row(row);
Ok(Some(row))
}
}
impl<'a, S: GraphStorageMut + GraphStorage + 'a> Drop for StreamingWriteCursor<'a, S> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.upstream);
}
}
}
struct DrainSilently<'a> {
inner: Option<Box<dyn RowSource + 'a>>,
}
impl RowSource for DrainSilently<'_> {
fn next_row(&mut self) -> ExecResult<Option<Row>> {
if let Some(mut inner) = self.inner.take() {
while inner.next_row()?.is_some() {}
}
Ok(None)
}
}