use std::cell::Cell;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::hash::Hash;
use std::sync::Arc;
use itertools::Itertools;
use vortex_error::VortexExpect;
use vortex_error::VortexResult;
use vortex_error::vortex_ensure;
use crate::dtype::DType;
use crate::expr::display::DisplayTreeExpr;
use crate::expr::traversal::TraversalOrder;
use crate::expr::traversal::pre_order_visit_down;
use crate::scalar_fn::ScalarFnRef;
use crate::scalar_fn::ScalarFnVTable;
const NO_CHILDREN: &[Expression] = &[];
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Expression {
Scalar {
scalar_fn: ScalarFnRef,
children: Arc<Vec<Expression>>,
},
Root,
}
impl Expression {
pub fn try_new(
scalar_fn: ScalarFnRef,
children: impl IntoIterator<Item = Expression>,
) -> VortexResult<Self> {
let children = Vec::from_iter(children);
vortex_ensure!(
scalar_fn.signature().arity().matches(children.len()),
"Expression arity mismatch: expected {} children but got {}",
scalar_fn.signature().arity(),
children.len()
);
Ok(Self::Scalar {
scalar_fn,
children: children.into(),
})
}
pub fn is_root(&self) -> bool {
matches!(self, Self::Root)
}
pub fn as_scalar(&self) -> Option<&ScalarFnRef> {
match self {
Self::Scalar { scalar_fn, .. } => Some(scalar_fn),
Self::Root => None,
}
}
pub fn is<V: ScalarFnVTable>(&self) -> bool {
self.as_scalar().is_some_and(|sf| sf.is::<V>())
}
pub fn as_opt<V: ScalarFnVTable>(&self) -> Option<&V::Options> {
self.as_scalar().and_then(|sf| sf.as_opt::<V>())
}
pub fn as_<V: ScalarFnVTable>(&self) -> &V::Options {
self.as_opt::<V>()
.vortex_expect("Expression options type mismatch")
}
pub fn children(&self) -> &[Expression] {
match self {
Self::Scalar { children, .. } => children.as_slice(),
Self::Root => NO_CHILDREN,
}
}
pub fn child(&self, n: usize) -> &Expression {
&self.children()[n]
}
pub fn with_children(
self,
children: impl IntoIterator<Item = Expression>,
) -> VortexResult<Self> {
let children = Vec::from_iter(children);
match &self {
Self::Root => {
vortex_ensure!(
children.is_empty(),
"Expression arity mismatch: root expects 0 children but got {}",
children.len()
);
Ok(Self::Root)
}
Self::Scalar { scalar_fn, .. } => {
vortex_ensure!(
scalar_fn.signature().arity().matches(children.len()),
"Expression arity mismatch: expected {} children but got {}",
scalar_fn.signature().arity(),
children.len()
);
Ok(Self::Scalar {
scalar_fn: scalar_fn.clone(),
children: children.into(),
})
}
}
}
pub fn return_dtype(&self, scope: &DType) -> VortexResult<DType> {
match self {
Self::Root => Ok(scope.clone()),
Self::Scalar {
scalar_fn,
children,
} => {
let dtypes: Vec<_> = children
.iter()
.map(|c| c.return_dtype(scope))
.try_collect()?;
scalar_fn.return_dtype(&dtypes)
}
}
}
pub fn validity(&self) -> VortexResult<Expression> {
match self {
Self::Root => Ok(Self::Root),
Self::Scalar { scalar_fn, .. } => scalar_fn.validity(self),
}
}
pub fn fmt_sql(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Root => write!(f, "$"),
Self::Scalar { scalar_fn, .. } => scalar_fn.fmt_sql(self, f),
}
}
pub fn display_tree(&self) -> impl Display {
DisplayTreeExpr(self)
}
pub fn contains<E: ScalarFnVTable>(&self) -> VortexResult<bool> {
let mut contains = false;
pre_order_visit_down(self, |node| {
if node.is::<E>() {
contains = true;
return Ok(TraversalOrder::Stop);
}
Ok(TraversalOrder::Continue)
})?;
Ok(contains)
}
}
impl Display for Expression {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_sql(f)
}
}
const MAX_DROP_DEPTH: u32 = 32;
thread_local! {
static DROP_DEPTH: Cell<u32> = const { Cell::new(0) };
}
struct DropDepthGuard;
impl DropDepthGuard {
fn enter() -> Option<Self> {
DROP_DEPTH.with(|depth| {
let current = depth.get();
(current < MAX_DROP_DEPTH).then(|| {
depth.set(current + 1);
Self
})
})
}
}
impl Drop for DropDepthGuard {
fn drop(&mut self) {
DROP_DEPTH.with(|depth| depth.set(depth.get() - 1));
}
}
impl Drop for Expression {
fn drop(&mut self) {
let Self::Scalar { children, .. } = self else {
return;
};
let Some(children) = Arc::get_mut(children) else {
return;
};
if children.is_empty() {
return;
}
let mut children_to_drop = std::mem::take(children);
match DropDepthGuard::enter() {
Some(_guard) => drop(children_to_drop),
None => {
while let Some(mut child) = children_to_drop.pop() {
if let Self::Scalar { children, .. } = &mut child
&& let Some(expr_children) = Arc::get_mut(children)
{
children_to_drop.append(expr_children);
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use std::thread;
use super::*;
use crate::expr::lit;
use crate::expr::not;
fn deep_expression(depth: usize) -> Expression {
let mut expr = lit(true);
for _ in 0..depth {
expr = not(expr);
}
expr
}
#[test]
fn deep_expression_drops_within_a_small_stack() -> VortexResult<()> {
const DEPTH: usize = 100_000;
const STACK_SIZE: usize = 256 * 1024;
let dropper = thread::Builder::new()
.stack_size(STACK_SIZE)
.spawn(|| drop(deep_expression(DEPTH)))?;
assert!(
dropper.join().is_ok(),
"dropping a tree of depth {DEPTH} exhausted a {STACK_SIZE} byte stack"
);
Ok(())
}
#[test]
fn shallow_expression_keeps_shared_children() {
let expr = not(lit(true));
let shared = expr.clone();
drop(expr);
assert_eq!(shared.children().len(), 1);
}
}