Repository: apollographql/apollo-rs
Pull request #303 (2022-09-20 17:51:30)

refactor(compiler): create a validation database
Validation was its own module, which isn't as useful as having it be a salsa query group! We can now take advantage of memoised validation functions, as well as have a more succinct RootDatabase.

In this process, we've also renamed all query groups to match their associated storage names, as well as add `Database` to their naming. This ensures there is no confusion to `Document` or `Definition` being confused with ast's document or definition for the code reader. 

To allow `ValidationDatabase` to continue making calls to specific queries in `DocumentDatabase`, we've also added an `Upcast` trait. 


Patch:
diff --git a/crates/apollo-compiler/README.md b/crates/apollo-compiler/README.md
index 2950b14..24235d1 100644
--- a/crates/apollo-compiler/README.md
+++ b/crates/apollo-compiler/README.md
@@ -86,7 +86,7 @@ assert!(diagnostics.is_empty());
 #### Accessing fragment definition field types
 
 ```rust
-use apollo_compiler::{ApolloCompiler, hir, Document};
+use apollo_compiler::{ApolloCompiler, hir, DocumentDatabase};
 use miette::Result;
 
 fn main() -> Result<()> {
@@ -151,7 +151,7 @@ fn main() -> Result<()> {
 
 #### Get a directive defined on a field used in a query operation definition.
 ```rust
-use apollo_compiler::{ApolloCompiler, hir, Definitions};
+use apollo_compiler::{ApolloCompiler, hir, HirDatabase};
 use anyhow::{anyhow, Result};
 
 fn main() -> Result<()> {
diff --git a/crates/apollo-compiler/examples/hello_world.rs b/crates/apollo-compiler/examples/hello_world.rs
index 9df5d83..86bd0a9 100644
--- a/crates/apollo-compiler/examples/hello_world.rs
+++ b/crates/apollo-compiler/examples/hello_world.rs
@@ -1,6 +1,6 @@
 use std::{fs, path::Path};
 
-use apollo_compiler::{hir, ApolloCompiler, Definitions};
+use apollo_compiler::{hir, ApolloCompiler, HirDatabase};
 
 fn compile_query() -> Option<hir::FragmentDefinition> {
     let file = Path::new("crates/apollo-compiler/examples/query_with_errors.graphql");
diff --git a/crates/apollo-compiler/src/database/ast.rs b/crates/apollo-compiler/src/database/ast.rs
index 8eedca9..5a43c90 100644
--- a/crates/apollo-compiler/src/database/ast.rs
+++ b/crates/apollo-compiler/src/database/ast.rs
@@ -1,10 +1,10 @@
 use apollo_parser::{Parser as ApolloParser, SyntaxTree};
 use rowan::GreenNode;
 
-use crate::{database::inputs::Inputs, diagnostics::SyntaxError, ApolloDiagnostic};
+use crate::{database::inputs::InputDatabase, diagnostics::SyntaxError, ApolloDiagnostic};
 
 #[salsa::query_group(AstStorage)]
-pub trait DocumentParser: Inputs {
+pub trait AstDatabase: InputDatabase {
     fn ast(&self) -> SyntaxTree;
 
     // root node
@@ -13,18 +13,18 @@ pub trait DocumentParser: Inputs {
     fn syntax_errors(&self) -> Vec<ApolloDiagnostic>;
 }
 
-fn ast(db: &dyn DocumentParser) -> SyntaxTree {
+fn ast(db: &dyn AstDatabase) -> SyntaxTree {
     let input = db.input();
 
     let parser = ApolloParser::new(&input);
     parser.parse()
 }
 
-fn document(db: &dyn DocumentParser) -> GreenNode {
+fn document(db: &dyn AstDatabase) -> GreenNode {
     db.ast().green()
 }
 
-fn syntax_errors(db: &dyn DocumentParser) -> Vec<ApolloDiagnostic> {
+fn syntax_errors(db: &dyn AstDatabase) -> Vec<ApolloDiagnostic> {
     db.ast()
         .errors()
         .into_iter()
diff --git a/crates/apollo-compiler/src/database/db.rs b/crates/apollo-compiler/src/database/db.rs
index 01a2140..09befba 100644
--- a/crates/apollo-compiler/src/database/db.rs
+++ b/crates/apollo-compiler/src/database/db.rs
@@ -1,11 +1,24 @@
 // All .expect() calls are used for parts of the GraphQL grammar that are
 // non-optional and will have an error produced in the parser if they are missing.
 
-use crate::database::{
-    ast::AstStorage, def::DefinitionsStorage, document::DocumentStorage, inputs::InputsStorage,
+use crate::{
+    database::{
+        ast::AstStorage, def::HirStorage, document::DocumentStorage, inputs::InputStorage,
+        DocumentDatabase,
+    },
+    validation::ValidationStorage,
 };
 
-#[salsa::database(DocumentStorage, InputsStorage, AstStorage, DefinitionsStorage)]
+pub trait Upcast<T: ?Sized> {
+    fn upcast(&self) -> &T;
+}
+#[salsa::database(
+    DocumentStorage,
+    InputStorage,
+    AstStorage,
+    HirStorage,
+    ValidationStorage
+)]
 #[derive(Default)]
 pub struct RootDatabase {
     pub storage: salsa::Storage<RootDatabase>,
@@ -22,3 +35,9 @@ impl salsa::ParallelDatabase for RootDatabase {
         })
     }
 }
+
+impl Upcast<dyn DocumentDatabase> for RootDatabase {
+    fn upcast(&self) -> &(dyn DocumentDatabase + 'static) {
+        self
+    }
+}
diff --git a/crates/apollo-compiler/src/database/def.rs b/crates/apollo-compiler/src/database/def.rs
index 26dcc1e..9b036da 100644
--- a/crates/apollo-compiler/src/database/def.rs
+++ b/crates/apollo-compiler/src/database/def.rs
@@ -3,10 +3,10 @@ use std::sync::Arc;
 use apollo_parser::ast::{self, AstChildren, AstNode, SyntaxNodePtr};
 use uuid::Uuid;
 
-use crate::{hir::*, DocumentParser, Inputs};
+use crate::{hir::*, AstDatabase, InputDatabase};
 
-#[salsa::query_group(DefinitionsStorage)]
-pub trait Definitions: Inputs + DocumentParser {
+#[salsa::query_group(HirStorage)]
+pub trait HirDatabase: InputDatabase + AstDatabase {
     // fn definitions(&self) -> Arc<Vec<ast::Definition>>;
 
     fn db_definitions(&self) -> Arc<Vec<Definition>>;
@@ -34,7 +34,7 @@ pub trait Definitions: Inputs + DocumentParser {
     fn input_objects(&self) -> Arc<Vec<InputObjectTypeDefinition>>;
 }
 
-fn db_definitions(db: &dyn Definitions) -> Arc<Vec<Definition>> {
+fn db_definitions(db: &dyn HirDatabase) -> Arc<Vec<Definition>> {
     let mut definitions = Vec::new();
 
     let operations: Vec<Definition> = db
@@ -98,7 +98,7 @@ fn db_definitions(db: &dyn Definitions) -> Arc<Vec<Definition>> {
     Arc::new(definitions)
 }
 
-fn type_system_definitions(db: &dyn Definitions) -> Arc<Vec<Definition>> {
+fn type_system_definitions(db: &dyn HirDatabase) -> Arc<Vec<Definition>> {
     let mut definitions = Vec::new();
 
     let directives: Vec<Definition> = db
@@ -150,7 +150,7 @@ fn type_system_definitions(db: &dyn Definitions) -> Arc<Vec<Definition>> {
     Arc::new(definitions)
 }
 
-fn operations(db: &dyn Definitions) -> Arc<Vec<OperationDefinition>> {
+fn operations(db: &dyn HirDatabase) -> Arc<Vec<OperationDefinition>> {
     let operations = db
         .ast()
         .document()
@@ -164,7 +164,7 @@ fn operations(db: &dyn Definitions) -> Arc<Vec<OperationDefinition>> {
     Arc::new(operations)
 }
 
-fn fragments(db: &dyn Definitions) -> Arc<Vec<FragmentDefinition>> {
+fn fragments(db: &dyn HirDatabase) -> Arc<Vec<FragmentDefinition>> {
     let fragments = db
         .ast()
         .document()
@@ -180,7 +180,7 @@ fn fragments(db: &dyn Definitions) -> Arc<Vec<FragmentDefinition>> {
     Arc::new(fragments)
 }
 
-fn schema(db: &dyn Definitions) -> Arc<SchemaDefinition> {
+fn schema(db: &dyn HirDatabase) -> Arc<SchemaDefinition> {
     let schema = db
         .ast()
         .document()
@@ -208,7 +208,7 @@ fn schema(db: &dyn Definitions) -> Arc<SchemaDefinition> {
     Arc::new(schema_def)
 }
 
-fn object_types(db: &dyn Definitions) -> Arc<Vec<ObjectTypeDefinition>> {
+fn object_types(db: &dyn HirDatabase) -> Arc<Vec<ObjectTypeDefinition>> {
     let objects = db
         .ast()
         .document()
@@ -222,7 +222,7 @@ fn object_types(db: &dyn Definitions) -> Arc<Vec<ObjectTypeDefinition>> {
     Arc::new(objects)
 }
 
-fn scalars(db: &dyn Definitions) -> Arc<Vec<ScalarTypeDefinition>> {
+fn scalars(db: &dyn HirDatabase) -> Arc<Vec<ScalarTypeDefinition>> {
     let scalars = db
         .ast()
         .document()
@@ -240,7 +240,7 @@ fn scalars(db: &dyn Definitions) -> Arc<Vec<ScalarTypeDefinition>> {
     Arc::new(scalars)
 }
 
-fn enums(db: &dyn Definitions) -> Arc<Vec<EnumTypeDefinition>> {
+fn enums(db: &dyn HirDatabase) -> Arc<Vec<EnumTypeDefinition>> {
     let enums = db
         .ast()
         .document()
@@ -254,7 +254,7 @@ fn enums(db: &dyn Definitions) -> Arc<Vec<EnumTypeDefinition>> {
     Arc::new(enums)
 }
 
-fn unions(db: &dyn Definitions) -> Arc<Vec<UnionTypeDefinition>> {
+fn unions(db: &dyn HirDatabase) -> Arc<Vec<UnionTypeDefinition>> {
     let unions = db
         .ast()
         .document()
@@ -268,7 +268,7 @@ fn unions(db: &dyn Definitions) -> Arc<Vec<UnionTypeDefinition>> {
     Arc::new(unions)
 }
 
-fn interfaces(db: &dyn Definitions) -> Arc<Vec<InterfaceTypeDefinition>> {
+fn interfaces(db: &dyn HirDatabase) -> Arc<Vec<InterfaceTypeDefinition>> {
     let interfaces = db
         .ast()
         .document()
@@ -284,7 +284,7 @@ fn interfaces(db: &dyn Definitions) -> Arc<Vec<InterfaceTypeDefinition>> {
     Arc::new(interfaces)
 }
 
-fn directive_definitions(db: &dyn Definitions) -> Arc<Vec<DirectiveDefinition>> {
+fn directive_definitions(db: &dyn HirDatabase) -> Arc<Vec<DirectiveDefinition>> {
     let directives = db
         .ast()
         .document()
@@ -303,7 +303,7 @@ fn directive_definitions(db: &dyn Definitions) -> Arc<Vec<DirectiveDefinition>>
     Arc::new(directives)
 }
 
-fn input_objects(db: &dyn Definitions) -> Arc<Vec<InputObjectTypeDefinition>> {
+fn input_objects(db: &dyn HirDatabase) -> Arc<Vec<InputObjectTypeDefinition>> {
     let directives = db
         .ast()
         .document()
@@ -321,7 +321,7 @@ fn input_objects(db: &dyn Definitions) -> Arc<Vec<InputObjectTypeDefinition>> {
 }
 
 fn operation_definition(
-    db: &dyn Definitions,
+    db: &dyn HirDatabase,
     op_def: ast::OperationDefinition,
 ) -> OperationDefinition {
     // check if there are already operations
@@ -357,7 +357,7 @@ fn operation_definition(
 }
 
 fn fragment_definition(
-    db: &dyn Definitions,
+    db: &dyn HirDatabase,
     fragment_def: ast::FragmentDefinition,
 ) -> FragmentDefinition {
     let name = name(
@@ -589,7 +589,7 @@ fn input_object_definition(input_obj: ast::InputObjectTypeDefinition) -> InputOb
     }
 }
 
-fn add_object_type_id_to_schema(db: &dyn Definitions) -> Arc<Vec<RootOperationTypeDefinition>> {
+fn add_object_type_id_to_schema(db: &dyn HirDatabase) -> Arc<Vec<RootOperationTypeDefinition>> {
     // Schema Definition does not have to be present in the SDL if ObjectType name is
     // - Query
     // - Subscription
@@ -945,7 +945,7 @@ fn value(val: ast::Value) -> Value {
 }
 
 fn selection_set(
-    db: &dyn Definitions,
+    db: &dyn HirDatabase,
     selections: Option<ast::SelectionSet>,
     parent_obj_ty: Option<String>,
 ) -> SelectionSet {
@@ -964,7 +964,7 @@ fn selection_set(
 }
 
 fn selection(
-    db: &dyn Definitions,
+    db: &dyn HirDatabase,
     selection: ast::Selection,
     parent_obj_ty: Option<String>,
 ) -> Selection {
@@ -985,7 +985,7 @@ fn selection(
 }
 
 fn inline_fragment(
-    db: &dyn Definitions,
+    db: &dyn HirDatabase,
     fragment: ast::InlineFragment,
     parent_obj: Option<String>,
 ) -> Arc<InlineFragment> {
@@ -1033,7 +1033,7 @@ fn fragment_spread(fragment: ast::FragmentSpread) -> Arc<FragmentSpread> {
     Arc::new(fragment_data)
 }
 
-fn field(db: &dyn Definitions, field: ast::Field, parent_obj: Option<String>) -> Arc<Field> {
+fn field(db: &dyn HirDatabase, field: ast::Field, parent_obj: Option<String>) -> Arc<Field> {
     let name = name(field.name());
     let alias = alias(field.alias());
     let new_parent_obj = parent_ty(db, &name, parent_obj.clone());
@@ -1054,7 +1054,7 @@ fn field(db: &dyn Definitions, field: ast::Field, parent_obj: Option<String>) ->
     Arc::new(field_data)
 }
 
-fn parent_ty(db: &dyn Definitions, field_name: &str, parent_obj: Option<String>) -> Option<String> {
+fn parent_ty(db: &dyn HirDatabase, field_name: &str, parent_obj: Option<String>) -> Option<String> {
     if let Some(name) = parent_obj {
         db.type_system_definitions().iter().find_map(|def| {
             if let Some(n) = def.name() {
diff --git a/crates/apollo-compiler/src/database/document.rs b/crates/apollo-compiler/src/database/document.rs
index 05f64f0..f89f425 100644
--- a/crates/apollo-compiler/src/database/document.rs
+++ b/crates/apollo-compiler/src/database/document.rs
@@ -2,10 +2,10 @@ use std::{collections::HashSet, sync::Arc};
 
 use uuid::Uuid;
 
-use crate::{hir::*, Definitions, DocumentParser, Inputs};
+use crate::{hir::*, AstDatabase, HirDatabase, InputDatabase};
 
 #[salsa::query_group(DocumentStorage)]
-pub trait Document: Inputs + DocumentParser + Definitions {
+pub trait DocumentDatabase: InputDatabase + AstDatabase + HirDatabase {
     fn find_operation(&self, id: Uuid) -> Option<Arc<OperationDefinition>>;
 
     fn find_operation_by_name(&self, name: String) -> Option<Arc<OperationDefinition>>;
@@ -55,7 +55,7 @@ pub trait Document: Inputs + DocumentParser + Definitions {
     fn operation_definition_variables(&self, id: Uuid) -> Arc<HashSet<Variable>>;
 }
 
-fn find_definition_by_name(db: &dyn Document, name: String) -> Option<Arc<Definition>> {
+fn find_definition_by_name(db: &dyn DocumentDatabase, name: String) -> Option<Arc<Definition>> {
     db.db_definitions().iter().find_map(|def| {
         if let Some(n) = def.name() {
             if name == n {
@@ -66,7 +66,7 @@ fn find_definition_by_name(db: &dyn Document, name: String) -> Option<Arc<Defini
     })
 }
 
-fn find_type_system_definition(db: &dyn Document, id: Uuid) -> Option<Arc<Definition>> {
+fn find_type_system_definition(db: &dyn DocumentDatabase, id: Uuid) -> Option<Arc<Definition>> {
     db.type_system_definitions().iter().find_map(|op| {
         if let Some(op_id) = op.id() {
             if op_id == &id {
@@ -77,7 +77,10 @@ fn find_type_system_definition(db: &dyn Document, id: Uuid) -> Option<Arc<Defini
     })
 }
 
-fn find_type_system_definition_by_name(db: &dyn Document, name: String) -> Option<Arc<Definition>> {
+fn find_type_system_definition_by_name(
+    db: &dyn DocumentDatabase,
+    name: String,
+) -> Option<Arc<Definition>> {
     db.type_system_definitions().iter().find_map(|def| {
         if let Some(n) = def.name() {
             if name == n {
@@ -88,7 +91,7 @@ fn find_type_system_definition_by_name(db: &dyn Document, name: String) -> Optio
     })
 }
 
-fn find_operation(db: &dyn Document, id: Uuid) -> Option<Arc<OperationDefinition>> {
+fn find_operation(db: &dyn DocumentDatabase, id: Uuid) -> Option<Arc<OperationDefinition>> {
     db.operations().iter().find_map(|op| {
         if &id == op.id() {
             return Some(Arc::new(op.clone()));
@@ -97,7 +100,10 @@ fn find_operation(db: &dyn Document, id: Uuid) -> Option<Arc<OperationDefinition
     })
 }
 
-fn find_operation_by_name(db: &dyn Document, name: String) -> Option<Arc<OperationDefinition>> {
+fn find_operation_by_name(
+    db: &dyn DocumentDatabase,
+    name: String,
+) -> Option<Arc<OperationDefinition>> {
     db.operations().iter().find_map(|op| {
         if let Some(n) = op.name() {
             if n == name {
@@ -108,7 +114,7 @@ fn find_operation_by_name(db: &dyn Document, name: String) -> Option<Arc<Operati
     })
 }
 
-fn find_fragment(db: &dyn Document, id: Uuid) -> Option<Arc<FragmentDefinition>> {
+fn find_fragment(db: &dyn DocumentDatabase, id: Uuid) -> Option<Arc<FragmentDefinition>> {
     db.fragments().iter().find_map(|fragment| {
         if &id == fragment.id() {
             return Some(Arc::new(fragment.clone()));
@@ -117,7 +123,10 @@ fn find_fragment(db: &dyn Document, id: Uuid) -> Option<Arc<FragmentDefinition>>
     })
 }
 
-fn find_fragment_by_name(db: &dyn Document, name: String) -> Option<Arc<FragmentDefinition>> {
+fn find_fragment_by_name(
+    db: &dyn DocumentDatabase,
+    name: String,
+) -> Option<Arc<FragmentDefinition>> {
     db.fragments().iter().find_map(|fragment| {
         if name == fragment.name() {
             return Some(Arc::new(fragment.clone()));
@@ -126,7 +135,7 @@ fn find_fragment_by_name(db: &dyn Document, name: String) -> Option<Arc<Fragment
     })
 }
 
-fn find_object_type(db: &dyn Document, id: Uuid) -> Option<Arc<ObjectTypeDefinition>> {
+fn find_object_type(db: &dyn DocumentDatabase, id: Uuid) -> Option<Arc<ObjectTypeDefinition>> {
     db.object_types().iter().find_map(|object_type| {
         if &id == object_type.id() {
             return Some(Arc::new(object_type.clone()));
@@ -135,7 +144,10 @@ fn find_object_type(db: &dyn Document, id: Uuid) -> Option<Arc<ObjectTypeDefinit
     })
 }
 
-fn find_object_type_by_name(db: &dyn Document, name: String) -> Option<Arc<ObjectTypeDefinition>> {
+fn find_object_type_by_name(
+    db: &dyn DocumentDatabase,
+    name: String,
+) -> Option<Arc<ObjectTypeDefinition>> {
     db.object_types().iter().find_map(|object_type| {
         if name == object_type.name() {
             return Some(Arc::new(object_type.clone()));
@@ -144,7 +156,7 @@ fn find_object_type_by_name(db: &dyn Document, name: String) -> Option<Arc<Objec
     })
 }
 
-fn find_union_by_name(db: &dyn Document, name: String) -> Option<Arc<UnionTypeDefinition>> {
+fn find_union_by_name(db: &dyn DocumentDatabase, name: String) -> Option<Arc<UnionTypeDefinition>> {
     db.unions().iter().find_map(|union| {
         if name == union.name() {
             return Some(Arc::new(union.clone()));
@@ -153,7 +165,7 @@ fn find_union_by_name(db: &dyn Document, name: String) -> Option<Arc<UnionTypeDe
     })
 }
 
-fn find_interface(db: &dyn Document, id: Uuid) -> Option<Arc<InterfaceTypeDefinition>> {
+fn find_interface(db: &dyn DocumentDatabase, id: Uuid) -> Option<Arc<InterfaceTypeDefinition>> {
     db.interfaces().iter().find_map(|interface| {
         if &id == interface.id() {
             return Some(Arc::new(interface.clone()));
@@ -162,7 +174,10 @@ fn find_interface(db: &dyn Document, id: Uuid) -> Option<Arc<InterfaceTypeDefini
     })
 }
 
-fn find_interface_by_name(db: &dyn Document, name: String) -> Option<Arc<InterfaceTypeDefinition>> {
+fn find_interface_by_name(
+    db: &dyn DocumentDatabase,
+    name: String,
+) -> Option<Arc<InterfaceTypeDefinition>> {
     db.interfaces().iter().find_map(|interface| {
         if name == interface.name() {
             return Some(Arc::new(interface.clone()));
@@ -171,7 +186,10 @@ fn find_interface_by_name(db: &dyn Document, name: String) -> Option<Arc<Interfa
     })
 }
 
-fn find_directive_definition(db: &dyn Document, id: Uuid) -> Option<Arc<DirectiveDefinition>> {
+fn find_directive_definition(
+    db: &dyn DocumentDatabase,
+    id: Uuid,
+) -> Option<Arc<DirectiveDefinition>> {
     db.directive_definitions().iter().find_map(|directive_def| {
         if &id == directive_def.id() {
             return Some(Arc::new(directive_def.clone()));
@@ -181,7 +199,7 @@ fn find_directive_definition(db: &dyn Document, id: Uuid) -> Option<Arc<Directiv
 }
 
 fn find_directive_definition_by_name(
-    db: &dyn Document,
+    db: &dyn DocumentDatabase,
     name: String,
 ) -> Option<Arc<DirectiveDefinition>> {
     db.directive_definitions().iter().find_map(|directive_def| {
@@ -192,7 +210,10 @@ fn find_directive_definition_by_name(
     })
 }
 
-fn find_input_object(db: &dyn Document, id: Uuid) -> Option<Arc<InputObjectTypeDefinition>> {
+fn find_input_object(
+    db: &dyn DocumentDatabase,
+    id: Uuid,
+) -> Option<Arc<InputObjectTypeDefinition>> {
     db.input_objects().iter().find_map(|input_obj| {
         if &id == input_obj.id() {
             return Some(Arc::new(input_obj.clone()));
@@ -202,7 +223,7 @@ fn find_input_object(db: &dyn Document, id: Uuid) -> Option<Arc<InputObjectTypeD
 }
 
 fn find_input_object_by_name(
-    db: &dyn Document,
+    db: &dyn DocumentDatabase,
     name: String,
 ) -> Option<Arc<InputObjectTypeDefinition>> {
     db.input_objects().iter().find_map(|input_obj| {
@@ -213,7 +234,7 @@ fn find_input_object_by_name(
     })
 }
 
-fn query_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
+fn query_operations(db: &dyn DocumentDatabase) -> Arc<Vec<OperationDefinition>> {
     let operations = db
         .operations()
         .iter()
@@ -222,7 +243,7 @@ fn query_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
     Arc::new(operations)
 }
 
-fn subscription_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
+fn subscription_operations(db: &dyn DocumentDatabase) -> Arc<Vec<OperationDefinition>> {
     let operations = db
         .operations()
         .iter()
@@ -231,7 +252,7 @@ fn subscription_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
     Arc::new(operations)
 }
 
-fn mutation_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
+fn mutation_operations(db: &dyn DocumentDatabase) -> Arc<Vec<OperationDefinition>> {
     let operations = db
         .operations()
         .iter()
@@ -240,7 +261,7 @@ fn mutation_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
     Arc::new(operations)
 }
 
-fn operation_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
+fn operation_fields(db: &dyn DocumentDatabase, id: Uuid) -> Arc<Vec<Field>> {
     let fields = match db.find_operation(id) {
         Some(op) => op
             .selection_set()
@@ -256,7 +277,7 @@ fn operation_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
     Arc::new(fields)
 }
 
-fn operation_inline_fragment_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
+fn operation_inline_fragment_fields(db: &dyn DocumentDatabase, id: Uuid) -> Arc<Vec<Field>> {
     let fields: Vec<Field> = match db.find_operation(id) {
         Some(op) => op
             .selection_set()
@@ -276,7 +297,7 @@ fn operation_inline_fragment_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Fiel
     Arc::new(fields)
 }
 
-fn operation_fragment_spread_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
+fn operation_fragment_spread_fields(db: &dyn DocumentDatabase, id: Uuid) -> Arc<Vec<Field>> {
     let fields: Vec<Field> = match db.find_operation(id) {
         Some(op) => op
             .selection_set()
@@ -298,7 +319,7 @@ fn operation_fragment_spread_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Fiel
 
 // Should be part of operation's db
 // NOTE: potentially want to return a hashmap of variables and their types?
-fn selection_variables(db: &dyn Document, id: Uuid) -> Arc<HashSet<Variable>> {
+fn selection_variables(db: &dyn DocumentDatabase, id: Uuid) -> Arc<HashSet<Variable>> {
     let vars = db
         .operation_fields(id)
         .iter()
@@ -331,7 +352,7 @@ fn get_field_variable_value(val: Value) -> Vec<Variable> {
 
 // should be part of operation's db
 // NOTE: potentially want to return a hashset of variables and their types?
-fn operation_definition_variables(db: &dyn Document, id: Uuid) -> Arc<HashSet<Variable>> {
+fn operation_definition_variables(db: &dyn DocumentDatabase, id: Uuid) -> Arc<HashSet<Variable>> {
     let vars: HashSet<Variable> = match db.find_operation(id) {
         Some(op) => op
             .variables()
diff --git a/crates/apollo-compiler/src/database/hir.rs b/crates/apollo-compiler/src/database/hir.rs
index cbd3517..1d02414 100644
--- a/crates/apollo-compiler/src/database/hir.rs
+++ b/crates/apollo-compiler/src/database/hir.rs
@@ -7,7 +7,7 @@ use apollo_parser::{
 use ordered_float::{self, OrderedFloat};
 use uuid::Uuid;
 
-use crate::Document;
+use crate::DocumentDatabase;
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum Definition {
@@ -244,7 +244,7 @@ impl FragmentDefinition {
     // which operation definition the fragment is used in.
 
     /// Get variables used in a fragment definition.
-    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn DocumentDatabase) -> Vec<Variable> {
         self.selection_set
             .selection()
             .iter()
@@ -252,7 +252,7 @@ impl FragmentDefinition {
             .collect()
     }
 
-    pub fn ty(&self, db: &dyn Document) -> Option<Arc<Definition>> {
+    pub fn ty(&self, db: &dyn DocumentDatabase) -> Option<Arc<Definition>> {
         db.find_type_system_definition_by_name(self.name.clone())
     }
 
@@ -262,7 +262,7 @@ impl FragmentDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -291,7 +291,7 @@ impl OperationDefinition {
     }
 
     /// Get operation's definition object type.
-    pub fn object_type(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn object_type(&self, db: &dyn DocumentDatabase) -> Option<Arc<ObjectTypeDefinition>> {
         match self.operation_ty {
             OperationType::Query => db.schema().query(db),
             OperationType::Mutation => db.schema().mutation(db),
@@ -321,7 +321,7 @@ impl OperationDefinition {
 
     /// Get fields in the operation definition (excluding inline fragments and
     /// fragment spreads).
-    pub fn fields(&self, db: &dyn Document) -> Arc<Vec<Field>> {
+    pub fn fields(&self, db: &dyn DocumentDatabase) -> Arc<Vec<Field>> {
         db.operation_fields(self.id)
     }
 
@@ -331,11 +331,11 @@ impl OperationDefinition {
     //
     // We will need to figure out how to store operation definition id on its
     // fragment spreads and inline fragments to do this
-    pub fn fields_in_inline_fragments(&self, db: &dyn Document) -> Arc<Vec<Field>> {
+    pub fn fields_in_inline_fragments(&self, db: &dyn DocumentDatabase) -> Arc<Vec<Field>> {
         db.operation_inline_fragment_fields(self.id)
     }
 
-    pub fn fields_in_fragment_spread(&self, db: &dyn Document) -> Arc<Vec<Field>> {
+    pub fn fields_in_fragment_spread(&self, db: &dyn DocumentDatabase) -> Arc<Vec<Field>> {
         db.operation_fragment_spread_fields(self.id)
     }
 
@@ -345,7 +345,7 @@ impl OperationDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -454,7 +454,7 @@ impl VariableDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -511,7 +511,7 @@ impl Type {
     /// [`UnionTypeDefinition`]: Definition::UnionTypeDefinition
     /// [`EnumTypeDefinition`]: Definition::EnumTypeDefinition
     #[must_use]
-    pub fn is_output_type(&self, db: &dyn Document) -> bool {
+    pub fn is_output_type(&self, db: &dyn DocumentDatabase) -> bool {
         if let Some(ty) = self.ty(db) {
             ty.as_ref().is_output_definition()
         } else {
@@ -526,7 +526,7 @@ impl Type {
     /// [`EnumTypeDefinition`]: Definition::EnumTypeDefinition
     /// [`InputObjectTypeDefinition`]: Definition::ObjectTypeDefinition
     #[must_use]
-    pub fn is_input_type(&self, db: &dyn Document) -> bool {
+    pub fn is_input_type(&self, db: &dyn DocumentDatabase) -> bool {
         if let Some(ty) = self.ty(db) {
             ty.as_ref().is_input_definition()
         } else {
@@ -543,12 +543,12 @@ impl Type {
         }
     }
 
-    pub fn ty(&self, db: &dyn Document) -> Option<Arc<Definition>> {
+    pub fn ty(&self, db: &dyn DocumentDatabase) -> Option<Arc<Definition>> {
         db.find_definition_by_name(self.name())
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -597,7 +597,7 @@ impl Directive {
     }
 
     // Get directive definition of the currently used directive
-    pub fn directive(&self, db: &dyn Document) -> Option<Arc<DirectiveDefinition>> {
+    pub fn directive(&self, db: &dyn DocumentDatabase) -> Option<Arc<DirectiveDefinition>> {
         db.find_directive_definition_by_name(self.name().to_string())
     }
 
@@ -607,7 +607,7 @@ impl Directive {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -656,7 +656,7 @@ impl DirectiveDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -779,7 +779,7 @@ impl Argument {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -828,7 +828,7 @@ impl Variable {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -913,7 +913,7 @@ pub enum Selection {
 }
 impl Selection {
     /// Get variables used in the selection set.
-    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn DocumentDatabase) -> Vec<Variable> {
         match self {
             Selection::Field(field) => field.variables(db),
             Selection::FragmentSpread(fragment_spread) => fragment_spread.variables(db),
@@ -972,7 +972,7 @@ impl Field {
     }
 
     // Get a reference to field's type.
-    pub fn ty(&self, db: &dyn Document) -> Option<Type> {
+    pub fn ty(&self, db: &dyn DocumentDatabase) -> Option<Type> {
         dbg!(&self.parent_obj);
 
         let def = db
@@ -984,7 +984,7 @@ impl Field {
     }
 
     // Get field's original field definition.
-    pub fn field_definition(&self, db: &dyn Document) -> Option<FieldDefinition> {
+    pub fn field_definition(&self, db: &dyn DocumentDatabase) -> Option<FieldDefinition> {
         db.find_object_type_by_name(self.parent_obj.as_ref()?.to_string())?
             .fields_definition()
             .iter()
@@ -1008,7 +1008,7 @@ impl Field {
     }
 
     /// Get variables used in the field.
-    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn DocumentDatabase) -> Vec<Variable> {
         let mut vars: Vec<_> = self
             .arguments
             .iter()
@@ -1032,7 +1032,7 @@ impl Field {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1066,7 +1066,7 @@ impl InlineFragment {
         &self.selection_set
     }
 
-    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn DocumentDatabase) -> Vec<Variable> {
         let vars = self
             .selection_set
             .selection()
@@ -1082,7 +1082,7 @@ impl InlineFragment {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1096,11 +1096,11 @@ pub struct FragmentSpread {
 }
 
 impl FragmentSpread {
-    pub fn fragment(&self, db: &dyn Document) -> Option<Arc<FragmentDefinition>> {
+    pub fn fragment(&self, db: &dyn DocumentDatabase) -> Option<Arc<FragmentDefinition>> {
         db.find_fragment_by_name(self.name.clone())
     }
 
-    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn DocumentDatabase) -> Vec<Variable> {
         let vars = match self.fragment(db) {
             Some(fragment) => fragment
                 .selection_set
@@ -1119,7 +1119,7 @@ impl FragmentSpread {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1168,7 +1168,7 @@ impl SchemaDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -1177,7 +1177,7 @@ impl SchemaDefinition {
     // so they are memoised as well
 
     /// Get Schema's query object type definition.
-    pub fn query(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn query(&self, db: &dyn DocumentDatabase) -> Option<Arc<ObjectTypeDefinition>> {
         self.root_operation_type_definition().iter().find_map(|op| {
             if op.operation_type.is_query() {
                 db.find_object_type(op.object_type_id(db)?)
@@ -1188,7 +1188,7 @@ impl SchemaDefinition {
     }
 
     /// Get Schema's mutation object type definition.
-    pub fn mutation(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn mutation(&self, db: &dyn DocumentDatabase) -> Option<Arc<ObjectTypeDefinition>> {
         self.root_operation_type_definition().iter().find_map(|op| {
             if op.operation_type.is_mutation() {
                 db.find_object_type(op.object_type_id(db)?)
@@ -1199,7 +1199,7 @@ impl SchemaDefinition {
     }
 
     /// Get Schema's subscription object type definition.
-    pub fn subscription(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn subscription(&self, db: &dyn DocumentDatabase) -> Option<Arc<ObjectTypeDefinition>> {
         self.root_operation_type_definition().iter().find_map(|op| {
             if op.operation_type.is_subscription() {
                 db.find_object_type(op.object_type_id(db)?)
@@ -1228,7 +1228,7 @@ impl RootOperationTypeDefinition {
         self.operation_type
     }
 
-    pub fn object_type_id(&self, db: &dyn Document) -> Option<Uuid> {
+    pub fn object_type_id(&self, db: &dyn DocumentDatabase) -> Option<Uuid> {
         db.find_object_type_by_name(self.named_type().name())
             .map(|object_type| *object_type.id())
     }
@@ -1239,7 +1239,7 @@ impl RootOperationTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -1311,7 +1311,7 @@ impl ObjectTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1324,7 +1324,10 @@ pub struct ImplementsInterface {
 }
 
 impl ImplementsInterface {
-    pub fn interface_definition(&self, db: &dyn Document) -> Option<Arc<InterfaceTypeDefinition>> {
+    pub fn interface_definition(
+        &self,
+        db: &dyn DocumentDatabase,
+    ) -> Option<Arc<InterfaceTypeDefinition>> {
         db.find_interface_by_name(self.interface.clone())
     }
 
@@ -1338,7 +1341,7 @@ impl ImplementsInterface {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1376,7 +1379,7 @@ impl FieldDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1410,7 +1413,7 @@ impl ArgumentsDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -1448,7 +1451,7 @@ impl InputValueDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -1501,7 +1504,7 @@ impl ScalarTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(&rowan::SyntaxNode::new_root(db.document())))
     }
@@ -1554,7 +1557,7 @@ impl EnumTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1579,7 +1582,7 @@ impl EnumValueDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1627,7 +1630,7 @@ impl UnionTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1645,7 +1648,7 @@ impl UnionMember {
         self.name.as_ref()
     }
 
-    pub fn object(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn object(&self, db: &dyn DocumentDatabase) -> Option<Arc<ObjectTypeDefinition>> {
         db.find_object_type_by_name(self.name.clone())
     }
 
@@ -1655,7 +1658,7 @@ impl UnionMember {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1709,7 +1712,7 @@ impl InterfaceTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
@@ -1756,7 +1759,7 @@ impl InputObjectTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn DocumentDatabase) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(&rowan::SyntaxNode::new_root(db.document()))
     }
diff --git a/crates/apollo-compiler/src/database/inputs.rs b/crates/apollo-compiler/src/database/inputs.rs
index be217d8..2437642 100644
--- a/crates/apollo-compiler/src/database/inputs.rs
+++ b/crates/apollo-compiler/src/database/inputs.rs
@@ -1,5 +1,5 @@
-#[salsa::query_group(InputsStorage)]
-pub trait Inputs {
+#[salsa::query_group(InputStorage)]
+pub trait InputDatabase {
     #[salsa::input]
     fn input(&self) -> String;
 }
diff --git a/crates/apollo-compiler/src/database/mod.rs b/crates/apollo-compiler/src/database/mod.rs
index cfa6de5..47dc2e6 100644
--- a/crates/apollo-compiler/src/database/mod.rs
+++ b/crates/apollo-compiler/src/database/mod.rs
@@ -6,8 +6,8 @@ mod def;
 mod document;
 mod inputs;
 
-pub use ast::DocumentParser;
+pub use ast::AstDatabase;
 pub use db::RootDatabase;
-pub use def::Definitions;
-pub use document::Document;
-pub use inputs::Inputs;
+pub use def::HirDatabase;
+pub use document::DocumentDatabase;
+pub use inputs::InputDatabase;
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index 94db660..5154b08 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -6,9 +6,9 @@ mod diagnostics;
 mod tests;
 mod validation;
 
-use validation::Validator;
+use validation::ValidationDatabase;
 
-pub use database::{hir, Definitions, Document, DocumentParser, Inputs, RootDatabase};
+pub use database::{hir, AstDatabase, DocumentDatabase, HirDatabase, InputDatabase, RootDatabase};
 pub use diagnostics::ApolloDiagnostic;
 
 pub struct ApolloCompiler {
@@ -94,8 +94,7 @@ impl ApolloCompiler {
     /// assert_eq!(diagnostics.len(), 1);
     /// ```
     pub fn validate(&self) -> Vec<ApolloDiagnostic> {
-        let mut validator = Validator::new(&self.db);
-        validator.validate().into()
+        self.db.validate()
     }
 }
 
@@ -103,7 +102,7 @@ impl ApolloCompiler {
 mod test {
     use std::collections::HashMap;
 
-    use crate::{hir::Definition, ApolloCompiler, Definitions, Document};
+    use crate::{hir::Definition, ApolloCompiler, DocumentDatabase, HirDatabase};
 
     #[test]
     fn it_accesses_operation_definition_parts() {
diff --git a/crates/apollo-compiler/src/validation/directives.rs b/crates/apollo-compiler/src/validation/directive.rs
similarity index 62%
rename from crates/apollo-compiler/src/validation/directives.rs
rename to crates/apollo-compiler/src/validation/directive.rs
index 3d20c9c..83c4f75 100644
--- a/crates/apollo-compiler/src/validation/directives.rs
+++ b/crates/apollo-compiler/src/validation/directive.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{RecursiveDefinition, UniqueDefinition},
     hir::DirectiveDefinition,
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut errors = Vec::new();
 
     // Directive definitions must have unique names.
@@ -16,14 +16,32 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for dir_def in db.directive_definitions().iter() {
         let name = dir_def.name();
         if let Some(prev_def) = seen.get(&name) {
-            if prev_def.ast_node(db).is_some() && dir_def.ast_node(db).is_some() {
-                let prev_offset: usize = prev_def.ast_node(db).unwrap().text_range().start().into();
-                let prev_node_len: usize = prev_def.ast_node(db).unwrap().text_range().len().into();
+            if prev_def.ast_node(db.upcast()).is_some() && dir_def.ast_node(db.upcast()).is_some() {
+                let prev_offset: usize = prev_def
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .start()
+                    .into();
+                let prev_node_len: usize = prev_def
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .len()
+                    .into();
 
-                let current_offset: usize =
-                    dir_def.ast_node(db).unwrap().text_range().start().into();
-                let current_node_len: usize =
-                    dir_def.ast_node(db).unwrap().text_range().len().into();
+                let current_offset: usize = dir_def
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .start()
+                    .into();
+                let current_node_len: usize = dir_def
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .len()
+                    .into();
                 errors.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     ty: "directive".into(),
                     name: name.into(),
@@ -50,8 +68,8 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             for directive in input_values.directives().iter() {
                 let directive_name = directive.name();
                 if name == directive_name {
-                    let offset = directive.ast_node(db).text_range().start().into();
-                    let len: usize = directive.ast_node(db).text_range().len().into();
+                    let offset = directive.ast_node(db.upcast()).text_range().start().into();
+                    let len: usize = directive.ast_node(db.upcast()).text_range().len().into();
                     errors.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
                         message: format!("{} directive definition cannot reference itself", name),
                         definition: (offset, len).into(),
diff --git a/crates/apollo-compiler/src/validation/enums.rs b/crates/apollo-compiler/src/validation/enum_.rs
similarity index 71%
rename from crates/apollo-compiler/src/validation/enums.rs
rename to crates/apollo-compiler/src/validation/enum_.rs
index 9598908..2860687 100644
--- a/crates/apollo-compiler/src/validation/enums.rs
+++ b/crates/apollo-compiler/src/validation/enum_.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{CapitalizedValue, UniqueDefinition},
     hir::EnumValueDefinition,
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // An Enum type must define one or more unique enum values.
@@ -17,11 +17,13 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
         for enum_value in enum_def.enum_values_definition().iter() {
             let value = enum_value.enum_value();
             if let Some(prev_def) = seen.get(&value) {
-                let prev_offset: usize = prev_def.ast_node(db).text_range().start().into();
-                let prev_node_len: usize = prev_def.ast_node(db).text_range().len().into();
+                let prev_offset: usize = prev_def.ast_node(db.upcast()).text_range().start().into();
+                let prev_node_len: usize = prev_def.ast_node(db.upcast()).text_range().len().into();
 
-                let current_offset: usize = enum_value.ast_node(db).text_range().start().into();
-                let current_node_len: usize = enum_value.ast_node(db).text_range().len().into();
+                let current_offset: usize =
+                    enum_value.ast_node(db.upcast()).text_range().start().into();
+                let current_node_len: usize =
+                    enum_value.ast_node(db.upcast()).text_range().len().into();
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     ty: "enum".into(),
                     name: value.into(),
@@ -42,8 +44,8 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for enum_def in db.enums().iter() {
         for enum_value in enum_def.enum_values_definition().iter() {
             let value = enum_value.enum_value();
-            let offset: usize = enum_value.ast_node(db).text_range().start().into();
-            let len: usize = enum_value.ast_node(db).text_range().len().into();
+            let offset: usize = enum_value.ast_node(db.upcast()).text_range().start().into();
+            let len: usize = enum_value.ast_node(db.upcast()).text_range().len().into();
 
             if value.to_uppercase() != value {
                 diagnostics.push(ApolloDiagnostic::CapitalizedValue(CapitalizedValue {
diff --git a/crates/apollo-compiler/src/validation/input_objects.rs b/crates/apollo-compiler/src/validation/input_object.rs
similarity index 56%
rename from crates/apollo-compiler/src/validation/input_objects.rs
rename to crates/apollo-compiler/src/validation/input_object.rs
index 025224a..9d73a54 100644
--- a/crates/apollo-compiler/src/validation/input_objects.rs
+++ b/crates/apollo-compiler/src/validation/input_object.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{UniqueDefinition, UniqueField},
     hir::{InputObjectTypeDefinition, InputValueDefinition},
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Input Object Definitions must have unique names.
@@ -16,11 +16,16 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for input_object in db.input_objects().iter() {
         let name = input_object.name();
         if let Some(prev_def) = seen.get(name) {
-            let prev_offset: usize = prev_def.ast_node(db).text_range().start().into();
-            let prev_node_len: usize = prev_def.ast_node(db).text_range().len().into();
+            let prev_offset: usize = prev_def.ast_node(db.upcast()).text_range().start().into();
+            let prev_node_len: usize = prev_def.ast_node(db.upcast()).text_range().len().into();
 
-            let current_offset: usize = input_object.ast_node(db).text_range().start().into();
-            let current_node_len: usize = input_object.ast_node(db).text_range().len().into();
+            let current_offset: usize = input_object
+                .ast_node(db.upcast())
+                .text_range()
+                .start()
+                .into();
+            let current_node_len: usize =
+                input_object.ast_node(db.upcast()).text_range().len().into();
             diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                 ty: "input object".into(),
                 name: name.into(),
@@ -47,16 +52,34 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
         for field in input_fields {
             let field_name = field.name();
             if let Some(prev_field) = seen.get(&field_name) {
-                if prev_field.ast_node(db).is_some() && field.ast_node(db).is_some() {
-                    let prev_offset: usize =
-                        prev_field.ast_node(db).unwrap().text_range().start().into();
-                    let prev_node_len: usize =
-                        prev_field.ast_node(db).unwrap().text_range().len().into();
+                if prev_field.ast_node(db.upcast()).is_some()
+                    && field.ast_node(db.upcast()).is_some()
+                {
+                    let prev_offset: usize = prev_field
+                        .ast_node(db.upcast())
+                        .unwrap()
+                        .text_range()
+                        .start()
+                        .into();
+                    let prev_node_len: usize = prev_field
+                        .ast_node(db.upcast())
+                        .unwrap()
+                        .text_range()
+                        .len()
+                        .into();
 
-                    let current_offset: usize =
-                        field.ast_node(db).unwrap().text_range().start().into();
-                    let current_node_len: usize =
-                        field.ast_node(db).unwrap().text_range().len().into();
+                    let current_offset: usize = field
+                        .ast_node(db.upcast())
+                        .unwrap()
+                        .text_range()
+                        .start()
+                        .into();
+                    let current_node_len: usize = field
+                        .ast_node(db.upcast())
+                        .unwrap()
+                        .text_range()
+                        .len()
+                        .into();
 
                     diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
                         field: field_name.into(),
diff --git a/crates/apollo-compiler/src/validation/interfaces.rs b/crates/apollo-compiler/src/validation/interface.rs
similarity index 84%
rename from crates/apollo-compiler/src/validation/interfaces.rs
rename to crates/apollo-compiler/src/validation/interface.rs
index 66f3e3b..e8069d3 100644
--- a/crates/apollo-compiler/src/validation/interfaces.rs
+++ b/crates/apollo-compiler/src/validation/interface.rs
@@ -7,10 +7,10 @@ use crate::{
     },
     hir::{FieldDefinition, InterfaceTypeDefinition},
     validation::ValidationSet,
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Interface definitions must have unique names.
@@ -20,11 +20,11 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for interface in db.interfaces().iter() {
         let name = interface.name();
         if let Some(prev_def) = seen.get(&name) {
-            let prev_offset: usize = prev_def.ast_node(db).text_range().start().into();
-            let prev_node_len: usize = prev_def.ast_node(db).text_range().len().into();
+            let prev_offset: usize = prev_def.ast_node(db.upcast()).text_range().start().into();
+            let prev_node_len: usize = prev_def.ast_node(db.upcast()).text_range().len().into();
 
-            let current_offset: usize = interface.ast_node(db).text_range().start().into();
-            let current_node_len: usize = interface.ast_node(db).text_range().len().into();
+            let current_offset: usize = interface.ast_node(db.upcast()).text_range().start().into();
+            let current_node_len: usize = interface.ast_node(db.upcast()).text_range().len().into();
             diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                 ty: "interface".into(),
                 name: name.into(),
@@ -58,15 +58,19 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for interface_def in db.interfaces().iter() {
         let name = interface_def.name();
         for implements_interface in interface_def.implements_interfaces() {
-            if let Some(interface) = implements_interface.interface_definition(db) {
+            if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
                 let i_name = (*interface.name()).to_string();
                 if name == i_name {
                     let offset = implements_interface
-                        .ast_node(db)
+                        .ast_node(db.upcast())
                         .text_range()
                         .start()
                         .into();
-                    let len: usize = implements_interface.ast_node(db).text_range().len().into();
+                    let len: usize = implements_interface
+                        .ast_node(db.upcast())
+                        .text_range()
+                        .len()
+                        .into();
                     diagnostics.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
                         message: format!("{} interface cannot implement itself", i_name),
                         definition: (offset, len).into(),
@@ -89,12 +93,14 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             //
             // Returns Unique Value error.
             let field_name = field.name();
-            let offset: usize = field.ast_node(db).text_range().start().into();
-            let len: usize = field.ast_node(db).text_range().len().into();
+            let offset: usize = field.ast_node(db.upcast()).text_range().start().into();
+            let len: usize = field.ast_node(db.upcast()).text_range().len().into();
 
             if let Some(prev_field) = seen.get(&field_name) {
-                let prev_offset: usize = prev_field.ast_node(db).text_range().start().into();
-                let prev_node_len: usize = prev_field.ast_node(db).text_range().len().into();
+                let prev_offset: usize =
+                    prev_field.ast_node(db.upcast()).text_range().start().into();
+                let prev_node_len: usize =
+                    prev_field.ast_node(db.upcast()).text_range().len().into();
 
                 diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
                     field: field_name.into(),
@@ -110,8 +116,8 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             }
 
             // Field types in interface types must be of output type
-            if let Some(field_ty) = field.ty().ty(db) {
-                if !field.ty().is_output_type(db) {
+            if let Some(field_ty) = field.ty().ty(db.upcast()) {
+                if !field.ty().is_output_type(db.upcast()) {
                     diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
                         name: field.name().into(),
                         ty: field_ty.ty(),
@@ -119,7 +125,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
                         definition: (offset, len).into(),
                     }))
                 }
-            } else if let Some(node) = field.ty().ast_node(db) {
+            } else if let Some(node) = field.ty().ast_node(db.upcast()) {
                 let field_ty_offset: usize = node.text_range().start().into();
                 let field_ty_len: usize = node.text_range().len().into();
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
@@ -142,7 +148,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
         .iter()
         .map(|interface| ValidationSet {
             name: interface.name().to_owned(),
-            node: interface.ast_node(db),
+            node: interface.ast_node(db.upcast()),
         })
         .collect();
     for interface_def in interfaces.iter() {
@@ -154,7 +160,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .iter()
             .map(|interface| ValidationSet {
                 name: interface.interface().to_owned(),
-                node: interface.ast_node(db),
+                node: interface.ast_node(db.upcast()),
             })
             .collect();
         let diff = implements_interfaces.difference(&defined_interfaces);
@@ -176,13 +182,13 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .implements_interfaces()
             .iter()
             .filter_map(|implements_interface| {
-                if let Some(interface) = implements_interface.interface_definition(db) {
+                if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
                     let child_interfaces: HashSet<ValidationSet> = interface
                         .implements_interfaces()
                         .iter()
                         .map(|interface| ValidationSet {
                             name: interface.interface().to_owned(),
-                            node: implements_interface.ast_node(db),
+                            node: implements_interface.ast_node(db.upcast()),
                         })
                         .collect();
                     Some(child_interfaces)
@@ -215,29 +221,37 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .iter()
             .map(|field| ValidationSet {
                 name: field.name().into(),
-                node: field.ast_node(db),
+                node: field.ast_node(db.upcast()),
             })
             .collect();
         for implements_interface in interface_def.implements_interfaces().iter() {
-            if let Some(interface) = implements_interface.interface_definition(db) {
+            if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
                 let implements_interface_fields: HashSet<ValidationSet> = interface
                     .fields_definition()
                     .iter()
                     .map(|field| ValidationSet {
                         name: field.name().into(),
-                        node: field.ast_node(db),
+                        node: field.ast_node(db.upcast()),
                     })
                     .collect();
 
                 let field_diff = implements_interface_fields.difference(&fields);
 
                 for missing_field in field_diff {
-                    let current_offset: usize =
-                        interface_def.ast_node(db).text_range().start().into();
-                    let current_len = interface_def.ast_node(db).text_range().len().into();
+                    let current_offset: usize = interface_def
+                        .ast_node(db.upcast())
+                        .text_range()
+                        .start()
+                        .into();
+                    let current_len = interface_def
+                        .ast_node(db.upcast())
+                        .text_range()
+                        .len()
+                        .into();
 
-                    let super_offset = interface.ast_node(db).text_range().start().into();
-                    let super_len: usize = interface.ast_node(db).text_range().len().into();
+                    let super_offset = interface.ast_node(db.upcast()).text_range().start().into();
+                    let super_len: usize =
+                        interface.ast_node(db.upcast()).text_range().len().into();
 
                     diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
                         ty: missing_field.name.clone(),
diff --git a/crates/apollo-compiler/src/validation/mod.rs b/crates/apollo-compiler/src/validation/mod.rs
index c0c4cbe..beb89bb 100644
--- a/crates/apollo-compiler/src/validation/mod.rs
+++ b/crates/apollo-compiler/src/validation/mod.rs
@@ -1,60 +1,119 @@
-use apollo_parser::SyntaxNode;
-
-use crate::{ApolloDiagnostic, Document};
-
 // schema
-pub mod schema;
+mod schema;
 
 // leaf nodes
-pub mod enums;
-pub mod scalars;
-pub mod unions;
+mod enum_;
+mod scalar;
+mod union_;
 
 // composite nodes
-pub mod directives;
-pub mod input_objects;
-pub mod interfaces;
-pub mod objects;
+mod directive;
+mod input_object;
+mod interface;
+mod object;
 
 // executable definitions
-pub mod operations;
+mod operation;
 
-pub mod unused_variables;
+mod unused_variable;
 
-pub struct Validator<'a> {
-    db: &'a dyn Document,
-    diagnostics: Vec<ApolloDiagnostic>,
+use apollo_parser::SyntaxNode;
+
+use crate::{
+    database::db::Upcast, ApolloDiagnostic, AstDatabase, DocumentDatabase, HirDatabase,
+    InputDatabase,
+};
+
+#[salsa::query_group(ValidationStorage)]
+pub trait ValidationDatabase:
+    Upcast<dyn DocumentDatabase> + InputDatabase + AstDatabase + HirDatabase
+{
+    fn validate(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_schema(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_scalar(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_enum(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_union(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_interface(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_directive(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_input_object(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_object(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_operation(&self) -> Vec<ApolloDiagnostic>;
+    fn validate_unused_variable(&self) -> Vec<ApolloDiagnostic>;
 }
 
-impl<'a> Validator<'a> {
-    pub fn new(db: &'a dyn Document) -> Self {
-        Self {
-            db,
-            diagnostics: Vec::new(),
-        }
-    }
+pub fn validate(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    let mut diagnostics = Vec::new();
+    diagnostics.extend(db.syntax_errors());
+
+    diagnostics.extend(db.validate_schema());
 
-    pub fn validate(&mut self) -> &mut [ApolloDiagnostic] {
-        self.diagnostics.extend(self.db.syntax_errors());
+    diagnostics.extend(db.validate_scalar());
+    diagnostics.extend(db.validate_enum());
+    diagnostics.extend(db.validate_union());
 
-        self.diagnostics.extend(schema::check(self.db));
+    diagnostics.extend(db.validate_interface());
+    diagnostics.extend(db.validate_directive());
+    diagnostics.extend(db.validate_input_object());
+    diagnostics.extend(db.validate_object());
+    diagnostics.extend(db.validate_operation());
 
-        self.diagnostics.extend(scalars::check(self.db));
-        self.diagnostics.extend(enums::check(self.db));
-        self.diagnostics.extend(unions::check(self.db));
+    diagnostics.extend(db.validate_unused_variable());
 
-        self.diagnostics.extend(interfaces::check(self.db));
-        self.diagnostics.extend(directives::check(self.db));
-        self.diagnostics.extend(input_objects::check(self.db));
-        self.diagnostics.extend(objects::check(self.db));
+    diagnostics
+}
 
-        self.diagnostics.extend(operations::check(self.db));
-        self.diagnostics.extend(unused_variables::check(self.db));
+pub fn validate_schema(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    schema::check(db)
+}
 
-        self.diagnostics.as_mut()
-    }
+pub fn validate_scalar(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    scalar::check(db)
+}
+
+pub fn validate_enum(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    enum_::check(db)
+}
+
+pub fn validate_union(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    union_::check(db)
 }
 
+pub fn validate_interface(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    interface::check(db)
+}
+
+pub fn validate_directive(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    directive::check(db)
+}
+
+pub fn validate_input_object(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    input_object::check(db)
+}
+
+pub fn validate_object(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    object::check(db)
+}
+
+pub fn validate_operation(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    operation::check(db)
+}
+
+pub fn validate_unused_variable(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
+    unused_variable::check(db)
+}
+
+// #[salsa::query_group(ValidationStorage)]
+// pub trait Validation: Document + Inputs + DocumentParser + Definitions {
+//     fn validate(&self) -> Arc<Vec<ApolloDiagnostic>>;
+// }
+//
+// pub fn validate(db: &dyn Validation) -> Arc<Vec<ApolloDiagnostic>> {
+//     let mut diagnostics = Vec::new();
+//     diagnostics.extend(schema::check(db));
+//
+//     Arc::new(diagnostics)
+// }
+
 #[derive(Debug, Eq)]
 struct ValidationSet {
     name: String,
diff --git a/crates/apollo-compiler/src/validation/objects.rs b/crates/apollo-compiler/src/validation/object.rs
similarity index 82%
rename from crates/apollo-compiler/src/validation/objects.rs
rename to crates/apollo-compiler/src/validation/object.rs
index 1fb45e0..c11a817 100644
--- a/crates/apollo-compiler/src/validation/objects.rs
+++ b/crates/apollo-compiler/src/validation/object.rs
@@ -7,10 +7,10 @@ use crate::{
     },
     hir::{FieldDefinition, ObjectTypeDefinition},
     validation::ValidationSet,
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Object Type definitions must have unique names.
@@ -20,11 +20,11 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for object in db.object_types().iter() {
         let name = object.name();
         if let Some(prev_def) = seen.get(&name) {
-            let prev_offset: usize = prev_def.ast_node(db).text_range().start().into();
-            let prev_node_len: usize = prev_def.ast_node(db).text_range().len().into();
+            let prev_offset: usize = prev_def.ast_node(db.upcast()).text_range().start().into();
+            let prev_node_len: usize = prev_def.ast_node(db.upcast()).text_range().len().into();
 
-            let current_offset: usize = object.ast_node(db).text_range().start().into();
-            let current_node_len: usize = object.ast_node(db).text_range().len().into();
+            let current_offset: usize = object.ast_node(db.upcast()).text_range().start().into();
+            let current_node_len: usize = object.ast_node(db.upcast()).text_range().len().into();
             diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                 ty: "object type".into(),
                 name: name.into(),
@@ -50,12 +50,14 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             //
             // Returns Unique Value error.
             let field_name = field.name();
-            let offset: usize = field.ast_node(db).text_range().start().into();
-            let len: usize = field.ast_node(db).text_range().len().into();
+            let offset: usize = field.ast_node(db.upcast()).text_range().start().into();
+            let len: usize = field.ast_node(db.upcast()).text_range().len().into();
 
             if let Some(prev_field) = seen.get(&field_name) {
-                let prev_offset: usize = prev_field.ast_node(db).text_range().start().into();
-                let prev_node_len: usize = prev_field.ast_node(db).text_range().len().into();
+                let prev_offset: usize =
+                    prev_field.ast_node(db.upcast()).text_range().start().into();
+                let prev_node_len: usize =
+                    prev_field.ast_node(db.upcast()).text_range().len().into();
 
                 diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
                     field: field_name.into(),
@@ -71,8 +73,8 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             }
 
             // Field types in Object Types must be of output type
-            if let Some(field_ty) = field.ty().ty(db) {
-                if !field.ty().is_output_type(db) {
+            if let Some(field_ty) = field.ty().ty(db.upcast()) {
+                if !field.ty().is_output_type(db.upcast()) {
                     diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
                         name: field.name().into(),
                         ty: field_ty.ty(),
@@ -80,7 +82,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
                         definition: (offset, len).into(),
                     }))
                 }
-            } else if let Some(node) = field.ty().ast_node(db) {
+            } else if let Some(node) = field.ty().ast_node(db.upcast()) {
                 let field_ty_offset: usize = node.text_range().start().into();
                 let field_ty_len: usize = node.text_range().len().into();
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
@@ -104,7 +106,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
         .iter()
         .map(|interface| ValidationSet {
             name: interface.name().to_owned(),
-            node: interface.ast_node(db),
+            node: interface.ast_node(db.upcast()),
         })
         .collect();
     for object in objects.iter() {
@@ -116,7 +118,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .iter()
             .map(|interface| ValidationSet {
                 name: interface.interface().to_owned(),
-                node: interface.ast_node(db),
+                node: interface.ast_node(db.upcast()),
             })
             .collect();
         let diff = implements_interfaces.difference(&defined_interfaces);
@@ -138,13 +140,13 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .implements_interfaces()
             .iter()
             .filter_map(|implements_interface| {
-                if let Some(interface) = implements_interface.interface_definition(db) {
+                if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
                     let child_interfaces: HashSet<ValidationSet> = interface
                         .implements_interfaces()
                         .iter()
                         .map(|interface| ValidationSet {
                             name: interface.interface().to_owned(),
-                            node: implements_interface.ast_node(db),
+                            node: implements_interface.ast_node(db.upcast()),
                         })
                         .collect();
                     Some(child_interfaces)
@@ -177,28 +179,30 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .iter()
             .map(|field| ValidationSet {
                 name: field.name().into(),
-                node: field.ast_node(db),
+                node: field.ast_node(db.upcast()),
             })
             .collect();
         for implements_interface in object.implements_interfaces().iter() {
-            if let Some(interface) = implements_interface.interface_definition(db) {
+            if let Some(interface) = implements_interface.interface_definition(db.upcast()) {
                 let implements_interface_fields: HashSet<ValidationSet> = interface
                     .fields_definition()
                     .iter()
                     .map(|field| ValidationSet {
                         name: field.name().into(),
-                        node: field.ast_node(db),
+                        node: field.ast_node(db.upcast()),
                     })
                     .collect();
 
                 let field_diff = implements_interface_fields.difference(&fields);
 
                 for missing_field in field_diff {
-                    let current_offset: usize = object.ast_node(db).text_range().start().into();
-                    let current_len = object.ast_node(db).text_range().len().into();
+                    let current_offset: usize =
+                        object.ast_node(db.upcast()).text_range().start().into();
+                    let current_len = object.ast_node(db.upcast()).text_range().len().into();
 
-                    let super_offset = interface.ast_node(db).text_range().start().into();
-                    let super_len: usize = interface.ast_node(db).text_range().len().into();
+                    let super_offset = interface.ast_node(db.upcast()).text_range().start().into();
+                    let super_len: usize =
+                        interface.ast_node(db.upcast()).text_range().len().into();
 
                     diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
                         ty: missing_field.name.clone(),
diff --git a/crates/apollo-compiler/src/validation/operations.rs b/crates/apollo-compiler/src/validation/operation.rs
similarity index 83%
rename from crates/apollo-compiler/src/validation/operations.rs
rename to crates/apollo-compiler/src/validation/operation.rs
index 3489ad6..b0e91e7 100644
--- a/crates/apollo-compiler/src/validation/operations.rs
+++ b/crates/apollo-compiler/src/validation/operation.rs
@@ -5,11 +5,11 @@ use crate::{
         MissingIdent, SingleRootField, UndefinedField, UniqueDefinition, UnsupportedOperation,
     },
     hir::{OperationDefinition, Selection},
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 // use crate::{diagnostics::ErrorDiagnostic, ApolloDiagnostic, Document};
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
     // It is possible to have an unnamed (anonymous) operation definition only
     // if there is **one** operation definition.
@@ -23,8 +23,8 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             .iter()
             .filter_map(|op| {
                 if op.name().is_none() {
-                    let offset = op.ast_node(db).text_range().start().into();
-                    let len: usize = op.ast_node(db).text_range().len().into();
+                    let offset = op.ast_node(db.upcast()).text_range().start().into();
+                    let len: usize = op.ast_node(db.upcast()).text_range().len().into();
                     return Some(ApolloDiagnostic::MissingIdent(MissingIdent {
                         src: db.input(),
                         definition: (offset, len).into(),
@@ -44,11 +44,11 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for op in db.operations().iter() {
         if let Some(name) = op.name() {
             if let Some(prev_def) = seen.get(&name) {
-                let prev_offset: usize = prev_def.ast_node(db).text_range().start().into();
-                let prev_node_len: usize = prev_def.ast_node(db).text_range().len().into();
+                let prev_offset: usize = prev_def.ast_node(db.upcast()).text_range().start().into();
+                let prev_node_len: usize = prev_def.ast_node(db.upcast()).text_range().len().into();
 
-                let current_offset: usize = op.ast_node(db).text_range().start().into();
-                let current_node_len: usize = op.ast_node(db).text_range().len().into();
+                let current_offset: usize = op.ast_node(db.upcast()).text_range().start().into();
+                let current_node_len: usize = op.ast_node(db.upcast()).text_range().len().into();
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     ty: "operation".into(),
                     name: name.into(),
@@ -67,18 +67,19 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
 
     // A Subscription operation definition can only have **one** root level
     // field.
-    if db.subscription_operations().len() >= 1 {
+    if db.upcast().subscription_operations().len() >= 1 {
         let single_root_field: Vec<ApolloDiagnostic> = db
+            .upcast()
             .subscription_operations()
             .iter()
             .filter_map(|op| {
-                let mut fields = op.fields(db).as_ref().clone();
-                fields.extend(op.fields_in_inline_fragments(db).as_ref().clone());
-                fields.extend(op.fields_in_fragment_spread(db).as_ref().clone());
+                let mut fields = op.fields(db.upcast()).as_ref().clone();
+                fields.extend(op.fields_in_inline_fragments(db.upcast()).as_ref().clone());
+                fields.extend(op.fields_in_fragment_spread(db.upcast()).as_ref().clone());
                 if fields.len() > 1 {
                     let field_names: Vec<&str> = fields.iter().map(|f| f.name()).collect();
-                    let offset = op.ast_node(db).text_range().start().into();
-                    let len: usize = op.ast_node(db).text_range().len().into();
+                    let offset = op.ast_node(db.upcast()).text_range().start().into();
+                    let len: usize = op.ast_node(db.upcast()).text_range().len().into();
                     Some(ApolloDiagnostic::SingleRootField(SingleRootField {
                         fields: fields.len(),
                         src: db.input(),
@@ -101,15 +102,18 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     // defined schema root operation types.
     //
     //   * subscription operation - subscription root operation
-    if db.subscription_operations().len() >= 1 && db.schema().subscription(db).is_none() {
+    if db.upcast().subscription_operations().len() >= 1
+        && db.schema().subscription(db.upcast()).is_none()
+    {
         let unsupported_ops: Vec<ApolloDiagnostic> = db
+            .upcast()
             .subscription_operations()
             .iter()
             .map(|op| {
-                let op_offset: usize = op.ast_node(db).text_range().start().into();
-                let op_len: usize = op.ast_node(db).text_range().len().into();
+                let op_offset: usize = op.ast_node(db.upcast()).text_range().start().into();
+                let op_len: usize = op.ast_node(db.upcast()).text_range().len().into();
 
-                if let Some(schema_node) = db.schema().ast_node(db) {
+                if let Some(schema_node) = db.schema().ast_node(db.upcast()) {
                     let schema_offset: usize = schema_node.text_range().start().into();
                     let schema_len: usize = schema_node.text_range().len().into();
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
@@ -137,15 +141,16 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     }
     //
     //   * query operation - query root operation
-    if db.query_operations().len() >= 1 && db.schema().query(db).is_none() {
+    if db.upcast().query_operations().len() >= 1 && db.schema().query(db.upcast()).is_none() {
         let unsupported_ops: Vec<ApolloDiagnostic> = db
+            .upcast()
             .query_operations()
             .iter()
             .map(|op| {
-                let op_offset: usize = op.ast_node(db).text_range().start().into();
-                let op_len: usize = op.ast_node(db).text_range().len().into();
+                let op_offset: usize = op.ast_node(db.upcast()).text_range().start().into();
+                let op_len: usize = op.ast_node(db.upcast()).text_range().len().into();
 
-                if let Some(schema_node) = db.schema().ast_node(db) {
+                if let Some(schema_node) = db.schema().ast_node(db.upcast()) {
                     let schema_offset: usize = schema_node.text_range().start().into();
                     let schema_len: usize = schema_node.text_range().len().into();
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
@@ -172,15 +177,16 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     }
 
     //   * mutation operation - mutation root operation
-    if db.mutation_operations().len() >= 1 && db.schema().mutation(db).is_none() {
+    if db.upcast().mutation_operations().len() >= 1 && db.schema().mutation(db.upcast()).is_none() {
         let unsupported_ops: Vec<ApolloDiagnostic> = db
+            .upcast()
             .mutation_operations()
             .iter()
             .map(|op| {
-                let op_offset: usize = op.ast_node(db).text_range().start().into();
-                let op_len: usize = op.ast_node(db).text_range().len().into();
+                let op_offset: usize = op.ast_node(db.upcast()).text_range().start().into();
+                let op_len: usize = op.ast_node(db.upcast()).text_range().len().into();
 
-                if let Some(schema_node) = db.schema().ast_node(db) {
+                if let Some(schema_node) = db.schema().ast_node(db.upcast()) {
                     let schema_offset: usize = schema_node.text_range().start().into();
                     let schema_len: usize = schema_node.text_range().len().into();
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
@@ -210,11 +216,11 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     // Fields must exist on the type being queried.
     for op in db.operations().iter() {
         for selection in op.selection_set().selection() {
-            let obj_name = op.object_type(db).map(|obj| obj.name().to_owned());
+            let obj_name = op.object_type(db.upcast()).map(|obj| obj.name().to_owned());
             if let Selection::Field(field) = selection {
-                if field.ty(db).is_none() {
-                    let offset: usize = field.ast_node(db).text_range().start().into();
-                    let len: usize = field.ast_node(db).text_range().len().into();
+                if field.ty(db.upcast()).is_none() {
+                    let offset: usize = field.ast_node(db.upcast()).text_range().start().into();
+                    let len: usize = field.ast_node(db.upcast()).text_range().len().into();
                     let field_name = field.name().into();
                     let help = if let Some(obj_type) = obj_name {
                         format!("`{}` is not defined on `{}` type", field_name, obj_type)
diff --git a/crates/apollo-compiler/src/validation/scalars.rs b/crates/apollo-compiler/src/validation/scalar.rs
similarity index 89%
rename from crates/apollo-compiler/src/validation/scalars.rs
rename to crates/apollo-compiler/src/validation/scalar.rs
index 9a146de..421a482 100644
--- a/crates/apollo-compiler/src/validation/scalars.rs
+++ b/crates/apollo-compiler/src/validation/scalar.rs
@@ -1,16 +1,16 @@
 use crate::{
     diagnostics::{BuiltInScalarDefinition, ScalarSpecificationURL},
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
 const BUILT_IN_SCALARS: [&str; 5] = ["Int", "Float", "Boolean", "String", "ID"];
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     for scalar in db.scalars().iter() {
         let name = scalar.name();
-        if let Some(node) = scalar.ast_node(db) {
+        if let Some(node) = scalar.ast_node(db.upcast()) {
             let offset: usize = node.text_range().start().into();
             let len: usize = node.text_range().len().into();
 
diff --git a/crates/apollo-compiler/src/validation/schema.rs b/crates/apollo-compiler/src/validation/schema.rs
index 59b367d..153ac83 100644
--- a/crates/apollo-compiler/src/validation/schema.rs
+++ b/crates/apollo-compiler/src/validation/schema.rs
@@ -3,15 +3,15 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{QueryRootOperationType, UniqueDefinition},
     hir::RootOperationTypeDefinition,
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // A GraphQL schema must have a Query root operation.
-    if db.schema().query(db).is_none() {
-        if let Some(node) = db.schema().ast_node(db) {
+    if db.schema().query(db.upcast()).is_none() {
+        if let Some(node) = db.schema().ast_node(db.upcast()) {
             let offset: usize = node.text_range().start().into();
             let len: usize = node.text_range().len().into();
             diagnostics.push(ApolloDiagnostic::QueryRootOperationType(
@@ -30,14 +30,32 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     for op_type in db.schema().root_operation_type_definition().iter() {
         let name = op_type.named_type().name();
         if let Some(prev_def) = seen.get(&name) {
-            if prev_def.ast_node(db).is_some() && op_type.ast_node(db).is_some() {
-                let prev_offset: usize = prev_def.ast_node(db).unwrap().text_range().start().into();
-                let prev_node_len: usize = prev_def.ast_node(db).unwrap().text_range().len().into();
+            if prev_def.ast_node(db.upcast()).is_some() && op_type.ast_node(db.upcast()).is_some() {
+                let prev_offset: usize = prev_def
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .start()
+                    .into();
+                let prev_node_len: usize = prev_def
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .len()
+                    .into();
 
-                let current_offset: usize =
-                    op_type.ast_node(db).unwrap().text_range().start().into();
-                let current_node_len: usize =
-                    op_type.ast_node(db).unwrap().text_range().len().into();
+                let current_offset: usize = op_type
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .start()
+                    .into();
+                let current_node_len: usize = op_type
+                    .ast_node(db.upcast())
+                    .unwrap()
+                    .text_range()
+                    .len()
+                    .into();
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     name: name.clone(),
                     ty: "root operation type definition".into(),
diff --git a/crates/apollo-compiler/src/validation/unions.rs b/crates/apollo-compiler/src/validation/union_.rs
similarity index 74%
rename from crates/apollo-compiler/src/validation/unions.rs
rename to crates/apollo-compiler/src/validation/union_.rs
index 3a6c7b5..37f2e33 100644
--- a/crates/apollo-compiler/src/validation/unions.rs
+++ b/crates/apollo-compiler/src/validation/union_.rs
@@ -3,24 +3,28 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{ObjectType, UndefinedDefinition, UniqueDefinition},
     hir::UnionMember,
-    ApolloDiagnostic, Document,
+    ApolloDiagnostic, ValidationDatabase,
 };
 
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     for union_def in db.unions().iter() {
         let mut seen: HashMap<&str, &UnionMember> = HashMap::new();
         for union_member in union_def.union_members().iter() {
             let name = union_member.name();
-            let offset: usize = union_member.ast_node(db).text_range().start().into();
-            let len: usize = union_member.ast_node(db).text_range().len().into();
+            let offset: usize = union_member
+                .ast_node(db.upcast())
+                .text_range()
+                .start()
+                .into();
+            let len: usize = union_member.ast_node(db.upcast()).text_range().len().into();
             // A Union type must include one or more unique member types.
             //
             // Return a Unique Value error in case of a duplicate member.
             if let Some(prev_def) = seen.get(&name) {
-                let prev_offset: usize = prev_def.ast_node(db).text_range().start().into();
-                let prev_node_len: usize = prev_def.ast_node(db).text_range().len().into();
+                let prev_offset: usize = prev_def.ast_node(db.upcast()).text_range().start().into();
+                let prev_node_len: usize = prev_def.ast_node(db.upcast()).text_range().len().into();
 
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     name: name.into(),
@@ -37,7 +41,9 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
             }
 
             // Union member must be defined.
-            let union_member_type = db.find_type_system_definition_by_name(name.to_string());
+            let union_member_type = db
+                .upcast()
+                .find_type_system_definition_by_name(name.to_string());
             if union_member_type.is_none() {
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                     ty: name.into(),
diff --git a/crates/apollo-compiler/src/validation/unused_variables.rs b/crates/apollo-compiler/src/validation/unused_variable.rs
similarity index 94%
rename from crates/apollo-compiler/src/validation/unused_variables.rs
rename to crates/apollo-compiler/src/validation/unused_variable.rs
index d7cfb2f..7ed0f76 100644
--- a/crates/apollo-compiler/src/validation/unused_variables.rs
+++ b/crates/apollo-compiler/src/validation/unused_variable.rs
@@ -3,13 +3,13 @@ use std::collections::HashSet;
 use crate::{
     diagnostics::{ApolloDiagnostic, UndefinedDefinition, UnusedVariable},
     validation::ValidationSet,
-    Document,
+    ValidationDatabase,
 };
 
 // check in scope
 // check in use
 // compare the two
-pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn ValidationDatabase) -> Vec<ApolloDiagnostic> {
     db.operations()
         .iter()
         .flat_map(|op| {
@@ -18,7 +18,7 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
                 .iter()
                 .map(|var| ValidationSet {
                     name: var.name().into(),
-                    node: var.ast_node(db),
+                    node: var.ast_node(db.upcast()),
                 })
                 .collect();
             let used_vars: HashSet<ValidationSet> = op
@@ -28,11 +28,11 @@ pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
                 .iter()
                 .flat_map(|sel| {
                     let vars: HashSet<ValidationSet> = sel
-                        .variables(db)
+                        .variables(db.upcast())
                         .iter()
                         .map(|var| ValidationSet {
                             name: var.name().into(),
-                            node: var.ast_node(db),
+                            node: var.ast_node(db.upcast()),
                         })
                         .collect();
                     vars


Test patch:
diff --git a/crates/apollo-compiler/src/tests.rs b/crates/apollo-compiler/src/tests.rs
index 44415b0..0c3497e 100644
--- a/crates/apollo-compiler/src/tests.rs
+++ b/crates/apollo-compiler/src/tests.rs
@@ -12,7 +12,7 @@ use std::{
 
 use expect_test::expect_file;
 
-use crate::{ApolloCompiler, ApolloDiagnostic, DocumentParser};
+use crate::{ApolloCompiler, ApolloDiagnostic, AstDatabase};
 
 // To run these tests and update files:
 // ```bash