1use super::{
10 allocation_error, blob_marker_info, chunk_bind_values, decode_legacy_document_body,
11 doc_id_in_placeholders, document_id_from_sqlite, hydrate_document_blobs,
12 load_marked_document_blob, params, read_doc_id, should_probe_doc_ids, sorted_unique_doc_ids,
13 sqlite_doc_id, take_requested_field, Arc, BTreeMap, DocId, Document, DocumentMetadata,
14 DocumentStore, OptionalExtension, SQLiteDocumentStore, SQLiteError, SQLiteResult,
15 StorageBackendResult, StoredDocument, Value, DOCUMENT_BLOBS_TABLE, DOC_ID_IN_CHUNK,
16};
17
18impl DocumentStore for SQLiteDocumentStore {
19 fn put(&mut self, doc_id: DocId, document: Document) -> StorageBackendResult<()> {
20 let metadata = self.get_metadata(doc_id)?.unwrap_or_default();
21 self.put_stored_inner(doc_id, &document, metadata)?;
22 Ok(())
23 }
24
25 fn get(&self, doc_id: DocId) -> StorageBackendResult<Option<Document>> {
26 Ok(self.get_inner(doc_id)?)
27 }
28
29 fn put_stored(&mut self, doc_id: DocId, document: StoredDocument) -> StorageBackendResult<()> {
30 let (fields, metadata) = document.into_parts();
31 self.put_stored_inner(doc_id, &fields, metadata)?;
32 Ok(())
33 }
34
35 fn get_stored(&self, doc_id: DocId) -> StorageBackendResult<Option<StoredDocument>> {
36 Ok(self.get_stored_inner(doc_id)?)
37 }
38
39 fn get_stored_many(
40 &self,
41 doc_ids: &[DocId],
42 ) -> StorageBackendResult<BTreeMap<DocId, StoredDocument>> {
43 let mut out = BTreeMap::new();
44 if doc_ids.is_empty() {
45 return Ok(out);
46 }
47
48 let decode_row = |connection: &rusqlite::Connection,
49 row: &rusqlite::Row<'_>|
50 -> SQLiteResult<(DocId, StoredDocument)> {
51 let doc_id = read_doc_id(row, 0)?;
52 let body = row.get::<_, String>(1)?;
53 let tuple_xmin = row.get::<_, Option<i64>>(2)?;
54 let mut fields = decode_legacy_document_body(&body)?;
55 hydrate_document_blobs(connection, &self.table, doc_id, &mut fields)?;
56 let metadata = tuple_xmin.map_or_else(
57 || Ok(DocumentMetadata::default()),
58 |tuple_xmin| {
59 u32::try_from(tuple_xmin)
60 .map(DocumentMetadata::with_tuple_xmin)
61 .map_err(|_| {
62 SQLiteError::StorageBackend(format!(
63 "document `{}` row {doc_id} has an out-of-range tuple xmin",
64 self.table
65 ))
66 })
67 },
68 )?;
69 Ok((doc_id, StoredDocument::with_metadata(fields, metadata)))
70 };
71
72 let should_probe =
73 doc_ids.len() <= DOC_ID_IN_CHUNK || should_probe_doc_ids(doc_ids.len(), self.len()?);
74 if should_probe {
75 let leading = [rusqlite::types::Value::Text(self.table.clone())];
76 let sql = format!(
77 "SELECT doc_id, body, tuple_xmin FROM _documents
78 WHERE table_name = ?1 AND doc_id IN ({})",
79 doc_id_in_placeholders(2, DOC_ID_IN_CHUNK)?
80 );
81 self.conn.with(|connection| {
82 for chunk in doc_ids.chunks(DOC_ID_IN_CHUNK) {
83 let mut statement = connection.prepare_cached(&sql)?;
84 let bind = chunk_bind_values(&leading, chunk)?;
85 let mut rows = statement.query(rusqlite::params_from_iter(bind))?;
86 while let Some(row) = rows.next()? {
87 let (doc_id, document) = decode_row(connection, row)?;
88 out.insert(doc_id, document);
89 }
90 }
91 Ok(())
92 })?;
93 return Ok(out);
94 }
95
96 let requested = sorted_unique_doc_ids(doc_ids)?;
97 self.conn.with(|connection| {
98 let mut statement = connection.prepare_cached(
99 "SELECT doc_id, body, tuple_xmin FROM _documents
100 WHERE table_name = ?1
101 ORDER BY doc_id",
102 )?;
103 let mut rows = statement.query(params![self.table])?;
104 while let Some(row) = rows.next()? {
105 let doc_id = read_doc_id(row, 0)?;
106 if requested.binary_search(&doc_id).is_err() {
107 continue;
108 }
109 let (doc_id, document) = decode_row(connection, row)?;
110 out.insert(doc_id, document);
111 }
112 Ok(())
113 })?;
114 Ok(out)
115 }
116
117 fn get_metadata(&self, doc_id: DocId) -> StorageBackendResult<Option<DocumentMetadata>> {
118 let sqlite_doc_id = sqlite_doc_id(doc_id)?;
119 let tuple_xmin = self.conn.with(|connection| {
120 Ok(connection
121 .prepare_cached(
122 "SELECT tuple_xmin FROM _documents
123 WHERE table_name = ?1 AND doc_id = ?2",
124 )?
125 .query_row(params![self.table, sqlite_doc_id], |row| {
126 row.get::<_, Option<i64>>(0)
127 })
128 .optional()?)
129 })?;
130 tuple_xmin
131 .map(|tuple_xmin| {
132 tuple_xmin.map_or_else(
133 || Ok(DocumentMetadata::default()),
134 |tuple_xmin| {
135 u32::try_from(tuple_xmin)
136 .map(DocumentMetadata::with_tuple_xmin)
137 .map_err(|_| {
138 SQLiteError::StorageBackend(format!(
139 "document `{}` row {doc_id} has an out-of-range tuple xmin",
140 self.table
141 ))
142 .into()
143 })
144 },
145 )
146 })
147 .transpose()
148 }
149
150 fn contains_doc_id(&self, doc_id: DocId) -> StorageBackendResult<bool> {
151 let sqlite_doc_id = sqlite_doc_id(doc_id)?;
152 Ok(self.conn.with(|c| {
153 let found: Option<i64> = c
154 .prepare_cached(
155 "SELECT 1 FROM _documents
156 WHERE table_name = ?1 AND doc_id = ?2
157 LIMIT 1",
158 )?
159 .query_row(params![self.table, sqlite_doc_id], |r| r.get(0))
160 .optional()?;
161 Ok(found.is_some())
162 })?)
163 }
164
165 fn get_field(
166 &self,
167 doc_id: DocId,
168 field: &str,
169 ) -> StorageBackendResult<Option<uqa_core::Value>> {
170 Ok(self.get_field_inner(doc_id, field)?)
171 }
172
173 fn find_doc_id_by_field(
174 &self,
175 field: &str,
176 value: &Value,
177 ) -> StorageBackendResult<Option<DocId>> {
178 Ok(self.find_doc_id_by_field_inner(field, value)?)
179 }
180
181 fn get_fields_bulk(
182 &self,
183 doc_ids: &[DocId],
184 field: &str,
185 ) -> StorageBackendResult<BTreeMap<DocId, Value>> {
186 let mut out: BTreeMap<DocId, Value> = doc_ids
187 .iter()
188 .copied()
189 .map(|doc_id| (doc_id, Value::Null))
190 .collect();
191 if doc_ids.is_empty() {
192 return Ok(out);
193 }
194 let mut decode_row = |c: &rusqlite::Connection,
199 row: &rusqlite::Row<'_>|
200 -> SQLiteResult<()> {
201 let doc_id = read_doc_id(row, 0)?;
202 let body = row.get::<_, String>(1)?;
203 let mut document = decode_legacy_document_body(&body)?;
204 if let Some(value) = take_requested_field(c, &self.table, doc_id, &mut document, field)?
205 {
206 out.insert(doc_id, value);
207 }
208 Ok(())
209 };
210
211 let should_probe =
215 doc_ids.len() <= DOC_ID_IN_CHUNK || should_probe_doc_ids(doc_ids.len(), self.len()?);
216 if should_probe {
217 let leading = [rusqlite::types::Value::Text(self.table.clone())];
218 let sql = format!(
219 "SELECT doc_id, body FROM _documents
220 WHERE table_name = ?1 AND doc_id IN ({})",
221 doc_id_in_placeholders(2, DOC_ID_IN_CHUNK)?
222 );
223 self.conn.with(|c| {
224 for chunk in doc_ids.chunks(DOC_ID_IN_CHUNK) {
225 let mut stmt = c.prepare_cached(&sql)?;
226 let bind = chunk_bind_values(&leading, chunk)?;
227 let mut rows = stmt.query(rusqlite::params_from_iter(bind))?;
228 while let Some(row) = rows.next()? {
229 decode_row(c, row)?;
230 }
231 }
232 Ok(())
233 })?;
234 return Ok(out);
235 }
236
237 let requested = sorted_unique_doc_ids(doc_ids)?;
238 self.conn.with(|c| {
239 let mut stmt = c.prepare_cached(
240 "SELECT doc_id, body FROM _documents
241 WHERE table_name = ?1
242 ORDER BY doc_id",
243 )?;
244 let mut rows = stmt.query(params![self.table])?;
245 while let Some(row) = rows.next()? {
246 let doc_id = read_doc_id(row, 0)?;
247 if requested.binary_search(&doc_id).is_err() {
248 continue;
249 }
250 decode_row(c, row)?;
251 }
252 Ok(())
253 })?;
254 Ok(out)
255 }
256
257 fn get_fields_multi(
258 &self,
259 doc_ids: &[DocId],
260 fields: &[&str],
261 ) -> StorageBackendResult<BTreeMap<DocId, Vec<Value>>> {
262 let mut out: BTreeMap<DocId, Vec<Value>> = BTreeMap::new();
263 if doc_ids.is_empty() || fields.is_empty() {
264 return Ok(out);
265 }
266 let decode_row = |c: &rusqlite::Connection,
271 row: &rusqlite::Row<'_>|
272 -> SQLiteResult<(DocId, Vec<Value>)> {
273 let doc_id = read_doc_id(row, 0)?;
274 let body = row.get::<_, String>(1)?;
275 let document = decode_legacy_document_body(&body)?;
276 let mut values = Vec::new();
277 values
278 .try_reserve_exact(fields.len())
279 .map_err(|error| allocation_error("multi-field document values", error))?;
280 for field in fields {
281 let mut value = document.get(*field).cloned().unwrap_or(Value::Null);
282 if let Some(marker) = blob_marker_info(&value) {
283 if let Some(decoded) =
284 load_marked_document_blob(c, &self.table, doc_id, field, &marker)?
285 {
286 value = decoded;
287 }
288 }
289 values.push(value);
290 }
291 Ok((doc_id, values))
292 };
293
294 let should_probe =
295 doc_ids.len() <= DOC_ID_IN_CHUNK || should_probe_doc_ids(doc_ids.len(), self.len()?);
296 if should_probe {
297 let leading = [rusqlite::types::Value::Text(self.table.clone())];
298 let sql = format!(
299 "SELECT doc_id, body FROM _documents
300 WHERE table_name = ?1 AND doc_id IN ({})",
301 doc_id_in_placeholders(2, DOC_ID_IN_CHUNK)?
302 );
303 self.conn.with(|c| {
304 for chunk in doc_ids.chunks(DOC_ID_IN_CHUNK) {
305 let mut stmt = c.prepare_cached(&sql)?;
306 let bind = chunk_bind_values(&leading, chunk)?;
307 let mut rows = stmt.query(rusqlite::params_from_iter(bind))?;
308 while let Some(row) = rows.next()? {
309 let (doc_id, values) = decode_row(c, row)?;
310 out.insert(doc_id, values);
311 }
312 }
313 Ok(())
314 })?;
315 return Ok(out);
316 }
317
318 let requested = sorted_unique_doc_ids(doc_ids)?;
319 self.conn.with(|c| {
320 let mut stmt = c.prepare_cached(
321 "SELECT doc_id, body FROM _documents
322 WHERE table_name = ?1
323 ORDER BY doc_id",
324 )?;
325 let mut rows = stmt.query(params![self.table])?;
326 while let Some(row) = rows.next()? {
327 let doc_id = read_doc_id(row, 0)?;
328 if requested.binary_search(&doc_id).is_err() {
329 continue;
330 }
331 let (doc_id, values) = decode_row(c, row)?;
332 out.insert(doc_id, values);
333 }
334 Ok(())
335 })?;
336 Ok(out)
337 }
338
339 fn get_many(&self, doc_ids: &[DocId]) -> StorageBackendResult<BTreeMap<DocId, Document>> {
340 self.get_stored_many(doc_ids).map(|documents| {
341 documents
342 .into_iter()
343 .map(|(doc_id, document)| (doc_id, document.into_fields()))
344 .collect()
345 })
346 }
347
348 fn patch_fields(
349 &mut self,
350 doc_id: DocId,
351 updates: &BTreeMap<String, Value>,
352 ) -> StorageBackendResult<bool> {
353 Ok(self.patch_fields_inner(doc_id, updates)?)
354 }
355
356 fn delete(&mut self, doc_id: DocId) -> StorageBackendResult<()> {
357 let sqlite_doc_id = sqlite_doc_id(doc_id)?;
358 self.conn.with(|c| {
359 c.prepare_cached(&format!(
360 "DELETE FROM {DOCUMENT_BLOBS_TABLE}
361 WHERE table_name = ?1 AND doc_id = ?2"
362 ))?
363 .execute(params![self.table, sqlite_doc_id])?;
364 c.prepare_cached("DELETE FROM _documents WHERE table_name = ?1 AND doc_id = ?2")?
365 .execute(params![self.table, sqlite_doc_id])?;
366 Ok(())
367 })?;
368 Ok(())
369 }
370
371 fn clear(&mut self) -> StorageBackendResult<()> {
372 self.conn.with(|c| {
373 c.execute(
374 &format!("DELETE FROM {DOCUMENT_BLOBS_TABLE} WHERE table_name = ?1"),
375 params![self.table],
376 )?;
377 c.execute(
378 "DELETE FROM _documents WHERE table_name = ?1",
379 params![self.table],
380 )?;
381 Ok(())
382 })?;
383 Ok(())
384 }
385
386 fn doc_ids(&self) -> StorageBackendResult<Vec<DocId>> {
387 Ok(self.conn.with(|c| {
388 let mut stmt = c.prepare_cached(
389 "SELECT doc_id FROM _documents WHERE table_name = ?1 ORDER BY doc_id",
390 )?;
391 let rows = stmt.query_map(params![self.table], |r| r.get::<_, i64>(0))?;
392 let mut out = Vec::new();
393 for row in rows {
394 out.push(document_id_from_sqlite(row?)?);
395 }
396 Ok(out)
397 })?)
398 }
399
400 fn next_doc_id(&self, after: Option<DocId>) -> StorageBackendResult<Option<DocId>> {
401 let after = after.map(sqlite_doc_id).transpose()?;
402 Ok(self.conn.with(|connection| {
403 let doc_id: Option<i64> = match after {
404 Some(after) => connection
405 .prepare_cached(
406 "SELECT doc_id FROM _documents
407 WHERE table_name = ?1 AND doc_id > ?2
408 ORDER BY doc_id LIMIT 1",
409 )?
410 .query_row(params![self.table, after], |row| row.get::<_, i64>(0))
411 .optional()?,
412 None => connection
413 .prepare_cached(
414 "SELECT doc_id FROM _documents
415 WHERE table_name = ?1
416 ORDER BY doc_id LIMIT 1",
417 )?
418 .query_row(params![self.table], |row| row.get::<_, i64>(0))
419 .optional()?,
420 };
421 doc_id.map(document_id_from_sqlite).transpose()
422 })?)
423 }
424
425 fn next_doc_ids(&self, after: Option<DocId>, limit: usize) -> StorageBackendResult<Vec<DocId>> {
426 if limit == 0 {
427 return Ok(Vec::new());
428 }
429 let after = after.map(sqlite_doc_id).transpose()?;
430 let limit = i64::try_from(limit).map_err(|_| {
431 SQLiteError::StorageBackend(format!(
432 "document cursor limit {limit} is outside SQLite's integer range"
433 ))
434 })?;
435 Ok(self.conn.with(|connection| {
436 let mut out = Vec::new();
437 if let Some(after) = after {
438 let mut stmt = connection.prepare_cached(
439 "SELECT doc_id FROM _documents
440 WHERE table_name = ?1 AND doc_id > ?2
441 ORDER BY doc_id LIMIT ?3",
442 )?;
443 let rows = stmt.query_map(params![self.table, after, limit], |row| {
444 row.get::<_, i64>(0)
445 })?;
446 for row in rows {
447 out.push(document_id_from_sqlite(row?)?);
448 }
449 } else {
450 let mut stmt = connection.prepare_cached(
451 "SELECT doc_id FROM _documents
452 WHERE table_name = ?1
453 ORDER BY doc_id LIMIT ?2",
454 )?;
455 let rows =
456 stmt.query_map(params![self.table, limit], |row| row.get::<_, i64>(0))?;
457 for row in rows {
458 out.push(document_id_from_sqlite(row?)?);
459 }
460 }
461 Ok(out)
462 })?)
463 }
464
465 fn max_doc_id(&self) -> StorageBackendResult<DocId> {
466 SQLiteDocumentStore::max_doc_id(self)
467 }
468
469 fn len(&self) -> StorageBackendResult<usize> {
470 Ok(self.conn.with(|c| {
471 let n: i64 = c
472 .prepare_cached("SELECT COUNT(*) FROM _documents WHERE table_name = ?1")?
473 .query_row(params![self.table], |r| r.get(0))?;
474 usize::try_from(n).map_err(|_| {
475 SQLiteError::StorageBackend(format!(
476 "document count {n} is outside the addressable range"
477 ))
478 })
479 })?)
480 }
481
482 fn snapshot(&self) -> StorageBackendResult<Arc<dyn DocumentStore>> {
483 Ok(Arc::new(self.clone()))
484 }
485}