nodedb_physical/physical_plan/document/row_source.rs
1// SPDX-License-Identifier: Apache-2.0
2
3//! The relation a Control-Plane-orchestrated `INSERT ... SELECT`, `MERGE`, or
4//! `UPDATE ... FROM` reads its source rows from.
5
6use nodedb_types::QualifiedCollection;
7
8use crate::physical_plan::PhysicalPlan;
9
10/// Source relation of an orchestrated copy or join write.
11#[derive(
12 Debug,
13 Clone,
14 PartialEq,
15 serde::Serialize,
16 serde::Deserialize,
17 zerompk::ToMessagePack,
18 zerompk::FromMessagePack,
19)]
20pub enum RowSource {
21 /// A plain document scan, read page by page through
22 /// `DocumentOp::MaterializeScan`. The B-tree key cursor is stable under
23 /// concurrent writes, so the read has no row-count bound. The Control
24 /// Plane applies the WHERE filter, the offset, the limit, and the column
25 /// map to each page.
26 DocumentPages(DocumentPageSource),
27 /// Any other SELECT, lowered by the normal plan converter and read once
28 /// through the Data Plane dispatch funnel. It has exactly SELECT
29 /// semantics: engine, filters, ordering, distinct, windows, joins, `AS
30 /// OF`, and read policies all come from the normal pipeline, under the
31 /// normal result-byte budget.
32 Query(QuerySource),
33}
34
35/// A plain document scan source, read page by page.
36#[derive(
37 Debug,
38 Clone,
39 PartialEq,
40 serde::Serialize,
41 serde::Deserialize,
42 zerompk::ToMessagePack,
43 zerompk::FromMessagePack,
44)]
45pub struct DocumentPageSource {
46 pub collection: QualifiedCollection,
47 /// Serialized `Vec<ScanFilter>`: the WHERE clause, ANDed with the
48 /// collection's read policy by the RLS pass.
49 pub filters: Vec<u8>,
50 /// `FOR SYSTEM_TIME AS OF` cut, in epoch milliseconds. `None` reads the
51 /// current version.
52 pub system_as_of_ms: Option<i64>,
53 /// Matching rows skipped before the first row is read.
54 pub offset: usize,
55 /// Matching rows read after the offset. `None` reads every matching row.
56 pub limit: Option<usize>,
57 /// zerompk `Vec<ComputedColumn>`: one entry per target column, its alias
58 /// the target column and its expression the source-row expression.
59 /// Empty passes each source row through unchanged.
60 pub column_map: Vec<u8>,
61}
62
63/// A source SELECT read once as a whole relation.
64#[derive(
65 Debug,
66 Clone,
67 PartialEq,
68 serde::Serialize,
69 serde::Deserialize,
70 zerompk::ToMessagePack,
71 zerompk::FromMessagePack,
72)]
73pub struct QuerySource {
74 /// The lowered SELECT.
75 pub plan: Box<PhysicalPlan>,
76 /// zerompk `Vec<ComputedColumn>` that renames each output column to the
77 /// target column it writes. Empty passes each output row through
78 /// unchanged.
79 pub column_map: Vec<u8>,
80}
81
82impl RowSource {
83 /// A page source that reads every current row of `collection` unchanged.
84 pub fn whole_collection(collection: QualifiedCollection) -> Self {
85 RowSource::DocumentPages(DocumentPageSource {
86 collection,
87 filters: Vec::new(),
88 system_as_of_ms: None,
89 offset: 0,
90 limit: None,
91 column_map: Vec::new(),
92 })
93 }
94
95 /// The collection a page source scans. `None` for a query source, whose
96 /// collections are the ones its plan reads.
97 pub fn paged_collection(&self) -> Option<&QualifiedCollection> {
98 match self {
99 RowSource::DocumentPages(page) => Some(&page.collection),
100 RowSource::Query(_) => None,
101 }
102 }
103
104 /// The plan a query source runs. `None` for a page source.
105 pub fn query_plan(&self) -> Option<&PhysicalPlan> {
106 match self {
107 RowSource::DocumentPages(_) => None,
108 RowSource::Query(query) => Some(&query.plan),
109 }
110 }
111
112 /// A short label for logs and error messages: the paged collection, or
113 /// the primary collection of the query plan.
114 pub fn label(&self) -> &str {
115 match self {
116 RowSource::DocumentPages(page) => page.collection.as_str(),
117 RowSource::Query(query) => query.plan.collection().unwrap_or("<query>"),
118 }
119 }
120}