1use std::path::Path;
2
3use rusqlite::Connection;
4
5use super::{scope_filters as code_query_scope, software};
6
7mod batch;
8mod feature_flags;
9mod generated;
10mod impact;
11pub(super) mod lifecycle;
12mod query;
13mod routes;
14mod schema;
15mod search;
16mod set;
17mod snapshot;
18mod symbols;
19mod tasks;
20mod views;
21mod workspace;
22
23#[cfg(test)]
24#[path = "tests/mod_tests.rs"]
25mod code_tests;
26
27#[cfg(test)]
28#[path = "tests/scope_status.rs"]
29mod code_scope_status_tests;
30
31#[cfg(test)]
32#[path = "tests/incremental_search.rs"]
33mod code_incremental_search_tests;
34
35#[cfg(test)]
36#[path = "tests/cross_language_calls.rs"]
37mod code_cross_language_call_tests;
38
39#[cfg(test)]
40#[path = "query/accuracy/mod.rs"]
41mod code_query_accuracy_tests;
42
43#[cfg(test)]
44#[path = "tests/metadata.rs"]
45mod code_metadata_tests;
46
47use crate::{
48 domain::{
49 CodeFeatureFlagGraph, CodeFeatureFlagRequest, CodeFileFingerprint, CodeImpactRequest,
50 CodeIndexBatch, CodeIndexCheckpoint, CodeIndexSession, CodeIndexSnapshot, CodeIndexSummary,
51 CodeRepositoryRegistration, CodeRepositoryReport, CodeRepositoryStatus,
52 CodeRepositoryTotals, CodeRetrievalHit, CodeRetrievalRequest, CodeSymbolGenerationCounts,
53 CodebaseViewRequest, CodebaseViewSnapshot, SoftwareGlobalProjection, SoftwareGlobalRequest,
54 },
55 storage::{CodeImpactChanges, CodeRepositoryStore, StorageError, StorageFuture},
56};
57
58use super::SqliteGraphStore;
59use lifecycle::{cleanup, removal, report, status};
60pub(super) use search::SearchDocumentInserter;
61
62const MAX_SYMBOL_SIGNATURE_LOOKUP_IDS_PER_STATEMENT: usize = 500;
63
64pub(super) fn initialize_code_schema(connection: &Connection) -> Result<(), StorageError> {
65 schema::initialize_code_schema(connection)?;
66 software::initialize_schema(connection)
67}
68
69pub(super) fn import_repository_from_database(
70 connection: &mut Connection,
71 source_path: &Path,
72 repository_id: &str,
73 source_scope: Option<&str>,
74) -> Result<(), StorageError> {
75 snapshot::import_repository_from_database(connection, source_path, repository_id, source_scope)
76}
77
78pub(super) fn repository_totals_excluding(
79 connection: &mut Connection,
80 excluded_repository_ids: &[String],
81) -> Result<CodeRepositoryTotals, StorageError> {
82 report::repository_totals_excluding(connection, excluded_repository_ids)
83}
84
85pub(super) fn prune_scopes_with_retained(
86 connection: &mut Connection,
87 request: crate::storage::CodeScopeRetentionRequest,
88 extra_retained_scopes: Vec<String>,
89) -> Result<crate::domain::CodeScopeRetentionSummary, StorageError> {
90 tasks::prune_scopes_with_retained(connection, request, extra_retained_scopes)
91}
92
93impl CodeRepositoryStore for SqliteGraphStore {
94 fn upsert_code_repository(
95 &self,
96 registration: CodeRepositoryRegistration,
97 ) -> StorageFuture<'_, CodeRepositoryStatus> {
98 self.run(move |connection| status::upsert_repository(connection, registration))
99 }
100
101 fn code_repository_status(
102 &self,
103 repository: String,
104 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
105 self.run_read(move |connection| status::repository_status(connection, &repository))
106 }
107
108 fn list_code_repositories(&self) -> StorageFuture<'_, Vec<CodeRepositoryStatus>> {
109 self.run_read(status::repository_statuses)
110 }
111
112 fn remove_code_repository(
113 &self,
114 repository: String,
115 now_ms: u64,
116 ) -> StorageFuture<'_, Option<crate::domain::CodeRepositoryRemovalSummary>> {
117 self.run(move |connection| removal::remove_repository(connection, &repository, now_ms))
118 }
119
120 fn code_repository_scope_status(
121 &self,
122 repository: String,
123 resolved_commit_sha: String,
124 path_filters: Vec<String>,
125 language_filters: Vec<String>,
126 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
127 self.run_read(move |connection| {
128 status::repository_scope_status(
129 connection,
130 &repository,
131 &resolved_commit_sha,
132 &path_filters,
133 &language_filters,
134 )
135 })
136 }
137
138 fn latest_code_repository_scope_status(
139 &self,
140 repository: String,
141 path_filters: Vec<String>,
142 language_filters: Vec<String>,
143 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
144 self.run_read(move |connection| {
145 status::latest_repository_scope_status(
146 connection,
147 &repository,
148 &path_filters,
149 &language_filters,
150 )
151 })
152 }
153
154 fn queue_code_index_task(
155 &self,
156 task: crate::storage::CodeIndexTaskSeed,
157 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
158 self.run(move |connection| tasks::queue_task(connection, task))
159 }
160
161 fn claim_code_index_task(
162 &self,
163 request: crate::storage::CodeIndexTaskClaimRequest,
164 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexTaskRecord>> {
165 self.run(move |connection| tasks::claim_task(connection, request))
166 }
167
168 fn recover_code_index_task_leases(
169 &self,
170 now_ms: u64,
171 max_attempts: u32,
172 ) -> StorageFuture<'_, ()> {
173 self.run(move |connection| {
174 tasks::recover_expired_task_leases(connection, now_ms, max_attempts)
175 })
176 }
177
178 fn running_code_index_task_leases(
179 &self,
180 ) -> StorageFuture<'_, Vec<crate::storage::CodeIndexTaskLeaseRecord>> {
181 self.run_read(tasks::running_task_leases)
182 }
183
184 fn recover_code_index_task_leases_by_task(
185 &self,
186 request: crate::storage::CodeIndexTaskLeaseRecovery,
187 ) -> StorageFuture<'_, usize> {
188 self.run(move |connection| tasks::recover_task_leases_by_task(connection, request))
189 }
190
191 fn reset_code_index_tasks(
192 &self,
193 repository_id: String,
194 now_ms: u64,
195 ) -> StorageFuture<'_, Vec<crate::domain::CodeIndexTaskRecord>> {
196 self.run(move |connection| tasks::reset_tasks(connection, &repository_id, now_ms))
197 }
198
199 fn renew_code_index_task_lease(
200 &self,
201 request: crate::storage::CodeIndexTaskLeaseRenewal,
202 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
203 self.run(move |connection| tasks::renew_task_lease(connection, request))
204 }
205
206 fn complete_code_index_task(
207 &self,
208 request: crate::storage::CodeIndexTaskCompletion,
209 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
210 self.run(move |connection| tasks::complete_task(connection, request))
211 }
212
213 fn fail_code_index_task(
214 &self,
215 request: crate::storage::CodeIndexTaskFailure,
216 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskRecord> {
217 self.run(move |connection| tasks::fail_task(connection, request))
218 }
219
220 fn code_index_task(
221 &self,
222 task_id: String,
223 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexTaskRecord>> {
224 self.run_read(move |connection| tasks::task_by_id(connection, &task_id))
225 }
226
227 fn active_code_index_task(
228 &self,
229 repository_id: String,
230 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexTaskRecord>> {
231 self.run_read(move |connection| tasks::active_task(connection, &repository_id))
232 }
233
234 fn code_index_task_queue_status(
235 &self,
236 ) -> StorageFuture<'_, crate::domain::CodeIndexTaskQueueStatus> {
237 self.run_read(tasks::queue_status)
238 }
239
240 fn code_index_checkpoint(
241 &self,
242 source_scope: String,
243 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexCheckpoint>> {
244 self.run_read(move |connection| tasks::checkpoint(connection, &source_scope))
245 }
246
247 fn latest_code_index_checkpoint(
248 &self,
249 repository_id: String,
250 ) -> StorageFuture<'_, Option<crate::domain::CodeIndexCheckpoint>> {
251 self.run_read(move |connection| {
252 tasks::latest_checkpoint_for_repository(connection, &repository_id)
253 })
254 }
255
256 fn code_scope_retention(
257 &self,
258 repository_id: String,
259 ) -> StorageFuture<'_, crate::domain::CodeScopeRetentionSummary> {
260 self.run_read(move |connection| tasks::retention_status(connection, &repository_id))
261 }
262
263 fn prune_code_repository_scopes(
264 &self,
265 request: crate::storage::CodeScopeRetentionRequest,
266 ) -> StorageFuture<'_, crate::domain::CodeScopeRetentionSummary> {
267 self.run(move |connection| tasks::prune_scopes(connection, request))
268 }
269
270 fn code_file_fingerprints(
271 &self,
272 repository_id: String,
273 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
274 self.run_read(move |connection| snapshot::file_fingerprints(connection, &repository_id))
275 }
276
277 fn code_file_fingerprints_for_scope(
278 &self,
279 source_scope: String,
280 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
281 self.run_read(move |connection| {
282 snapshot::file_fingerprints_for_scope(connection, &source_scope)
283 })
284 }
285
286 fn code_file_fingerprints_for_paths(
287 &self,
288 source_scope: String,
289 paths: Vec<String>,
290 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
291 self.run_read(move |connection| {
292 snapshot::file_fingerprints_for_paths(connection, &source_scope, &paths)
293 })
294 }
295
296 fn code_file_candidate_paths_for_scope(
297 &self,
298 source_scope: String,
299 path_filters: Vec<String>,
300 language_filters: Vec<String>,
301 exclude_generated: bool,
302 limit: usize,
303 ) -> StorageFuture<'_, Vec<String>> {
304 self.run_read(move |connection| {
305 snapshot::file_candidate_paths_for_scope(
306 connection,
307 &source_scope,
308 &path_filters,
309 &language_filters,
310 exclude_generated,
311 limit,
312 )
313 })
314 }
315
316 fn code_file_candidate_paths_for_query_scope(
317 &self,
318 source_scope: String,
319 query: String,
320 path_filters: Vec<String>,
321 language_filters: Vec<String>,
322 exclude_generated: bool,
323 limit: usize,
324 ) -> StorageFuture<'_, Vec<String>> {
325 self.run_read(move |connection| {
326 snapshot::file_candidate_paths_for_query_scope(
327 connection,
328 &source_scope,
329 &query,
330 &path_filters,
331 &language_filters,
332 exclude_generated,
333 limit,
334 )
335 })
336 }
337
338 fn apply_code_index_snapshot(
339 &self,
340 snapshot: CodeIndexSnapshot,
341 ) -> StorageFuture<'_, CodeIndexSummary> {
342 let maintenance = self.maintenance.clone();
343 self.run(move |connection| {
344 let summary = snapshot::apply_snapshot(connection, snapshot)?;
345 super::connection_runtime::maintenance::run_post_index_maintenance(
346 connection,
347 &maintenance,
348 );
349
350 Ok(summary)
351 })
352 }
353
354 fn clear_code_workspace_state(
355 &self,
356 repository_id: String,
357 source_scope: String,
358 ) -> StorageFuture<'_, ()> {
359 self.run(move |connection| {
360 workspace::clear_auto_workspace_state(connection, &repository_id, &source_scope)
361 })
362 }
363
364 fn begin_code_index_session(
365 &self,
366 session: CodeIndexSession,
367 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
368 self.run(move |connection| batch::begin_session(connection, session))
369 }
370
371 fn apply_code_index_batch(
372 &self,
373 batch: CodeIndexBatch,
374 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
375 self.run(move |connection| batch::apply_batch(connection, batch))
376 }
377
378 fn finalize_code_index_session(
379 &self,
380 session: CodeIndexSession,
381 ) -> StorageFuture<'_, CodeIndexSummary> {
382 let maintenance = self.maintenance.clone();
383 self.run(move |connection| {
384 let summary = batch::finalize_session(connection, session)?;
385 super::connection_runtime::maintenance::run_post_index_maintenance(
386 connection,
387 &maintenance,
388 );
389
390 Ok(summary)
391 })
392 }
393
394 fn search_code(
395 &self,
396 request: CodeRetrievalRequest,
397 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
398 self.run_read(move |connection| query::search_code(connection, request))
399 }
400
401 fn search_code_feature_flags(
402 &self,
403 request: CodeFeatureFlagRequest,
404 ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
405 self.run_read(move |connection| feature_flags::search(connection, request))
406 }
407
408 fn search_code_feature_flags_scope(
409 &self,
410 source_scope: String,
411 request: CodeFeatureFlagRequest,
412 ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
413 self.run_read(move |connection| {
414 feature_flags::search_scope(connection, &source_scope, request)
415 })
416 }
417
418 fn search_code_scope(
419 &self,
420 source_scope: String,
421 request: CodeRetrievalRequest,
422 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
423 self.run_read(move |connection| {
424 query::search_code_scope(connection, &source_scope, request)
425 })
426 }
427
428 fn analyze_code_impact(
429 &self,
430 request: CodeImpactRequest,
431 changes: CodeImpactChanges,
432 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
433 self.run_read(move |connection| impact::analyze_impact(connection, request, changes))
434 }
435
436 fn analyze_code_impact_scope(
437 &self,
438 source_scope: String,
439 request: CodeImpactRequest,
440 changes: CodeImpactChanges,
441 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
442 self.run_read(move |connection| {
443 impact::analyze_impact_scope(connection, &source_scope, request, changes)
444 })
445 }
446
447 fn codebase_view_snapshot(
448 &self,
449 source_scope: String,
450 request: CodebaseViewRequest,
451 row_limit: usize,
452 ) -> StorageFuture<'_, CodebaseViewSnapshot> {
453 self.run_read(move |connection| {
454 views::snapshot(connection, &source_scope, &request, row_limit)
455 })
456 }
457
458 fn code_repository_totals(&self) -> StorageFuture<'_, CodeRepositoryTotals> {
459 self.run_read(report::repository_totals)
460 }
461
462 fn code_repository_report(
463 &self,
464 repository: String,
465 ) -> StorageFuture<'_, CodeRepositoryReport> {
466 self.run_read(move |connection| report::repository_report(connection, &repository))
467 }
468
469 fn code_repository_scope_symbol_generation_counts(
470 &self,
471 source_scope: String,
472 ) -> StorageFuture<'_, CodeSymbolGenerationCounts> {
473 self.run_read(move |connection| {
474 let counts = report::scope_symbol_generation_counts(connection, &source_scope)?;
475 Ok(CodeSymbolGenerationCounts {
476 handwritten_symbol_count: counts.handwritten,
477 generated_symbol_count: counts.generated,
478 })
479 })
480 }
481
482 fn refresh_software_global_projection(
483 &self,
484 source_scope: String,
485 ) -> StorageFuture<'_, SoftwareGlobalProjection> {
486 self.run(move |connection| software::refresh_projection(connection, &source_scope))
487 }
488
489 fn software_global_projection(
490 &self,
491 request: SoftwareGlobalRequest,
492 ) -> StorageFuture<'_, SoftwareGlobalProjection> {
493 self.run_read(move |connection| software::projection(connection, request))
494 }
495
496 fn software_global_projection_for_scope(
497 &self,
498 source_scope: String,
499 request: SoftwareGlobalRequest,
500 ) -> StorageFuture<'_, SoftwareGlobalProjection> {
501 self.run_read(move |connection| {
502 software::projection_for_scope(connection, &source_scope, request)
503 })
504 }
505
506 fn create_code_repository_set(
507 &self,
508 seed: crate::storage::CodeRepositorySetSeed,
509 ) -> StorageFuture<'_, crate::domain::CodeRepositorySet> {
510 self.run(move |connection| set::create_set(connection, seed))
511 }
512
513 fn add_code_repository_set_member(
514 &self,
515 seed: crate::storage::CodeRepositorySetMemberSeed,
516 ) -> StorageFuture<'_, crate::domain::CodeRepositorySetMember> {
517 self.run(move |connection| set::add_member(connection, seed))
518 }
519
520 fn remove_code_repository_set_member(
521 &self,
522 set_alias: String,
523 repository_alias: String,
524 ) -> StorageFuture<'_, crate::domain::CodeRepositorySetMember> {
525 self.run(move |connection| set::remove_member(connection, &set_alias, &repository_alias))
526 }
527
528 fn code_repository_set(
529 &self,
530 set_alias: String,
531 ) -> StorageFuture<'_, Option<crate::domain::CodeRepositorySet>> {
532 self.run_read(move |connection| set::set_by_alias(connection, &set_alias))
533 }
534
535 fn code_repository_set_status(
536 &self,
537 set_alias: String,
538 ) -> StorageFuture<'_, Option<crate::domain::CodeRepositorySetStatus>> {
539 self.run_read(move |connection| set::set_status(connection, &set_alias))
540 }
541
542 fn refresh_code_repository_set_overlay(
543 &self,
544 set_alias: String,
545 now_ms: u64,
546 ) -> StorageFuture<'_, crate::domain::CodeRepositorySetRefreshSummary> {
547 self.run(move |connection| set::refresh_overlay(connection, &set_alias, now_ms))
548 }
549
550 fn code_repository_set_cross_edges(
551 &self,
552 set_id: String,
553 ) -> StorageFuture<'_, Vec<crate::domain::CodeRepositoryCrossEdge>> {
554 self.run_read(move |connection| set::cross_edges_for_set(connection, &set_id))
555 }
556
557 fn code_repository_set_cross_edges_for_selector(
558 &self,
559 set_id: String,
560 selector: crate::storage::CodeRepositorySetEdgeSelector,
561 ) -> StorageFuture<'_, Vec<crate::domain::CodeRepositoryCrossEdge>> {
562 self.run_read(move |connection| {
563 set::cross_edges_for_selector(connection, &set_id, &selector)
564 })
565 }
566
567 fn queue_code_repository_set_refresh_task(
568 &self,
569 task: crate::storage::CodeRepositorySetRefreshTaskSeed,
570 ) -> StorageFuture<'_, crate::domain::CodeRepositorySetRefreshTaskRecord> {
571 self.run(move |connection| set::refresh_tasks::queue_refresh_task(connection, task))
572 }
573
574 fn claim_code_repository_set_refresh_task(
575 &self,
576 request: crate::storage::CodeRepositorySetRefreshTaskClaimRequest,
577 ) -> StorageFuture<'_, Option<crate::domain::CodeRepositorySetRefreshTaskRecord>> {
578 self.run(move |connection| set::refresh_tasks::claim_refresh_task(connection, request))
579 }
580
581 fn complete_code_repository_set_refresh_task(
582 &self,
583 request: crate::storage::CodeRepositorySetRefreshTaskCompletion,
584 ) -> StorageFuture<'_, crate::domain::CodeRepositorySetRefreshTaskRecord> {
585 self.run(move |connection| set::refresh_tasks::complete_refresh_task(connection, request))
586 }
587
588 fn fail_code_repository_set_refresh_task(
589 &self,
590 request: crate::storage::CodeRepositorySetRefreshTaskFailure,
591 ) -> StorageFuture<'_, crate::domain::CodeRepositorySetRefreshTaskRecord> {
592 self.run(move |connection| set::refresh_tasks::fail_refresh_task(connection, request))
593 }
594}