reifydb_sub_flow/operator/
gate.rs1use std::sync::LazyLock;
5
6use reifydb_abi::operator::capabilities::OperatorCapability;
7use reifydb_codec::{encoded::row::EncodedRow, key::encoded::EncodedKey};
8use reifydb_core::{
9 interface::{
10 catalog::flow::FlowNodeId,
11 change::{Change, Diff},
12 },
13 key::flow_node_internal_state::FlowNodeInternalStateKey,
14 value::column::columns::Columns,
15};
16use reifydb_engine::{
17 expression::{
18 compile::{CompiledExpr, compile_expression},
19 context::{CompileContext, EvalContext},
20 },
21 vm::stack::SymbolTable,
22};
23use reifydb_routine::routine::registry::Routines;
24use reifydb_rql::expression::Expression;
25use reifydb_runtime::context::RuntimeContext;
26use reifydb_value::{
27 Result,
28 params::Params,
29 util::cowvec::CowVec,
30 value::{Value, identity::IdentityId, row_number::RowNumber},
31};
32
33use crate::{
34 operator::{Operator, OperatorCell, stateful::raw::RawStatefulOperator},
35 transaction::FlowTransaction,
36};
37
38static EMPTY_PARAMS: Params = Params::None;
39static EMPTY_SYMBOL_TABLE: LazyLock<SymbolTable> = LazyLock::new(SymbolTable::new);
40
41static VISIBLE_MARKER: LazyLock<EncodedRow> = LazyLock::new(|| EncodedRow(CowVec::new(vec![1])));
42
43pub struct GateOperator {
44 parent: OperatorCell,
45 node: FlowNodeId,
46 compiled_conditions: Vec<CompiledExpr>,
47 routines: Routines,
48 runtime_context: RuntimeContext,
49}
50
51impl GateOperator {
52 pub fn new(
53 parent: OperatorCell,
54 node: FlowNodeId,
55 conditions: Vec<Expression>,
56 routines: Routines,
57 runtime_context: RuntimeContext,
58 ) -> Self {
59 let compile_ctx = CompileContext {
60 symbols: &EMPTY_SYMBOL_TABLE,
61 };
62 let compiled_conditions: Vec<CompiledExpr> = conditions
63 .iter()
64 .map(|e| compile_expression(&compile_ctx, e).expect("Failed to compile gate condition"))
65 .collect();
66
67 Self {
68 parent,
69 node,
70 compiled_conditions,
71 routines,
72 runtime_context,
73 }
74 }
75
76 pub(crate) fn output_schema(&self) -> Option<Columns> {
77 self.parent.output_schema()
78 }
79
80 fn evaluate(&self, columns: &Columns) -> Result<Vec<bool>> {
81 let row_count = columns.row_count();
82 if row_count == 0 {
83 return Ok(Vec::new());
84 }
85
86 let session = EvalContext {
87 params: &EMPTY_PARAMS,
88 symbols: &EMPTY_SYMBOL_TABLE,
89 routines: &self.routines,
90 runtime_context: &self.runtime_context,
91 arena: None,
92 identity: IdentityId::root(),
93 is_aggregate_context: false,
94 columns: Columns::empty(),
95 row_count: 1,
96 target: None,
97 take: None,
98 };
99 let exec_ctx = session.with_eval(columns.clone(), row_count);
100
101 let mut mask = vec![true; row_count];
102
103 for compiled_condition in &self.compiled_conditions {
104 let result_col = compiled_condition.execute(&exec_ctx)?;
105
106 for (row_idx, mask_val) in mask.iter_mut().enumerate() {
107 if *mask_val {
108 match result_col.data().get_value(row_idx) {
109 Value::Boolean(true) => {}
110 Value::Boolean(false) => *mask_val = false,
111 _ => *mask_val = false,
112 }
113 }
114 }
115 }
116
117 Ok(mask)
118 }
119
120 fn row_number_key(rn: RowNumber) -> EncodedKey {
121 let mut bytes = Vec::with_capacity(1 + 8);
122 bytes.push(FlowNodeInternalStateKey::GATE_VISIBILITY_TAG);
123 bytes.extend_from_slice(&rn.0.to_be_bytes());
124 EncodedKey::new(bytes)
125 }
126
127 fn is_visible(&self, txn: &mut FlowTransaction, rn: RowNumber) -> Result<bool> {
128 Ok(self.internal_state_get(txn, &Self::row_number_key(rn))?.is_some())
129 }
130
131 fn mark_visible(&self, txn: &mut FlowTransaction, rn: RowNumber) -> Result<()> {
132 self.internal_state_set(txn, &Self::row_number_key(rn), VISIBLE_MARKER.clone())
133 }
134
135 fn mark_invisible(&self, txn: &mut FlowTransaction, rn: RowNumber) -> Result<()> {
136 self.internal_state_drop(txn, &Self::row_number_key(rn))
137 }
138}
139
140impl RawStatefulOperator for GateOperator {}
141
142impl Operator for GateOperator {
143 fn id(&self) -> FlowNodeId {
144 self.node
145 }
146
147 fn capabilities(&self) -> &[OperatorCapability] {
148 OperatorCapability::STANDARD
149 }
150
151 fn apply(&self, txn: &mut FlowTransaction, change: Change) -> Result<Change> {
152 let mut result = Vec::new();
153
154 for diff in change.diffs {
155 match diff {
156 Diff::Insert {
157 post,
158 ..
159 } => self.apply_gate_insert(txn, &post, &mut result)?,
160 Diff::Update {
161 pre,
162 post,
163 ..
164 } => self.apply_gate_update(txn, pre, post, &mut result)?,
165 Diff::Remove {
166 pre,
167 ..
168 } => self.apply_gate_remove(txn, pre, &mut result)?,
169 }
170 }
171
172 Ok(Change::from_flow(self.node, change.version, result, change.changed_at))
173 }
174}
175
176impl GateOperator {
177 #[inline]
178 fn apply_gate_insert(&self, txn: &mut FlowTransaction, post: &Columns, result: &mut Vec<Diff>) -> Result<()> {
179 if post.row_numbers.is_empty() {
180 let mask = self.evaluate(post)?;
181 let passing_indices: Vec<usize> =
182 mask.iter().enumerate().filter(|&(_, pass)| *pass).map(|(idx, _)| idx).collect();
183 if !passing_indices.is_empty() {
184 result.push(Diff::insert(post.extract_by_indices(&passing_indices)));
185 }
186 return Ok(());
187 }
188
189 let mask = self.evaluate(post)?;
190 let mut passing_indices = Vec::new();
191 for (i, &pass) in mask.iter().enumerate() {
192 let rn = post.row_numbers[i];
193 if pass {
194 self.mark_visible(txn, rn)?;
195 passing_indices.push(i);
196 }
197 }
198 if !passing_indices.is_empty() {
199 result.push(Diff::insert(post.extract_by_indices(&passing_indices)));
200 }
201 Ok(())
202 }
203
204 #[inline]
205 fn apply_gate_update(
206 &self,
207 txn: &mut FlowTransaction,
208 pre: Columns,
209 post: Columns,
210 result: &mut Vec<Diff>,
211 ) -> Result<()> {
212 if post.row_numbers.is_empty() {
213 result.push(Diff::Update {
214 pre,
215 post,
216 origin: None,
217 });
218 return Ok(());
219 }
220
221 let mask = self.evaluate(&post)?;
222 let mut update_indices = Vec::new();
223 let mut insert_indices = Vec::new();
224
225 for (i, (&rn, &mask_val)) in post.row_numbers.iter().zip(mask.iter()).enumerate() {
226 if self.is_visible(txn, rn)? {
227 update_indices.push(i);
228 } else if mask_val {
229 self.mark_visible(txn, rn)?;
230 insert_indices.push(i);
231 }
232 }
233
234 if !update_indices.is_empty() {
235 result.push(Diff::update(
236 pre.extract_by_indices(&update_indices),
237 post.extract_by_indices(&update_indices),
238 ));
239 }
240 if !insert_indices.is_empty() {
241 result.push(Diff::insert(post.extract_by_indices(&insert_indices)));
242 }
243 Ok(())
244 }
245
246 #[inline]
247 fn apply_gate_remove(&self, txn: &mut FlowTransaction, pre: Columns, result: &mut Vec<Diff>) -> Result<()> {
248 if pre.row_numbers.is_empty() {
249 result.push(Diff::Remove {
250 pre,
251 origin: None,
252 });
253 return Ok(());
254 }
255
256 let mut remove_indices = Vec::new();
257 for i in 0..pre.row_numbers.len() {
258 let rn = pre.row_numbers[i];
259 if self.is_visible(txn, rn)? {
260 self.mark_invisible(txn, rn)?;
261 remove_indices.push(i);
262 }
263 }
264
265 if !remove_indices.is_empty() {
266 result.push(Diff::remove(pre.extract_by_indices(&remove_indices)));
267 }
268 Ok(())
269 }
270}