1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//! BIG (`nb`) reverse partition iteration at the `SSTableManager` seam (Issue #1184).
//!
//! `ORDER BY <clustering> DESC` on a single BIG wide partition is served by walking
//! the promoted `IndexInfo` blocks back-to-front (see
//! [`SSTableReader::big_reverse_partition_rows`]) instead of a post-fetch in-memory
//! sort over a forward full-partition read. This module is the manager-level entry
//! the query executor calls; it prunes to the single candidate generation and
//! delegates the block-walk to the reader. It returns `Ok(None)` (the executor then
//! keeps the in-memory sort) for every case the reverse iterator does not cover —
//! zero or multiple candidate generations (cross-generation reconcile still needs a
//! sort), a non-BIG / narrow partition, a static or variable-width clustering, or an
//! open range-tombstone marker.
//!
//! Kept in its own file (declared from `sstable/mod.rs`, which is over the
//! campsite-rule threshold) so the manager gains the surface without growing
//! `mod.rs`. Tombstones-gated: the seek/reverse paths exist only on the default build.
#![cfg(not(feature = "tombstones"))]
use std::sync::Arc;
use super::{reader, SSTableManager};
use crate::schema::TableSchema;
use crate::types::{ScanRow, TableId};
use crate::{Result, RowKey};
impl SSTableManager {
/// Reverse single-partition clustering scan for a BIG (`nb`) wide partition.
///
/// Returns `Ok(Some(rows))` with the partition's rows in DESCENDING clustering
/// order when the BIG reverse iterator applied, or `Ok(None)` to signal the
/// caller should fall back to the in-memory `ORDER BY DESC` sort.
pub(crate) async fn scan_partition_clustering_reverse(
&self,
table_id: &TableId,
partition_key: &[u8],
schema: Option<&TableSchema>,
) -> Result<Option<Vec<(RowKey, ScanRow)>>> {
// Issue #1591: snapshot the reader list and DROP the read guard before any
// I/O (candidate prune + the block-walk delegated to the reader).
let (reader_list, _fully_qualified_match) = self.resolve_reader_snapshot(table_id).await;
// Prune to the candidate generations that admit the key. Reverse iteration
// covers ONLY the single-generation case: with several generations the same
// (partition, clustering) row may appear in more than one, and reconciling
// them is the in-memory-sort path's job (cross-generation last-write-wins).
let candidates: Vec<Arc<reader::SSTableReader>> = reader_list
.iter()
.filter(|r| r.might_contain_partition(partition_key))
.cloned()
.collect();
if candidates.len() != 1 {
return Ok(None);
}
candidates[0]
.big_reverse_partition_rows(partition_key, schema)
.await
}
}