diff --git a/Cargo.lock b/Cargo.lock
index b4d466fb63d..5113317b02c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -607,6 +607,7 @@ dependencies = [
  "id-arena 1.0.2 (git+https://github.com/fitzgen/id-arena/?rev=43ecd67)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ra_db 0.1.0",
  "ra_editor 0.1.0",
  "ra_syntax 0.1.0",
  "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -628,6 +629,21 @@ dependencies = [
  "tools 0.1.0",
 ]
 
+[[package]]
+name = "ra_db"
+version = "0.1.0"
+dependencies = [
+ "id-arena 1.0.2 (git+https://github.com/fitzgen/id-arena/?rev=43ecd67)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ra_editor 0.1.0",
+ "ra_syntax 0.1.0",
+ "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "salsa 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "test_utils 0.1.0",
+]
+
 [[package]]
 name = "ra_editor"
 version = "0.1.0"
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml
index 5dae458575a..48d8e56e372 100644
--- a/crates/ra_analysis/Cargo.toml
+++ b/crates/ra_analysis/Cargo.toml
@@ -15,4 +15,5 @@ parking_lot = "0.6.4"
 id-arena = { git = "https://github.com/fitzgen/id-arena/", rev = "43ecd67" }
 ra_syntax = { path = "../ra_syntax" }
 ra_editor = { path = "../ra_editor" }
+ra_db = { path = "../ra_db" }
 test_utils = { path = "../test_utils" }
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs
index 844dabb1928..538b51633fa 100644
--- a/crates/ra_analysis/src/completion/mod.rs
+++ b/crates/ra_analysis/src/completion/mod.rs
@@ -7,10 +7,11 @@ use ra_syntax::{
     AstNode, AtomEdit,
     SyntaxNodeRef,
 };
+use ra_db::SyntaxDatabase;
 use rustc_hash::{FxHashMap};
 
 use crate::{
-    db::{self, SyntaxDatabase},
+    db,
     hir,
     Cancelable, FilePosition
 };
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index e0b7afac55a..1b2dd4b3daf 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,15 +1,13 @@
 use std::sync::Arc;
 #[cfg(test)]
 use parking_lot::Mutex;
-use ra_editor::LineIndex;
-use ra_syntax::{SourceFileNode};
 use salsa::{self, Database};
+use ra_db::{LocationIntener, BaseDatabase};
 
 use crate::{
     hir,
     symbol_index,
-    loc2id::{IdMaps, LocationIntener, DefId, DefLoc, FnId},
-    Cancelable, Canceled, FileId,
+    loc2id::{IdMaps, DefId, DefLoc, FnId},
 };
 
 #[derive(Debug)]
@@ -47,11 +45,11 @@ impl Default for RootDatabase {
             runtime: salsa::Runtime::default(),
             id_maps: Default::default(),
         };
-        db.query_mut(crate::input::SourceRootQuery)
-            .set(crate::input::WORKSPACE, Default::default());
-        db.query_mut(crate::input::CrateGraphQuery)
+        db.query_mut(ra_db::SourceRootQuery)
+            .set(ra_db::WORKSPACE, Default::default());
+        db.query_mut(ra_db::CrateGraphQuery)
             .set((), Default::default());
-        db.query_mut(crate::input::LibrariesQuery)
+        db.query_mut(ra_db::LibrariesQuery)
             .set((), Default::default());
         db
     }
@@ -67,22 +65,7 @@ impl salsa::ParallelDatabase for RootDatabase {
     }
 }
 
-pub(crate) trait BaseDatabase: salsa::Database {
-    fn id_maps(&self) -> &IdMaps;
-    fn check_canceled(&self) -> Cancelable<()> {
-        if self.salsa_runtime().is_current_revision_canceled() {
-            Err(Canceled)
-        } else {
-            Ok(())
-        }
-    }
-}
-
-impl BaseDatabase for RootDatabase {
-    fn id_maps(&self) -> &IdMaps {
-        &self.id_maps
-    }
-}
+impl BaseDatabase for RootDatabase {}
 
 impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase {
     fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> {
@@ -121,16 +104,16 @@ impl RootDatabase {
 
 salsa::database_storage! {
     pub(crate) struct RootDatabaseStorage for RootDatabase {
-        impl crate::input::FilesDatabase {
-            fn file_text() for crate::input::FileTextQuery;
-            fn file_source_root() for crate::input::FileSourceRootQuery;
-            fn source_root() for crate::input::SourceRootQuery;
-            fn libraries() for crate::input::LibrariesQuery;
-            fn crate_graph() for crate::input::CrateGraphQuery;
+        impl ra_db::FilesDatabase {
+            fn file_text() for ra_db::FileTextQuery;
+            fn file_source_root() for ra_db::FileSourceRootQuery;
+            fn source_root() for ra_db::SourceRootQuery;
+            fn libraries() for ra_db::LibrariesQuery;
+            fn crate_graph() for ra_db::CrateGraphQuery;
         }
-        impl SyntaxDatabase {
-            fn source_file() for SourceFileQuery;
-            fn file_lines() for FileLinesQuery;
+        impl ra_db::SyntaxDatabase {
+            fn source_file() for ra_db::SourceFileQuery;
+            fn file_lines() for ra_db::FileLinesQuery;
         }
         impl symbol_index::SymbolsDatabase {
             fn file_symbols() for symbol_index::FileSymbolsQuery;
@@ -148,23 +131,3 @@ salsa::database_storage! {
         }
     }
 }
-
-salsa::query_group! {
-    pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase {
-        fn source_file(file_id: FileId) -> SourceFileNode {
-            type SourceFileQuery;
-        }
-        fn file_lines(file_id: FileId) -> Arc<LineIndex> {
-            type FileLinesQuery;
-        }
-    }
-}
-
-fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
-    let text = db.file_text(file_id);
-    SourceFileNode::parse(&*text)
-}
-fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
-    let text = db.file_text(file_id);
-    Arc::new(LineIndex::new(&*text))
-}
diff --git a/crates/ra_analysis/src/hir/db.rs b/crates/ra_analysis/src/hir/db.rs
index bf0dc393abf..0ae2086ff65 100644
--- a/crates/ra_analysis/src/hir/db.rs
+++ b/crates/ra_analysis/src/hir/db.rs
@@ -4,10 +4,10 @@ use ra_syntax::{
     SyntaxNode,
     ast::FnDefNode,
 };
+use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase};
 
 use crate::{
     FileId,
-    db::SyntaxDatabase,
     hir::{
         SourceFileItems, SourceItemId,
         query_definitions,
@@ -15,8 +15,7 @@ use crate::{
         module::{ModuleId, ModuleTree, ModuleSource,
         nameres::{ItemMap, InputModuleItems}},
     },
-    input::SourceRootId,
-    loc2id::{DefLoc, DefId, FnId, LocationIntener},
+    loc2id::{DefLoc, DefId, FnId},
     Cancelable,
 };
 
diff --git a/crates/ra_analysis/src/hir/function/scope.rs b/crates/ra_analysis/src/hir/function/scope.rs
index 76b2fea68b7..ed789fede2e 100644
--- a/crates/ra_analysis/src/hir/function/scope.rs
+++ b/crates/ra_analysis/src/hir/function/scope.rs
@@ -5,9 +5,10 @@ use ra_syntax::{
     algo::generate,
     ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
 };
+use ra_db::LocalSyntaxPtr;
 
 use crate::{
-    syntax_ptr::LocalSyntaxPtr,
+
     arena::{Arena, Id},
 };
 
diff --git a/crates/ra_analysis/src/hir/module/imp.rs b/crates/ra_analysis/src/hir/module/imp.rs
index d51ca2d59ad..c8f7ed58d19 100644
--- a/crates/ra_analysis/src/hir/module/imp.rs
+++ b/crates/ra_analysis/src/hir/module/imp.rs
@@ -6,11 +6,11 @@ use ra_syntax::{
 };
 use relative_path::RelativePathBuf;
 use rustc_hash::{FxHashMap, FxHashSet};
+use ra_db::{SourceRoot, SourceRootId, FileResolverImp};
 
 use crate::{
     hir::HirDatabase,
-    input::{SourceRoot, SourceRootId},
-    Cancelable, FileId, FileResolverImp,
+    Cancelable, FileId,
 };
 
 use super::{
diff --git a/crates/ra_analysis/src/hir/module/mod.rs b/crates/ra_analysis/src/hir/module/mod.rs
index 893ec3a1043..683cb5d4c07 100644
--- a/crates/ra_analysis/src/hir/module/mod.rs
+++ b/crates/ra_analysis/src/hir/module/mod.rs
@@ -10,12 +10,12 @@ use ra_syntax::{
     ast::{self, AstNode, NameOwner},
     SmolStr, SyntaxNode,
 };
+use ra_db::SourceRootId;
 use relative_path::RelativePathBuf;
 
 use crate::{
     FileId, FilePosition, Cancelable,
     hir::{Path, PathKind, HirDatabase, SourceItemId},
-    input::SourceRootId,
     arena::{Arena, Id},
     loc2id::{DefLoc, DefId},
 };
diff --git a/crates/ra_analysis/src/hir/module/nameres.rs b/crates/ra_analysis/src/hir/module/nameres.rs
index f48f51c8dfd..5c87e7af205 100644
--- a/crates/ra_analysis/src/hir/module/nameres.rs
+++ b/crates/ra_analysis/src/hir/module/nameres.rs
@@ -19,12 +19,12 @@ use std::{
 };
 
 use rustc_hash::FxHashMap;
-
 use ra_syntax::{
     TextRange,
     SmolStr, SyntaxKind::{self, *},
     ast::{self, AstNode}
 };
+use ra_db::SourceRootId;
 
 use crate::{
     Cancelable, FileId,
@@ -35,7 +35,6 @@ use crate::{
         HirDatabase,
         module::{ModuleId, ModuleTree},
     },
-    input::SourceRootId,
 };
 
 /// Item map is the result of the name resolution. Item map contains, for each
@@ -342,11 +341,11 @@ where
 
 #[cfg(test)]
 mod tests {
+    use ra_db::FilesDatabase;
     use crate::{
         AnalysisChange,
         mock_analysis::{MockAnalysis, analysis_and_position},
         hir::{self, HirDatabase},
-        input::FilesDatabase,
 };
     use super::*;
 
diff --git a/crates/ra_analysis/src/hir/query_definitions.rs b/crates/ra_analysis/src/hir/query_definitions.rs
index 6570ca994b2..fbdf8eb6784 100644
--- a/crates/ra_analysis/src/hir/query_definitions.rs
+++ b/crates/ra_analysis/src/hir/query_definitions.rs
@@ -8,6 +8,7 @@ use ra_syntax::{
     AstNode,  SyntaxNode, SmolStr,
     ast::{self, FnDef, FnDefNode, NameOwner, ModuleItemOwner}
 };
+use ra_db::SourceRootId;
 
 use crate::{
     FileId, Cancelable,
@@ -21,7 +22,6 @@ use crate::{
             nameres::{InputModuleItems, ItemMap, Resolver},
         },
     },
-    input::SourceRootId,
 };
 
 /// Resolve `FnId` to the corresponding `SyntaxNode`
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index c86bc111a5f..9a86942214f 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -1,6 +1,5 @@
 use std::{
     fmt,
-    hash::{Hash, Hasher},
     sync::Arc,
 };
 
@@ -11,84 +10,24 @@ use ra_syntax::{
     SyntaxKind::*,
     SyntaxNodeRef, TextRange, TextUnit,
 };
+use ra_db::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE, SyntaxDatabase, SourceFileQuery};
 use rayon::prelude::*;
-use relative_path::RelativePath;
 use rustc_hash::FxHashSet;
 use salsa::{Database, ParallelDatabase};
 
 use crate::{
     completion::{completions, CompletionItem},
-    db::{self, SourceFileQuery, SyntaxDatabase},
+    db,
     hir::{
         self,
         FnSignatureInfo,
         Problem,
     },
-    input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
     symbol_index::{SymbolIndex, SymbolsDatabase},
-    AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver,
+    AnalysisChange, Cancelable, CrateId, Diagnostic, FileId,
     FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit,
 };
 
-#[derive(Clone, Debug)]
-pub(crate) struct FileResolverImp {
-    inner: Arc<FileResolver>,
-}
-
-impl PartialEq for FileResolverImp {
-    fn eq(&self, other: &FileResolverImp) -> bool {
-        self.inner() == other.inner()
-    }
-}
-
-impl Eq for FileResolverImp {}
-
-impl Hash for FileResolverImp {
-    fn hash<H: Hasher>(&self, hasher: &mut H) {
-        self.inner().hash(hasher);
-    }
-}
-
-impl FileResolverImp {
-    pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp {
-        FileResolverImp { inner }
-    }
-    pub(crate) fn file_stem(&self, file_id: FileId) -> String {
-        self.inner.file_stem(file_id)
-    }
-    pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> {
-        self.inner.resolve(file_id, path)
-    }
-    pub(crate) fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> {
-        self.inner.debug_path(file_id)
-    }
-    fn inner(&self) -> *const FileResolver {
-        &*self.inner
-    }
-}
-
-impl Default for FileResolverImp {
-    fn default() -> FileResolverImp {
-        #[derive(Debug)]
-        struct DummyResolver;
-        impl FileResolver for DummyResolver {
-            fn file_stem(&self, _file_: FileId) -> String {
-                panic!("file resolver not set")
-            }
-            fn resolve(
-                &self,
-                _file_id: FileId,
-                _path: &::relative_path::RelativePath,
-            ) -> Option<FileId> {
-                panic!("file resolver not set")
-            }
-        }
-        FileResolverImp {
-            inner: Arc::new(DummyResolver),
-        }
-    }
-}
-
 #[derive(Debug, Default)]
 pub(crate) struct AnalysisHostImpl {
     db: db::RootDatabase,
@@ -105,7 +44,7 @@ impl AnalysisHostImpl {
 
         for (file_id, text) in change.files_changed {
             self.db
-                .query_mut(crate::input::FileTextQuery)
+                .query_mut(ra_db::FileTextQuery)
                 .set(file_id, Arc::new(text))
         }
         if !(change.files_added.is_empty() && change.files_removed.is_empty()) {
@@ -115,22 +54,22 @@ impl AnalysisHostImpl {
             let mut source_root = SourceRoot::clone(&self.db.source_root(WORKSPACE));
             for (file_id, text) in change.files_added {
                 self.db
-                    .query_mut(crate::input::FileTextQuery)
+                    .query_mut(ra_db::FileTextQuery)
                     .set(file_id, Arc::new(text));
                 self.db
-                    .query_mut(crate::input::FileSourceRootQuery)
-                    .set(file_id, crate::input::WORKSPACE);
+                    .query_mut(ra_db::FileSourceRootQuery)
+                    .set(file_id, ra_db::WORKSPACE);
                 source_root.files.insert(file_id);
             }
             for file_id in change.files_removed {
                 self.db
-                    .query_mut(crate::input::FileTextQuery)
+                    .query_mut(ra_db::FileTextQuery)
                     .set(file_id, Arc::new(String::new()));
                 source_root.files.remove(&file_id);
             }
             source_root.file_resolver = file_resolver;
             self.db
-                .query_mut(crate::input::SourceRootQuery)
+                .query_mut(ra_db::SourceRootQuery)
                 .set(WORKSPACE, Arc::new(source_root))
         }
         if !change.libraries_added.is_empty() {
@@ -147,10 +86,10 @@ impl AnalysisHostImpl {
                         library.file_resolver.debug_path(file_id)
                     );
                     self.db
-                        .query_mut(crate::input::FileSourceRootQuery)
+                        .query_mut(ra_db::FileSourceRootQuery)
                         .set_constant(file_id, source_root_id);
                     self.db
-                        .query_mut(crate::input::FileTextQuery)
+                        .query_mut(ra_db::FileTextQuery)
                         .set_constant(file_id, Arc::new(text));
                 }
                 let source_root = SourceRoot {
@@ -158,19 +97,19 @@ impl AnalysisHostImpl {
                     file_resolver: library.file_resolver,
                 };
                 self.db
-                    .query_mut(crate::input::SourceRootQuery)
+                    .query_mut(ra_db::SourceRootQuery)
                     .set(source_root_id, Arc::new(source_root));
                 self.db
                     .query_mut(crate::symbol_index::LibrarySymbolsQuery)
                     .set(source_root_id, Arc::new(library.symbol_index));
             }
             self.db
-                .query_mut(crate::input::LibrariesQuery)
+                .query_mut(ra_db::LibrariesQuery)
                 .set((), Arc::new(libraries));
         }
         if let Some(crate_graph) = change.crate_graph {
             self.db
-                .query_mut(crate::input::CrateGraphQuery)
+                .query_mut(ra_db::CrateGraphQuery)
                 .set((), Arc::new(crate_graph))
         }
     }
@@ -261,7 +200,7 @@ impl AnalysisImpl {
         Ok(crate_id.into_iter().collect())
     }
     pub fn crate_root(&self, crate_id: CrateId) -> FileId {
-        self.db.crate_graph().crate_roots[&crate_id]
+        self.db.crate_graph().crate_root(crate_id)
     }
     pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
         completions(&self.db, position)
@@ -546,16 +485,6 @@ impl SourceChange {
     }
 }
 
-impl CrateGraph {
-    fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
-        let (&crate_id, _) = self
-            .crate_roots
-            .iter()
-            .find(|(_crate_id, &root_id)| root_id == file_id)?;
-        Some(crate_id)
-    }
-}
-
 enum FnCallNode<'a> {
     CallExpr(ast::CallExpr<'a>),
     MethodCallExpr(ast::MethodCallExpr<'a>),
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index c0e43544ea0..012d36b8ed3 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -19,8 +19,6 @@ macro_rules! ctry {
 }
 
 mod arena;
-mod syntax_ptr;
-mod input;
 mod db;
 mod loc2id;
 mod imp;
@@ -32,35 +30,27 @@ pub mod mock_analysis;
 use std::{fmt, sync::Arc};
 
 use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit};
+use ra_db::FileResolverImp;
 use rayon::prelude::*;
 use relative_path::RelativePathBuf;
 
 use crate::{
-    imp::{AnalysisHostImpl, AnalysisImpl, FileResolverImp},
+    imp::{AnalysisHostImpl, AnalysisImpl},
     symbol_index::SymbolIndex,
 };
 
 pub use crate::{
     completion::CompletionItem,
     hir::FnSignatureInfo,
-    input::{CrateGraph, CrateId, FileId, FileResolver},
 };
 pub use ra_editor::{
     FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
 };
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct Canceled;
-
-pub type Cancelable<T> = Result<T, Canceled>;
-
-impl std::fmt::Display for Canceled {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        fmt.write_str("Canceled")
-    }
-}
-
-impl std::error::Error for Canceled {}
+pub use ra_db::{
+    Canceled, Cancelable,
+    CrateGraph, CrateId, FileId, FileResolver
+};
 
 #[derive(Default)]
 pub struct AnalysisChange {
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs
index 2aa1411301e..7956431ab05 100644
--- a/crates/ra_analysis/src/loc2id.rs
+++ b/crates/ra_analysis/src/loc2id.rs
@@ -1,74 +1,10 @@
-use parking_lot::Mutex;
-
-use std::hash::Hash;
-
-use rustc_hash::FxHashMap;
+use ra_db::SourceRootId;
 
 use crate::{
     hir::{SourceItemId, ModuleId},
-    input::SourceRootId,
 };
 
-/// There are two principle ways to refer to things:
-///   - by their locatinon (module in foo/bar/baz.rs at line 42)
-///   - by their numeric id (module `ModuleId(42)`)
-///
-/// The first one is more powerful (you can actually find the thing in question
-/// by id), but the second one is so much more compact.
-///
-/// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a
-/// bidirectional mapping between positional and numeric ids, we can use compact
-/// representation wich still allows us to get the actual item
-#[derive(Debug)]
-struct Loc2IdMap<LOC, ID>
-where
-    ID: NumericId,
-    LOC: Clone + Eq + Hash,
-{
-    loc2id: FxHashMap<LOC, ID>,
-    id2loc: FxHashMap<ID, LOC>,
-}
-
-impl<LOC, ID> Default for Loc2IdMap<LOC, ID>
-where
-    ID: NumericId,
-    LOC: Clone + Eq + Hash,
-{
-    fn default() -> Self {
-        Loc2IdMap {
-            loc2id: FxHashMap::default(),
-            id2loc: FxHashMap::default(),
-        }
-    }
-}
-
-impl<LOC, ID> Loc2IdMap<LOC, ID>
-where
-    ID: NumericId,
-    LOC: Clone + Eq + Hash,
-{
-    pub fn loc2id(&mut self, loc: &LOC) -> ID {
-        match self.loc2id.get(loc) {
-            Some(id) => return id.clone(),
-            None => (),
-        }
-        let id = self.loc2id.len();
-        assert!(id < u32::max_value() as usize);
-        let id = ID::from_u32(id as u32);
-        self.loc2id.insert(loc.clone(), id.clone());
-        self.id2loc.insert(id.clone(), loc.clone());
-        id
-    }
-
-    pub fn id2loc(&self, id: ID) -> LOC {
-        self.id2loc[&id].clone()
-    }
-}
-
-pub(crate) trait NumericId: Clone + Eq + Hash {
-    fn from_u32(id: u32) -> Self;
-    fn to_u32(self) -> u32;
-}
+use ra_db::{NumericId, LocationIntener};
 
 macro_rules! impl_numeric_id {
     ($id:ident) => {
@@ -131,37 +67,3 @@ pub(crate) struct IdMaps {
     pub(crate) fns: LocationIntener<SourceItemId, FnId>,
     pub(crate) defs: LocationIntener<DefLoc, DefId>,
 }
-
-#[derive(Debug)]
-pub(crate) struct LocationIntener<LOC, ID>
-where
-    ID: NumericId,
-    LOC: Clone + Eq + Hash,
-{
-    map: Mutex<Loc2IdMap<LOC, ID>>,
-}
-
-impl<LOC, ID> Default for LocationIntener<LOC, ID>
-where
-    ID: NumericId,
-    LOC: Clone + Eq + Hash,
-{
-    fn default() -> Self {
-        LocationIntener {
-            map: Default::default(),
-        }
-    }
-}
-
-impl<LOC, ID> LocationIntener<LOC, ID>
-where
-    ID: NumericId,
-    LOC: Clone + Eq + Hash,
-{
-    fn loc2id(&self, loc: &LOC) -> ID {
-        self.map.lock().loc2id(loc)
-    }
-    fn id2loc(&self, id: ID) -> LOC {
-        self.map.lock().id2loc(id)
-    }
-}
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index 747b34e3873..b48a3722987 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -9,13 +9,12 @@ use ra_syntax::{
     SourceFileNode,
     SyntaxKind::{self, *},
 };
+use ra_db::{SyntaxDatabase, SourceRootId};
 use rayon::prelude::*;
 
 use crate::{
     Cancelable,
     FileId, Query,
-    db::SyntaxDatabase,
-    input::SourceRootId,
 };
 
 salsa::query_group! {
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 8e785602773..fbe89f44489 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -126,7 +126,7 @@ fn test_resolve_crate_root() {
     let mut host = mock.analysis_host();
     assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
 
-    let mut crate_graph = CrateGraph::new();
+    let mut crate_graph = CrateGraph::default();
     let crate_id = crate_graph.add_crate_root(root_file);
     let mut change = AnalysisChange::new();
     change.set_crate_graph(crate_graph);
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
new file mode 100644
index 00000000000..3bf2f635e75
--- /dev/null
+++ b/crates/ra_db/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+edition = "2018"
+name = "ra_db"
+version = "0.1.0"
+authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
+
+[dependencies]
+log = "0.4.5"
+relative-path = "0.4.0"
+salsa = "0.8.0"
+rustc-hash = "1.0"
+parking_lot = "0.6.4"
+id-arena = { git = "https://github.com/fitzgen/id-arena/", rev = "43ecd67" }
+ra_syntax = { path = "../ra_syntax" }
+ra_editor = { path = "../ra_editor" }
+test_utils = { path = "../test_utils" }
diff --git a/crates/ra_db/src/file_resolver.rs b/crates/ra_db/src/file_resolver.rs
new file mode 100644
index 00000000000..f849ac75214
--- /dev/null
+++ b/crates/ra_db/src/file_resolver.rs
@@ -0,0 +1,76 @@
+use std::{
+    sync::Arc,
+    hash::{Hash, Hasher},
+    fmt,
+};
+
+use relative_path::RelativePath;
+
+use crate::input::FileId;
+
+pub trait FileResolver: fmt::Debug + Send + Sync + 'static {
+    fn file_stem(&self, file_id: FileId) -> String;
+    fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>;
+    fn debug_path(&self, _1file_id: FileId) -> Option<std::path::PathBuf> {
+        None
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct FileResolverImp {
+    inner: Arc<FileResolver>,
+}
+
+impl PartialEq for FileResolverImp {
+    fn eq(&self, other: &FileResolverImp) -> bool {
+        self.inner() == other.inner()
+    }
+}
+
+impl Eq for FileResolverImp {}
+
+impl Hash for FileResolverImp {
+    fn hash<H: Hasher>(&self, hasher: &mut H) {
+        self.inner().hash(hasher);
+    }
+}
+
+impl FileResolverImp {
+    pub fn new(inner: Arc<FileResolver>) -> FileResolverImp {
+        FileResolverImp { inner }
+    }
+    pub fn file_stem(&self, file_id: FileId) -> String {
+        self.inner.file_stem(file_id)
+    }
+    pub fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> {
+        self.inner.resolve(file_id, path)
+    }
+    pub fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> {
+        self.inner.debug_path(file_id)
+    }
+    fn inner(&self) -> *const FileResolver {
+        &*self.inner
+    }
+}
+
+impl Default for FileResolverImp {
+    fn default() -> FileResolverImp {
+        #[derive(Debug)]
+        struct DummyResolver;
+        impl FileResolver for DummyResolver {
+            fn file_stem(&self, _file_: FileId) -> String {
+                panic!("file resolver not set")
+            }
+            fn resolve(
+                &self,
+                _file_id: FileId,
+                _path: &::relative_path::RelativePath,
+            ) -> Option<FileId> {
+                panic!("file resolver not set")
+            }
+        }
+        FileResolverImp {
+            inner: Arc::new(DummyResolver),
+        }
+    }
+}
diff --git a/crates/ra_analysis/src/input.rs b/crates/ra_db/src/input.rs
similarity index 67%
rename from crates/ra_analysis/src/input.rs
rename to crates/ra_db/src/input.rs
index e601cd58a70..9101ac7a8a3 100644
--- a/crates/ra_analysis/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -1,11 +1,10 @@
-use std::{fmt, sync::Arc};
+use std::sync::Arc;
 
-use relative_path::RelativePath;
 use rustc_hash::FxHashMap;
 use rustc_hash::FxHashSet;
 use salsa;
 
-use crate::FileResolverImp;
+use crate::file_resolver::FileResolverImp;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct FileId(pub u32);
@@ -19,8 +18,8 @@ pub struct CrateGraph {
 }
 
 impl CrateGraph {
-    pub fn new() -> CrateGraph {
-        CrateGraph::default()
+    pub fn crate_root(&self, crate_id: CrateId) -> FileId {
+        self.crate_roots[&crate_id]
     }
     pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
         let crate_id = CrateId(self.crate_roots.len() as u32);
@@ -28,18 +27,17 @@ impl CrateGraph {
         assert!(prev.is_none());
         crate_id
     }
-}
-
-pub trait FileResolver: fmt::Debug + Send + Sync + 'static {
-    fn file_stem(&self, file_id: FileId) -> String;
-    fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>;
-    fn debug_path(&self, _1file_id: FileId) -> Option<std::path::PathBuf> {
-        None
+    pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
+        let (&crate_id, _) = self
+            .crate_roots
+            .iter()
+            .find(|(_crate_id, &root_id)| root_id == file_id)?;
+        Some(crate_id)
     }
 }
 
 salsa::query_group! {
-    pub(crate) trait FilesDatabase: salsa::Database {
+    pub trait FilesDatabase: salsa::Database {
         fn file_text(file_id: FileId) -> Arc<String> {
             type FileTextQuery;
             storage input;
@@ -64,12 +62,12 @@ salsa::query_group! {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub(crate) struct SourceRootId(pub(crate) u32);
+pub struct SourceRootId(pub u32);
 
 #[derive(Default, Clone, Debug, PartialEq, Eq)]
-pub(crate) struct SourceRoot {
-    pub(crate) file_resolver: FileResolverImp,
-    pub(crate) files: FxHashSet<FileId>,
+pub struct SourceRoot {
+    pub file_resolver: FileResolverImp,
+    pub files: FxHashSet<FileId>,
 }
 
-pub(crate) const WORKSPACE: SourceRootId = SourceRootId(0);
+pub const WORKSPACE: SourceRootId = SourceRootId(0);
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
new file mode 100644
index 00000000000..833f95eeb01
--- /dev/null
+++ b/crates/ra_db/src/lib.rs
@@ -0,0 +1,69 @@
+//! ra_db defines basic database traits. Concrete DB is defined by ra_analysis.
+
+extern crate ra_editor;
+extern crate ra_syntax;
+extern crate relative_path;
+extern crate rustc_hash;
+extern crate salsa;
+
+mod syntax_ptr;
+mod file_resolver;
+mod input;
+mod loc2id;
+
+use std::sync::Arc;
+use ra_editor::LineIndex;
+use ra_syntax::SourceFileNode;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Canceled;
+
+pub type Cancelable<T> = Result<T, Canceled>;
+
+impl std::fmt::Display for Canceled {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        fmt.write_str("Canceled")
+    }
+}
+
+impl std::error::Error for Canceled {}
+
+pub use crate::{
+    syntax_ptr::LocalSyntaxPtr,
+    file_resolver::{FileResolver, FileResolverImp},
+    input::{
+        FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, WORKSPACE,
+        FileTextQuery, FileSourceRootQuery, SourceRootQuery, LibrariesQuery, CrateGraphQuery,
+    },
+    loc2id::{LocationIntener, NumericId},
+};
+
+pub trait BaseDatabase: salsa::Database {
+    fn check_canceled(&self) -> Cancelable<()> {
+        if self.salsa_runtime().is_current_revision_canceled() {
+            Err(Canceled)
+        } else {
+            Ok(())
+        }
+    }
+}
+
+salsa::query_group! {
+    pub trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase {
+        fn source_file(file_id: FileId) -> SourceFileNode {
+            type SourceFileQuery;
+        }
+        fn file_lines(file_id: FileId) -> Arc<LineIndex> {
+            type FileLinesQuery;
+        }
+    }
+}
+
+fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
+    let text = db.file_text(file_id);
+    SourceFileNode::parse(&*text)
+}
+fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
+    let text = db.file_text(file_id);
+    Arc::new(LineIndex::new(&*text))
+}
diff --git a/crates/ra_db/src/loc2id.rs b/crates/ra_db/src/loc2id.rs
new file mode 100644
index 00000000000..69ba43d0f76
--- /dev/null
+++ b/crates/ra_db/src/loc2id.rs
@@ -0,0 +1,100 @@
+use parking_lot::Mutex;
+
+use std::hash::Hash;
+
+use rustc_hash::FxHashMap;
+
+/// There are two principle ways to refer to things:
+///   - by their locatinon (module in foo/bar/baz.rs at line 42)
+///   - by their numeric id (module `ModuleId(42)`)
+///
+/// The first one is more powerful (you can actually find the thing in question
+/// by id), but the second one is so much more compact.
+///
+/// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a
+/// bidirectional mapping between positional and numeric ids, we can use compact
+/// representation wich still allows us to get the actual item
+#[derive(Debug)]
+struct Loc2IdMap<LOC, ID>
+where
+    ID: NumericId,
+    LOC: Clone + Eq + Hash,
+{
+    loc2id: FxHashMap<LOC, ID>,
+    id2loc: FxHashMap<ID, LOC>,
+}
+
+impl<LOC, ID> Default for Loc2IdMap<LOC, ID>
+where
+    ID: NumericId,
+    LOC: Clone + Eq + Hash,
+{
+    fn default() -> Self {
+        Loc2IdMap {
+            loc2id: FxHashMap::default(),
+            id2loc: FxHashMap::default(),
+        }
+    }
+}
+
+impl<LOC, ID> Loc2IdMap<LOC, ID>
+where
+    ID: NumericId,
+    LOC: Clone + Eq + Hash,
+{
+    pub fn loc2id(&mut self, loc: &LOC) -> ID {
+        match self.loc2id.get(loc) {
+            Some(id) => return id.clone(),
+            None => (),
+        }
+        let id = self.loc2id.len();
+        assert!(id < u32::max_value() as usize);
+        let id = ID::from_u32(id as u32);
+        self.loc2id.insert(loc.clone(), id.clone());
+        self.id2loc.insert(id.clone(), loc.clone());
+        id
+    }
+
+    pub fn id2loc(&self, id: ID) -> LOC {
+        self.id2loc[&id].clone()
+    }
+}
+
+pub trait NumericId: Clone + Eq + Hash {
+    fn from_u32(id: u32) -> Self;
+    fn to_u32(self) -> u32;
+}
+
+#[derive(Debug)]
+pub struct LocationIntener<LOC, ID>
+where
+    ID: NumericId,
+    LOC: Clone + Eq + Hash,
+{
+    map: Mutex<Loc2IdMap<LOC, ID>>,
+}
+
+impl<LOC, ID> Default for LocationIntener<LOC, ID>
+where
+    ID: NumericId,
+    LOC: Clone + Eq + Hash,
+{
+    fn default() -> Self {
+        LocationIntener {
+            map: Default::default(),
+        }
+    }
+}
+
+impl<LOC, ID> LocationIntener<LOC, ID>
+where
+    ID: NumericId,
+    LOC: Clone + Eq + Hash,
+{
+    pub fn loc2id(&self, loc: &LOC) -> ID {
+        self.map.lock().loc2id(loc)
+    }
+    pub fn id2loc(&self, id: ID) -> LOC {
+        self.map.lock().id2loc(id)
+    }
+}
diff --git a/crates/ra_analysis/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs
similarity index 84%
rename from crates/ra_analysis/src/syntax_ptr.rs
rename to crates/ra_db/src/syntax_ptr.rs
index f4b05fc19bd..dac94dd36dd 100644
--- a/crates/ra_analysis/src/syntax_ptr.rs
+++ b/crates/ra_db/src/syntax_ptr.rs
@@ -2,20 +2,20 @@ use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange
 
 /// A pionter to a syntax node inside a file.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) struct LocalSyntaxPtr {
+pub struct LocalSyntaxPtr {
     range: TextRange,
     kind: SyntaxKind,
 }
 
 impl LocalSyntaxPtr {
-    pub(crate) fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
+    pub fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
         LocalSyntaxPtr {
             range: node.range(),
             kind: node.kind(),
         }
     }
 
-    pub(crate) fn resolve(self, file: &SourceFileNode) -> SyntaxNode {
+    pub fn resolve(self, file: &SourceFileNode) -> SyntaxNode {
         let mut curr = file.syntax();
         loop {
             if curr.range() == self.range && curr.kind() == self.kind {
@@ -28,7 +28,7 @@ impl LocalSyntaxPtr {
         }
     }
 
-    pub(crate) fn range(self) -> TextRange {
+    pub fn range(self) -> TextRange {
         self.range
     }
 }
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index 3e7670fccec..12faeb93afe 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -140,7 +140,7 @@ impl ServerWorldState {
         Ok(file_id)
     }
     pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
-        let mut crate_graph = CrateGraph::new();
+        let mut crate_graph = CrateGraph::default();
         ws.iter()
             .flat_map(|ws| {
                 ws.packages()