1use std::path::Path;
2
3use rusqlite::{Connection, OptionalExtension};
4
5use super::{business, scope_filters as code_query_scope, software};
6
7mod batch;
8mod checkpoint_receipt;
9mod documents;
10mod feature_flags;
11mod frameworks;
12mod generated;
13mod impact;
14pub(in crate::storage) mod lifecycle;
15pub(in crate::storage::sqlite) mod publication;
16mod query;
17mod repository_set_store;
18mod routes;
19pub(in crate::storage::sqlite) mod schema;
20mod search;
21mod session_finalization;
22mod set;
23mod snapshot;
24mod software_projection_store;
25mod symbols;
26mod tasks;
27mod views;
28mod workspace;
29
30#[cfg(test)]
31pub(in crate::storage) use schema::ensure_code_query_indexes;
32
33#[cfg(test)]
34#[path = "tests/mod_tests.rs"]
35mod code_tests;
36
37#[cfg(test)]
38#[path = "tests/scope_status.rs"]
39mod code_scope_status_tests;
40
41#[cfg(test)]
42#[path = "tests/incremental_search.rs"]
43mod code_incremental_search_tests;
44
45#[cfg(test)]
46#[path = "tests/cross_language_calls.rs"]
47mod code_cross_language_call_tests;
48
49#[cfg(test)]
50#[path = "query/accuracy/mod.rs"]
51mod code_query_accuracy_tests;
52
53#[cfg(test)]
54#[path = "tests/metadata.rs"]
55mod code_metadata_tests;
56
57#[cfg(test)]
58#[path = "tests/unfenced_authority.rs"]
59mod code_unfenced_authority_tests;
60
61use crate::{
62 domain::{
63 BusinessKnowledgeProjection, BusinessKnowledgeProjectionInput,
64 BusinessKnowledgeQueryRequest, BusinessKnowledgeStatus, CodeFeatureFlagGraph,
65 CodeFeatureFlagRequest, CodeFileFingerprint, CodeImpactRequest, CodeIndexBatch,
66 CodeIndexCheckpoint, CodeIndexPublicationFence, CodeIndexSession, CodeIndexSnapshot,
67 CodeIndexSummary, CodeRepositoryRegistration, CodeRepositoryReport, CodeRepositoryStatus,
68 CodeRepositoryTotals, CodeRetrievalHit, CodeRetrievalRequest, CodeSymbolGenerationCounts,
69 CodebaseViewRequest, CodebaseViewSnapshot, IndexedRepositoryDocument,
70 },
71 storage::{
72 BusinessKnowledgeStore, CodeImpactChanges, CodeIndexPublicationStore, CodeIndexSourceStore,
73 CodeIndexTaskStore, CodeQueryReadStore, CodeScopeRetentionStore, RepositoryCatalogStore,
74 StorageError, StorageFuture,
75 },
76};
77
78use super::SqliteGraphStore;
79pub(in crate::storage) use lifecycle::commit_scope::{
80 preserve_existing_scope_commit, record as record_commit_scope,
81};
82use lifecycle::{cleanup, removal, report, status};
83pub(in crate::storage) use publication::record_receipt_from_active_fence;
84pub(super) use search::SearchDocumentInserter;
85#[cfg(test)]
86pub(in crate::storage) use tasks::MAX_SCOPE_SLOTS_PER_REPOSITORY;
87
88impl SqliteGraphStore {
89 pub(in crate::storage) fn code_query_indexes_ready_for_publication(
90 &self,
91 ) -> StorageFuture<'_, bool> {
92 self.run_read(|connection| schema::query_indexes_ready_for_fact_publication(connection))
93 }
94
95 pub(in crate::storage) fn materialize_partitioned_completed_checkpoint(
96 &self,
97 expected: CodeIndexCheckpoint,
98 fence: Option<CodeIndexPublicationFence>,
99 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
100 let authority_path = self.publication_authority_path.clone();
101 self.run(move |connection| {
102 let guard = fence
103 .map(|fence| {
104 lifecycle::publication_fence::prepare_guard(
105 connection,
106 fence,
107 authority_path.as_deref(),
108 )
109 })
110 .transpose()?;
111 batch::materialize_partitioned_completed_checkpoint(
112 connection,
113 expected,
114 guard.as_ref(),
115 )
116 })
117 }
118
119 pub(in crate::storage) fn reopen_completed_checkpoint_for_partitioned_repair(
120 &self,
121 expected: CodeIndexCheckpoint,
122 fence: CodeIndexPublicationFence,
123 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
124 let authority_path = self.publication_authority_path.clone();
125 self.run(move |connection| {
126 let guard = lifecycle::publication_fence::prepare_guard(
127 connection,
128 fence,
129 authority_path.as_deref(),
130 )?;
131 batch::reopen_completed_checkpoint_for_partitioned_repair(connection, expected, &guard)
132 })
133 }
134}
135
136pub(super) fn initialize_code_schema(connection: &Connection) -> Result<(), StorageError> {
137 schema::initialize_code_schema(connection)?;
138 software::initialize_schema(connection)?;
139 business::initialize_schema(connection)
140}
141
142pub(super) fn import_repository_from_database(
143 connection: &mut Connection,
144 source_path: &Path,
145 repository_id: &str,
146 source_scope: Option<&str>,
147) -> Result<(), StorageError> {
148 snapshot::import_repository_from_database(connection, source_path, repository_id, source_scope)
149}
150
151pub(super) fn repository_totals_excluding(
152 connection: &mut Connection,
153 excluded_repository_ids: &[String],
154) -> Result<CodeRepositoryTotals, StorageError> {
155 report::repository_totals_excluding(connection, excluded_repository_ids)
156}
157
158pub(super) fn prune_scopes_with_retained(
159 connection: &mut Connection,
160 request: crate::storage::CodeScopeRetentionRequest,
161 extra_retained_scopes: Vec<String>,
162) -> Result<crate::domain::CodeScopeRetentionSummary, StorageError> {
163 tasks::prune_scopes_with_retained(connection, request, extra_retained_scopes)
164}
165
166pub(super) fn complete_repository_retention(
167 connection: &mut Connection,
168 repository_id: &str,
169 cutoff_ms: u64,
170) -> Result<bool, StorageError> {
171 tasks::complete_repository_retention(connection, repository_id, cutoff_ms)
172}
173
174pub(super) fn repository_retention_republished_initial_scope(
175 connection: &Connection,
176 repository_id: &str,
177 initial_scope: &str,
178 cutoff_ms: u64,
179 cutoff_publication_generation: u64,
180) -> Result<Option<String>, StorageError> {
181 tasks::repository_retention_republished_initial_scope(
182 connection,
183 repository_id,
184 initial_scope,
185 cutoff_ms,
186 cutoff_publication_generation,
187 )
188}
189
190fn ensure_queryable_code_scope(
191 connection: &Connection,
192 source_scope: &str,
193) -> Result<(), StorageError> {
194 tasks::retention_gc::reject_retiring_scope(connection, source_scope)?;
195 let stale = connection
196 .query_row(
197 "SELECT stale FROM code_repository_scopes WHERE source_scope = ?1",
198 [source_scope],
199 |row| row.get::<_, bool>(0),
200 )
201 .optional()?
202 .ok_or_else(|| {
203 StorageError::InvalidInput(format!(
204 "code repository scope '{source_scope}' is unavailable"
205 ))
206 })?;
207 if stale {
208 return Err(StorageError::InvalidInput(format!(
209 "code repository scope '{source_scope}' is not published"
210 )));
211 }
212 #[cfg(test)]
213 read_snapshot_test_hook::after_retiring_check();
214 Ok(())
215}
216
217impl RepositoryCatalogStore for SqliteGraphStore {
218 fn upsert_code_repository(
219 &self,
220 registration: CodeRepositoryRegistration,
221 ) -> StorageFuture<'_, CodeRepositoryStatus> {
222 self.run(move |connection| status::upsert_repository(connection, registration))
223 }
224
225 fn code_repository_status(
226 &self,
227 repository: String,
228 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
229 self.run_read_snapshot(move |connection| status::repository_status(connection, &repository))
230 }
231
232 fn list_code_repositories(&self) -> StorageFuture<'_, Vec<CodeRepositoryStatus>> {
233 self.run_read_snapshot(status::repository_statuses)
234 }
235
236 fn remove_code_repository(
237 &self,
238 repository: String,
239 now_ms: u64,
240 ) -> StorageFuture<'_, Option<crate::domain::CodeRepositoryRemovalSummary>> {
241 self.run(move |connection| removal::remove_repository(connection, &repository, now_ms))
242 }
243
244 fn code_repository_scope_status(
245 &self,
246 repository: String,
247 resolved_commit_sha: String,
248 path_filters: Vec<String>,
249 language_filters: Vec<String>,
250 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
251 self.run_read_snapshot(move |connection| {
252 status::repository_scope_status(
253 connection,
254 &repository,
255 &resolved_commit_sha,
256 &path_filters,
257 &language_filters,
258 )
259 })
260 }
261
262 fn latest_code_repository_scope_status(
263 &self,
264 repository: String,
265 path_filters: Vec<String>,
266 language_filters: Vec<String>,
267 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
268 self.run_read_snapshot(move |connection| {
269 status::latest_repository_scope_status(
270 connection,
271 &repository,
272 &path_filters,
273 &language_filters,
274 )
275 })
276 }
277}
278
279impl CodeIndexTaskStore for SqliteGraphStore {
280 fn queue_code_index_task(
281 &self,
282 task: crate::storage::CodeIndexTaskSeed,
283 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
284 self.run(move |connection| tasks::queue_task(connection, task))
285 }
286
287 fn claim_code_index_task(
288 &self,
289 request: crate::storage::CodeIndexTaskClaimRequest,
290 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexTaskRecord>> {
291 self.run(move |connection| tasks::claim_task(connection, request))
292 }
293
294 fn recover_code_index_task_leases(
295 &self,
296 now_ms: u64,
297 max_attempts: u32,
298 ) -> StorageFuture<'_, ()> {
299 self.run(move |connection| {
300 tasks::recover_expired_task_leases(connection, now_ms, max_attempts)
301 })
302 }
303
304 fn running_code_index_task_leases(
305 &self,
306 ) -> StorageFuture<'_, Vec<crate::storage::CodeIndexTaskLeaseRecord>> {
307 self.run_read(tasks::running_task_leases)
308 }
309
310 fn recover_code_index_task_leases_by_task(
311 &self,
312 request: crate::storage::CodeIndexTaskLeaseRecovery,
313 ) -> StorageFuture<'_, usize> {
314 self.run(move |connection| tasks::recover_task_leases_by_task(connection, request))
315 }
316
317 fn reset_code_index_tasks(
318 &self,
319 repository_id: String,
320 now_ms: u64,
321 ) -> StorageFuture<'_, Vec<crate::domain::CodeIndexTaskRecord>> {
322 self.run(move |connection| tasks::reset_tasks(connection, &repository_id, now_ms))
323 }
324
325 fn renew_code_index_task_lease(
326 &self,
327 request: crate::storage::CodeIndexTaskLeaseRenewal,
328 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
329 self.run(move |connection| tasks::renew_task_lease(connection, request))
330 }
331
332 fn complete_code_index_task(
333 &self,
334 request: crate::storage::CodeIndexTaskCompletion,
335 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
336 self.run(move |connection| tasks::complete_task(connection, request))
337 }
338
339 fn run_code_index_post_maintenance(
340 &self,
341 _repository_id: String,
342 _source_scope: String,
343 ) -> StorageFuture<'_, ()> {
344 let maintenance = self.maintenance.clone();
345 self.run(move |connection| {
346 super::connection_runtime::maintenance::run_post_index_maintenance(
347 connection,
348 &maintenance,
349 );
350 Ok(())
351 })
352 }
353
354 fn code_index_publication_receipt(
355 &self,
356 task_id: String,
357 repository_id: String,
358 source_scope: String,
359 now_ms: u64,
360 ) -> StorageFuture<'_, bool> {
361 self.run_read(move |connection| {
362 tasks::publication_receipt(connection, &task_id, &repository_id, &source_scope, now_ms)
363 })
364 }
365
366 fn reconcile_code_index_publication_with_fence(
367 &self,
368 target: crate::storage::CodeIndexPublicationTarget,
369 fence: CodeIndexPublicationFence,
370 ) -> StorageFuture<'_, bool> {
371 let authority_path = self.publication_authority_path.clone();
372 self.run(move |connection| {
373 let guard = lifecycle::publication_fence::prepare_guard(
374 connection,
375 fence,
376 authority_path.as_deref(),
377 )?;
378 publication::adopt_active_target(connection, &target, &guard)
379 })
380 }
381
382 fn fail_code_index_task(
383 &self,
384 request: crate::storage::CodeIndexTaskFailure,
385 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
386 self.run(move |connection| tasks::fail_task(connection, request))
387 }
388
389 fn code_index_task(
390 &self,
391 task_id: String,
392 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexTaskRecord>> {
393 self.run_read(move |connection| tasks::task_by_id(connection, &task_id))
394 }
395
396 fn active_code_index_task(
397 &self,
398 repository_id: String,
399 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexTaskRecord>> {
400 self.run_read(move |connection| tasks::active_task(connection, &repository_id))
401 }
402
403 fn code_index_task_queue_status(
404 &self,
405 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskQueueStatus> {
406 self.run_read(tasks::queue_status)
407 }
408}
409
410impl CodeScopeRetentionStore for SqliteGraphStore {
411 fn code_scope_retention(
412 &self,
413 repository_id: String,
414 ) -> StorageFuture<'_, crate::domain::CodeScopeRetentionSummary> {
415 self.run_read_snapshot(move |connection| {
416 tasks::retention_status(connection, &repository_id)
417 })
418 }
419
420 fn prune_code_repository_scopes(
421 &self,
422 request: crate::storage::CodeScopeRetentionRequest,
423 ) -> StorageFuture<'_, crate::domain::CodeScopeRetentionSummary> {
424 self.run(move |connection| tasks::prune_scopes(connection, request))
425 }
426
427 fn schedule_code_repository_retention(
428 &self,
429 max_indexed_repositories: usize,
430 now_ms: u64,
431 ) -> StorageFuture<'_, Option<String>> {
432 self.run(move |connection| {
433 tasks::schedule_repository_retention(connection, max_indexed_repositories, now_ms)
434 })
435 }
436
437 fn code_repository_retention_scan_pending(&self) -> StorageFuture<'_, bool> {
438 self.run_read(|connection| tasks::repository_retention_scan_pending(connection))
439 }
440}
441
442impl CodeIndexSourceStore for SqliteGraphStore {
443 fn code_file_fingerprints(
444 &self,
445 repository_id: String,
446 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
447 self.run_read(move |connection| snapshot::file_fingerprints(connection, &repository_id))
448 }
449
450 fn code_file_fingerprints_for_scope(
451 &self,
452 source_scope: String,
453 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
454 self.run_read_snapshot(move |connection| {
455 ensure_queryable_code_scope(connection, &source_scope)?;
456 snapshot::file_fingerprints_for_scope(connection, &source_scope)
457 })
458 }
459
460 fn code_file_fingerprints_for_paths(
461 &self,
462 source_scope: String,
463 paths: Vec<String>,
464 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
465 self.run_read_snapshot(move |connection| {
466 ensure_queryable_code_scope(connection, &source_scope)?;
467 snapshot::file_fingerprints_for_paths(connection, &source_scope, &paths)
468 })
469 }
470
471 fn code_file_candidate_paths_for_scope(
472 &self,
473 source_scope: String,
474 path_filters: Vec<String>,
475 language_filters: Vec<String>,
476 exclude_generated: bool,
477 limit: usize,
478 ) -> StorageFuture<'_, Vec<String>> {
479 self.run_read_snapshot(move |connection| {
480 ensure_queryable_code_scope(connection, &source_scope)?;
481 snapshot::file_candidate_paths_for_scope(
482 connection,
483 &source_scope,
484 &path_filters,
485 &language_filters,
486 exclude_generated,
487 limit,
488 )
489 })
490 }
491
492 fn code_file_candidate_paths_for_query_scope(
493 &self,
494 source_scope: String,
495 query: String,
496 path_filters: Vec<String>,
497 language_filters: Vec<String>,
498 exclude_generated: bool,
499 limit: usize,
500 ) -> StorageFuture<'_, Vec<String>> {
501 self.run_read_snapshot(move |connection| {
502 ensure_queryable_code_scope(connection, &source_scope)?;
503 snapshot::file_candidate_paths_for_query_scope(
504 connection,
505 &source_scope,
506 &query,
507 &path_filters,
508 &language_filters,
509 exclude_generated,
510 limit,
511 )
512 })
513 }
514
515 fn repository_documents_for_scope(
516 &self,
517 source_scope: String,
518 path_filters: Vec<String>,
519 max_files: usize,
520 max_bytes: usize,
521 ) -> StorageFuture<'_, Vec<IndexedRepositoryDocument>> {
522 self.run_read_snapshot(move |connection| {
523 ensure_queryable_code_scope(connection, &source_scope)?;
524 documents::read_indexed_markdown_in_snapshot(
525 connection,
526 &source_scope,
527 &path_filters,
528 max_files,
529 max_bytes,
530 )
531 })
532 }
533}
534
535impl CodeIndexPublicationStore for SqliteGraphStore {
536 fn code_index_checkpoint(
537 &self,
538 source_scope: String,
539 ) -> StorageFuture<'_, Option<CodeIndexCheckpoint>> {
540 self.run_read(move |connection| tasks::checkpoint(connection, &source_scope))
541 }
542
543 fn latest_code_index_checkpoint(
544 &self,
545 repository_id: String,
546 ) -> StorageFuture<'_, Option<CodeIndexCheckpoint>> {
547 self.run_read(move |connection| {
548 tasks::latest_checkpoint_for_repository(connection, &repository_id)
549 })
550 }
551
552 fn apply_code_index_snapshot(
553 &self,
554 snapshot: CodeIndexSnapshot,
555 ) -> StorageFuture<'_, CodeIndexSummary> {
556 let this = self.clone();
557 Box::pin(async move {
558 let summary = this
559 .run(move |connection| snapshot::apply_snapshot(connection, snapshot))
560 .await?;
561 session_finalization::run_best_effort_maintenance(&this).await;
562 Ok(summary)
563 })
564 }
565
566 fn apply_code_index_snapshot_with_fence(
567 &self,
568 snapshot: CodeIndexSnapshot,
569 fence: CodeIndexPublicationFence,
570 ) -> StorageFuture<'_, CodeIndexSummary> {
571 let authority_path = self.publication_authority_path.clone();
572 self.run(move |connection| {
573 let guard = lifecycle::publication_fence::prepare_guard(
574 connection,
575 fence,
576 authority_path.as_deref(),
577 )?;
578 snapshot::apply_snapshot_with_fence(connection, snapshot, Some(&guard))
579 })
580 }
581
582 fn clear_code_workspace_state(
583 &self,
584 repository_id: String,
585 source_scope: String,
586 ) -> StorageFuture<'_, ()> {
587 self.run(move |connection| {
588 workspace::clear_auto_workspace_state(connection, &repository_id, &source_scope)
589 })
590 }
591
592 fn code_repository_auto_workspace_state_exists(
593 &self,
594 repository_id: String,
595 ) -> StorageFuture<'_, bool> {
596 self.run_read(move |connection| {
597 workspace::has_auto_workspace_state(connection, &repository_id)
598 })
599 }
600
601 fn clear_code_workspace_state_with_fence(
602 &self,
603 repository_id: String,
604 source_scope: String,
605 fence: CodeIndexPublicationFence,
606 ) -> StorageFuture<'_, ()> {
607 let authority_path = self.publication_authority_path.clone();
608 self.run(move |connection| {
609 let guard = lifecycle::publication_fence::prepare_guard(
610 connection,
611 fence,
612 authority_path.as_deref(),
613 )?;
614 workspace::clear_auto_workspace_state_with_fence(
615 connection,
616 &repository_id,
617 &source_scope,
618 &guard,
619 )
620 })
621 }
622
623 fn begin_code_index_session(
624 &self,
625 session: CodeIndexSession,
626 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
627 self.run(move |connection| batch::begin_session(connection, session))
628 }
629
630 fn begin_code_index_session_with_fence(
631 &self,
632 session: CodeIndexSession,
633 fence: CodeIndexPublicationFence,
634 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
635 let authority_path = self.publication_authority_path.clone();
636 self.run(move |connection| {
637 let guard = lifecycle::publication_fence::prepare_guard(
638 connection,
639 fence,
640 authority_path.as_deref(),
641 )?;
642 batch::begin_session_with_fence(connection, session, Some(&guard))
643 })
644 }
645
646 fn begin_code_index_session_at_checkpoint(
647 &self,
648 session: CodeIndexSession,
649 expected_checkpoint: Option<CodeIndexCheckpoint>,
650 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
651 self.run(move |connection| {
652 batch::begin_session_at_checkpoint(connection, session, expected_checkpoint)
653 })
654 }
655
656 fn begin_code_index_session_at_checkpoint_with_fence(
657 &self,
658 session: CodeIndexSession,
659 expected_checkpoint: Option<CodeIndexCheckpoint>,
660 fence: CodeIndexPublicationFence,
661 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
662 let authority_path = self.publication_authority_path.clone();
663 self.run(move |connection| {
664 let guard = lifecycle::publication_fence::prepare_guard(
665 connection,
666 fence,
667 authority_path.as_deref(),
668 )?;
669 batch::begin_session_at_checkpoint_with_fence(
670 connection,
671 session,
672 expected_checkpoint,
673 Some(&guard),
674 )
675 })
676 }
677
678 fn apply_code_index_batch(
679 &self,
680 batch: CodeIndexBatch,
681 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
682 self.run(move |connection| batch::apply_batch(connection, batch))
683 }
684
685 fn apply_code_index_batch_with_fence(
686 &self,
687 batch: CodeIndexBatch,
688 fence: CodeIndexPublicationFence,
689 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
690 let authority_path = self.publication_authority_path.clone();
691 self.run(move |connection| {
692 let guard = lifecycle::publication_fence::prepare_guard(
693 connection,
694 fence,
695 authority_path.as_deref(),
696 )?;
697 batch::apply_batch_with_fence(connection, batch, Some(&guard))
698 })
699 }
700
701 fn finalize_code_index_session(
702 &self,
703 session: CodeIndexSession,
704 ) -> StorageFuture<'_, CodeIndexSummary> {
705 session_finalization::finalize_session(self, session)
706 }
707
708 fn finalize_code_index_session_with_fence(
709 &self,
710 session: CodeIndexSession,
711 fence: CodeIndexPublicationFence,
712 ) -> StorageFuture<'_, CodeIndexSummary> {
713 session_finalization::finalize_session_with_fence(self, session, fence)
714 }
715
716 fn advance_code_index_session_with_fence(
717 &self,
718 session: CodeIndexSession,
719 fence: CodeIndexPublicationFence,
720 ) -> StorageFuture<'_, crate::storage::CodeIndexFinalizationStep> {
721 session_finalization::advance_session_with_fence(self, session, fence)
722 }
723}
724
725impl CodeQueryReadStore for SqliteGraphStore {
726 fn search_code(
727 &self,
728 request: CodeRetrievalRequest,
729 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
730 self.run_read_snapshot(move |connection| query::search_code(connection, request))
731 }
732
733 fn search_code_feature_flags(
734 &self,
735 request: CodeFeatureFlagRequest,
736 ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
737 self.run_read_snapshot(move |connection| feature_flags::search(connection, request))
738 }
739
740 fn search_code_feature_flags_scope(
741 &self,
742 source_scope: String,
743 request: CodeFeatureFlagRequest,
744 ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
745 self.run_read_snapshot(move |connection| {
746 ensure_queryable_code_scope(connection, &source_scope)?;
747 feature_flags::search_scope(connection, &source_scope, request)
748 })
749 }
750
751 fn search_code_scope(
752 &self,
753 source_scope: String,
754 request: CodeRetrievalRequest,
755 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
756 self.run_read_snapshot(move |connection| {
757 ensure_queryable_code_scope(connection, &source_scope)?;
758 query::search_code_scope(connection, &source_scope, request)
759 })
760 }
761
762 fn analyze_code_impact(
763 &self,
764 request: CodeImpactRequest,
765 changes: CodeImpactChanges,
766 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
767 self.run_read_snapshot(move |connection| {
768 impact::analyze_impact(connection, request, changes)
769 })
770 }
771
772 fn analyze_code_impact_scope(
773 &self,
774 source_scope: String,
775 request: CodeImpactRequest,
776 changes: CodeImpactChanges,
777 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
778 self.run_read_snapshot(move |connection| {
779 ensure_queryable_code_scope(connection, &source_scope)?;
780 impact::analyze_impact_scope(connection, &source_scope, request, changes)
781 })
782 }
783
784 fn codebase_view_snapshot(
785 &self,
786 source_scope: String,
787 request: CodebaseViewRequest,
788 row_limit: usize,
789 ) -> StorageFuture<'_, CodebaseViewSnapshot> {
790 self.run_read_snapshot(move |connection| {
791 ensure_queryable_code_scope(connection, &source_scope)?;
792 views::snapshot(connection, &source_scope, &request, row_limit)
793 })
794 }
795
796 fn code_repository_totals(&self) -> StorageFuture<'_, CodeRepositoryTotals> {
797 self.run_read(report::repository_totals)
798 }
799
800 fn code_repository_report(
801 &self,
802 repository: String,
803 ) -> StorageFuture<'_, CodeRepositoryReport> {
804 self.run_read(move |connection| report::repository_report(connection, &repository))
805 }
806
807 fn code_repository_scope_symbol_generation_counts(
808 &self,
809 source_scope: String,
810 ) -> StorageFuture<'_, CodeSymbolGenerationCounts> {
811 self.run_read_snapshot(move |connection| {
812 ensure_queryable_code_scope(connection, &source_scope)?;
813 let counts = report::scope_symbol_generation_counts(connection, &source_scope)?;
814 Ok(CodeSymbolGenerationCounts {
815 handwritten_symbol_count: counts.handwritten,
816 generated_symbol_count: counts.generated,
817 })
818 })
819 }
820}
821
822impl BusinessKnowledgeStore for SqliteGraphStore {
823 fn replace_business_knowledge_projection(
824 &self,
825 input: BusinessKnowledgeProjectionInput,
826 ) -> StorageFuture<'_, BusinessKnowledgeStatus> {
827 self.run(move |connection| business::replace_projection(connection, input, None))
828 }
829
830 fn replace_business_knowledge_projection_with_fence(
831 &self,
832 input: BusinessKnowledgeProjectionInput,
833 fence: CodeIndexPublicationFence,
834 ) -> StorageFuture<'_, BusinessKnowledgeStatus> {
835 let authority_path = self.publication_authority_path.clone();
836 self.run(move |connection| {
837 let guard = lifecycle::publication_fence::prepare_guard(
838 connection,
839 fence,
840 authority_path.as_deref(),
841 )?;
842 business::replace_projection(connection, input, Some(&guard))
843 })
844 }
845
846 fn business_knowledge_projection_for_scope(
847 &self,
848 source_scope: String,
849 request: BusinessKnowledgeQueryRequest,
850 ) -> StorageFuture<'_, BusinessKnowledgeProjection> {
851 self.run_read_snapshot(move |connection| {
852 ensure_queryable_code_scope(connection, &source_scope)?;
853 business::projection_for_scope(connection, &source_scope, request)
854 })
855 }
856
857 fn business_knowledge_status(
858 &self,
859 source_scope: String,
860 ) -> StorageFuture<'_, Option<BusinessKnowledgeStatus>> {
861 self.run_read_snapshot(move |connection| {
862 business::status_for_scope(connection, &source_scope)
863 })
864 }
865}
866
867#[cfg(test)]
868#[path = "tests/read_snapshot_test_hook.rs"]
869mod read_snapshot_test_hook;