use super::*;
impl IndexDatabase {
pub fn rebuild(config: &Config) -> anyhow::Result<Self> {
Self::rebuild_with_progress(config, |_| {})
}
pub fn rebuild_with_progress<F>(config: &Config, mut progress: F) -> anyhow::Result<Self>
where
F: FnMut(IndexProgress),
{
progress(IndexProgress::Started {
database: config.database.clone(),
mode: IndexMode::Full,
});
let mut db = Self::create_or_migrate(&config.database)?;
let (commit_sha, worktree_id) = resolve_git_context(&config.root);
db.set_context(&commit_sha, &worktree_id)?;
progress(IndexProgress::IndexingGitHistory);
let mut git_history = Some(spawn_git_history_prepare(&config.root));
db.storage.execute_batch(
"PRAGMA synchronous = OFF;
PRAGMA cache_size = -262144;",
)?;
let result = (|| -> anyhow::Result<()> {
db.storage.execute_batch("BEGIN TRANSACTION")?;
db.clear_full_rebuild_tables()?;
db.set_meta("source_root", &config.root.display().to_string())?;
db.storage.set_source_root(config.root.clone());
db.write_git_meta(&config.root)?;
let indexed = db.index_targets_with_progress(config, &mut progress)?;
db.apply_prepared_git_history(
&config.root,
git_history
.take()
.ok_or_else(|| anyhow::anyhow!("git history preparation was already used"))?,
)?;
progress(IndexProgress::RebuildingLogicalSymbols);
db.rebuild_logical_symbols()?;
progress(IndexProgress::ResolvingGraph);
db.mark_graph_index_current()?;
progress(IndexProgress::RebuildingFts);
db.rebuild_fts()?;
db.set_meta("indexed_at_ms", &now_ms().to_string())?;
db.storage.execute_batch("COMMIT")?;
progress(IndexProgress::Finished { files: indexed });
Ok(())
})();
if result.is_err() {
if let Some(handle) = git_history.take() {
let _ = join_git_history_prepare(handle);
}
let _ = db.storage.execute_batch("ROLLBACK");
}
let _ = db.storage.execute_batch("PRAGMA synchronous = NORMAL;");
result?;
Ok(db)
}
fn clear_full_rebuild_tables(&self) -> anyhow::Result<()> {
self.storage.execute_batch(
"
CREATE TEMP TABLE IF NOT EXISTS staged_file_ids(id INTEGER PRIMARY KEY);
DELETE FROM temp.staged_file_ids;
INSERT OR IGNORE INTO temp.staged_file_ids(id)
SELECT id
FROM main.files
WHERE worktree_id = (SELECT value FROM temp.connection_context WHERE key = \
'worktree_id')
AND worktree_id != '';
INSERT OR IGNORE INTO temp.staged_file_ids(id)
SELECT id
FROM main.files
WHERE commit_sha = (SELECT value FROM temp.connection_context WHERE key = 'commit_sha')
AND commit_sha != ''
AND path NOT IN (
SELECT path FROM main.files
WHERE worktree_id = (SELECT value FROM temp.connection_context WHERE key = \
'worktree_id')
AND worktree_id != ''
);
",
)?;
self.delete_staged_files_cascade()?;
self.storage.execute_batch("DELETE FROM temp.staged_file_ids;")?;
Ok(())
}
pub(super) fn delete_staged_files_cascade(&self) -> anyhow::Result<()> {
self.storage.execute_batch(
"
UPDATE main.edges
SET to_symbol_id = NULL,
target_start_line = NULL,
target_end_line = NULL,
resolution = 'unresolved'
WHERE to_symbol_id IN (
SELECT symbols.id
FROM main.symbols
JOIN temp.staged_file_ids ON staged_file_ids.id = symbols.file_id
);
DELETE FROM main.edges
WHERE source_file_id IN (SELECT id FROM temp.staged_file_ids)
OR from_symbol_id IN (
SELECT symbols.id
FROM main.symbols
JOIN temp.staged_file_ids ON staged_file_ids.id = symbols.file_id
);
DELETE FROM main.logical_symbol_members
WHERE symbol_id IN (
SELECT symbols.id
FROM main.symbols
JOIN temp.staged_file_ids ON staged_file_ids.id = symbols.file_id
);
DELETE FROM main.logical_symbols
WHERE id NOT IN (
SELECT logical_symbol_id FROM main.logical_symbol_members
);
DELETE FROM main.symbol_facts
WHERE symbol_id IN (
SELECT symbols.id
FROM main.symbols
JOIN temp.staged_file_ids ON staged_file_ids.id = symbols.file_id
);
DELETE FROM main.chunk_fts
WHERE rowid IN (
SELECT chunks.id
FROM main.chunks
JOIN temp.staged_file_ids ON staged_file_ids.id = chunks.file_id
);
DELETE FROM main.chunk_summaries
WHERE chunk_id IN (
SELECT chunks.id
FROM main.chunks
JOIN temp.staged_file_ids ON staged_file_ids.id = chunks.file_id
);
DELETE FROM main.chunk_embeddings
WHERE chunk_id IN (
SELECT chunks.id
FROM main.chunks
JOIN temp.staged_file_ids ON staged_file_ids.id = chunks.file_id
);
DELETE FROM main.git_chunk_blame
WHERE chunk_id IN (
SELECT chunks.id
FROM main.chunks
JOIN temp.staged_file_ids ON staged_file_ids.id = chunks.file_id
);
DELETE FROM main.docs
WHERE chunk_id IN (
SELECT chunks.id
FROM main.chunks
JOIN temp.staged_file_ids ON staged_file_ids.id = chunks.file_id
);
DELETE FROM main.parser_failures
WHERE path IN (
SELECT path
FROM main.files
JOIN temp.staged_file_ids ON staged_file_ids.id = files.id
);
DELETE FROM main.symbols
WHERE file_id IN (SELECT id FROM temp.staged_file_ids);
DELETE FROM main.chunks
WHERE file_id IN (SELECT id FROM temp.staged_file_ids);
DELETE FROM main.files
WHERE id IN (SELECT id FROM temp.staged_file_ids);
",
)?;
Ok(())
}
}