1use std::{collections::BTreeMap, sync::Arc};
10
11use uqa_core::{
12 memory::{Budgeted, BudgetedVec, MemoryReservation},
13 ordering::sort_by_with_control,
14 DocId, Value,
15};
16
17use super::{Document, DocumentMetadata, DocumentStore, StoredDocument};
18use crate::{read_control::StorageReadControl, StorageBackendError, StorageBackendResult};
19
20type Documents = Vec<(DocId, StoredDocument)>;
21
22pub struct RetainedDocumentStoreBuilder {
24 entries: BudgetedVec<(DocId, StoredDocument)>,
25 payload: MemoryReservation,
26 control: StorageReadControl,
27}
28
29impl RetainedDocumentStoreBuilder {
30 pub fn new(control: &StorageReadControl) -> Self {
31 Self {
32 entries: BudgetedVec::new(control.memory()),
33 payload: control.memory().empty_reservation(),
34 control: control.clone(),
35 }
36 }
37
38 pub fn add_document(
39 &mut self,
40 id: DocId,
41 document: StoredDocument,
42 ) -> StorageBackendResult<()> {
43 self.control.check()?;
44 let (fields, metadata) = document.into_parts();
45 let value = Value::Map(fields);
46 let memory = value
47 .reserve_retained_payload(self.control.memory(), self.control.cancellation())
48 .map_err(super::retained::retention_error)?;
49 let Value::Map(fields) = value else {
50 unreachable!("document field map");
51 };
52 self.push_document(
53 id,
54 Budgeted::new(StoredDocument::with_metadata(fields, metadata), memory),
55 )
56 }
57
58 pub fn add_budgeted_document(
60 &mut self,
61 id: DocId,
62 document: Budgeted<StoredDocument>,
63 ) -> StorageBackendResult<()> {
64 self.control.check()?;
65 if !document.budget().shares_allowance(self.control.memory()) {
66 return Err(StorageBackendError::Other(
67 "retained document input belongs to a different allowance".into(),
68 ));
69 }
70 let (document, mut memory) = document.into_parts();
71 let (fields, metadata) = document.into_parts();
72 let value = Value::Map(fields);
73 let required = value
74 .retained_payload_bytes(self.control.memory(), self.control.cancellation())
75 .map_err(super::retained::retention_error)?;
76 if memory.bytes() < required {
77 return Err(StorageBackendError::Other(
78 "retained document input reservation does not cover its payload".into(),
79 ));
80 }
81 let surplus = memory.bytes() - required;
82 drop(memory.split(surplus));
83 let Value::Map(fields) = value else {
84 unreachable!("document field map");
85 };
86 self.push_document(
87 id,
88 Budgeted::new(StoredDocument::with_metadata(fields, metadata), memory),
89 )
90 }
91
92 pub fn add_retained_document(
94 &mut self,
95 id: DocId,
96 document: super::RetainedStoredDocument,
97 ) -> StorageBackendResult<()> {
98 let document = document.into_budgeted(&self.control)?;
99 self.add_budgeted_document(id, document)
100 }
101
102 fn push_document(
103 &mut self,
104 id: DocId,
105 pending: Budgeted<StoredDocument>,
106 ) -> StorageBackendResult<()> {
107 self.entries.reserve(1)?;
109 self.control.check()?;
110 let (document, memory) = pending.into_parts();
111 self.entries.push((id, document))?;
112 self.payload.absorb(memory);
113 Ok(())
114 }
115
116 pub fn finish(mut self) -> StorageBackendResult<RetainedDocumentStore> {
117 sort_by_with_control(
118 &mut self.entries,
119 &mut || self.control.check(),
120 |left, right, _| Ok(left.0.cmp(&right.0)),
121 )?;
122 for pair in self.entries.windows(2) {
123 self.control.check()?;
124 if pair[0].0 == pair[1].0 {
125 return Err(StorageBackendError::Other(
126 "retained document input repeats a document identity".into(),
127 ));
128 }
129 }
130 self.control.check()?;
131 let (entries, mut memory) = self.entries.into_parts();
132 memory.absorb(self.payload);
133 Ok(RetainedDocumentStore {
134 entries: Budgeted::new(entries, memory).into_shared()?,
135 control: self.control,
136 })
137 }
138}
139
140#[derive(Clone)]
142pub struct RetainedDocumentStore {
143 entries: Arc<Budgeted<Documents>>,
144 control: StorageReadControl,
145}
146
147impl RetainedDocumentStore {
148 fn document(&self, id: DocId) -> Option<&StoredDocument> {
149 self.entries
150 .binary_search_by_key(&id, |entry| entry.0)
151 .ok()
152 .map(|index| &self.entries[index].1)
153 }
154
155 fn after(&self, after: Option<DocId>) -> &[(DocId, StoredDocument)] {
156 &self.entries[self
157 .entries
158 .partition_point(|(id, _)| after.is_some_and(|after| *id <= after))..]
159 }
160
161 fn visit<'a>(
162 &'a self,
163 rows: impl Iterator<Item = (DocId, Option<&'a StoredDocument>)>,
164 fields: &[&str],
165 visitor: &mut dyn FnMut(DocId, bool, &[&Value]) -> bool,
166 ) -> StorageBackendResult<()> {
167 self.control.check()?;
168 let mut projected = BudgetedVec::new(self.control.memory());
169 projected.reserve(fields.len())?;
170 for (id, document) in rows {
171 self.control.check()?;
172 projected.clear();
173 for field in fields {
174 self.control.check()?;
175 projected.push(
176 document
177 .and_then(|document| document.fields().get(*field))
178 .unwrap_or(&Value::Null),
179 )?;
180 }
181 if !visitor(id, document.is_some(), &projected) {
182 break;
183 }
184 }
185 self.control.check()
186 }
187}
188
189fn read_only() -> StorageBackendError {
190 StorageBackendError::Other("cannot write a retained document snapshot".into())
191}
192
193impl DocumentStore for RetainedDocumentStore {
194 fn put(&mut self, _: DocId, _: Document) -> StorageBackendResult<()> {
195 Err(read_only())
196 }
197 fn put_stored(&mut self, _: DocId, _: StoredDocument) -> StorageBackendResult<()> {
198 Err(read_only())
199 }
200 fn patch_fields(&mut self, _: DocId, _: &Document) -> StorageBackendResult<bool> {
201 Err(read_only())
202 }
203 fn delete(&mut self, _: DocId) -> StorageBackendResult<()> {
204 Err(read_only())
205 }
206 fn clear(&mut self) -> StorageBackendResult<()> {
207 Err(read_only())
208 }
209
210 fn get_stored(&self, id: DocId) -> StorageBackendResult<Option<StoredDocument>> {
211 self.control.check()?;
212 Ok(self.document(id).cloned())
213 }
214
215 fn get_stored_many_controlled(
216 &self,
217 ids: &[DocId],
218 control: &StorageReadControl,
219 ) -> StorageBackendResult<super::RetainedDocumentPage> {
220 self.control.check()?;
221 control.check()?;
222 let mut page = BudgetedVec::new(control.memory());
223 page.reserve(ids.len())?;
224 for id in ids {
225 self.control.check()?;
226 control.check()?;
227 let row = self
228 .document(*id)
229 .map(|row| -> StorageBackendResult<_> {
230 let fields = super::controlled_rows::copy_fields(
231 row.fields()
232 .iter()
233 .map(|(name, value)| (name.as_str(), value)),
234 control,
235 )?;
236 Ok(super::RetainedStoredDocument::with_metadata(
237 fields,
238 row.metadata(),
239 ))
240 })
241 .transpose()?;
242 page.push(row)?;
243 }
244 self.control.check()?;
245 control.check()?;
246 Ok(page)
247 }
248 fn get_metadata(&self, id: DocId) -> StorageBackendResult<Option<DocumentMetadata>> {
249 self.control.check()?;
250 Ok(self.document(id).map(StoredDocument::metadata))
251 }
252 fn contains_doc_id(&self, id: DocId) -> StorageBackendResult<bool> {
253 self.control.check()?;
254 Ok(self.document(id).is_some())
255 }
256 fn get_field(&self, id: DocId, field: &str) -> StorageBackendResult<Option<Value>> {
257 self.control.check()?;
258 Ok(self
259 .document(id)
260 .and_then(|document| document.fields().get(field).cloned()))
261 }
262
263 fn get_fields_multi(
264 &self,
265 ids: &[DocId],
266 fields: &[&str],
267 ) -> StorageBackendResult<BTreeMap<DocId, Vec<Value>>> {
268 let mut rows = BTreeMap::new();
269 self.for_each_fields_multi_ref_with_presence(ids, fields, &mut |id, present, values| {
270 if present {
271 rows.insert(id, values.iter().map(|value| (*value).clone()).collect());
272 }
273 true
274 })?;
275 Ok(rows)
276 }
277 fn for_each_fields_multi(
278 &self,
279 ids: &[DocId],
280 fields: &[&str],
281 visitor: &mut dyn FnMut(DocId, Vec<Value>) -> bool,
282 ) -> StorageBackendResult<()> {
283 self.for_each_fields_multi_ref(ids, fields, &mut |id, values| {
284 visitor(id, values.iter().map(|value| (*value).clone()).collect())
285 })
286 }
287 fn for_each_fields_multi_ref(
288 &self,
289 ids: &[DocId],
290 fields: &[&str],
291 visitor: &mut dyn FnMut(DocId, &[&Value]) -> bool,
292 ) -> StorageBackendResult<()> {
293 self.for_each_fields_multi_ref_with_presence(ids, fields, &mut |id, _, values| {
294 visitor(id, values)
295 })
296 }
297 fn for_each_fields_multi_ref_with_presence(
298 &self,
299 ids: &[DocId],
300 fields: &[&str],
301 visitor: &mut dyn FnMut(DocId, bool, &[&Value]) -> bool,
302 ) -> StorageBackendResult<()> {
303 self.visit(
304 ids.iter().map(|id| (*id, self.document(*id))),
305 fields,
306 visitor,
307 )
308 }
309 fn for_each_next_fields(
310 &self,
311 after: Option<DocId>,
312 limit: usize,
313 fields: &[&str],
314 visitor: &mut dyn FnMut(DocId, &[&Value]) -> bool,
315 ) -> StorageBackendResult<Option<usize>> {
316 let mut visited = 0;
317 self.visit(
318 self.after(after)
319 .iter()
320 .take(limit)
321 .map(|(id, row)| (*id, Some(row))),
322 fields,
323 &mut |id, _, values| {
324 visited += 1;
325 visitor(id, values)
326 },
327 )?;
328 Ok(Some(visited))
329 }
330
331 fn with_field_ref_controlled(
332 &self,
333 id: DocId,
334 field: &str,
335 control: &StorageReadControl,
336 visitor: &mut dyn FnMut(Option<&Value>) -> StorageBackendResult<()>,
337 ) -> StorageBackendResult<()> {
338 self.control.check()?;
339 control.check()?;
340 visitor(self.document(id).and_then(|row| row.fields().get(field)))?;
341 self.control.check()?;
342 control.check()
343 }
344
345 fn field_presence_controlled(
346 &self,
347 ids: &[DocId],
348 fields: &[&str],
349 control: &StorageReadControl,
350 ) -> StorageBackendResult<BudgetedVec<bool>> {
351 self.control.check()?;
352 control.check()?;
353 let mut present = BudgetedVec::new(control.memory());
354 for id in ids {
355 let row = self.document(*id);
356 for field in fields {
357 self.control.check()?;
358 control.check()?;
359 present.push(row.is_some_and(|row| row.fields().contains_key(*field)))?;
360 }
361 }
362 self.control.check()?;
363 control.check()?;
364 Ok(present)
365 }
366
367 fn find_doc_id_by_field(
368 &self,
369 field: &str,
370 value: &Value,
371 ) -> StorageBackendResult<Option<DocId>> {
372 self.control.check()?;
373 for (id, document) in self.entries.iter() {
374 self.control.check()?;
375 if document.fields().get(field) == Some(value) {
376 return Ok(Some(*id));
377 }
378 }
379 Ok(None)
380 }
381 fn find_doc_id_by_fields(
382 &self,
383 fields: &[String],
384 values: &[Value],
385 ) -> StorageBackendResult<Option<DocId>> {
386 self.control.check()?;
387 if fields.is_empty() || fields.len() != values.len() {
388 return Ok(None);
389 }
390 for (id, document) in self.entries.iter() {
391 self.control.check()?;
392 let mut matched = true;
393 for (field, value) in fields.iter().zip(values) {
394 self.control.check()?;
395 if document.fields().get(field).unwrap_or(&Value::Null) != value {
396 matched = false;
397 break;
398 }
399 }
400 if matched {
401 return Ok(Some(*id));
402 }
403 }
404 Ok(None)
405 }
406 fn has_value(&self, field: &str, value: &Value) -> StorageBackendResult<bool> {
407 Ok(self.find_doc_id_by_field(field, value)?.is_some())
408 }
409
410 fn doc_ids(&self) -> StorageBackendResult<Vec<DocId>> {
411 self.next_doc_ids(None, self.entries.len())
412 }
413 fn next_doc_id(&self, after: Option<DocId>) -> StorageBackendResult<Option<DocId>> {
414 self.control.check()?;
415 Ok(self.after(after).first().map(|(id, _)| *id))
416 }
417 fn next_doc_ids(&self, after: Option<DocId>, limit: usize) -> StorageBackendResult<Vec<DocId>> {
418 let (ids, _memory) = self
419 .next_doc_ids_controlled(after, limit, &self.control)?
420 .into_parts();
421 Ok(ids)
422 }
423 fn next_doc_ids_controlled(
424 &self,
425 after: Option<DocId>,
426 limit: usize,
427 control: &StorageReadControl,
428 ) -> StorageBackendResult<BudgetedVec<DocId>> {
429 self.control.check()?;
430 control.check()?;
431 let mut ids = BudgetedVec::new(control.memory());
432 for (id, _) in self.after(after).iter().take(limit) {
433 self.control.check()?;
434 control.check()?;
435 ids.push(*id)?;
436 }
437 control.check()?;
438 Ok(ids)
439 }
440 fn max_doc_id(&self) -> StorageBackendResult<DocId> {
441 self.control.check()?;
442 Ok(self.entries.last().map_or(0, |(id, _)| *id))
443 }
444 fn len(&self) -> StorageBackendResult<usize> {
445 self.control.check()?;
446 Ok(self.entries.len())
447 }
448 fn iter_all(&self) -> StorageBackendResult<Box<dyn Iterator<Item = (DocId, Document)> + '_>> {
449 self.control.check()?;
450 Ok(Box::new(
451 self.entries
452 .iter()
453 .map(|(id, document)| (*id, document.fields().clone())),
454 ))
455 }
456 fn snapshot(&self) -> StorageBackendResult<Arc<dyn DocumentStore>> {
457 self.control.check()?;
458 Ok(Arc::new(self.clone()))
459 }
460
461 fn retained_snapshot(&self) -> StorageBackendResult<Option<Arc<dyn DocumentStore>>> {
462 self.snapshot().map(Some)
463 }
464}
465
466#[cfg(test)]
467mod tests;