reifydb_engine/vm/volcano/scan/
dictionary.rs1use std::sync::Arc;
5
6use postcard::from_bytes;
7use reifydb_core::{
8 interface::{catalog::dictionary::Dictionary, resolved::ResolvedDictionary, store::SingleVersionRange},
9 internal_error,
10 key::{any::TaggedKey, bound::TaggedKeyBoundRange, catalog::DictionaryEntryIndexKey},
11 value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns, headers::ColumnHeaders},
12};
13use reifydb_transaction::transaction::Transaction;
14use reifydb_value::{
15 fragment::Fragment,
16 reifydb_assertions,
17 value::{Value, dictionary::DictionaryEntryId, value_type::ValueType},
18};
19use tracing::instrument;
20
21use crate::{
22 Result,
23 vm::volcano::query::{QueryContext, QueryNode},
24};
25
26pub struct DictionaryScanNode {
27 dictionary: ResolvedDictionary,
28 context: Option<Arc<QueryContext>>,
29 headers: ColumnHeaders,
30 last_key: Option<TaggedKey>,
31 exhausted: bool,
32}
33
34impl DictionaryScanNode {
35 pub fn new(dictionary: ResolvedDictionary, context: Arc<QueryContext>) -> Result<Self> {
36 let headers = ColumnHeaders {
37 columns: vec![Fragment::internal("id"), Fragment::internal("value")],
38 };
39
40 Ok(Self {
41 dictionary,
42 context: Some(context),
43 headers,
44 last_key: None,
45 exhausted: false,
46 })
47 }
48
49 #[instrument(level = "trace", skip_all, name = "volcano::scan::dictionary::drain")]
50 fn drain_batch<'a>(
51 rx: &mut Transaction<'a>,
52 range: TaggedKeyBoundRange,
53 batch_size: u64,
54 dict_def: &Dictionary,
55 ) -> Result<(Vec<DictionaryEntryId>, Vec<Value>, Option<TaggedKey>)> {
56 let mut ids: Vec<DictionaryEntryId> = Vec::new();
57 let mut values: Vec<Value> = Vec::new();
58 let mut new_last_key = None;
59
60 let single = rx
61 .single()
62 .ok_or_else(|| internal_error!("single-version store is not available for dictionary scans"))?;
63 let store = single.read_store();
64 let batch = SingleVersionRange::range_batch(&store, range.encode(), batch_size)?;
65
66 for entry in batch.items {
67 let Some(key) = DictionaryEntryIndexKey::decode(&entry.key) else {
68 panic!(
69 "dictionary {} holds an entry index key that does not decode: {:?}",
70 dict_def.id, entry.key
71 );
72 };
73
74 let entry_id = DictionaryEntryId::from_u128(key.id, dict_def.id_type.clone())?;
75 new_last_key = Some(TaggedKey::from(key));
76
77 let value: Value = from_bytes(&entry.bytes)
78 .map_err(|e| internal_error!("Failed to deserialize dictionary value: {}", e))?;
79
80 ids.push(entry_id);
81 values.push(value);
82 }
83
84 Ok((ids, values, new_last_key))
85 }
86
87 #[instrument(level = "trace", skip_all, name = "volcano::scan::dictionary::empty_columns")]
88 fn empty_columns(dict_def: &Dictionary) -> Vec<ColumnWithName> {
89 vec![
90 ColumnWithName {
91 name: Fragment::internal("id"),
92 data: ColumnBuffer::none_typed(dict_def.id_type.clone(), 0),
93 },
94 ColumnWithName {
95 name: Fragment::internal("value"),
96 data: ColumnBuffer::none_typed(dict_def.value_type.clone(), 0),
97 },
98 ]
99 }
100
101 #[instrument(level = "trace", skip_all, name = "volcano::scan::dictionary::assemble")]
102 fn assemble(ids: &[DictionaryEntryId], values: &[Value], dict_def: &Dictionary) -> Result<Option<Columns>> {
103 let id_column = build_id_column(ids, dict_def.id_type.clone())?;
104 let value_column = build_value_column(values, dict_def.value_type.clone())?;
105
106 Ok(Some(Columns::new(vec![id_column, value_column])))
107 }
108}
109
110impl QueryNode for DictionaryScanNode {
111 #[instrument(name = "volcano::scan::dictionary::initialize", level = "trace", skip_all)]
112 fn initialize<'a>(&mut self, _rx: &mut Transaction<'a>, _ctx: &QueryContext) -> Result<()> {
113 Ok(())
114 }
115
116 #[instrument(name = "volcano::scan::dictionary::next", level = "trace", skip_all)]
117 fn next<'a>(&mut self, rx: &mut Transaction<'a>, _ctx: &mut QueryContext) -> Result<Option<Columns>> {
118 reifydb_assertions! {
119 assert!(self.context.is_some(), "DictionaryScan::next() called before initialize()");
120 }
121 let stored_ctx = self.context.as_ref().unwrap();
122
123 if self.exhausted {
124 return Ok(None);
125 }
126
127 let batch_size = stored_ctx.batch_size;
128 let dict_def = self.dictionary.def();
129
130 let range = DictionaryEntryIndexKey::full_scan(dict_def.id).resume_after(self.last_key.as_ref());
131
132 let (ids, values, new_last_key) = Self::drain_batch(rx, range, batch_size, dict_def)?;
133
134 if ids.is_empty() {
135 self.exhausted = true;
136 if self.last_key.is_none() {
137 return Ok(Some(Columns::new(Self::empty_columns(dict_def))));
138 }
139 return Ok(None);
140 }
141
142 self.last_key = new_last_key;
143
144 Self::assemble(&ids, &values, dict_def)
145 }
146
147 fn headers(&self) -> Option<ColumnHeaders> {
148 Some(self.headers.clone())
149 }
150}
151
152fn build_id_column(ids: &[DictionaryEntryId], id_type: ValueType) -> Result<ColumnWithName> {
153 let data = match id_type {
154 ValueType::Uint1 => {
155 let vals: Vec<u8> = ids.iter().map(|id| id.to_u128() as u8).collect();
156 ColumnBuffer::uint1(vals)
157 }
158 ValueType::Uint2 => {
159 let vals: Vec<u16> = ids.iter().map(|id| id.to_u128() as u16).collect();
160 ColumnBuffer::uint2(vals)
161 }
162 ValueType::Uint4 => {
163 let vals: Vec<u32> = ids.iter().map(|id| id.to_u128() as u32).collect();
164 ColumnBuffer::uint4(vals)
165 }
166 ValueType::Uint8 => {
167 let vals: Vec<u64> = ids.iter().map(|id| id.to_u128() as u64).collect();
168 ColumnBuffer::uint8(vals)
169 }
170 ValueType::Uint16 => {
171 let vals: Vec<u128> = ids.iter().map(|id| id.to_u128()).collect();
172 ColumnBuffer::uint16(vals)
173 }
174 _ => return Err(internal_error!("Invalid dictionary id_type: {:?}", id_type)),
175 };
176
177 Ok(ColumnWithName {
178 name: Fragment::internal("id"),
179 data,
180 })
181}
182
183fn build_value_column(values: &[Value], value_type: ValueType) -> Result<ColumnWithName> {
184 let data = match value_type {
185 ValueType::Utf8 => {
186 let vals: Vec<String> = values
187 .iter()
188 .map(|v| match v {
189 Value::Utf8(s) => s.clone(),
190 _ => format!("{:?}", v),
191 })
192 .collect();
193 ColumnBuffer::utf8(vals)
194 }
195 ValueType::Int1 => {
196 let vals: Vec<i8> = values
197 .iter()
198 .map(|v| match v {
199 Value::Int1(n) => *n,
200 _ => 0,
201 })
202 .collect();
203 ColumnBuffer::int1(vals)
204 }
205 ValueType::Int2 => {
206 let vals: Vec<i16> = values
207 .iter()
208 .map(|v| match v {
209 Value::Int2(n) => *n,
210 _ => 0,
211 })
212 .collect();
213 ColumnBuffer::int2(vals)
214 }
215 ValueType::Int4 => {
216 let vals: Vec<i32> = values
217 .iter()
218 .map(|v| match v {
219 Value::Int4(n) => *n,
220 _ => 0,
221 })
222 .collect();
223 ColumnBuffer::int4(vals)
224 }
225 ValueType::Int8 => {
226 let vals: Vec<i64> = values
227 .iter()
228 .map(|v| match v {
229 Value::Int8(n) => *n,
230 _ => 0,
231 })
232 .collect();
233 ColumnBuffer::int8(vals)
234 }
235 ValueType::Uint1 => {
236 let vals: Vec<u8> = values
237 .iter()
238 .map(|v| match v {
239 Value::Uint1(n) => *n,
240 _ => 0,
241 })
242 .collect();
243 ColumnBuffer::uint1(vals)
244 }
245 ValueType::Uint2 => {
246 let vals: Vec<u16> = values
247 .iter()
248 .map(|v| match v {
249 Value::Uint2(n) => *n,
250 _ => 0,
251 })
252 .collect();
253 ColumnBuffer::uint2(vals)
254 }
255 ValueType::Uint4 => {
256 let vals: Vec<u32> = values
257 .iter()
258 .map(|v| match v {
259 Value::Uint4(n) => *n,
260 _ => 0,
261 })
262 .collect();
263 ColumnBuffer::uint4(vals)
264 }
265 ValueType::Uint8 => {
266 let vals: Vec<u64> = values
267 .iter()
268 .map(|v| match v {
269 Value::Uint8(n) => *n,
270 _ => 0,
271 })
272 .collect();
273 ColumnBuffer::uint8(vals)
274 }
275 _ => {
276 let vals: Vec<String> = values.iter().map(|v| format!("{:?}", v)).collect();
277 ColumnBuffer::utf8(vals)
278 }
279 };
280
281 Ok(ColumnWithName {
282 name: Fragment::internal("value"),
283 data,
284 })
285}