diff --git a/query-algebrizer/src/clauses/where_fn.rs b/query-algebrizer/src/clauses/where_fn.rs index 40c047fb..3dade69f 100644 --- a/query-algebrizer/src/clauses/where_fn.rs +++ b/query-algebrizer/src/clauses/where_fn.rs @@ -159,7 +159,12 @@ impl ConjoiningClauses { match self.bound_value(&in_var) { // The type is already known if it's a bound variable…. Some(ref in_value) => Ok(Val(in_value.clone())), - None => bail!(ErrorKind::UnboundVariable((*in_var.0).clone())), + None => { + // The variable is present in `:in`, but it hasn't yet been provided. + // This is a restriction we will eventually relax: we don't yet have a way + // to collect variables as part of a computed table or substitution. + bail!(ErrorKind::UnboundVariable((*in_var.0).clone())) + }, } }, diff --git a/query-algebrizer/tests/ground.rs b/query-algebrizer/tests/ground.rs index 0a2bdf69..b7f811db 100644 --- a/query-algebrizer/tests/ground.rs +++ b/query-algebrizer/tests/ground.rs @@ -13,6 +13,8 @@ extern crate mentat_query; extern crate mentat_query_algebrizer; extern crate mentat_query_parser; +use std::collections::BTreeMap; + use mentat_core::{ Attribute, Entid, @@ -37,7 +39,9 @@ use mentat_query_algebrizer::{ ComputedTable, Error, ErrorKind, + QueryInputs, algebrize, + algebrize_with_inputs, }; // These are helpers that tests use to build Schema instances. @@ -92,6 +96,11 @@ fn bails(schema: &Schema, input: &str) -> Error { algebrize(schema.into(), parsed).expect_err("algebrize to have failed") } +fn bails_with_inputs(schema: &Schema, input: &str, inputs: QueryInputs) -> Error { + let parsed = parse_find_string(input).expect("query input to have parsed"); + algebrize_with_inputs(schema, parsed, 0, inputs).expect_err("algebrize to have failed") +} + fn alg(schema: &Schema, input: &str) -> ConjoiningClauses { let parsed = parse_find_string(input).expect("query input to have parsed"); algebrize(schema.into(), parsed).expect("algebrizing to have succeeded").cc @@ -313,3 +322,26 @@ fn test_ground_nonexistent_variable_invalid() { }, } } + +#[test] +fn test_unbound_input_variable_invalid() { + let schema = prepopulated_schema(); + let q = r#"[:find ?y ?age :in ?x :where [(ground [?x]) [?y ...]] [?y :foo/age ?age]]"#; + + // This fails even if we know the type: we don't support grounding bindings + // that aren't known at algebrizing time. + let mut types = BTreeMap::default(); + types.insert(Variable::from_valid_name("?x"), ValueType::Ref); + + let i = QueryInputs::new(types, BTreeMap::default()).expect("valid QueryInputs"); + + let e = bails_with_inputs(&schema, &q, i); + match e { + Error(ErrorKind::UnboundVariable(v), _) => { + assert_eq!(v.0, "?x"); + }, + _ => { + panic!(); + }, + } +}