Skip to main content

densa_data/
view.rs

1//! Views: the live rows that pass a filter, in a sort order, kept up to date as rows change.
2
3use std::cmp::Ordering;
4
5use arrow_array::RecordBatch;
6use arrow_schema::Schema;
7
8use crate::filter::{resolve, Filter, Predicate};
9use crate::sorted::{Entry, SortedRows};
10use crate::table::Slot;
11use crate::value::{compare, prefix, ValueFn};
12use crate::{DataError, RowId, RowRef};
13
14/// Sort direction.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16#[non_exhaustive]
17pub enum Order {
18    /// Smallest first.
19    #[default]
20    Ascending,
21    /// Largest first.
22    Descending,
23}
24
25/// What a view shows: an optional filter and sort columns.
26#[derive(Debug, Clone, Default, PartialEq)]
27pub struct ViewSpec {
28    sort: Vec<(String, Order)>,
29    filter: Option<Filter>,
30}
31
32impl ViewSpec {
33    /// Every row, in no particular order (stable while rows keep their keys).
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    /// Sorts by `column` in `order`, after the sort columns given before it. Nulls come last
39    /// either way; floats sort `-inf`, numbers, `inf`, then NaN, with `-0.0` equal to `0.0`.
40    #[must_use]
41    pub fn sort_by(mut self, column: impl Into<String>, order: Order) -> Self {
42        self.sort.push((column.into(), order));
43        self
44    }
45
46    /// Shows only rows for which `filter` is true.
47    #[must_use]
48    pub fn filter(mut self, filter: Filter) -> Self {
49        self.filter = Some(filter);
50        self
51    }
52}
53
54/// A sort column bound to its index and reader.
55#[derive(Debug, Clone)]
56pub(crate) struct SortColumn {
57    column: usize,
58    read: ValueFn,
59    descending: bool,
60}
61
62impl SortColumn {
63    pub(crate) fn ascending(column: usize, read: ValueFn) -> Self {
64        Self {
65            column,
66            read,
67            descending: false,
68        }
69    }
70}
71
72/// A view's compiled spec and its rows.
73#[derive(Debug, Clone)]
74pub(crate) struct ViewState {
75    sort: Vec<SortColumn>,
76    filter: Option<Predicate>,
77    rows: SortedRows,
78    /// Whether each row id is in the view.
79    member: Vec<bool>,
80}
81
82/// The tables views and groups read from: its batches and where each row id lives.
83#[derive(Debug, Clone, Copy)]
84pub(crate) struct Rows<'a> {
85    pub(crate) batches: &'a [RecordBatch],
86    pub(crate) slots: &'a [Option<Slot>],
87}
88
89impl Rows<'_> {
90    pub(crate) fn at(&self, id: RowId) -> RowRef {
91        match self.slots.get(id as usize) {
92            Some(Some(slot)) => slot.row_ref(),
93            // Views and groups drop a row before its slot is freed, so every id they hold is
94            // live.
95            _ => unreachable!("row id {id} is held, but its slot is free"),
96        }
97    }
98}
99
100impl ViewState {
101    pub(crate) fn compile(spec: &ViewSpec, schema: &Schema) -> Result<Self, DataError> {
102        let sort = spec
103            .sort
104            .iter()
105            .map(|(name, order)| {
106                let (column, _, read) = resolve(schema, name)?;
107                Ok(SortColumn {
108                    column,
109                    read,
110                    descending: *order == Order::Descending,
111                })
112            })
113            .collect::<Result<_, DataError>>()?;
114        let filter = spec
115            .filter
116            .as_ref()
117            .map(|f| Predicate::compile(f, schema))
118            .transpose()?;
119        Ok(Self {
120            sort,
121            filter,
122            rows: SortedRows::default(),
123            member: Vec::new(),
124        })
125    }
126
127    /// Every row (no filter), sorted by `column` alone.
128    pub(crate) fn sorted(column: SortColumn) -> Self {
129        Self {
130            sort: vec![column],
131            filter: None,
132            rows: SortedRows::default(),
133            member: Vec::new(),
134        }
135    }
136
137    pub(crate) fn len(&self) -> usize {
138        self.rows.len()
139    }
140
141    /// How many rows, from the first, satisfy `pred` (true for a prefix of the rows).
142    pub(crate) fn partition_point(&self, pred: impl Fn(RowId) -> bool) -> usize {
143        self.rows.partition_point(pred)
144    }
145
146    pub(crate) fn get(&self, index: usize) -> Option<RowId> {
147        self.rows.get(index)
148    }
149
150    /// Whether the row at `at` passes the filter (unknown does not).
151    fn passes(&self, batches: &[RecordBatch], at: RowRef) -> bool {
152        self.filter
153            .as_ref()
154            .is_none_or(|f| f.eval(&batches[at.batch], at.row) == Some(true))
155    }
156
157    /// Drops row `id`, whose values are at `at` (where they were when it was inserted).
158    pub(crate) fn remove(&mut self, rows: Rows<'_>, id: RowId, at: RowRef) {
159        if !self.member.get(id as usize).copied().unwrap_or(false) {
160            return;
161        }
162        let probe = entry(&self.sort, rows.batches, id, at);
163        let sort = &self.sort;
164        let found = self
165            .rows
166            .remove(id, |other| entry_order(sort, rows, other, probe, at));
167        debug_assert!(found, "a member row is in the sorted rows");
168        self.member[id as usize] = false;
169    }
170
171    /// Where live row `id` is in the view, if it is a member.
172    pub(crate) fn position(&self, rows: Rows<'_>, id: RowId) -> Option<usize> {
173        if !self.member.get(id as usize).copied().unwrap_or(false) {
174            return None;
175        }
176        let at = rows.at(id);
177        let probe = entry(&self.sort, rows.batches, id, at);
178        let sort = &self.sort;
179        self.rows
180            .position(id, |other| entry_order(sort, rows, other, probe, at))
181    }
182
183    /// Adds row `id` at `at` if it passes the filter.
184    pub(crate) fn insert(&mut self, rows: Rows<'_>, id: RowId, at: RowRef) {
185        if !self.passes(rows.batches, at) {
186            return;
187        }
188        let probe = entry(&self.sort, rows.batches, id, at);
189        let sort = &self.sort;
190        self.rows
191            .insert(probe, |other| entry_order(sort, rows, other, probe, at));
192        if self.member.len() <= id as usize {
193            self.member.resize(id as usize + 1, false);
194        }
195        self.member[id as usize] = true;
196    }
197
198    /// Recomputes the view from every live row.
199    pub(crate) fn rebuild(&mut self, rows: Rows<'_>) {
200        let mut entries: Vec<Entry> = rows
201            .slots
202            .iter()
203            .enumerate()
204            .filter_map(|(id, slot)| Some((RowId::try_from(id).ok()?, slot.as_ref()?.row_ref())))
205            .filter(|&(_, at)| self.passes(rows.batches, at))
206            .map(|(id, at)| entry(&self.sort, rows.batches, id, at))
207            .collect();
208        entries.sort_unstable_by(|&a, &b| entry_order(&self.sort, rows, a, b, rows.at(b.id)));
209        self.member.clear();
210        self.member.resize(rows.slots.len(), false);
211        for entry in &entries {
212            self.member[entry.id as usize] = true;
213        }
214        self.rows.rebuild(&entries);
215    }
216}
217
218/// Row `id`'s view entry: its id and the prefix of its first sort column (0 if unsorted).
219pub(crate) fn entry(sort: &[SortColumn], batches: &[RecordBatch], id: RowId, at: RowRef) -> Entry {
220    let key = sort.first().map_or(0, |first| {
221        let value = (first.read)(batches[at.batch].column(first.column).as_ref(), at.row);
222        prefix(value, first.descending)
223    });
224    Entry { key, id }
225}
226
227/// Orders entry `other` (a stored row) against `probe` (whose values are at `probe_at`):
228/// by prefix, reading the rows' values only when prefixes tie.
229pub(crate) fn entry_order(
230    sort: &[SortColumn],
231    rows: Rows<'_>,
232    other: Entry,
233    probe: Entry,
234    probe_at: RowRef,
235) -> Ordering {
236    other.key.cmp(&probe.key).then_with(|| {
237        row_order(
238            sort,
239            rows.batches,
240            (other.id, rows.at(other.id)),
241            (probe.id, probe_at),
242        )
243    })
244}
245
246/// Orders row `a` against row `b` (each an id and where its values are): by the sort
247/// columns, then by id, so the order is total.
248fn row_order(
249    sort: &[SortColumn],
250    batches: &[RecordBatch],
251    a: (RowId, RowRef),
252    b: (RowId, RowRef),
253) -> Ordering {
254    for key in sort {
255        let read = |at: RowRef| (key.read)(batches[at.batch].column(key.column).as_ref(), at.row);
256        let order = compare(read(a.1), read(b.1), key.descending);
257        if order.is_ne() {
258            return order;
259        }
260    }
261    a.0.cmp(&b.0)
262}