Implement basic query limits. (#361) r=nalexander
This commit is contained in:
parent
85f3b79f75
commit
e898df8842
7 changed files with 73 additions and 18 deletions
|
@ -29,10 +29,26 @@ pub struct AlgebraicQuery {
|
|||
default_source: SrcVar,
|
||||
pub find_spec: FindSpec,
|
||||
has_aggregates: bool,
|
||||
pub limit: Option<i64>,
|
||||
pub limit: Option<u64>,
|
||||
pub cc: cc::ConjoiningClauses,
|
||||
}
|
||||
|
||||
impl AlgebraicQuery {
|
||||
pub fn apply_limit(&mut self, limit: Option<u64>) {
|
||||
match self.limit {
|
||||
None => self.limit = limit,
|
||||
Some(existing) =>
|
||||
match limit {
|
||||
None => (),
|
||||
Some(new) =>
|
||||
if new < existing {
|
||||
self.limit = limit;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn algebrize(schema: &Schema, parsed: FindQuery) -> AlgebraicQuery {
|
||||
// TODO: integrate default source into pattern processing.
|
||||
|
|
|
@ -108,6 +108,7 @@ pub struct SelectQuery {
|
|||
pub projection: Projection,
|
||||
pub from: FromClause,
|
||||
pub constraints: Vec<Constraint>,
|
||||
pub limit: Option<u64>,
|
||||
}
|
||||
|
||||
// We know that DatomsColumns are safe to serialize.
|
||||
|
@ -264,6 +265,12 @@ impl QueryFragment for SelectQuery {
|
|||
constraint.push_sql(out)?;
|
||||
}
|
||||
|
||||
// Guaranteed to be positive: u64.
|
||||
if let Some(limit) = self.limit {
|
||||
out.push_sql(" LIMIT ");
|
||||
out.push_sql(limit.to_string().as_str());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -316,6 +323,7 @@ mod tests {
|
|||
right: ColumnOrExpression::Entid(65536),
|
||||
},
|
||||
],
|
||||
limit: None,
|
||||
};
|
||||
|
||||
let SQLQuery { sql, args } = query.to_sql_query().unwrap();
|
||||
|
|
|
@ -77,7 +77,7 @@ pub struct CombinedSelectQuery {
|
|||
pub projector: Box<Projector>,
|
||||
}
|
||||
|
||||
fn cc_to_select_query(projection: Projection, cc: ConjoiningClauses) -> SelectQuery {
|
||||
fn cc_to_select_query<T: Into<Option<u64>>>(projection: Projection, cc: ConjoiningClauses, limit: T) -> SelectQuery {
|
||||
SelectQuery {
|
||||
projection: projection,
|
||||
from: FromClause::TableList(TableList(cc.from)),
|
||||
|
@ -85,23 +85,24 @@ fn cc_to_select_query(projection: Projection, cc: ConjoiningClauses) -> SelectQu
|
|||
.into_iter()
|
||||
.map(|c| c.to_constraint())
|
||||
.collect(),
|
||||
limit: limit.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume a provided `ConjoiningClauses` to yield a new
|
||||
/// `SelectQuery`. A projection list must also be provided.
|
||||
pub fn cc_to_select(projection: CombinedProjection, cc: ConjoiningClauses) -> CombinedSelectQuery {
|
||||
pub fn cc_to_select(projection: CombinedProjection, cc: ConjoiningClauses, limit: Option<u64>) -> CombinedSelectQuery {
|
||||
let CombinedProjection { sql_projection, datalog_projector } = projection;
|
||||
CombinedSelectQuery {
|
||||
query: cc_to_select_query(sql_projection, cc),
|
||||
query: cc_to_select_query(sql_projection, cc, limit),
|
||||
projector: datalog_projector,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_to_select(query: AlgebraicQuery) -> CombinedSelectQuery {
|
||||
cc_to_select(query_projection(&query), query.cc)
|
||||
cc_to_select(query_projection(&query), query.cc, query.limit)
|
||||
}
|
||||
|
||||
pub fn cc_to_exists(cc: ConjoiningClauses) -> SelectQuery {
|
||||
cc_to_select_query(Projection::One, cc)
|
||||
cc_to_select_query(Projection::One, cc, 1)
|
||||
}
|
||||
|
|
|
@ -76,3 +76,22 @@ fn test_rel() {
|
|||
assert_eq!(sql, "SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0");
|
||||
assert_eq!(args, vec![("$v0".to_string(), "yyy".to_string())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_limit() {
|
||||
let mut schema = Schema::default();
|
||||
associate_ident(&mut schema, NamespacedKeyword::new("foo", "bar"), 99);
|
||||
add_attribute(&mut schema, 99, Attribute {
|
||||
value_type: ValueType::String,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let input = r#"[:find ?x :where [?x :foo/bar "yyy"]]"#;
|
||||
let parsed = parse_find_string(input).expect("parse failed");
|
||||
let mut algebrized = algebrize(&schema, parsed);
|
||||
algebrized.limit = Some(5);
|
||||
let select = query_to_select(algebrized);
|
||||
let SQLQuery { sql, args } = select.query.to_sql_query().unwrap();
|
||||
assert_eq!(sql, "SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 5");
|
||||
assert_eq!(args, vec![("$v0".to_string(), "yyy".to_string())]);
|
||||
}
|
||||
|
|
12
src/conn.rs
12
src/conn.rs
|
@ -107,16 +107,20 @@ impl Conn {
|
|||
}
|
||||
|
||||
/// Query the Mentat store, using the given connection and the current metadata.
|
||||
pub fn q_once<T>(&self,
|
||||
pub fn q_once<T, U>(&self,
|
||||
sqlite: &rusqlite::Connection,
|
||||
query: &str,
|
||||
inputs: T) -> Result<QueryResults>
|
||||
where T: Into<Option<HashMap<String, TypedValue>>> {
|
||||
inputs: T,
|
||||
limit: U) -> Result<QueryResults>
|
||||
where T: Into<Option<HashMap<String, TypedValue>>>,
|
||||
U: Into<Option<u64>>
|
||||
{
|
||||
|
||||
q_once(sqlite,
|
||||
&*self.current_schema(),
|
||||
query,
|
||||
inputs.into())
|
||||
inputs,
|
||||
limit)
|
||||
}
|
||||
|
||||
/// Transact entities against the Mentat store, using the given connection and the current
|
||||
|
|
13
src/query.rs
13
src/query.rs
|
@ -53,14 +53,21 @@ pub type QueryExecutionResult = Result<QueryResults>;
|
|||
/// The caller is responsible for ensuring that the SQLite connection is in a transaction if
|
||||
/// isolation is required.
|
||||
#[allow(unused_variables)]
|
||||
pub fn q_once<'sqlite, 'schema, 'query>
|
||||
pub fn q_once<'sqlite, 'schema, 'query, T, U>
|
||||
(sqlite: &'sqlite rusqlite::Connection,
|
||||
schema: &'schema Schema,
|
||||
query: &'query str,
|
||||
inputs: Option<HashMap<String, TypedValue>>) -> QueryExecutionResult {
|
||||
inputs: T,
|
||||
limit: U) -> QueryExecutionResult
|
||||
where T: Into<Option<HashMap<String, TypedValue>>>,
|
||||
U: Into<Option<u64>>
|
||||
{
|
||||
// TODO: validate inputs.
|
||||
|
||||
let parsed = parse_find_string(query)?;
|
||||
let algebrized = algebrize(schema, parsed);
|
||||
let mut algebrized = algebrize(schema, parsed);
|
||||
algebrized.apply_limit(limit.into());
|
||||
|
||||
let select = query_to_select(algebrized);
|
||||
let SQLQuery { sql, args } = select.query.to_sql_query()?;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ fn test_rel() {
|
|||
// Rel.
|
||||
let start = time::PreciseTime::now();
|
||||
let results = q_once(&c, &db.schema,
|
||||
"[:find ?x ?ident :where [?x :db/ident ?ident]]", None)
|
||||
"[:find ?x ?ident :where [?x :db/ident ?ident]]", None, None)
|
||||
.expect("Query failed");
|
||||
let end = time::PreciseTime::now();
|
||||
|
||||
|
@ -64,7 +64,7 @@ fn test_failing_scalar() {
|
|||
// Scalar that fails.
|
||||
let start = time::PreciseTime::now();
|
||||
let results = q_once(&c, &db.schema,
|
||||
"[:find ?x . :where [?x :db/fulltext true]]", None)
|
||||
"[:find ?x . :where [?x :db/fulltext true]]", None, None)
|
||||
.expect("Query failed");
|
||||
let end = time::PreciseTime::now();
|
||||
|
||||
|
@ -86,7 +86,7 @@ fn test_scalar() {
|
|||
// Scalar that succeeds.
|
||||
let start = time::PreciseTime::now();
|
||||
let results = q_once(&c, &db.schema,
|
||||
"[:find ?ident . :where [24 :db/ident ?ident]]", None)
|
||||
"[:find ?ident . :where [24 :db/ident ?ident]]", None, None)
|
||||
.expect("Query failed");
|
||||
let end = time::PreciseTime::now();
|
||||
|
||||
|
@ -116,7 +116,7 @@ fn test_tuple() {
|
|||
"[:find [?index ?cardinality]
|
||||
:where [:db/txInstant :db/index ?index]
|
||||
[:db/txInstant :db/cardinality ?cardinality]]",
|
||||
None)
|
||||
None, None)
|
||||
.expect("Query failed");
|
||||
let end = time::PreciseTime::now();
|
||||
|
||||
|
@ -143,7 +143,7 @@ fn test_coll() {
|
|||
// Coll.
|
||||
let start = time::PreciseTime::now();
|
||||
let results = q_once(&c, &db.schema,
|
||||
"[:find [?e ...] :where [?e :db/ident _]]", None)
|
||||
"[:find [?e ...] :where [?e :db/ident _]]", None, None)
|
||||
.expect("Query failed");
|
||||
let end = time::PreciseTime::now();
|
||||
|
||||
|
|
Loading…
Reference in a new issue