#[cfg(feature = "parallel")]
use rayon::prelude::*;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use std::cmp::Ordering;
use radixdb_core::row_vec::RowVec;
use radixdb_core::value::NULL_VALUE;
use radixdb_core::{CompactVec, StringMap};
use radixdb_core::{Error, Result, Row, Value};
type PartitionKey = SmallVec<[Value; 4]>;
use radixdb_functions::{FunctionRegistry, WindowFunction};
use radixdb_sql::ast::*;
use radixdb_storage::traits::{QueryResult, Table};
use super::context::ExecutionContext;
use super::expression::{ExpressionEval, MultiExpressionEval};
use super::result::{ColumnarResult, ExecutorResult};
use super::utils::build_column_index_map;
mod aggregate;
mod execute;
mod partition;
mod planning;
#[cfg(test)]
mod tests;
pub trait WindowHost: Sync {
fn window_function_registry(&self) -> &FunctionRegistry;
}
pub struct WindowExecutor<'a, H: WindowHost + ?Sized> {
host: &'a H,
}
impl<'a, H: WindowHost + ?Sized> WindowExecutor<'a, H> {
fn new(host: &'a H) -> Self {
Self { host }
}
}
pub trait WindowExecutorExt: WindowHost {
fn execute_select_with_window_functions(
&self,
stmt: &SelectStatement,
ctx: &ExecutionContext,
base_rows: &[(i64, Row)],
base_columns: &[String],
) -> Result<Box<dyn QueryResult>> {
WindowExecutor::new(self).execute_select_with_window_functions(
stmt,
ctx,
base_rows,
base_columns,
)
}
fn execute_select_with_window_functions_presorted(
&self,
stmt: &SelectStatement,
ctx: &ExecutionContext,
base_rows: &[(i64, Row)],
base_columns: &[String],
pre_sorted: Option<WindowPreSortedState>,
) -> Result<Box<dyn QueryResult>> {
WindowExecutor::new(self).execute_select_with_window_functions_presorted(
stmt,
ctx,
base_rows,
base_columns,
pre_sorted,
)
}
fn execute_select_with_window_functions_pregrouped(
&self,
stmt: &SelectStatement,
ctx: &ExecutionContext,
base_rows: &[(i64, Row)],
base_columns: &[String],
pre_grouped: WindowPreGroupedState,
) -> Result<Box<dyn QueryResult>> {
WindowExecutor::new(self).execute_select_with_window_functions_pregrouped(
stmt,
ctx,
base_rows,
base_columns,
pre_grouped,
)
}
fn execute_select_with_window_functions_lazy_partition(
&self,
stmt: &SelectStatement,
ctx: &ExecutionContext,
table: &dyn Table,
base_columns: &[String],
partition_col: &str,
limit: usize,
) -> Result<Box<dyn QueryResult>> {
WindowExecutor::new(self).execute_select_with_window_functions_lazy_partition(
stmt,
ctx,
table,
base_columns,
partition_col,
limit,
)
}
}
impl<T: WindowHost + ?Sized> WindowExecutorExt for T {}
#[derive(Clone, Debug)]
pub struct WindowFunctionInfo {
pub name: String,
pub arguments: Vec<Expression>,
pub partition_by: Vec<String>,
pub partition_by_exprs: Vec<Expression>,
pub order_by: Vec<OrderByExpression>,
pub frame: Option<WindowFrame>,
pub column_name: String,
pub is_distinct: bool,
}
pub struct SelectItem {
pub output_name: String,
pub source: SelectItemSource,
}
#[allow(clippy::large_enum_variant)]
pub enum SelectItemSource {
BaseColumn(usize),
WindowFunction(String),
Expression(Expression),
ExpressionWithWindow(Expression, Vec<String>),
}
#[derive(Clone, Debug)]
pub struct WindowPreSortedState {
pub column: String,
pub ascending: bool,
}
#[derive(Clone, Debug)]
pub struct ColumnarOrderByValues {
columns: Vec<Vec<Value>>,
ascending: Vec<bool>,
nulls_first: Vec<bool>,
num_rows: usize,
}
impl ColumnarOrderByValues {
#[inline]
pub fn is_empty(&self) -> bool {
self.num_rows == 0 || self.columns.is_empty()
}
#[inline]
pub fn num_columns(&self) -> usize {
self.columns.len()
}
#[inline]
pub fn get(&self, row_idx: usize, col_idx: usize) -> Option<&Value> {
self.columns.get(col_idx).and_then(|col| col.get(row_idx))
}
#[inline]
pub fn get_first(&self, row_idx: usize) -> Option<&Value> {
self.get(row_idx, 0)
}
#[inline]
pub fn is_ascending(&self, col_idx: usize) -> bool {
self.ascending.get(col_idx).copied().unwrap_or(true)
}
#[inline]
pub fn nulls_first(&self, col_idx: usize) -> bool {
self.nulls_first.get(col_idx).copied().unwrap_or(false)
}
#[inline]
pub fn rows_equal(&self, row_a: usize, row_b: usize) -> bool {
for col in &self.columns {
let val_a = col.get(row_a);
let val_b = col.get(row_b);
match (val_a, val_b) {
(Some(a), Some(b)) if a == b => continue,
(None, None) => continue,
_ => return false,
}
}
true
}
}
#[derive(Clone)]
pub struct WindowPreGroupedState {
pub partition_map: FxHashMap<PartitionKey, Vec<usize>>,
pub partition_column: String,
}