Skip to main content

risingwave_stream/common/table/
state_table.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::{BTreeMap, HashMap};
16use std::marker::PhantomData;
17use std::ops::Bound;
18use std::ops::Bound::*;
19use std::sync::Arc;
20use std::time::Instant;
21
22use anyhow::anyhow;
23use await_tree::InstrumentAwait;
24use bytes::Bytes;
25use educe::Educe;
26use either::Either;
27use foyer::Hint;
28use futures::future::{ready, try_join_all};
29use futures::stream::BoxStream;
30use futures::{Stream, StreamExt, TryStreamExt, pin_mut};
31use itertools::Itertools;
32use risingwave_common::array::stream_record::Record;
33use risingwave_common::array::{ArrayImplBuilder, ArrayRef, DataChunk, Op, StreamChunk};
34use risingwave_common::bitmap::Bitmap;
35use risingwave_common::catalog::{
36    ColumnDesc, ColumnId, TableId, TableOption, get_dist_key_in_pk_indices,
37};
38use risingwave_common::config::StreamingConfig;
39use risingwave_common::hash::{VirtualNode, VnodeBitmapExt, VnodeCountCompat};
40use risingwave_common::id::FragmentId;
41use risingwave_common::row::{self, OwnedRow, Row, RowExt};
42use risingwave_common::types::{DataType, ScalarImpl};
43use risingwave_common::util::column_index_mapping::ColIndexMapping;
44use risingwave_common::util::epoch::EpochPair;
45use risingwave_common::util::row_serde::OrderedRowSerde;
46use risingwave_common::util::sort_util::{OrderType, cmp_datum};
47use risingwave_common::util::value_encoding::BasicSerde;
48use risingwave_hummock_sdk::HummockReadEpoch;
49use risingwave_hummock_sdk::key::{
50    CopyFromSlice, TableKey, TableKeyRange, end_bound_of_prefix, is_empty_key_range, next_key,
51    prefix_slice_with_vnode, prefixed_range_with_vnode, start_bound_of_excluded_prefix,
52};
53use risingwave_hummock_sdk::table_watermark::{
54    VnodeWatermark, WatermarkDirection, WatermarkSerdeType, rewrite_range_with_watermark,
55};
56use risingwave_pb::catalog::Table;
57use risingwave_pb::plan_common::StorageTableDesc;
58use risingwave_storage::StateStore;
59use risingwave_storage::error::{ErrorKind, StorageError, StorageResult};
60use risingwave_storage::hummock::CachePolicy;
61use risingwave_storage::mem_table::MemTableError;
62use risingwave_storage::row_serde::find_columns_by_ids;
63use risingwave_storage::row_serde::row_serde_util::{
64    deserialize_pk_with_vnode, serialize_pk, serialize_pk_with_vnode, serialize_row,
65};
66use risingwave_storage::row_serde::value_serde::ValueRowSerde;
67use risingwave_storage::store::*;
68use risingwave_storage::table::{KeyedRow, TableDistribution, should_calculate_prefix_hint};
69use thiserror_ext::AsReport;
70use tracing::{Instrument, trace};
71
72use crate::cache::keyed_cache_may_stale;
73use crate::executor::monitor::streaming_stats::StateTableMetrics;
74use crate::executor::{StreamExecutorError, StreamExecutorResult};
75
76/// This macro is used to mark a point where we want to randomly discard the operation and early
77/// return, only in insane mode.
78macro_rules! insane_mode_discard_point {
79    () => {{
80        use rand::Rng;
81        if crate::consistency::insane() && rand::rng().random_bool(0.3) {
82            return;
83        }
84    }};
85}
86
87/// Per-vnode statistics for pruning. None means this stat is not maintained.
88/// For each vnode, we maintain the min and max storage table key (excluding the vnode part) observed in the vnode.
89/// The stat won't differentiate between tombstone and normal keys.
90struct VnodeStatistics {
91    min_key: Option<Bytes>,
92    max_key: Option<Bytes>,
93}
94
95impl VnodeStatistics {
96    fn new() -> Self {
97        Self {
98            min_key: None,
99            max_key: None,
100        }
101    }
102
103    fn update_with_key(&mut self, key: &Bytes) {
104        if let Some(min) = &self.min_key {
105            if key < min {
106                self.min_key = Some(key.clone());
107            }
108        } else {
109            self.min_key = Some(key.clone());
110        }
111
112        if let Some(max) = &self.max_key {
113            if key > max {
114                self.max_key = Some(key.clone());
115            }
116        } else {
117            self.max_key = Some(key.clone());
118        }
119    }
120
121    fn can_prune(&self, key: &Bytes) -> bool {
122        if let Some(min) = &self.min_key
123            && key < min
124        {
125            return true;
126        }
127        if let Some(max) = &self.max_key
128            && key > max
129        {
130            return true;
131        }
132        false
133    }
134
135    fn can_prune_range(&self, start: &Bound<Bytes>, end: &Bound<Bytes>) -> bool {
136        // Check if the range is completely outside vnode bounds
137        if let Some(max) = &self.max_key {
138            match start {
139                Included(s) if s > max => return true,
140                Excluded(s) if s >= max => return true,
141                _ => {}
142            }
143        }
144        if let Some(min) = &self.min_key {
145            match end {
146                Included(e) if e < min => return true,
147                Excluded(e) if e <= min => return true,
148                _ => {}
149            }
150        }
151        false
152    }
153
154    fn pruned_key_range(
155        &self,
156        start: &Bound<Bytes>,
157        end: &Bound<Bytes>,
158    ) -> Option<(Bound<Bytes>, Bound<Bytes>)> {
159        if self.can_prune_range(start, end) {
160            return None;
161        }
162        let new_start = if let Some(min) = &self.min_key {
163            match start {
164                Included(s) if s <= min => Included(min.clone()),
165                Excluded(s) if s < min => Included(min.clone()),
166                _ => start.clone(),
167            }
168        } else {
169            start.clone()
170        };
171
172        let new_end = if let Some(max) = &self.max_key {
173            match end {
174                Included(e) if e >= max => Included(max.clone()),
175                Excluded(e) if e > max => Included(max.clone()),
176                _ => end.clone(),
177            }
178        } else {
179            end.clone()
180        };
181
182        Some((new_start, new_end))
183    }
184}
185
186/// `StateTableInner` is the interface accessing relational data in KV(`StateStore`) with
187/// row-based encoding.
188pub struct StateTableInner<S, SD = BasicSerde, const IS_REPLICATED: bool = false>
189where
190    S: StateStore,
191    SD: ValueRowSerde,
192{
193    /// Id for this table.
194    table_id: TableId,
195
196    /// State store backend.
197    row_store: StateTableRowStore<S::Local, SD>,
198
199    /// State store for accessing snapshot data
200    store: S,
201
202    /// Current epoch
203    epoch: Option<EpochPair>,
204
205    /// Used for serializing and deserializing the primary key.
206    pk_serde: OrderedRowSerde,
207
208    /// Indices of primary key.
209    /// Note that the index is based on the all columns of the table, instead of the output ones.
210    // FIXME: revisit constructions and usages.
211    pk_indices: Vec<usize>,
212
213    /// Distribution of the state table.
214    ///
215    /// It holds vnode bitmap. Only the rows whose vnode of the primary key is in this set will be visible to the
216    /// executor. The table will also check whether the written rows
217    /// conform to this partition.
218    distribution: TableDistribution,
219
220    prefix_hint_len: usize,
221
222    value_indices: Option<Vec<usize>>,
223
224    /// The index of the watermark column used for state cleaning in all columns.
225    pub clean_watermark_index: Option<usize>,
226    /// Pending watermark for state cleaning. Old states below this watermark will be cleaned when committing.
227    pending_watermark: Option<ScalarImpl>,
228    /// Last committed watermark for state cleaning. Will be restored on state table recovery.
229    committed_watermark: Option<ScalarImpl>,
230    /// Serializer and serde type for the watermark column.
231    watermark_serde: Option<(OrderedRowSerde, WatermarkSerdeType)>,
232
233    /// Data Types
234    /// We will need to use to build data chunks from state table rows.
235    data_types: Vec<DataType>,
236
237    /// "i" here refers to the base `state_table`'s actual schema.
238    /// "o" here refers to the replicated state table's output schema.
239    /// This mapping is used to reconstruct a row being written from replicated state table.
240    /// Such that the schema of this row will match the full schema of the base state table.
241    /// It is only applicable for replication.
242    i2o_mapping: ColIndexMapping,
243
244    /// Output indices
245    /// Used for:
246    /// 1. Computing `output_value_indices` to ser/de replicated rows.
247    /// 2. Computing output pk indices to used them for backfill state.
248    pub output_indices: Vec<usize>,
249
250    op_consistency_level: StateTableOpConsistencyLevel,
251
252    /// Flag to indicate whether the state table has called `commit`, but has not called
253    /// `post_yield_barrier` on the `StateTablePostCommit` callback yet.
254    on_post_commit: bool,
255}
256
257/// `StateTable` will use `BasicSerde` as default
258pub type StateTable<S> = StateTableInner<S, BasicSerde>;
259/// `ReplicatedStateTable` is meant to replicate upstream shared buffer.
260/// Used for `ArrangementBackfill` executor.
261pub type ReplicatedStateTable<S, SD> = StateTableInner<S, SD, true>;
262
263pub type FlushedStateTableReader<S, SD = BasicSerde> = StateTableFlushedSnapshotReader<
264    <<S as StateStore>::Local as LocalStateStore>::FlushedSnapshotReader,
265    SD,
266>;
267
268#[derive(Educe)]
269#[educe(Clone)]
270pub struct StateTableFlushedSnapshotReader<R, SD = BasicSerde>
271where
272    R: StateStoreRead,
273    SD: ValueRowSerde,
274{
275    reader: Arc<R>,
276    pk_serde: OrderedRowSerde,
277    vnodes: Arc<Bitmap>,
278    row_serde: Arc<SD>,
279    metrics: Option<StateTableMetrics>,
280}
281
282// initialize
283impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
284where
285    S: StateStore,
286    SD: ValueRowSerde,
287{
288    /// In streaming executors, this methods must be called **after** receiving and yielding the first barrier,
289    /// and otherwise, deadlock can be likely to happen.
290    pub async fn init_epoch(&mut self, epoch: EpochPair) -> StreamExecutorResult<()> {
291        self.row_store
292            .init(epoch, self.distribution.vnodes())
293            .await?;
294        assert_eq!(None, self.epoch.replace(epoch), "should not init for twice");
295        Ok(())
296    }
297
298    pub async fn try_wait_committed_epoch(&self, prev_epoch: u64) -> StorageResult<()> {
299        self.store
300            .try_wait_epoch(
301                HummockReadEpoch::Committed(prev_epoch),
302                TryWaitEpochOptions {
303                    table_id: self.table_id,
304                },
305            )
306            .await
307    }
308
309    pub fn state_store(&self) -> &S {
310        &self.store
311    }
312}
313
314fn consistent_old_value_op(
315    row_serde: Arc<impl ValueRowSerde>,
316    is_log_store: bool,
317) -> OpConsistencyLevel {
318    OpConsistencyLevel::ConsistentOldValue {
319        check_old_value: Arc::new(move |first: &Bytes, second: &Bytes| {
320            if first == second {
321                return true;
322            }
323            let first = match row_serde.deserialize(first) {
324                Ok(rows) => rows,
325                Err(e) => {
326                    error!(error = %e.as_report(), value = ?first, "failed to deserialize the serialized value");
327                    return false;
328                }
329            };
330            let second = match row_serde.deserialize(second) {
331                Ok(rows) => rows,
332                Err(e) => {
333                    error!(error = %e.as_report(), value = ?second, "failed to deserialize the serialized value");
334                    return false;
335                }
336            };
337            if first != second {
338                error!(first = ?first, second = ?second, "sanity check fail");
339                false
340            } else {
341                true
342            }
343        }),
344        is_log_store,
345    }
346}
347
348macro_rules! dispatch_value_indices {
349    ($value_indices:expr, [$($row_var_name:ident),+], $body:expr) => {
350        if let Some(value_indices) = $value_indices {
351            $(
352                let $row_var_name = $row_var_name.project(value_indices);
353            )+
354            $body
355        } else {
356            $body
357        }
358    };
359}
360
361/// Extract the logic of `StateTableRowStore` from `StateTable`, which serves
362/// a similar functionality as a `BTreeMap<TableKey<Bytes>, OwnedRow>`, and
363/// provides method to read (`get` and `iter`) over serialized key (or key range)
364/// and returns `OwnedRow`, and write (`insert`, `delete`, `update`) on `(TableKey<Bytes>, OwnedRow)`.
365struct StateTableRowStore<LS: LocalStateStore, SD: ValueRowSerde> {
366    state_store: LS,
367    all_rows: Option<HashMap<VirtualNode, BTreeMap<Bytes, OwnedRow>>>,
368    /// The pk-prefix watermark fed in the current epoch, with its direction. The state store only
369    /// applies it once committed, so the reads from the state store hide the keys it cleans until
370    /// then.
371    pending_watermark: Option<(WatermarkDirection, Bytes)>,
372
373    table_id: TableId,
374    row_serde: Arc<SD>,
375    // should be only used for debugging in panic message of handle_mem_table_error
376    pk_serde: OrderedRowSerde,
377
378    // Per-vnode min/max key statistics for pruning
379    vnode_stats: Option<HashMap<VirtualNode, VnodeStatistics>>,
380    /// When false, vnode stats pruning is in dry-run mode:
381    /// we maintain stats and verify pruning correctness but don't actually apply pruning.
382    /// Reads still go to cache/storage even when pruning would indicate no results.
383    enable_state_table_vnode_stats_pruning: bool,
384    // Optional metrics for state table operations
385    pub metrics: Option<StateTableMetrics>,
386}
387
388impl<LS: LocalStateStore, SD: ValueRowSerde> StateTableRowStore<LS, SD> {
389    async fn may_load_vnode_stats(&mut self, vnode_bitmap: &Bitmap) -> StreamExecutorResult<()> {
390        if self.vnode_stats.is_none() {
391            return Ok(());
392        }
393
394        // vnode stats must be disabled when all rows are preloaded
395        assert!(self.all_rows.is_none());
396
397        let start_time = Instant::now();
398        let mut stats_map = HashMap::new();
399
400        // Scan each vnode to get min/max keys
401        for vnode in vnode_bitmap.iter_vnodes() {
402            let mut stats = VnodeStatistics::new();
403
404            // Get min key via forward iteration
405            let memcomparable_range_with_vnode = prefixed_range_with_vnode::<Bytes>(.., vnode);
406            let read_options = ReadOptions {
407                cache_policy: CachePolicy::Fill(Hint::Low),
408                ..Default::default()
409            };
410
411            let mut iter = self
412                .state_store
413                .iter(memcomparable_range_with_vnode.clone(), read_options.clone())
414                .await?;
415            if let Some(item) = iter.try_next().await? {
416                let (key_vnode, key_without_vnode) = item.0.user_key.table_key.split_vnode();
417                assert_eq!(vnode, key_vnode);
418                stats.min_key = Some(Bytes::copy_from_slice(key_without_vnode));
419            }
420
421            // Get max key via reverse iteration
422            let mut rev_iter = self
423                .state_store
424                .rev_iter(memcomparable_range_with_vnode, read_options)
425                .await?;
426            if let Some(item) = rev_iter.try_next().await? {
427                let (key_vnode, key_without_vnode) = item.0.user_key.table_key.split_vnode();
428                assert_eq!(vnode, key_vnode);
429                stats.max_key = Some(Bytes::copy_from_slice(key_without_vnode));
430            }
431
432            stats_map.insert(vnode, stats);
433        }
434
435        self.vnode_stats = Some(stats_map);
436
437        // avoid flooding e2e-test log
438        if !cfg!(debug_assertions) {
439            info!(
440                table_id = %self.table_id,
441                vnode_count = vnode_bitmap.count_ones(),
442                duration = ?start_time.elapsed(),
443                "finished initializing vnode statistics"
444            );
445        }
446
447        Ok(())
448    }
449
450    async fn may_reload_all_rows(&mut self, vnode_bitmap: &Bitmap) -> StreamExecutorResult<()> {
451        if let Some(rows) = &mut self.all_rows {
452            rows.clear();
453            let start_time = Instant::now();
454            *rows = try_join_all(vnode_bitmap.iter_vnodes().map(|vnode| {
455                let state_store = &self.state_store;
456                let row_serde = &self.row_serde;
457                async move {
458                    let mut rows = BTreeMap::new();
459                    let memcomparable_range_with_vnode =
460                        prefixed_range_with_vnode::<Bytes>(.., vnode);
461                    // TODO: set read options
462                    let stream = deserialize_keyed_row_stream::<Bytes>(
463                        state_store
464                            .iter(
465                                memcomparable_range_with_vnode,
466                                ReadOptions {
467                                    prefix_hint: None,
468                                    prefetch_options: Default::default(),
469                                    cache_policy: Default::default(),
470                                },
471                            )
472                            .await?,
473                        &**row_serde,
474                    );
475                    pin_mut!(stream);
476                    while let Some((encoded_key, row)) = stream.try_next().await? {
477                        let key = TableKey(encoded_key);
478                        let (iter_vnode, key) = key.split_vnode_bytes();
479                        assert_eq!(vnode, iter_vnode);
480                        rows.try_insert(key, row).expect("non-duplicated");
481                    }
482                    Ok((vnode, rows)) as StreamExecutorResult<_>
483                }
484            }))
485            .await?
486            .into_iter()
487            .collect();
488            // avoid flooding e2e-test log
489            if !cfg!(debug_assertions) {
490                info!(table_id = %self.table_id, vnode_count = vnode_bitmap.count_ones(), duration = ?start_time.elapsed(),"finished reloading all rows");
491            }
492        }
493        Ok(())
494    }
495
496    async fn init(&mut self, epoch: EpochPair, vnode_bitmap: &Bitmap) -> StreamExecutorResult<()> {
497        self.state_store.init(InitOptions::new(epoch)).await?;
498        self.may_reload_all_rows(vnode_bitmap).await?;
499        self.may_load_vnode_stats(vnode_bitmap).await
500    }
501
502    async fn update_vnode_bitmap(
503        &mut self,
504        vnodes: Arc<Bitmap>,
505    ) -> StreamExecutorResult<Arc<Bitmap>> {
506        let prev_vnodes = self.state_store.update_vnode_bitmap(vnodes.clone()).await?;
507        self.may_reload_all_rows(&vnodes).await?;
508        self.may_load_vnode_stats(&vnodes).await?;
509
510        Ok(prev_vnodes)
511    }
512
513    async fn try_flush(&mut self) -> StreamExecutorResult<()> {
514        self.state_store.try_flush().await?;
515        Ok(())
516    }
517
518    /// Remove the preloaded rows cleaned by `watermarks`.
519    fn clean_preloaded_rows(
520        &mut self,
521        direction: WatermarkDirection,
522        watermarks: &[VnodeWatermark],
523        serde_type: WatermarkSerdeType,
524    ) {
525        let Some(rows) = &mut self.all_rows else {
526            return;
527        };
528        match serde_type {
529            WatermarkSerdeType::PkPrefix => {
530                for vnode_watermark in watermarks {
531                    match direction {
532                        WatermarkDirection::Ascending => {
533                            for vnode in vnode_watermark.vnode_bitmap().iter_vnodes() {
534                                let rows = rows.get_mut(&vnode).expect("covered vnode");
535                                // split_off returns everything after the given key, including the key.
536                                *rows = rows.split_off(vnode_watermark.watermark());
537                            }
538                        }
539                        WatermarkDirection::Descending => {
540                            // Turn Exclude(vnode_watermark.watermark()) into Include(next_key(vnode_watermark.watermark())).
541                            let split_off_key = next_key(vnode_watermark.watermark());
542                            for vnode in vnode_watermark.vnode_bitmap().iter_vnodes() {
543                                let rows = rows.get_mut(&vnode).expect("covered vnode");
544                                // split_off away (Exclude(vnode_watermark.watermark())..) and keep
545                                // (..Include(vnode_watermark.watermark()))
546                                rows.split_off(split_off_key.as_slice());
547                            }
548                        }
549                    }
550                }
551            }
552            WatermarkSerdeType::NonPkPrefix => {
553                warn!(table_id = %self.table_id, "table enabled preloading rows got disabled by written non pk prefix watermark");
554                self.all_rows = None;
555            }
556            WatermarkSerdeType::Value => {
557                warn!(table_id = %self.table_id, "table enabled preloading rows got disabled by written value watermark");
558                self.all_rows = None;
559            }
560        }
561    }
562
563    /// Feed the pk-prefix watermark of the current epoch: the preloaded rows below it are removed
564    /// at once, and the reads from the state store hide them until the commit.
565    fn update_pending_watermark(
566        &mut self,
567        direction: WatermarkDirection,
568        vnode_watermark: VnodeWatermark,
569    ) {
570        self.clean_preloaded_rows(
571            direction,
572            std::slice::from_ref(&vnode_watermark),
573            WatermarkSerdeType::PkPrefix,
574        );
575        self.pending_watermark = Some((direction, vnode_watermark.watermark().clone()));
576    }
577
578    fn narrow_by_pending_watermark(&self, key_range: &mut TableKeyRange) {
579        if let Some((direction, watermark)) = &self.pending_watermark {
580            rewrite_range_with_watermark(*direction, watermark, key_range);
581        }
582    }
583
584    fn below_pending_watermark(&self, key: &TableKey<Bytes>) -> bool {
585        let mut key_range = (Bound::Included(key.clone()), Bound::Included(key.clone()));
586        self.narrow_by_pending_watermark(&mut key_range);
587        is_empty_key_range(&key_range)
588    }
589
590    async fn seal_current_epoch(
591        &mut self,
592        next_epoch: u64,
593        table_watermarks: Option<(WatermarkDirection, Vec<VnodeWatermark>, WatermarkSerdeType)>,
594        switch_consistent_op: Option<StateTableOpConsistencyLevel>,
595    ) -> StreamExecutorResult<()> {
596        if let Some((direction, watermarks, serde_type)) = &table_watermarks {
597            self.clean_preloaded_rows(*direction, watermarks, *serde_type);
598        }
599        self.state_store
600            .flush()
601            .instrument(tracing::info_span!("state_table_flush"))
602            .await?;
603        let switch_op_consistency_level =
604            switch_consistent_op.map(|new_consistency_level| match new_consistency_level {
605                StateTableOpConsistencyLevel::Inconsistent => OpConsistencyLevel::Inconsistent,
606                StateTableOpConsistencyLevel::ConsistentOldValue => {
607                    consistent_old_value_op(self.row_serde.clone(), false)
608                }
609                StateTableOpConsistencyLevel::LogStoreEnabled => {
610                    consistent_old_value_op(self.row_serde.clone(), true)
611                }
612            });
613        self.state_store.seal_current_epoch(
614            next_epoch,
615            SealCurrentEpochOptions {
616                table_watermarks,
617                switch_op_consistency_level,
618            },
619        );
620        Ok(())
621    }
622}
623
624#[derive(Eq, PartialEq, Copy, Clone, Debug)]
625pub enum StateTableOpConsistencyLevel {
626    /// Op is inconsistent
627    Inconsistent,
628    /// Op is consistent.
629    /// - Insert op should ensure that the key does not exist previously
630    /// - Delete and Update op should ensure that the key exists and the previous value matches the passed old value
631    ConsistentOldValue,
632    /// The requirement on operation consistency is the same as `ConsistentOldValue`.
633    /// The difference is that in the `LogStoreEnabled`, the state table should also flush and store and old value.
634    LogStoreEnabled,
635}
636
637pub struct StateTableBuilder<S, SD, const IS_REPLICATED: bool, PreloadAllRow> {
638    // Extracted intermediate fields (source-agnostic)
639    table_id: TableId,
640    table_name_for_debug: String,
641    table_columns: Vec<ColumnDesc>,
642    order_types: Vec<OrderType>,
643    pk_indices: Vec<usize>,
644    dist_key_in_pk_indices: Vec<usize>,
645    vnode_col_idx_in_pk: Option<usize>,
646    expected_vnode_count: usize,
647    value_indices: Vec<usize>,
648    prefix_hint_len: usize,
649    retention_seconds: Option<u32>,
650    versioned: bool,
651    fragment_id: FragmentId,
652    clean_watermark_index: Option<usize>,
653
654    // Builder configuration fields
655    store: S,
656    vnodes: Option<Arc<Bitmap>>,
657    op_consistency_level: Option<StateTableOpConsistencyLevel>,
658    output_column_ids: Option<Vec<ColumnId>>,
659    preload_all_rows: PreloadAllRow,
660    enable_vnode_key_stats: Option<bool>,
661    /// When false, vnode stats pruning is in dry-run mode:
662    /// we maintain stats and verify pruning correctness but don't actually apply pruning.
663    enable_state_table_vnode_stats_pruning: bool,
664    metrics: Option<StateTableMetrics>,
665
666    _serde: PhantomData<SD>,
667}
668
669impl<S: StateStore, SD: ValueRowSerde, const IS_REPLICATED: bool>
670    StateTableBuilder<S, SD, IS_REPLICATED, ()>
671{
672    fn with_preload_all_rows(
673        self,
674        preload_all_rows: bool,
675    ) -> StateTableBuilder<S, SD, IS_REPLICATED, bool> {
676        StateTableBuilder {
677            table_id: self.table_id,
678            table_name_for_debug: self.table_name_for_debug,
679            table_columns: self.table_columns,
680            order_types: self.order_types,
681            pk_indices: self.pk_indices,
682            dist_key_in_pk_indices: self.dist_key_in_pk_indices,
683            vnode_col_idx_in_pk: self.vnode_col_idx_in_pk,
684            expected_vnode_count: self.expected_vnode_count,
685            value_indices: self.value_indices,
686            prefix_hint_len: self.prefix_hint_len,
687            retention_seconds: self.retention_seconds,
688            versioned: self.versioned,
689            fragment_id: self.fragment_id,
690            clean_watermark_index: self.clean_watermark_index,
691            store: self.store,
692            vnodes: self.vnodes,
693            op_consistency_level: self.op_consistency_level,
694            output_column_ids: self.output_column_ids,
695            preload_all_rows,
696            enable_vnode_key_stats: self.enable_vnode_key_stats,
697            enable_state_table_vnode_stats_pruning: self.enable_state_table_vnode_stats_pruning,
698            metrics: self.metrics,
699            _serde: Default::default(),
700        }
701    }
702
703    pub fn enable_preload_all_rows_by_config(
704        self,
705        config: &StreamingConfig,
706    ) -> StateTableBuilder<S, SD, IS_REPLICATED, bool> {
707        let developer = &config.developer;
708        let preload_all_rows = if developer.default_enable_mem_preload_state_table {
709            !developer
710                .mem_preload_state_table_ids_blacklist
711                .contains(&self.table_id.as_raw_id())
712        } else {
713            developer
714                .mem_preload_state_table_ids_whitelist
715                .contains(&self.table_id.as_raw_id())
716        };
717        self.with_preload_all_rows(preload_all_rows)
718    }
719
720    pub fn forbid_preload_all_rows(self) -> StateTableBuilder<S, SD, IS_REPLICATED, bool> {
721        self.with_preload_all_rows(false)
722    }
723}
724
725impl<S: StateStore, SD: ValueRowSerde, const IS_REPLICATED: bool, PreloadAllRow>
726    StateTableBuilder<S, SD, IS_REPLICATED, PreloadAllRow>
727{
728    pub fn with_op_consistency_level(
729        mut self,
730        op_consistency_level: StateTableOpConsistencyLevel,
731    ) -> Self {
732        self.op_consistency_level = Some(op_consistency_level);
733        self
734    }
735
736    pub fn enable_vnode_key_stats(mut self, enable: bool, config: &StreamingConfig) -> Self {
737        self.enable_vnode_key_stats = Some(enable);
738        self.enable_state_table_vnode_stats_pruning =
739            enable && config.developer.enable_state_table_vnode_stats_pruning;
740        self
741    }
742
743    pub fn with_metrics(mut self, metrics: StateTableMetrics) -> Self {
744        self.metrics = Some(metrics);
745        self
746    }
747}
748
749impl<S: StateStore, SD: ValueRowSerde, PreloadAllRow>
750    StateTableBuilder<S, SD, true, PreloadAllRow>
751{
752    pub fn with_output_column_ids(mut self, output_column_ids: Vec<ColumnId>) -> Self {
753        self.output_column_ids = Some(output_column_ids);
754        self
755    }
756}
757
758impl<S: StateStore, SD: ValueRowSerde, const IS_REPLICATED: bool>
759    StateTableBuilder<S, SD, IS_REPLICATED, bool>
760{
761    pub async fn build(self) -> StateTableInner<S, SD, IS_REPLICATED> {
762        let mut preload_all_rows = self.preload_all_rows;
763        if preload_all_rows
764            && let Err(e) =
765                risingwave_common::license::Feature::StateTableMemoryPreload.check_available()
766        {
767            warn!(table_id=%self.table_id, e=%e.as_report(), "table configured to preload rows to memory but disabled by license");
768            preload_all_rows = false;
769        }
770
771        let should_enable_vnode_key_stats = if preload_all_rows
772            && let Some(enable_vnode_key_stats) = self.enable_vnode_key_stats
773            && enable_vnode_key_stats
774        {
775            false
776        } else {
777            self.enable_vnode_key_stats.unwrap_or(false)
778        };
779        self.build_inner(preload_all_rows, should_enable_vnode_key_stats)
780            .await
781    }
782}
783
784// initialize
785// FIXME(kwannoel): Enforce that none of the constructors here
786// should be used by replicated state table.
787// Apart from from_table_catalog_inner.
788impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
789where
790    S: StateStore,
791    SD: ValueRowSerde,
792{
793    /// Create state table from table catalog and store.
794    ///
795    /// If `vnodes` is `None`, [`TableDistribution::singleton()`] will be used.
796    #[cfg(any(test, feature = "test"))]
797    pub async fn from_table_catalog(
798        table_catalog: &Table,
799        store: S,
800        vnodes: Option<Arc<Bitmap>>,
801    ) -> Self {
802        StateTableBuilder::new(table_catalog, store, vnodes)
803            .forbid_preload_all_rows()
804            .build()
805            .await
806    }
807
808    /// Create state table from table catalog and store with sanity check disabled.
809    pub async fn from_table_catalog_inconsistent_op(
810        table_catalog: &Table,
811        store: S,
812        vnodes: Option<Arc<Bitmap>>,
813    ) -> Self {
814        StateTableBuilder::new(table_catalog, store, vnodes)
815            .with_op_consistency_level(StateTableOpConsistencyLevel::Inconsistent)
816            .forbid_preload_all_rows()
817            .build()
818            .await
819    }
820}
821
822impl<S: StateStore, SD: ValueRowSerde, const IS_REPLICATED: bool>
823    StateTableBuilder<S, SD, IS_REPLICATED, ()>
824{
825    pub fn new(table_catalog: &Table, store: S, vnodes: Option<Arc<Bitmap>>) -> Self {
826        let table_id = table_catalog.id;
827        let table_columns: Vec<ColumnDesc> = table_catalog
828            .columns
829            .iter()
830            .map(|col| col.column_desc.as_ref().unwrap().into())
831            .collect();
832        let order_types: Vec<OrderType> = table_catalog
833            .pk
834            .iter()
835            .map(|col_order| OrderType::from_protobuf(col_order.get_order_type().unwrap()))
836            .collect();
837        let dist_key_indices: Vec<usize> = table_catalog
838            .distribution_key
839            .iter()
840            .map(|dist_index| *dist_index as usize)
841            .collect();
842
843        let pk_indices = table_catalog
844            .pk
845            .iter()
846            .map(|col_order| col_order.column_index as usize)
847            .collect_vec();
848
849        // FIXME(yuhao): only use `dist_key_in_pk` in the proto
850        let dist_key_in_pk_indices = if table_catalog.get_dist_key_in_pk().is_empty() {
851            get_dist_key_in_pk_indices(&dist_key_indices, &pk_indices).unwrap()
852        } else {
853            table_catalog
854                .get_dist_key_in_pk()
855                .iter()
856                .map(|idx| *idx as usize)
857                .collect()
858        };
859
860        let vnode_col_idx_in_pk = table_catalog.vnode_col_index.as_ref().and_then(|idx| {
861            let vnode_col_idx = *idx as usize;
862            pk_indices.iter().position(|&i| vnode_col_idx == i)
863        });
864        let value_indices = table_catalog
865            .value_indices
866            .iter()
867            .map(|val| *val as usize)
868            .collect_vec();
869        let clean_watermark_indices = table_catalog.get_clean_watermark_column_indices();
870        if clean_watermark_indices.len() > 1 {
871            unimplemented!("multiple clean watermark columns are not supported yet")
872        }
873        let clean_watermark_index = clean_watermark_indices.first().map(|&i| i as usize);
874
875        Self {
876            table_id,
877            table_name_for_debug: table_catalog.name.clone(),
878            table_columns,
879            order_types,
880            pk_indices,
881            dist_key_in_pk_indices,
882            vnode_col_idx_in_pk,
883            expected_vnode_count: table_catalog.vnode_count(),
884            value_indices,
885            prefix_hint_len: table_catalog.read_prefix_len_hint as usize,
886            retention_seconds: table_catalog.retention_seconds,
887            versioned: table_catalog.version.is_some(),
888            fragment_id: table_catalog.fragment_id,
889            clean_watermark_index,
890            store,
891            vnodes,
892            op_consistency_level: None,
893            output_column_ids: None,
894            preload_all_rows: (),
895            enable_vnode_key_stats: None,
896            enable_state_table_vnode_stats_pruning: false,
897            metrics: None,
898            _serde: Default::default(),
899        }
900    }
901}
902
903impl<S: StateStore, SD: ValueRowSerde, const IS_REPLICATED: bool>
904    StateTableBuilder<S, SD, IS_REPLICATED, bool>
905{
906    async fn build_inner(
907        self,
908        preload_all_rows: bool,
909        should_enable_vnode_key_stats: bool,
910    ) -> StateTableInner<S, SD, IS_REPLICATED> {
911        let table_id = self.table_id;
912        let table_columns = self.table_columns;
913        let order_types = self.order_types;
914        let pk_indices = self.pk_indices;
915        let dist_key_in_pk_indices = self.dist_key_in_pk_indices;
916        let vnode_col_idx_in_pk = self.vnode_col_idx_in_pk;
917        let prefix_hint_len = self.prefix_hint_len;
918        let metrics = self.metrics;
919
920        let op_consistency_level = self
921            .op_consistency_level
922            .unwrap_or(StateTableOpConsistencyLevel::ConsistentOldValue);
923
924        let output_column_ids = self.output_column_ids.unwrap_or_default();
925
926        let data_types: Vec<DataType> = table_columns
927            .iter()
928            .map(|col| col.data_type.clone())
929            .collect();
930
931        // For replicated state tables (used in hash temporal join), the join key (pk_prefix) is
932        // guaranteed by the optimizer to cover the distribution key, which is required by
933        // `compute_prefix_vnode`. Assert this invariant at build time.
934        if IS_REPLICATED && prefix_hint_len > 0 {
935            assert!(
936                dist_key_in_pk_indices.iter().all(|&d| d < prefix_hint_len),
937                "replicated state table: distribution key indices {:?} must all be covered by \
938                 prefix_hint_len {}",
939                dist_key_in_pk_indices,
940                prefix_hint_len,
941            );
942        }
943
944        let distribution =
945            TableDistribution::new(self.vnodes, dist_key_in_pk_indices, vnode_col_idx_in_pk);
946        assert_eq!(
947            distribution.vnode_count(),
948            self.expected_vnode_count,
949            "vnode count mismatch, scanning table {} under wrong distribution?",
950            self.table_name_for_debug,
951        );
952
953        let pk_data_types = pk_indices
954            .iter()
955            .map(|i| table_columns[*i].data_type.clone())
956            .collect();
957        let pk_serde = OrderedRowSerde::new(pk_data_types, order_types);
958
959        let input_value_indices = self.value_indices;
960
961        let no_shuffle_value_indices = (0..table_columns.len()).collect_vec();
962
963        // if value_indices is the no shuffle full columns.
964        let value_indices = match input_value_indices.len() == table_columns.len()
965            && input_value_indices == no_shuffle_value_indices
966        {
967            true => None,
968            false => Some(input_value_indices.clone()),
969        };
970
971        let row_serde = Arc::new(SD::new(
972            Arc::from_iter(input_value_indices.iter().copied()),
973            Arc::from(table_columns.clone().into_boxed_slice()),
974        ));
975
976        let state_table_op_consistency_level = op_consistency_level;
977        let op_consistency_level = match state_table_op_consistency_level {
978            StateTableOpConsistencyLevel::Inconsistent => OpConsistencyLevel::Inconsistent,
979            StateTableOpConsistencyLevel::ConsistentOldValue => {
980                consistent_old_value_op(row_serde.clone(), false)
981            }
982            StateTableOpConsistencyLevel::LogStoreEnabled => {
983                consistent_old_value_op(row_serde.clone(), true)
984            }
985        };
986
987        let table_option = TableOption::new(self.retention_seconds);
988        let new_local_options = if IS_REPLICATED {
989            NewLocalOptions::new_replicated(
990                table_id,
991                self.fragment_id,
992                op_consistency_level,
993                table_option,
994                distribution.vnodes().clone(),
995            )
996        } else {
997            NewLocalOptions::new(
998                table_id,
999                self.fragment_id,
1000                op_consistency_level,
1001                table_option,
1002                distribution.vnodes().clone(),
1003                true,
1004            )
1005        };
1006        let local_state_store = self.store.new_local(new_local_options).await;
1007
1008        // If state table has versioning, that means it supports
1009        // Schema change. In that case, the row encoding should be column aware as well.
1010        // Otherwise both will be false.
1011        // NOTE(kwannoel): Replicated table will follow upstream table's versioning. I'm not sure
1012        // If ALTER TABLE will propagate to this replicated table as well. Ideally it won't
1013        assert_eq!(self.versioned, row_serde.kind().is_column_aware());
1014
1015        // Get info for replicated state table.
1016        let output_column_ids_to_input_idx = output_column_ids
1017            .iter()
1018            .enumerate()
1019            .map(|(pos, id)| (*id, pos))
1020            .collect::<HashMap<_, _>>();
1021
1022        let columns = table_columns;
1023
1024        // Compute i2o mapping
1025        // Note that this can be a partial mapping, since we use the i2o mapping to get
1026        // any 1 of the output columns, and use that to fill the input column.
1027        let mut i2o_mapping = vec![None; columns.len()];
1028        for (i, column) in columns.iter().enumerate() {
1029            if let Some(pos) = output_column_ids_to_input_idx.get(&column.column_id) {
1030                i2o_mapping[i] = Some(*pos);
1031            }
1032        }
1033        // We can prune any duplicate column indices
1034        let i2o_mapping = ColIndexMapping::new(i2o_mapping, output_column_ids.len());
1035
1036        // Compute output indices
1037        let (_, output_indices) = find_columns_by_ids(&columns[..], &output_column_ids);
1038
1039        // For replicated state tables, pk columns must be explicitly provided by the write caller
1040        // rather than being filled with None internally via i2o_mapping.
1041        if IS_REPLICATED {
1042            assert!(
1043                pk_indices
1044                    .iter()
1045                    .all(|&pk_idx| output_indices.contains(&pk_idx)),
1046                "all pk columns must be included in output_column_ids for replicated state table"
1047            );
1048        }
1049
1050        let clean_watermark_index = self.clean_watermark_index;
1051        let watermark_serde = clean_watermark_index.map(|idx| {
1052            let pk_idx = pk_indices.iter().position(|&i| i == idx);
1053            let (watermark_serde, watermark_serde_type) = match pk_idx {
1054                Some(0) => (pk_serde.index(0).into_owned(), WatermarkSerdeType::PkPrefix),
1055                Some(pk_idx) => (
1056                    pk_serde.index(pk_idx).into_owned(),
1057                    WatermarkSerdeType::NonPkPrefix,
1058                ),
1059                None => (
1060                    OrderedRowSerde::new(
1061                        vec![data_types[idx].clone()],
1062                        vec![OrderType::ascending()],
1063                    ),
1064                    WatermarkSerdeType::Value,
1065                ),
1066            };
1067            (watermark_serde, watermark_serde_type)
1068        });
1069
1070        // Restore persisted table watermark.
1071        let committed_watermark = if let Some((deser, _)) = watermark_serde.as_ref() {
1072            distribution
1073                .vnodes()
1074                .iter_vnodes()
1075                .filter_map(|vnode| {
1076                    let bytes = local_state_store.get_table_watermark(vnode)?;
1077                    let datum = deser.deserialize(&bytes).ok().and_then(|row| {
1078                        assert!(row.len() == 1);
1079                        row[0].clone()
1080                    });
1081                    if datum.is_none() {
1082                        tracing::error!(
1083                            ?vnode,
1084                            watermark = ?bytes,
1085                            "Failed to deserialize persisted watermark from state store.",
1086                        );
1087                    }
1088                    datum
1089                })
1090                .max_by(|a, b| cmp_datum(Some(a), Some(b), OrderType::ascending()))
1091        } else {
1092            None
1093        };
1094
1095        StateTableInner {
1096            table_id,
1097            row_store: StateTableRowStore {
1098                all_rows: preload_all_rows.then(HashMap::new),
1099                pending_watermark: None,
1100                state_store: local_state_store,
1101                row_serde,
1102                pk_serde: pk_serde.clone(),
1103                table_id,
1104                // Need to maintain vnode min/max key stats when vnode key pruning is enabled
1105                vnode_stats: should_enable_vnode_key_stats.then(HashMap::new),
1106                enable_state_table_vnode_stats_pruning: self.enable_state_table_vnode_stats_pruning,
1107                metrics,
1108            },
1109            store: self.store,
1110            epoch: None,
1111            pk_serde,
1112            pk_indices,
1113            distribution,
1114            prefix_hint_len,
1115            value_indices,
1116            pending_watermark: None,
1117            committed_watermark,
1118            watermark_serde,
1119            data_types,
1120            output_indices,
1121            i2o_mapping,
1122            op_consistency_level: state_table_op_consistency_level,
1123            clean_watermark_index,
1124            on_post_commit: false,
1125        }
1126    }
1127}
1128
1129impl<S: StateStore, SD: ValueRowSerde, const IS_REPLICATED: bool>
1130    StateTableBuilder<S, SD, IS_REPLICATED, ()>
1131{
1132    pub fn new_from_storage_table_desc(
1133        table_desc: &StorageTableDesc,
1134        store: S,
1135        vnodes: Option<Arc<Bitmap>>,
1136        fragment_id: FragmentId,
1137    ) -> Self {
1138        let table_id = table_desc.table_id;
1139        let table_columns: Vec<ColumnDesc> =
1140            table_desc.columns.iter().map(ColumnDesc::from).collect();
1141        let order_types: Vec<OrderType> = table_desc
1142            .pk
1143            .iter()
1144            .map(|col_order| OrderType::from_protobuf(col_order.get_order_type().unwrap()))
1145            .collect();
1146        let pk_indices = table_desc
1147            .pk
1148            .iter()
1149            .map(|col_order| col_order.column_index as usize)
1150            .collect_vec();
1151        let dist_key_in_pk_indices = table_desc
1152            .dist_key_in_pk_indices
1153            .iter()
1154            .map(|&idx| idx as usize)
1155            .collect();
1156        // StorageTableDesc provides vnode_col_idx_in_pk directly (already pk-relative),
1157        // unlike Table which has an absolute column index that needs conversion.
1158        let vnode_col_idx_in_pk = table_desc.vnode_col_idx_in_pk.map(|k| k as usize);
1159        let raw_value_indices = table_desc
1160            .value_indices
1161            .iter()
1162            .map(|val| *val as usize)
1163            .collect_vec();
1164
1165        Self {
1166            table_id,
1167            table_name_for_debug: table_id.to_string(),
1168            table_columns,
1169            order_types,
1170            pk_indices,
1171            dist_key_in_pk_indices,
1172            vnode_col_idx_in_pk,
1173            expected_vnode_count: table_desc.vnode_count(),
1174            value_indices: raw_value_indices,
1175            prefix_hint_len: table_desc.read_prefix_len_hint as usize,
1176            retention_seconds: table_desc.retention_seconds,
1177            versioned: table_desc.versioned,
1178            fragment_id,
1179            clean_watermark_index: None,
1180            store,
1181            vnodes,
1182            op_consistency_level: None,
1183            output_column_ids: None,
1184            preload_all_rows: (),
1185            enable_vnode_key_stats: None,
1186            enable_state_table_vnode_stats_pruning: false,
1187            metrics: None,
1188            _serde: Default::default(),
1189        }
1190    }
1191}
1192
1193impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
1194where
1195    S: StateStore,
1196    SD: ValueRowSerde,
1197{
1198    pub fn get_data_types(&self) -> &[DataType] {
1199        &self.data_types
1200    }
1201
1202    pub fn table_id(&self) -> TableId {
1203        self.table_id
1204    }
1205
1206    /// Get the vnode value with given (prefix of) primary key
1207    fn compute_prefix_vnode(&self, pk_prefix: &impl Row) -> VirtualNode {
1208        self.distribution
1209            .try_compute_vnode_by_pk_prefix(pk_prefix)
1210            .expect("For streaming, the given prefix must be enough to calculate the vnode")
1211    }
1212
1213    /// Get the vnode value of the given primary key
1214    pub fn compute_vnode_by_pk(&self, pk: impl Row) -> VirtualNode {
1215        self.distribution.compute_vnode_by_pk(pk)
1216    }
1217
1218    /// NOTE(kwannoel): This is used by backfill.
1219    /// We want to check pk indices of upstream table.
1220    pub fn pk_indices(&self) -> &[usize] {
1221        &self.pk_indices
1222    }
1223
1224    /// Get the indices of the primary key columns in the output columns.
1225    ///
1226    /// Returns `None` if any of the primary key columns is not in the output columns.
1227    pub fn pk_in_output_indices(&self) -> Option<Vec<usize>> {
1228        assert!(IS_REPLICATED);
1229        self.pk_indices
1230            .iter()
1231            .map(|&i| self.output_indices.iter().position(|&j| i == j))
1232            .collect()
1233    }
1234
1235    pub fn pk_serde(&self) -> &OrderedRowSerde {
1236        &self.pk_serde
1237    }
1238
1239    pub fn vnodes(&self) -> &Arc<Bitmap> {
1240        self.distribution.vnodes()
1241    }
1242
1243    pub fn flushed_snapshot_reader(&self) -> FlushedStateTableReader<S, SD> {
1244        StateTableFlushedSnapshotReader {
1245            reader: Arc::new(self.row_store.state_store.new_flushed_snapshot_reader()),
1246            pk_serde: self.pk_serde.clone(),
1247            vnodes: self.distribution.vnodes().clone(),
1248            row_serde: self.row_store.row_serde.clone(),
1249            metrics: self.row_store.metrics.clone(),
1250        }
1251    }
1252
1253    pub fn value_indices(&self) -> &Option<Vec<usize>> {
1254        &self.value_indices
1255    }
1256
1257    pub fn is_consistent_op(&self) -> bool {
1258        matches!(
1259            self.op_consistency_level,
1260            StateTableOpConsistencyLevel::ConsistentOldValue
1261                | StateTableOpConsistencyLevel::LogStoreEnabled
1262        )
1263    }
1264
1265    pub fn metrics(&self) -> Option<&StateTableMetrics> {
1266        self.row_store.metrics.as_ref()
1267    }
1268}
1269
1270impl<S, SD> StateTableInner<S, SD, true>
1271where
1272    S: StateStore,
1273    SD: ValueRowSerde,
1274{
1275    /// Create replicated state table from table catalog with output indices
1276    pub async fn new_replicated(
1277        table_catalog: &Table,
1278        store: S,
1279        vnodes: Option<Arc<Bitmap>>,
1280        output_column_ids: Vec<ColumnId>,
1281    ) -> Self {
1282        // TODO: can it be ConsistentOldValue?
1283        // TODO: may enable preload_all_rows
1284        StateTableBuilder::new(table_catalog, store, vnodes)
1285            .with_op_consistency_level(StateTableOpConsistencyLevel::Inconsistent)
1286            .with_output_column_ids(output_column_ids)
1287            .forbid_preload_all_rows()
1288            .build()
1289            .await
1290    }
1291}
1292
1293// point get
1294impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
1295where
1296    S: StateStore,
1297    SD: ValueRowSerde,
1298{
1299    /// Get a single row from state table.
1300    pub async fn get_row(&self, pk: impl Row) -> StreamExecutorResult<Option<OwnedRow>> {
1301        let (serialized_pk, prefix_hint) = self.serialize_pk_and_get_prefix_hint(&pk);
1302        let row = self.row_store.get(serialized_pk, prefix_hint).await?;
1303        match row {
1304            Some(row) => {
1305                if IS_REPLICATED {
1306                    // If the table is replicated, we need to deserialize the row with the output
1307                    // indices.
1308                    let row = row.project(&self.output_indices);
1309                    Ok(Some(row.into_owned_row()))
1310                } else {
1311                    Ok(Some(row))
1312                }
1313            }
1314            None => Ok(None),
1315        }
1316    }
1317
1318    /// Get a raw encoded row from state table.
1319    pub async fn exists(&self, pk: impl Row) -> StreamExecutorResult<bool> {
1320        let (serialized_pk, prefix_hint) = self.serialize_pk_and_get_prefix_hint(&pk);
1321        self.row_store.exists(serialized_pk, prefix_hint).await
1322    }
1323
1324    fn serialize_pk(&self, pk: &impl Row) -> TableKey<Bytes> {
1325        assert!(pk.len() <= self.pk_indices.len());
1326        serialize_pk_with_vnode(pk, &self.pk_serde, self.compute_vnode_by_pk(pk))
1327    }
1328
1329    fn serialize_pk_and_get_prefix_hint(&self, pk: &impl Row) -> (TableKey<Bytes>, Option<Bytes>) {
1330        let serialized_pk = self.serialize_pk(&pk);
1331        let prefix_hint = if should_calculate_prefix_hint(self.prefix_hint_len, pk.len(), false) {
1332            Some(serialized_pk.slice(VirtualNode::SIZE..))
1333        } else {
1334            #[cfg(debug_assertions)]
1335            if self.prefix_hint_len != 0 {
1336                warn!(
1337                    "prefix_hint_len is not equal to pk.len(), may not be able to utilize bloom filter"
1338                );
1339            }
1340            None
1341        };
1342        (serialized_pk, prefix_hint)
1343    }
1344}
1345
1346impl<LS: LocalStateStore, SD: ValueRowSerde> StateTableRowStore<LS, SD> {
1347    async fn get(
1348        &self,
1349        key_bytes: TableKey<Bytes>,
1350        prefix_hint: Option<Bytes>,
1351    ) -> StreamExecutorResult<Option<OwnedRow>> {
1352        if let Some(m) = &self.metrics {
1353            m.get_count.inc();
1354        }
1355        if let Some(rows) = &self.all_rows {
1356            let (vnode, key) = key_bytes.split_vnode_bytes();
1357            return Ok(rows.get(&vnode).expect("covered vnode").get(&key).cloned());
1358        }
1359        if self.below_pending_watermark(&key_bytes) {
1360            return Ok(None);
1361        }
1362
1363        // Try to prune using vnode statistics
1364        let should_prune = if let Some(stats) = &self.vnode_stats
1365            && let (vnode, key) = key_bytes.split_vnode_bytes()
1366            && let Some(vnode_stat) = stats.get(&vnode)
1367            && vnode_stat.can_prune(&key)
1368        {
1369            if let Some(m) = &self.metrics {
1370                m.get_vnode_pruned_count.inc();
1371            }
1372            true
1373        } else {
1374            false
1375        };
1376
1377        if should_prune && self.enable_state_table_vnode_stats_pruning {
1378            return Ok(None);
1379        }
1380
1381        let read_options = ReadOptions {
1382            prefix_hint,
1383            cache_policy: CachePolicy::Fill(Hint::Normal),
1384            ..Default::default()
1385        };
1386
1387        let result = self
1388            .state_store
1389            .on_key_value(key_bytes, read_options, move |_, value| {
1390                let row = self.row_serde.deserialize(value)?;
1391                Ok(OwnedRow::new(row))
1392            })
1393            .await
1394            .map_err(Into::<StreamExecutorError>::into)?;
1395
1396        // In dry-run mode, verify that pruning would have been correct
1397        if should_prune && result.is_some() {
1398            tracing::warn!(
1399                table_id = %self.table_id,
1400                "vnode stats pruning dry run fails for get. This will not affect correctness."
1401            );
1402        }
1403
1404        Ok(result)
1405    }
1406
1407    async fn exists(
1408        &self,
1409        key_bytes: TableKey<Bytes>,
1410        prefix_hint: Option<Bytes>,
1411    ) -> StreamExecutorResult<bool> {
1412        if let Some(m) = &self.metrics {
1413            m.get_count.inc();
1414        }
1415        if let Some(rows) = &self.all_rows {
1416            let (vnode, key) = key_bytes.split_vnode_bytes();
1417            return Ok(rows.get(&vnode).expect("covered vnode").contains_key(&key));
1418        }
1419        if self.below_pending_watermark(&key_bytes) {
1420            return Ok(false);
1421        }
1422
1423        // Try to prune using vnode statistics
1424        let should_prune = if let Some(stats) = &self.vnode_stats
1425            && let (vnode, key) = key_bytes.split_vnode_bytes()
1426            && let Some(vnode_stat) = stats.get(&vnode)
1427            && vnode_stat.can_prune(&key)
1428        {
1429            if let Some(m) = &self.metrics {
1430                m.get_vnode_pruned_count.inc();
1431            }
1432            true
1433        } else {
1434            false
1435        };
1436
1437        if should_prune && self.enable_state_table_vnode_stats_pruning {
1438            return Ok(false);
1439        }
1440
1441        let read_options = ReadOptions {
1442            prefix_hint,
1443            cache_policy: CachePolicy::Fill(Hint::Normal),
1444            ..Default::default()
1445        };
1446        let result = self
1447            .state_store
1448            .on_key_value(key_bytes, read_options, move |_, _| Ok(()))
1449            .await?;
1450        let exists = result.is_some();
1451
1452        // In dry-run mode, verify that pruning would have been correct
1453        if should_prune && exists {
1454            tracing::warn!(
1455                table_id = %self.table_id,
1456                "vnode stats pruning dry run fails for exists. This will not affect correctness."
1457            );
1458        }
1459
1460        Ok(exists)
1461    }
1462}
1463
1464/// A callback struct returned from [`StateTableInner::commit`].
1465///
1466/// Introduced to support single barrier configuration change proposed in <https://github.com/risingwavelabs/risingwave/issues/18312>.
1467/// In brief, to correctly handle the configuration change, when each stateful executor receives an upstream barrier, it should handle
1468/// the barrier in the order of `state_table.commit()` -> `yield barrier` -> `update_vnode_bitmap`.
1469///
1470/// The `StateTablePostCommit` captures the mutable reference of `state_table` when calling `state_table.commit()`, and after the executor
1471/// runs `yield barrier`, it should call `StateTablePostCommit::post_yield_barrier` to apply the vnode bitmap update if there is any.
1472/// The `StateTablePostCommit` is marked with `must_use`. The method name `post_yield_barrier` indicates that it should be called after
1473/// we have yielded the barrier. In `StateTable`, we add a flag `on_post_commit`, to indicate that whether the `StateTablePostCommit` is handled
1474/// properly. On `state_table.commit()`, we will mark the `on_post_commit` as true, and in `StateTablePostCommit::post_yield_barrier`, we will
1475/// remark the flag as false, and on `state_table.commit()`, we will assert that the `on_post_commit` must be false. Note that, the `post_yield_barrier`
1476/// should be called for all barriers rather than only for the barrier with update vnode bitmap. In this way, though we don't have scale test for all
1477/// streaming executor, we can ensure that all executor covered by normal e2e test have properly handled the `StateTablePostCommit`.
1478#[must_use]
1479pub struct StateTablePostCommit<'a, S, SD = BasicSerde, const IS_REPLICATED: bool = false>
1480where
1481    S: StateStore,
1482    SD: ValueRowSerde,
1483{
1484    inner: &'a mut StateTableInner<S, SD, IS_REPLICATED>,
1485}
1486
1487impl<'a, S, SD, const IS_REPLICATED: bool> StateTablePostCommit<'a, S, SD, IS_REPLICATED>
1488where
1489    S: StateStore,
1490    SD: ValueRowSerde,
1491{
1492    /// Returns `Some((new_vnodes, old_vnodes, state_table), keyed_cache_may_stale)` if the vnode bitmap is updated.
1493    ///
1494    /// Note the `keyed_cache_may_stale` only applies to keyed cache. If the executor's cache is not keyed, but will
1495    /// be consumed with all vnodes it owns, the executor may need to ALWAYS clear the cache regardless of this flag.
1496    pub async fn post_yield_barrier(
1497        mut self,
1498        new_vnodes: Option<Arc<Bitmap>>,
1499    ) -> StreamExecutorResult<
1500        Option<(
1501            (
1502                Arc<Bitmap>,
1503                Arc<Bitmap>,
1504                &'a mut StateTableInner<S, SD, IS_REPLICATED>,
1505            ),
1506            bool,
1507        )>,
1508    > {
1509        self.inner.on_post_commit = false;
1510        Ok(if let Some(new_vnodes) = new_vnodes {
1511            let (old_vnodes, keyed_cache_may_stale) =
1512                self.update_vnode_bitmap(new_vnodes.clone()).await?;
1513            Some(((new_vnodes, old_vnodes, self.inner), keyed_cache_may_stale))
1514        } else {
1515            None
1516        })
1517    }
1518
1519    pub fn inner(&self) -> &StateTableInner<S, SD, IS_REPLICATED> {
1520        &*self.inner
1521    }
1522
1523    /// Update the vnode bitmap of the state table, returns the previous vnode bitmap.
1524    async fn update_vnode_bitmap(
1525        &mut self,
1526        new_vnodes: Arc<Bitmap>,
1527    ) -> StreamExecutorResult<(Arc<Bitmap>, bool)> {
1528        let prev_vnodes = self
1529            .inner
1530            .row_store
1531            .update_vnode_bitmap(new_vnodes.clone())
1532            .await?;
1533        assert_eq!(
1534            &prev_vnodes,
1535            self.inner.vnodes(),
1536            "state table and state store vnode bitmap mismatches"
1537        );
1538
1539        if self.inner.distribution.is_singleton() {
1540            assert_eq!(
1541                &new_vnodes,
1542                self.inner.vnodes(),
1543                "should not update vnode bitmap for singleton table"
1544            );
1545        }
1546        assert_eq!(self.inner.vnodes().len(), new_vnodes.len());
1547
1548        let keyed_cache_may_stale = keyed_cache_may_stale(self.inner.vnodes(), &new_vnodes);
1549
1550        if keyed_cache_may_stale {
1551            self.inner.pending_watermark = None;
1552            self.inner.row_store.pending_watermark = None;
1553        }
1554
1555        Ok((
1556            self.inner.distribution.update_vnode_bitmap(new_vnodes),
1557            keyed_cache_may_stale,
1558        ))
1559    }
1560}
1561
1562// write
1563impl<LS: LocalStateStore, SD: ValueRowSerde> StateTableRowStore<LS, SD> {
1564    fn handle_mem_table_error(&self, e: StorageError) {
1565        let e = match e.into_inner() {
1566            ErrorKind::MemTable(e) => e,
1567            _ => unreachable!("should only get memtable error"),
1568        };
1569        match *e {
1570            MemTableError::InconsistentOperation { key, prev, new, .. } => {
1571                let (vnode, key) = deserialize_pk_with_vnode(&key, &self.pk_serde).unwrap();
1572                panic!(
1573                    "mem-table operation inconsistent! table_id: {}, vnode: {}, key: {:?}, prev: {}, new: {}",
1574                    self.table_id,
1575                    vnode,
1576                    key,
1577                    prev.debug_fmt(&*self.row_serde),
1578                    new.debug_fmt(&*self.row_serde),
1579                )
1580            }
1581        }
1582    }
1583
1584    fn insert(&mut self, key: TableKey<Bytes>, value: impl Row) {
1585        insane_mode_discard_point!();
1586        let value_bytes = self.row_serde.serialize(&value).into();
1587
1588        let (vnode, key_without_vnode) = key.split_vnode_bytes();
1589
1590        // Update vnode statistics (skip if all_rows is present)
1591        if self.all_rows.is_none()
1592            && let Some(stats) = &mut self.vnode_stats
1593            && let Some(vnode_stat) = stats.get_mut(&vnode)
1594        {
1595            vnode_stat.update_with_key(&key_without_vnode);
1596        }
1597
1598        if let Some(rows) = &mut self.all_rows {
1599            rows.get_mut(&vnode)
1600                .expect("covered vnode")
1601                .insert(key_without_vnode, value.into_owned_row());
1602        }
1603        self.state_store
1604            .insert(key, value_bytes, None)
1605            .unwrap_or_else(|e| self.handle_mem_table_error(e));
1606    }
1607
1608    fn delete(&mut self, key: TableKey<Bytes>, value: impl Row) {
1609        insane_mode_discard_point!();
1610        let value_bytes = self.row_serde.serialize(value).into();
1611
1612        let (vnode, key_without_vnode) = key.split_vnode_bytes();
1613
1614        if self.all_rows.is_none()
1615            && let Some(stats) = &mut self.vnode_stats
1616            && let Some(vnode_stat) = stats.get_mut(&vnode)
1617        {
1618            vnode_stat.update_with_key(&key_without_vnode);
1619        }
1620
1621        if let Some(rows) = &mut self.all_rows {
1622            rows.get_mut(&vnode)
1623                .expect("covered vnode")
1624                .remove(&key_without_vnode);
1625        }
1626        self.state_store
1627            .delete(key, value_bytes)
1628            .unwrap_or_else(|e| self.handle_mem_table_error(e));
1629    }
1630
1631    fn update(&mut self, key_bytes: TableKey<Bytes>, old_value: impl Row, new_value: impl Row) {
1632        insane_mode_discard_point!();
1633        let new_value_bytes = self.row_serde.serialize(&new_value).into();
1634        let old_value_bytes = self.row_serde.serialize(old_value).into();
1635
1636        let (vnode, key_without_vnode) = key_bytes.split_vnode_bytes();
1637
1638        // Update does not change the key, so statistics remain valid (skip if all_rows is present)
1639        // But we update to ensure consistency
1640        if self.all_rows.is_none()
1641            && let Some(stats) = &mut self.vnode_stats
1642            && let Some(vnode_stat) = stats.get_mut(&vnode)
1643        {
1644            vnode_stat.update_with_key(&key_without_vnode);
1645        }
1646
1647        if let Some(rows) = &mut self.all_rows {
1648            rows.get_mut(&vnode)
1649                .expect("covered vnode")
1650                .insert(key_without_vnode, new_value.into_owned_row());
1651        }
1652        self.state_store
1653            .insert(key_bytes, new_value_bytes, Some(old_value_bytes))
1654            .unwrap_or_else(|e| self.handle_mem_table_error(e));
1655    }
1656}
1657
1658impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
1659where
1660    S: StateStore,
1661    SD: ValueRowSerde,
1662{
1663    /// Insert a row into state table. Must provide a full row corresponding to the column desc of
1664    /// the table.
1665    pub fn insert(&mut self, value: impl Row) {
1666        let pk_indices = &self.pk_indices;
1667        let pk = (&value).project(pk_indices);
1668
1669        let key_bytes = self.serialize_pk(&pk);
1670        dispatch_value_indices!(&self.value_indices, [value], {
1671            self.row_store.insert(key_bytes, value)
1672        })
1673    }
1674
1675    /// Delete a row from state table. Must provide a full row of old value corresponding to the
1676    /// column desc of the table.
1677    pub fn delete(&mut self, old_value: impl Row) {
1678        let pk_indices = &self.pk_indices;
1679        let pk = (&old_value).project(pk_indices);
1680
1681        let key_bytes = self.serialize_pk(&pk);
1682        dispatch_value_indices!(&self.value_indices, [old_value], {
1683            self.row_store.delete(key_bytes, old_value)
1684        })
1685    }
1686
1687    /// Update a row. The old and new value should have the same pk.
1688    pub fn update(&mut self, old_value: impl Row, new_value: impl Row) {
1689        let old_pk = (&old_value).project(self.pk_indices());
1690        let new_pk = (&new_value).project(self.pk_indices());
1691        debug_assert!(
1692            Row::eq(&old_pk, new_pk),
1693            "pk should not change: {old_pk:?} vs {new_pk:?}. {}",
1694            self.table_id
1695        );
1696
1697        let key_bytes = self.serialize_pk(&new_pk);
1698        dispatch_value_indices!(&self.value_indices, [old_value, new_value], {
1699            self.row_store.update(key_bytes, old_value, new_value)
1700        })
1701    }
1702
1703    /// Write a record into state table. Must have the same schema with the table.
1704    pub fn write_record(&mut self, record: Record<impl Row>) {
1705        match record {
1706            Record::Insert { new_row } => self.insert(new_row),
1707            Record::Delete { old_row } => self.delete(old_row),
1708            Record::Update { old_row, new_row } => self.update(old_row, new_row),
1709        }
1710    }
1711
1712    fn fill_non_output_indices(&self, chunk: StreamChunk) -> StreamChunk {
1713        fill_non_output_indices(&self.i2o_mapping, &self.data_types, chunk)
1714    }
1715
1716    /// Write batch with a `StreamChunk` which should have the same schema with the table.
1717    // allow(izip, which use zip instead of zip_eq)
1718    #[allow(clippy::disallowed_methods)]
1719    pub fn write_chunk(&mut self, chunk: StreamChunk) {
1720        let chunk = if IS_REPLICATED {
1721            self.fill_non_output_indices(chunk)
1722        } else {
1723            chunk
1724        };
1725
1726        let vnodes = self
1727            .distribution
1728            .compute_chunk_vnode(&chunk, &self.pk_indices);
1729
1730        for (idx, optional_row) in chunk.rows_with_holes().enumerate() {
1731            let Some((op, row)) = optional_row else {
1732                continue;
1733            };
1734            let pk = row.project(&self.pk_indices);
1735            let vnode = vnodes[idx];
1736            let key_bytes = serialize_pk_with_vnode(pk, &self.pk_serde, vnode);
1737            match op {
1738                Op::Insert | Op::UpdateInsert => {
1739                    dispatch_value_indices!(&self.value_indices, [row], {
1740                        self.row_store.insert(key_bytes, row);
1741                    });
1742                }
1743                Op::Delete | Op::UpdateDelete => {
1744                    dispatch_value_indices!(&self.value_indices, [row], {
1745                        self.row_store.delete(key_bytes, row);
1746                    });
1747                }
1748            }
1749        }
1750    }
1751
1752    /// Update watermark for state cleaning. The state store cleans the rows below it at the commit,
1753    /// but for a pk-prefix watermark, reads stop returning them at once.
1754    ///
1755    /// # Arguments
1756    ///
1757    /// * `watermark` - Latest watermark received.
1758    pub fn update_watermark(&mut self, watermark: ScalarImpl) {
1759        trace!(table_id = %self.table_id, watermark = ?watermark, "update watermark");
1760        if let Some((direction, vnode_watermark, WatermarkSerdeType::PkPrefix)) =
1761            self.serialize_watermark(&watermark)
1762        {
1763            self.row_store
1764                .update_pending_watermark(direction, vnode_watermark);
1765        }
1766        self.pending_watermark = Some(watermark);
1767    }
1768
1769    /// Get the committed watermark of the state table. Watermarks should be fed into the state
1770    /// table through `update_watermark` method.
1771    pub fn get_committed_watermark(&self) -> Option<&ScalarImpl> {
1772        self.committed_watermark.as_ref()
1773    }
1774
1775    /// Get the watermark fed through `update_watermark` in the current epoch, which the state store
1776    /// applies at the commit.
1777    pub fn get_pending_watermark(&self) -> Option<&ScalarImpl> {
1778        self.pending_watermark.as_ref()
1779    }
1780
1781    pub async fn commit(
1782        &mut self,
1783        new_epoch: EpochPair,
1784    ) -> StreamExecutorResult<StateTablePostCommit<'_, S, SD, IS_REPLICATED>> {
1785        self.commit_inner(new_epoch, None).await
1786    }
1787
1788    #[cfg(test)]
1789    pub async fn commit_for_test(&mut self, new_epoch: EpochPair) -> StreamExecutorResult<()> {
1790        self.commit_assert_no_update_vnode_bitmap(new_epoch).await
1791    }
1792
1793    pub async fn commit_assert_no_update_vnode_bitmap(
1794        &mut self,
1795        new_epoch: EpochPair,
1796    ) -> StreamExecutorResult<()> {
1797        let post_commit = self.commit_inner(new_epoch, None).await?;
1798        post_commit.post_yield_barrier(None).await?;
1799        Ok(())
1800    }
1801
1802    pub async fn commit_may_switch_consistent_op(
1803        &mut self,
1804        new_epoch: EpochPair,
1805        op_consistency_level: StateTableOpConsistencyLevel,
1806    ) -> StreamExecutorResult<StateTablePostCommit<'_, S, SD, IS_REPLICATED>> {
1807        if self.op_consistency_level != op_consistency_level {
1808            // avoid flooding e2e-test log
1809            if !cfg!(debug_assertions) {
1810                info!(
1811                    ?new_epoch,
1812                    prev_op_consistency_level = ?self.op_consistency_level,
1813                    ?op_consistency_level,
1814                    table_id = %self.table_id,
1815                    "switch to new op consistency level"
1816                );
1817            }
1818            self.commit_inner(new_epoch, Some(op_consistency_level))
1819                .await
1820        } else {
1821            self.commit_inner(new_epoch, None).await
1822        }
1823    }
1824
1825    async fn commit_inner(
1826        &mut self,
1827        new_epoch: EpochPair,
1828        switch_consistent_op: Option<StateTableOpConsistencyLevel>,
1829    ) -> StreamExecutorResult<StateTablePostCommit<'_, S, SD, IS_REPLICATED>> {
1830        assert!(!self.on_post_commit);
1831        assert_eq!(
1832            self.epoch.expect("should only be called after init").curr,
1833            new_epoch.prev
1834        );
1835        if let Some(new_consistency_level) = switch_consistent_op {
1836            assert_ne!(self.op_consistency_level, new_consistency_level);
1837            self.op_consistency_level = new_consistency_level;
1838        }
1839        trace!(
1840            table_id = %self.table_id,
1841            epoch = ?self.epoch,
1842            "commit state table"
1843        );
1844
1845        let table_watermarks = self.commit_pending_watermark();
1846        self.row_store
1847            .seal_current_epoch(new_epoch.curr, table_watermarks, switch_consistent_op)
1848            .instrument_await(await_tree::span!(
1849                "state_table_commit table_id={} epoch={}",
1850                self.table_id,
1851                new_epoch.curr
1852            ))
1853            .await?;
1854        self.epoch = Some(new_epoch);
1855
1856        self.on_post_commit = true;
1857        Ok(StateTablePostCommit { inner: self })
1858    }
1859
1860    /// Commit pending watermark and return vnode bitmap-watermark pairs to seal.
1861    fn commit_pending_watermark(
1862        &mut self,
1863    ) -> Option<(WatermarkDirection, Vec<VnodeWatermark>, WatermarkSerdeType)> {
1864        let watermark = self.pending_watermark.take()?;
1865        trace!(table_id = %self.table_id, watermark = ?watermark, "state cleaning");
1866
1867        assert!(
1868            !self.pk_indices().is_empty(),
1869            "see pending watermark on empty pk"
1870        );
1871        let (direction, vnode_watermark, watermark_type) = self
1872            .serialize_watermark(&watermark)
1873            .expect("watermark serde should be initialized to commit watermark");
1874        trace!(table_id = %self.table_id, ?vnode_watermark, "table watermark");
1875
1876        // The state store applies the watermark from now on.
1877        self.row_store.pending_watermark = None;
1878        self.committed_watermark = Some(watermark);
1879        Some((direction, vec![vnode_watermark], watermark_type))
1880    }
1881
1882    /// Serialize `watermark` into the watermark of the vnodes of this table, with its direction and
1883    /// serde type. Returns `None` if the table has no clean watermark column.
1884    fn serialize_watermark(
1885        &self,
1886        watermark: &ScalarImpl,
1887    ) -> Option<(WatermarkDirection, VnodeWatermark, WatermarkSerdeType)> {
1888        let (watermark_serializer, watermark_type) = self.watermark_serde.as_ref()?;
1889        let watermark_suffix =
1890            serialize_row(row::once(Some(watermark.clone())), watermark_serializer);
1891        let vnode_watermark = VnodeWatermark::new(
1892            self.vnodes().clone(),
1893            Bytes::copy_from_slice(watermark_suffix.as_ref()),
1894        );
1895        let order_type = watermark_serializer.get_order_types().get(0).unwrap();
1896        let direction = if order_type.is_ascending() {
1897            WatermarkDirection::Ascending
1898        } else {
1899            WatermarkDirection::Descending
1900        };
1901        Some((direction, vnode_watermark, *watermark_type))
1902    }
1903
1904    pub async fn try_flush(&mut self) -> StreamExecutorResult<()> {
1905        self.row_store.try_flush().await?;
1906        Ok(())
1907    }
1908}
1909
1910// Manually expand trait alias for better IDE experience.
1911pub trait RowStream<'a>: Stream<Item = StreamExecutorResult<OwnedRow>> + 'a {}
1912impl<'a, S: Stream<Item = StreamExecutorResult<OwnedRow>> + 'a> RowStream<'a> for S {}
1913
1914pub trait KeyedRowStream<'a>: Stream<Item = StreamExecutorResult<KeyedRow<Bytes>>> + 'a {}
1915impl<'a, S: Stream<Item = StreamExecutorResult<KeyedRow<Bytes>>> + 'a> KeyedRowStream<'a> for S {}
1916
1917pub trait PkRowStream<'a, K>: Stream<Item = StreamExecutorResult<(K, OwnedRow)>> + 'a {}
1918impl<'a, K, S: Stream<Item = StreamExecutorResult<(K, OwnedRow)>> + 'a> PkRowStream<'a, K> for S {}
1919
1920pub type BoxedRowStream<'a> = BoxStream<'a, StreamExecutorResult<OwnedRow>>;
1921
1922pub trait FromVnodeBytes {
1923    fn from_vnode_bytes(vnode: VirtualNode, bytes: &Bytes) -> Self;
1924}
1925
1926impl FromVnodeBytes for Bytes {
1927    fn from_vnode_bytes(vnode: VirtualNode, bytes: &Bytes) -> Self {
1928        prefix_slice_with_vnode(vnode, bytes)
1929    }
1930}
1931
1932impl FromVnodeBytes for () {
1933    fn from_vnode_bytes(_vnode: VirtualNode, _bytes: &Bytes) -> Self {}
1934}
1935
1936impl<R, SD> StateTableFlushedSnapshotReader<R, SD>
1937where
1938    R: StateStoreRead,
1939    SD: ValueRowSerde,
1940{
1941    pub fn vnodes(&self) -> &Arc<Bitmap> {
1942        &self.vnodes
1943    }
1944
1945    /// Scans flushed local state without reading uncommitted mem-table data.
1946    pub async fn iter_with_vnode(
1947        &self,
1948        vnode: VirtualNode,
1949        pk_range: &(Bound<impl Row>, Bound<impl Row>),
1950        prefetch_options: PrefetchOptions,
1951    ) -> StreamExecutorResult<impl RowStream<'static>> {
1952        if let Some(m) = &self.metrics {
1953            m.iter_count.inc();
1954        }
1955
1956        let memcomparable_range = prefix_range_to_memcomparable(&self.pk_serde, pk_range);
1957        let iter = self
1958            .reader
1959            .iter(
1960                prefixed_range_with_vnode(memcomparable_range, vnode),
1961                ReadOptions {
1962                    prefix_hint: None,
1963                    prefetch_options,
1964                    cache_policy: CachePolicy::Fill(Hint::Normal),
1965                },
1966            )
1967            .await?;
1968        let row_serde = self.row_serde.clone();
1969        Ok(iter
1970            .into_stream(move |(_key, value)| Ok(OwnedRow::new(row_serde.deserialize(value)?)))
1971            .map_err(Into::into))
1972    }
1973}
1974
1975// Iterator functions
1976impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
1977where
1978    S: StateStore,
1979    SD: ValueRowSerde,
1980{
1981    /// This function scans rows from the relational table with specific `pk_range` under the same
1982    /// `vnode`.
1983    pub async fn iter_with_vnode(
1984        &self,
1985
1986        // Optional vnode that returns an iterator only over the given range under that vnode.
1987        // For now, we require this parameter, and will panic. In the future, when `None`, we can
1988        // iterate over each vnode that the `StateTableInner` owns.
1989        vnode: VirtualNode,
1990        pk_range: &(Bound<impl Row>, Bound<impl Row>),
1991        prefetch_options: PrefetchOptions,
1992    ) -> StreamExecutorResult<impl RowStream<'_>> {
1993        Ok(self
1994            .iter_kv_with_pk_range::<()>(pk_range, vnode, prefetch_options)
1995            .await?
1996            .map_ok(|(_, row)| {
1997                if IS_REPLICATED {
1998                    row.project(&self.output_indices).into_owned_row()
1999                } else {
2000                    row
2001                }
2002            }))
2003    }
2004
2005    pub async fn iter_keyed_row_with_vnode(
2006        &self,
2007        vnode: VirtualNode,
2008        pk_range: &(Bound<impl Row>, Bound<impl Row>),
2009        prefetch_options: PrefetchOptions,
2010    ) -> StreamExecutorResult<impl KeyedRowStream<'_>> {
2011        Ok(self
2012            .iter_kv_with_pk_range(pk_range, vnode, prefetch_options)
2013            .await?
2014            .map_ok(|(key, row)| KeyedRow::new(TableKey(key), row)))
2015    }
2016}
2017
2018impl<LS: LocalStateStore, SD: ValueRowSerde> StateTableRowStore<LS, SD> {
2019    // The lowest-level API.
2020    /// Middle-level APIs:
2021    /// - [`StateTableInner::iter_with_prefix_inner`]
2022    /// - [`StateTableInner::iter_kv_with_pk_range`]
2023    async fn iter_kv<K: CopyFromSlice + FromVnodeBytes>(
2024        &self,
2025        vnode: VirtualNode,
2026        (start, end): (Bound<Bytes>, Bound<Bytes>),
2027        prefix_hint: Option<Bytes>,
2028        prefetch_options: PrefetchOptions,
2029    ) -> StreamExecutorResult<impl PkRowStream<'_, K>> {
2030        if let Some(m) = &self.metrics {
2031            m.iter_count.inc();
2032        }
2033        // Check if we can prune the entire range using vnode statistics
2034        let (pruned_start, pruned_end, should_prune_entirely) = if let Some(stats) =
2035            &self.vnode_stats
2036            && let Some(vnode_stat) = stats.get(&vnode)
2037        {
2038            match vnode_stat.pruned_key_range(&start, &end) {
2039                Some((new_start, new_end)) => {
2040                    if self.enable_state_table_vnode_stats_pruning {
2041                        (new_start, new_end, false)
2042                    } else {
2043                        // In dry-run mode, we don't apply pruning but verify correctness
2044                        (start, end, false)
2045                    }
2046                }
2047                None => {
2048                    if let Some(m) = &self.metrics {
2049                        m.iter_vnode_pruned_count.inc();
2050                    }
2051                    // Mark that we should prune entirely, but handle dry-run below
2052                    (start.clone(), end.clone(), true)
2053                }
2054            }
2055        } else {
2056            (start, end, false)
2057        };
2058
2059        if should_prune_entirely && self.enable_state_table_vnode_stats_pruning {
2060            return Ok(futures::future::Either::Left(futures::stream::empty()));
2061        }
2062
2063        let table_id = self.table_id;
2064        let inspect_fn = move |result: &StreamExecutorResult<(K, OwnedRow)>| {
2065            // Only log when in dry-run mode and we would have pruned but got results
2066            if should_prune_entirely && result.is_ok() {
2067                tracing::warn!(
2068                    table_id = %table_id,
2069                    "vnode stats pruning dry run fails for iter. This will not affect correctness."
2070                );
2071            }
2072        };
2073
2074        if let Some(rows) = &self.all_rows {
2075            return Ok(futures::future::Either::Right(
2076                futures::future::Either::Left(
2077                    futures::stream::iter(
2078                        rows.get(&vnode)
2079                            .expect("covered vnode")
2080                            .range((pruned_start, pruned_end))
2081                            .map(move |(key, value)| {
2082                                Ok((K::from_vnode_bytes(vnode, key), value.clone()))
2083                            }),
2084                    )
2085                    .inspect(inspect_fn),
2086                ),
2087            ));
2088        }
2089        let read_options = ReadOptions {
2090            prefix_hint,
2091            prefetch_options,
2092            cache_policy: CachePolicy::Fill(Hint::Normal),
2093        };
2094        let mut key_range = prefixed_range_with_vnode((pruned_start, pruned_end), vnode);
2095        self.narrow_by_pending_watermark(&mut key_range);
2096
2097        Ok(futures::future::Either::Right(
2098            futures::future::Either::Right(
2099                deserialize_keyed_row_stream(
2100                    self.state_store.iter(key_range, read_options).await?,
2101                    &*self.row_serde,
2102                )
2103                .inspect(inspect_fn),
2104            ),
2105        ))
2106    }
2107
2108    async fn rev_iter_kv<K: CopyFromSlice + FromVnodeBytes>(
2109        &self,
2110        vnode: VirtualNode,
2111        (start, end): (Bound<Bytes>, Bound<Bytes>),
2112        prefix_hint: Option<Bytes>,
2113        prefetch_options: PrefetchOptions,
2114    ) -> StreamExecutorResult<impl PkRowStream<'_, K>> {
2115        if let Some(m) = &self.metrics {
2116            m.iter_count.inc();
2117        }
2118        // Check if we can prune the entire range using vnode statistics
2119        let (pruned_start, pruned_end, should_prune_entirely) = if let Some(stats) =
2120            &self.vnode_stats
2121            && let Some(vnode_stat) = stats.get(&vnode)
2122        {
2123            match vnode_stat.pruned_key_range(&start, &end) {
2124                Some((new_start, new_end)) => {
2125                    if self.enable_state_table_vnode_stats_pruning {
2126                        (new_start, new_end, false)
2127                    } else {
2128                        // In dry-run mode, we don't apply pruning but verify correctness
2129                        (start, end, false)
2130                    }
2131                }
2132                None => {
2133                    if let Some(m) = &self.metrics {
2134                        m.iter_vnode_pruned_count.inc();
2135                    }
2136                    // Mark that we should prune entirely, but handle dry-run below
2137                    (start, end, true)
2138                }
2139            }
2140        } else {
2141            (start, end, false)
2142        };
2143
2144        if should_prune_entirely && self.enable_state_table_vnode_stats_pruning {
2145            return Ok(futures::future::Either::Left(futures::stream::empty()));
2146        }
2147
2148        let table_id = self.table_id;
2149        let inspect_fn = move |result: &StreamExecutorResult<(K, OwnedRow)>| {
2150            // Only log when in dry-run mode and we would have pruned but got results
2151            if should_prune_entirely && result.is_ok() {
2152                tracing::warn!(
2153                    table_id = %table_id,
2154                    "vnode stats pruning dry run fails for rev_iter. This will not affect correctness."
2155                );
2156            }
2157        };
2158
2159        if let Some(rows) = &self.all_rows {
2160            return Ok(futures::future::Either::Right(
2161                futures::future::Either::Left(
2162                    futures::stream::iter(
2163                        rows.get(&vnode)
2164                            .expect("covered vnode")
2165                            .range((pruned_start, pruned_end))
2166                            .rev()
2167                            .map(move |(key, value)| {
2168                                Ok((K::from_vnode_bytes(vnode, key), value.clone()))
2169                            }),
2170                    )
2171                    .inspect(inspect_fn),
2172                ),
2173            ));
2174        }
2175        let read_options = ReadOptions {
2176            prefix_hint,
2177            prefetch_options,
2178            cache_policy: CachePolicy::Fill(Hint::Normal),
2179        };
2180        let mut key_range = prefixed_range_with_vnode((pruned_start, pruned_end), vnode);
2181        self.narrow_by_pending_watermark(&mut key_range);
2182
2183        Ok(futures::future::Either::Right(
2184            futures::future::Either::Right(
2185                deserialize_keyed_row_stream(
2186                    self.state_store.rev_iter(key_range, read_options).await?,
2187                    &*self.row_serde,
2188                )
2189                .inspect(inspect_fn),
2190            ),
2191        ))
2192    }
2193}
2194
2195impl<S, SD, const IS_REPLICATED: bool> StateTableInner<S, SD, IS_REPLICATED>
2196where
2197    S: StateStore,
2198    SD: ValueRowSerde,
2199{
2200    /// This function scans rows from the relational table with specific `prefix` and `sub_range` under the same
2201    /// `vnode`. If `sub_range` is (Unbounded, Unbounded), it scans rows from the relational table with specific `pk_prefix`.
2202    /// `pk_prefix` is used to identify the exact vnode the scan should perform on.
2203    pub async fn iter_with_prefix(
2204        &self,
2205        pk_prefix: impl Row,
2206        sub_range: &(Bound<impl Row>, Bound<impl Row>),
2207        prefetch_options: PrefetchOptions,
2208    ) -> StreamExecutorResult<impl RowStream<'_>> {
2209        let stream = self.iter_with_prefix_inner::</* REVERSE */ false, ()>(pk_prefix, sub_range, prefetch_options)
2210            .await?;
2211        Ok(stream.map_ok(|(_, row)| {
2212            if IS_REPLICATED {
2213                row.project(&self.output_indices).into_owned_row()
2214            } else {
2215                row
2216            }
2217        }))
2218    }
2219
2220    /// This function scans rows from the relational table with specific `prefix` and `sub_range` under the same
2221    /// `vnode`, and filters out rows based on watermarks. It calls `iter_with_prefix` and further filters rows
2222    /// based on the table watermark retrieved from the state store.
2223    ///
2224    /// The caller must ensure that `clean_watermark_index` is set before calling this method, otherwise it will return all rows without filtering.
2225    pub async fn iter_with_prefix_respecting_watermark(
2226        &self,
2227        pk_prefix: impl Row,
2228        sub_range: &(Bound<impl Row>, Bound<impl Row>),
2229        prefetch_options: PrefetchOptions,
2230    ) -> StreamExecutorResult<BoxedRowStream<'_>> {
2231        let vnode = self.compute_prefix_vnode(&pk_prefix);
2232        let Some(clean_watermark_index) = self.clean_watermark_index else {
2233            return self
2234                .iter_with_prefix(pk_prefix, sub_range, prefetch_options)
2235                .await
2236                .map(|s| s.boxed());
2237        };
2238        let Some((watermark_serde, watermark_type)) = &self.watermark_serde else {
2239            return Err(StreamExecutorError::from(anyhow!(
2240                "Missing watermark serde"
2241            )));
2242        };
2243        // Fast path. TableWatermarksIndex::rewrite_range_with_table_watermark has already filtered the rows.
2244        if matches!(watermark_type, WatermarkSerdeType::PkPrefix) {
2245            return self
2246                .iter_with_prefix(pk_prefix, sub_range, prefetch_options)
2247                .await
2248                .map(|s| s.boxed());
2249        }
2250
2251        let watermark_bytes = self.row_store.state_store.get_table_watermark(vnode);
2252        let Some(watermark_bytes) = watermark_bytes else {
2253            return self
2254                .iter_with_prefix(pk_prefix, sub_range, prefetch_options)
2255                .await
2256                .map(|s| s.boxed());
2257        };
2258        let watermark_row = watermark_serde.deserialize(&watermark_bytes)?;
2259        if watermark_row.len() != 1 {
2260            return Err(StreamExecutorError::from(format!(
2261                "Watermark row should have exactly 1 column, got {}",
2262                watermark_row.len()
2263            )));
2264        }
2265        let watermark_value = watermark_row[0].clone();
2266        // StateTableInner::update_watermark should ensure that the watermark is not NULL
2267        if watermark_value.is_none() {
2268            return Err(StreamExecutorError::from(anyhow!(
2269                "Watermark cannot be NULL"
2270            )));
2271        }
2272        let order_type = watermark_serde.get_order_types().get(0).ok_or_else(|| {
2273            StreamExecutorError::from(anyhow!(
2274                "Watermark serde should have at least one order type"
2275            ))
2276        })?;
2277
2278        let direction = if order_type.is_ascending() {
2279            WatermarkDirection::Ascending
2280        } else {
2281            WatermarkDirection::Descending
2282        };
2283        let clean_watermark_index_in_pk = self
2284            .pk_indices
2285            .iter()
2286            .position(|&i| i == clean_watermark_index);
2287        let clean_watermark_index_in_value = match &self.value_indices {
2288            Some(value_indices) => value_indices
2289                .iter()
2290                .position(|idx| *idx == clean_watermark_index)
2291                .ok_or_else(|| {
2292                    StreamExecutorError::from(anyhow!(
2293                        "clean watermark column index {} is not included in table value indices {:?}",
2294                        clean_watermark_index,
2295                        value_indices
2296                    ))
2297                })?,
2298            None => clean_watermark_index,
2299        };
2300
2301        let stream = self
2302            .iter_with_prefix_inner::</* REVERSE */ false, Bytes>(pk_prefix, sub_range, prefetch_options)
2303            .await?
2304            .try_filter_map(move |(pk, row)| {
2305                let should_filter =  match watermark_type {
2306                    WatermarkSerdeType::PkPrefix => unreachable!(),
2307                    WatermarkSerdeType::NonPkPrefix => {
2308                        let table_key = TableKey(pk);
2309                        let (vnode, key) = table_key.split_vnode();
2310                        let pk_cols = self.pk_serde
2311                        .deserialize(key)
2312                        .unwrap_or_else(|e| {
2313                            panic!("Failed to deserialize table {} vnode {:?} key {:?} error: {:?}", self.table_id(), vnode, key, e.as_report());
2314                        });
2315                        direction.datum_filter_by_watermark(
2316                            pk_cols.datum_at(clean_watermark_index_in_pk.unwrap()),
2317                            &watermark_value,
2318                            *order_type,
2319                        )
2320                    },
2321                    WatermarkSerdeType::Value => {
2322                        direction.datum_filter_by_watermark(
2323                            row.datum_at(clean_watermark_index_in_value),
2324                            &watermark_value,
2325                            *order_type,
2326                        )
2327                    }
2328                };
2329                if should_filter {
2330                    ready(Ok(None))
2331                } else {
2332                    ready(Ok(Some(row)))
2333                }
2334            });
2335        Ok(stream.boxed())
2336    }
2337
2338    /// Get the row from a state table with only 1 row.
2339    pub async fn get_from_one_row_table(&self) -> StreamExecutorResult<Option<OwnedRow>> {
2340        let sub_range: &(Bound<OwnedRow>, Bound<OwnedRow>) = &(Unbounded, Unbounded);
2341        let stream = self
2342            .iter_with_prefix(row::empty(), sub_range, Default::default())
2343            .await?;
2344        pin_mut!(stream);
2345
2346        if let Some(res) = stream.next().await {
2347            let value = res?.into_owned_row();
2348            assert!(stream.next().await.is_none());
2349            Ok(Some(value))
2350        } else {
2351            Ok(None)
2352        }
2353    }
2354
2355    /// Get the row from a state table with only 1 row, and the row has only 1 col.
2356    ///
2357    /// `None` can mean either the row is never persisted, or is a persisted `NULL`,
2358    /// which does not matter in the use case.
2359    pub async fn get_from_one_value_table(&self) -> StreamExecutorResult<Option<ScalarImpl>> {
2360        Ok(self
2361            .get_from_one_row_table()
2362            .await?
2363            .and_then(|row| row[0].clone()))
2364    }
2365
2366    pub async fn iter_keyed_row_with_prefix(
2367        &self,
2368        pk_prefix: impl Row,
2369        sub_range: &(Bound<impl Row>, Bound<impl Row>),
2370        prefetch_options: PrefetchOptions,
2371    ) -> StreamExecutorResult<impl KeyedRowStream<'_>> {
2372        Ok(
2373            self.iter_with_prefix_inner::</* REVERSE */ false, Bytes>(pk_prefix, sub_range, prefetch_options)
2374                .await?.map_ok(|(key, row)| KeyedRow::new(TableKey(key), row)),
2375        )
2376    }
2377
2378    pub async fn rev_iter_keyed_row_with_prefix(
2379        &self,
2380        pk_prefix: impl Row,
2381        sub_range: &(Bound<impl Row>, Bound<impl Row>),
2382        prefetch_options: PrefetchOptions,
2383    ) -> StreamExecutorResult<impl KeyedRowStream<'_>> {
2384        Ok(
2385            self.iter_with_prefix_inner::</* REVERSE */ true, Bytes>(pk_prefix, sub_range, prefetch_options)
2386            .await?.map_ok(|(key, row)| KeyedRow::new(TableKey(key), row)),
2387        )
2388    }
2389
2390    /// This function scans the table just like `iter_with_prefix`, but in reverse order.
2391    pub async fn rev_iter_with_prefix(
2392        &self,
2393        pk_prefix: impl Row,
2394        sub_range: &(Bound<impl Row>, Bound<impl Row>),
2395        prefetch_options: PrefetchOptions,
2396    ) -> StreamExecutorResult<impl RowStream<'_>> {
2397        Ok(
2398            self.iter_with_prefix_inner::</* REVERSE */ true, ()>(pk_prefix, sub_range, prefetch_options)
2399                .await?.map_ok(|(_, row)| row),
2400        )
2401    }
2402
2403    async fn iter_with_prefix_inner<const REVERSE: bool, K: CopyFromSlice + FromVnodeBytes>(
2404        &self,
2405        pk_prefix: impl Row,
2406        sub_range: &(Bound<impl Row>, Bound<impl Row>),
2407        prefetch_options: PrefetchOptions,
2408    ) -> StreamExecutorResult<impl PkRowStream<'_, K>> {
2409        let prefix_serializer = self.pk_serde.prefix(pk_prefix.len());
2410        let encoded_prefix = serialize_pk(&pk_prefix, &prefix_serializer);
2411
2412        // We assume that all usages of iterating the state table only access a single vnode.
2413        // If this assertion fails, then something must be wrong with the operator implementation or
2414        // the distribution derivation from the optimizer.
2415        let vnode = self.compute_prefix_vnode(&pk_prefix);
2416
2417        // Construct prefix hint for prefix bloom filter.
2418        let pk_prefix_indices = &self.pk_indices[..pk_prefix.len()];
2419        if self.prefix_hint_len != 0 && !IS_REPLICATED {
2420            debug_assert_eq!(self.prefix_hint_len, pk_prefix.len());
2421        }
2422        let prefix_hint = {
2423            if should_calculate_prefix_hint(self.prefix_hint_len, pk_prefix.len(), true) {
2424                let encoded_prefix_len = self
2425                    .pk_serde
2426                    .deserialize_prefix_len(&encoded_prefix, self.prefix_hint_len)?;
2427
2428                Some(Bytes::copy_from_slice(
2429                    &encoded_prefix[..encoded_prefix_len],
2430                ))
2431            } else {
2432                None
2433            }
2434        };
2435
2436        trace!(
2437            table_id = %self.table_id(),
2438            ?prefix_hint, ?pk_prefix,
2439            ?pk_prefix_indices,
2440            iter_direction = if REVERSE { "reverse" } else { "forward" },
2441            "storage_iter_with_prefix"
2442        );
2443
2444        let memcomparable_range =
2445            prefix_and_sub_range_to_memcomparable(&self.pk_serde, sub_range, pk_prefix);
2446
2447        Ok(if REVERSE {
2448            futures::future::Either::Left(
2449                self.row_store
2450                    .rev_iter_kv(vnode, memcomparable_range, prefix_hint, prefetch_options)
2451                    .await?,
2452            )
2453        } else {
2454            futures::future::Either::Right(
2455                self.row_store
2456                    .iter_kv(vnode, memcomparable_range, prefix_hint, prefetch_options)
2457                    .await?,
2458            )
2459        })
2460    }
2461
2462    /// This function scans raw key-values from the relational table with specific `pk_range` under
2463    /// the same `vnode`.
2464    async fn iter_kv_with_pk_range<'a, K: CopyFromSlice + FromVnodeBytes>(
2465        &'a self,
2466        pk_range: &(Bound<impl Row>, Bound<impl Row>),
2467        // Optional vnode that returns an iterator only over the given range under that vnode.
2468        // For now, we require this parameter, and will panic. In the future, when `None`, we can
2469        // iterate over each vnode that the `StateTableInner` owns.
2470        vnode: VirtualNode,
2471        prefetch_options: PrefetchOptions,
2472    ) -> StreamExecutorResult<impl PkRowStream<'a, K>> {
2473        let memcomparable_range = prefix_range_to_memcomparable(&self.pk_serde, pk_range);
2474
2475        // TODO: provide a trace of useful params.
2476        self.row_store
2477            .iter_kv(vnode, memcomparable_range, None, prefetch_options)
2478            .await
2479    }
2480}
2481
2482fn deserialize_keyed_row_stream<'a, K: CopyFromSlice>(
2483    iter: impl StateStoreIter + 'a,
2484    deserializer: &'a impl ValueRowSerde,
2485) -> impl PkRowStream<'a, K> {
2486    iter.into_stream(move |(key, value)| {
2487        Ok((
2488            K::copy_from_slice(key.user_key.table_key.as_ref()),
2489            deserializer.deserialize(value).map(OwnedRow::new)?,
2490        ))
2491    })
2492    .map_err(Into::into)
2493}
2494
2495pub fn prefix_range_to_memcomparable(
2496    pk_serde: &OrderedRowSerde,
2497    range: &(Bound<impl Row>, Bound<impl Row>),
2498) -> (Bound<Bytes>, Bound<Bytes>) {
2499    (
2500        start_range_to_memcomparable(pk_serde, &range.0),
2501        end_range_to_memcomparable(pk_serde, &range.1, None),
2502    )
2503}
2504
2505fn prefix_and_sub_range_to_memcomparable(
2506    pk_serde: &OrderedRowSerde,
2507    sub_range: &(Bound<impl Row>, Bound<impl Row>),
2508    pk_prefix: impl Row,
2509) -> (Bound<Bytes>, Bound<Bytes>) {
2510    let (range_start, range_end) = sub_range;
2511    let prefix_serializer = pk_serde.prefix(pk_prefix.len());
2512    let serialized_pk_prefix = serialize_pk(&pk_prefix, &prefix_serializer);
2513    let start_range = match range_start {
2514        Included(start_range) => Bound::Included(Either::Left((&pk_prefix).chain(start_range))),
2515        Excluded(start_range) => Bound::Excluded(Either::Left((&pk_prefix).chain(start_range))),
2516        Unbounded => Bound::Included(Either::Right(&pk_prefix)),
2517    };
2518    let end_range = match range_end {
2519        Included(end_range) => Bound::Included((&pk_prefix).chain(end_range)),
2520        Excluded(end_range) => Bound::Excluded((&pk_prefix).chain(end_range)),
2521        Unbounded => Unbounded,
2522    };
2523    (
2524        start_range_to_memcomparable(pk_serde, &start_range),
2525        end_range_to_memcomparable(pk_serde, &end_range, Some(serialized_pk_prefix)),
2526    )
2527}
2528
2529fn start_range_to_memcomparable<R: Row>(
2530    pk_serde: &OrderedRowSerde,
2531    bound: &Bound<R>,
2532) -> Bound<Bytes> {
2533    let serialize_pk_prefix = |pk_prefix: &R| {
2534        let prefix_serializer = pk_serde.prefix(pk_prefix.len());
2535        serialize_pk(pk_prefix, &prefix_serializer)
2536    };
2537    match bound {
2538        Unbounded => Unbounded,
2539        Included(r) => {
2540            let serialized = serialize_pk_prefix(r);
2541
2542            Included(serialized)
2543        }
2544        Excluded(r) => {
2545            let serialized = serialize_pk_prefix(r);
2546
2547            start_bound_of_excluded_prefix(&serialized)
2548        }
2549    }
2550}
2551
2552fn end_range_to_memcomparable<R: Row>(
2553    pk_serde: &OrderedRowSerde,
2554    bound: &Bound<R>,
2555    serialized_pk_prefix: Option<Bytes>,
2556) -> Bound<Bytes> {
2557    let serialize_pk_prefix = |pk_prefix: &R| {
2558        let prefix_serializer = pk_serde.prefix(pk_prefix.len());
2559        serialize_pk(pk_prefix, &prefix_serializer)
2560    };
2561    match bound {
2562        Unbounded => match serialized_pk_prefix {
2563            Some(serialized_pk_prefix) => end_bound_of_prefix(&serialized_pk_prefix),
2564            None => Unbounded,
2565        },
2566        Included(r) => {
2567            let serialized = serialize_pk_prefix(r);
2568            // TODO: may use Included(serialized)?
2569            end_bound_of_prefix(&serialized)
2570        }
2571        Excluded(r) => {
2572            let serialized = serialize_pk_prefix(r);
2573            Excluded(serialized)
2574        }
2575    }
2576}
2577
2578fn fill_non_output_indices(
2579    i2o_mapping: &ColIndexMapping,
2580    data_types: &[DataType],
2581    chunk: StreamChunk,
2582) -> StreamChunk {
2583    let (ops, columns, vis) = chunk.into_inner();
2584    let capacity = vis.len();
2585    let mut full_columns = Vec::with_capacity(data_types.len());
2586    for (i, data_type) in data_types.iter().enumerate() {
2587        if let Some(j) = i2o_mapping.try_map(i) {
2588            full_columns.push(columns[j].clone());
2589        } else {
2590            let mut column_builder = ArrayImplBuilder::with_type(capacity, data_type.clone());
2591            column_builder.append_n_null(capacity);
2592            let column: ArrayRef = column_builder.finish().into();
2593            full_columns.push(column)
2594        }
2595    }
2596    let data_chunk = DataChunk::new(full_columns, vis);
2597    StreamChunk::from_parts(ops, data_chunk)
2598}
2599
2600#[cfg(test)]
2601mod tests {
2602    use std::fmt::Debug;
2603
2604    use expect_test::{Expect, expect};
2605
2606    use super::*;
2607
2608    fn check(actual: impl Debug, expect: Expect) {
2609        let actual = format!("{:#?}", actual);
2610        expect.assert_eq(&actual);
2611    }
2612
2613    #[test]
2614    fn test_fill_non_output_indices() {
2615        let data_types = vec![DataType::Int32, DataType::Int32, DataType::Int32];
2616        let replicated_chunk = [OwnedRow::new(vec![
2617            Some(222_i32.into()),
2618            Some(2_i32.into()),
2619        ])];
2620        let replicated_chunk = StreamChunk::from_parts(
2621            vec![Op::Insert],
2622            DataChunk::from_rows(&replicated_chunk, &[DataType::Int32, DataType::Int32]),
2623        );
2624        let i2o_mapping = ColIndexMapping::new(vec![Some(1), None, Some(0)], 2);
2625        let filled_chunk = fill_non_output_indices(&i2o_mapping, &data_types, replicated_chunk);
2626        check(
2627            filled_chunk,
2628            expect![[r#"
2629            StreamChunk { cardinality: 1, capacity: 1, data:
2630            +---+---+---+-----+
2631            | + | 2 |   | 222 |
2632            +---+---+---+-----+
2633             }"#]],
2634        );
2635    }
2636
2637    #[test]
2638    fn test_fill_non_output_indices_with_invisible_rows() {
2639        let data_types = vec![DataType::Int32, DataType::Int32, DataType::Int32];
2640        let replicated_chunk = [
2641            OwnedRow::new(vec![Some(222_i32.into()), Some(2_i32.into())]),
2642            OwnedRow::new(vec![Some(333_i32.into()), Some(3_i32.into())]),
2643        ];
2644        let (columns, _) =
2645            DataChunk::from_rows(&replicated_chunk, &[DataType::Int32, DataType::Int32])
2646                .into_parts();
2647        let replicated_chunk = StreamChunk::with_visibility(
2648            vec![Op::Insert, Op::Insert],
2649            columns,
2650            Bitmap::from_iter([false, false]),
2651        );
2652        let i2o_mapping = ColIndexMapping::new(vec![Some(1), None, Some(0)], 2);
2653        let filled_chunk = fill_non_output_indices(&i2o_mapping, &data_types, replicated_chunk);
2654        check(
2655            filled_chunk,
2656            expect![[r#"
2657            StreamChunk { cardinality: 0, capacity: 2, data:
2658            (empty)
2659             }"#]],
2660        );
2661    }
2662}