1use rusqlite;
12use rusqlite::params_from_iter;
13use rusqlite::types::ToSql;
14
15use std::rc::Rc;
16
17use core_traits::{Binding, Entid, KnownEntid, TypedValue};
18
19use mentat_core::{HasSchema, Schema};
20
21use mentat_query_algebrizer::{
22 algebrize_with_inputs, algebrize_with_inputs_and_temporal, parse_find_string, AlgebraicQuery,
23 EmptyBecause, FindQuery, TemporalBound,
24};
25
26pub use mentat_query_algebrizer::QueryInputs;
27
28pub use edn::query::{Keyword, PlainSymbol, Variable};
29
30use edn::query::{
31 Element, FindSpec, Pattern, PatternNonValuePlace, PatternValuePlace, WhereClause,
32};
33
34use mentat_query_projector::{ConstantProjector, Projector};
35
36use mentat_query_projector::translate::{query_to_select, ProjectedSelect};
37
38use mentat_sql::{SQLQuery, SqlConn, SqlValue};
39
40use mentat_query_projector::{RusqliteRows, VecRows};
41
42pub use mentat_query_algebrizer::Known;
43
44pub use mentat_query_projector::{
45 QueryOutput, QueryResults, RelResult,
48};
49
50use public_traits::errors::{MentatError, Result};
51
52pub type QueryExecutionResult = Result<QueryOutput>;
53pub type PreparedResult<'sqlite> = Result<PreparedQuery<'sqlite>>;
54
55pub enum PreparedQuery<'sqlite> {
56 Empty {
57 find_spec: Rc<FindSpec>,
58 },
59 Constant {
60 select: ConstantProjector,
61 },
62 Bound {
63 statement: rusqlite::Statement<'sqlite>,
64 schema: Schema,
65 connection: &'sqlite rusqlite::Connection,
66 args: Vec<(String, Rc<rusqlite::types::Value>)>,
67 projector: Box<dyn Projector>,
68 },
69}
70
71impl<'sqlite> PreparedQuery<'sqlite> {
72 pub fn run<T>(&mut self, _inputs: T) -> QueryExecutionResult
73 where
74 T: Into<Option<QueryInputs>>,
75 {
76 match self {
77 PreparedQuery::Empty { ref find_spec } => Ok(QueryOutput::empty(find_spec)),
78 PreparedQuery::Constant { ref select } => {
79 select.project_without_rows().map_err(|e| e.into())
80 }
81 PreparedQuery::Bound {
82 ref mut statement,
83 ref schema,
84 connection,
85 ref args,
86 ref projector,
87 } => {
88 let mut rows = RusqliteRows::new(run_statement(statement, args)?);
89 projector
90 .project(schema, &mentat_sql::sqlite(connection), &mut rows)
91 .map_err(|e| e.into())
92 }
93 }
94 }
95}
96
97pub trait IntoResult {
98 fn into_scalar_result(self) -> Result<Option<Binding>>;
99 fn into_coll_result(self) -> Result<Vec<Binding>>;
100 fn into_tuple_result(self) -> Result<Option<Vec<Binding>>>;
101 fn into_rel_result(self) -> Result<RelResult<Binding>>;
102}
103
104impl IntoResult for QueryExecutionResult {
105 fn into_scalar_result(self) -> Result<Option<Binding>> {
106 self?.into_scalar().map_err(|e| e.into())
107 }
108
109 fn into_coll_result(self) -> Result<Vec<Binding>> {
110 self?.into_coll().map_err(|e| e.into())
111 }
112
113 fn into_tuple_result(self) -> Result<Option<Vec<Binding>>> {
114 self?.into_tuple().map_err(|e| e.into())
115 }
116
117 fn into_rel_result(self) -> Result<RelResult<Binding>> {
118 self?.into_rel().map_err(|e| e.into())
119 }
120}
121
122pub enum QueryExplanation {
124 KnownEmpty(EmptyBecause),
126
127 KnownConstant,
129
130 ExecutionPlan {
132 query: SQLQuery,
134 steps: Vec<QueryPlanStep>,
136 },
137}
138
139pub struct QueryPlanStep {
142 pub select_id: i32,
143 pub order: i32,
144 pub from: i32,
145 pub detail: String,
146}
147
148fn algebrize_query<T>(known: Known, query: FindQuery, inputs: T) -> Result<AlgebraicQuery>
149where
150 T: Into<Option<QueryInputs>>,
151{
152 let algebrized = algebrize_with_inputs(known, query, 0, inputs.into().unwrap_or_default())?;
153 let unbound = algebrized.unbound_variables();
154 if !unbound.is_empty() {
158 bail!(MentatError::UnboundVariables(
159 unbound.into_iter().map(|v| v.to_string()).collect()
160 ));
161 }
162 Ok(algebrized)
163}
164
165fn fetch_values(
166 sqlite: &rusqlite::Connection,
167 known: Known,
168 entity: Entid,
169 attribute: Entid,
170 only_one: bool,
171) -> QueryExecutionResult {
172 let v = Variable::from_valid_name("?v");
173
174 let pattern = Pattern::simple(
178 PatternNonValuePlace::Entid(entity),
179 PatternNonValuePlace::Entid(attribute),
180 PatternValuePlace::Variable(v.clone()),
181 )
182 .unwrap();
183
184 let element = Element::Variable(v);
185 let spec = if only_one {
186 FindSpec::FindScalar(element)
187 } else {
188 FindSpec::FindColl(element)
189 };
190 let query = FindQuery::simple(spec, vec![WhereClause::Pattern(pattern)]);
191
192 let algebrized = algebrize_query(known, query, None)?;
193
194 run_algebrized_query(known, sqlite, algebrized)
195}
196
197fn lookup_attribute(schema: &Schema, attribute: &Keyword) -> Result<KnownEntid> {
198 schema
199 .get_entid(attribute)
200 .ok_or_else(|| MentatError::UnknownAttribute(attribute.name().into()))
201}
202
203#[allow(clippy::extra_unused_lifetimes)]
208pub fn lookup_value<'schema, 'cache, E, A>(
209 sqlite: &rusqlite::Connection,
210 known: Known,
211 entity: E,
212 attribute: A,
213) -> Result<Option<TypedValue>>
214where
215 E: Into<Entid>,
216 A: Into<Entid>,
217{
218 let entid = entity.into();
219 let attrid = attribute.into();
220
221 if known.is_attribute_cached_forward(attrid) {
222 Ok(known
223 .get_value_for_entid(known.schema, attrid, entid)
224 .cloned())
225 } else {
226 fetch_values(sqlite, known, entid, attrid, true)
227 .into_scalar_result()
228 .map(|r| r.map(|v| v.into_scalar().unwrap()))
230 }
231}
232
233pub fn lookup_values<E, A>(
234 sqlite: &rusqlite::Connection,
235 known: Known,
236 entity: E,
237 attribute: A,
238) -> Result<Vec<TypedValue>>
239where
240 E: Into<Entid>,
241 A: Into<Entid>,
242{
243 let entid = entity.into();
244 let attrid = attribute.into();
245
246 if known.is_attribute_cached_forward(attrid) {
247 Ok(known
248 .get_values_for_entid(known.schema, attrid, entid)
249 .cloned()
250 .unwrap_or_else(Vec::new))
251 } else {
252 fetch_values(sqlite, known, entid, attrid, false)
253 .into_coll_result()
254 .map(|v| v.into_iter().map(|x| x.into_scalar().unwrap()).collect())
256 }
257}
258
259pub fn lookup_value_for_attribute<E>(
264 sqlite: &rusqlite::Connection,
265 known: Known,
266 entity: E,
267 attribute: &Keyword,
268) -> Result<Option<TypedValue>>
269where
270 E: Into<Entid>,
271{
272 let attribute = lookup_attribute(known.schema, attribute)?;
273 lookup_value(sqlite, known, entity.into(), attribute)
274}
275
276pub fn lookup_values_for_attribute<E>(
277 sqlite: &rusqlite::Connection,
278 known: Known,
279 entity: E,
280 attribute: &Keyword,
281) -> Result<Vec<TypedValue>>
282where
283 E: Into<Entid>,
284{
285 let attribute = lookup_attribute(known.schema, attribute)?;
286 lookup_values(sqlite, known, entity.into(), attribute)
287}
288
289fn run_statement<'sqlite, 'stmt, 'bound>(
290 statement: &'stmt mut rusqlite::Statement<'sqlite>,
291 bindings: &'bound [(String, Rc<rusqlite::types::Value>)],
292) -> Result<rusqlite::Rows<'stmt>> {
293 let rows = if bindings.is_empty() {
294 statement.query(rusqlite::params![])?
295 } else {
296 let params: Vec<&dyn ToSql> = bindings
297 .iter()
298 .map(|(_, v)| v.as_ref() as &dyn ToSql)
299 .collect();
300 statement.query(params_from_iter(params.iter()))?
301 };
302 Ok(rows)
303}
304
305fn run_sql_query<T, F>(
306 sqlite: &rusqlite::Connection,
307 sql: &str,
308 bindings: &[(String, Rc<rusqlite::types::Value>)],
309 mut mapper: F,
310) -> Result<Vec<T>>
311where
312 F: FnMut(&rusqlite::Row) -> rusqlite::Result<T>,
313{
314 let mut statement = sqlite.prepare(sql)?;
315 let mut rows = run_statement(&mut statement, bindings)?;
316 let mut result = vec![];
317 while let Some(row) = rows.next()? {
318 result.push(mapper(row)?);
319 }
320 Ok(result)
321}
322
323fn algebrize_query_str<T>(known: Known, query: &str, inputs: T) -> Result<AlgebraicQuery>
324where
325 T: Into<Option<QueryInputs>>,
326{
327 let parsed = parse_find_string(query)?;
328 algebrize_query(known, parsed, inputs)
329}
330
331fn run_algebrized_query(
332 known: Known,
333 sqlite: &rusqlite::Connection,
334 algebrized: AlgebraicQuery,
335) -> QueryExecutionResult {
336 assert!(
337 algebrized.unbound_variables().is_empty(),
338 "Unbound variables should be checked by now"
339 );
340 if algebrized.is_known_empty() {
341 return Ok(QueryOutput::empty(&algebrized.find_spec));
343 }
344
345 let select = query_to_select(known.schema, algebrized)?;
346 match select {
347 ProjectedSelect::Constant(constant) => {
348 constant.project_without_rows().map_err(|e| e.into())
349 }
350 ProjectedSelect::Query { query, projector } => {
351 let SQLQuery { sql, args } = query.to_sql_query()?;
352
353 let mut statement = sqlite.prepare(sql.as_str())?;
354 let mut rows = RusqliteRows::new(run_statement(&mut statement, &args)?);
355
356 projector
357 .project(known.schema, &mentat_sql::sqlite(sqlite), &mut rows)
358 .map_err(|e| e.into())
359 }
360 }
361}
362
363pub fn run_algebrized_query_on(
367 known: Known,
368 conn: &dyn SqlConn,
369 algebrized: AlgebraicQuery,
370) -> QueryExecutionResult {
371 if !algebrized.unbound_variables().is_empty() {
372 bail!(MentatError::UnboundVariables(
373 algebrized
374 .unbound_variables()
375 .into_iter()
376 .map(|v| v.to_string())
377 .collect()
378 ));
379 }
380 if algebrized.is_known_empty() {
381 return Ok(QueryOutput::empty(&algebrized.find_spec));
382 }
383 match query_to_select(known.schema, algebrized)? {
384 ProjectedSelect::Constant(constant) => {
385 constant.project_without_rows().map_err(|e| e.into())
386 }
387 ProjectedSelect::Query { query, projector } => {
388 let SQLQuery { sql, args } = query.to_sql_query_for(conn.dialect())?;
389 let params: Vec<(&str, SqlValue)> = args
390 .iter()
391 .map(|(name, v)| (name.as_str(), mentat_sql::from_rusqlite(v.as_ref().clone())))
392 .collect();
393 let mut rows: Vec<Vec<SqlValue>> = Vec::new();
394 conn.query_named(&sql, ¶ms, &mut |row| {
395 let n = row.column_count();
396 let mut vals = Vec::with_capacity(n);
397 for i in 0..n {
398 vals.push(row.get_value(i)?);
399 }
400 rows.push(vals);
401 Ok(())
402 })?;
403 projector
404 .project(known.schema, conn, &mut VecRows::new(rows))
405 .map_err(|e| e.into())
406 }
407 }
408}
409
410pub fn q_once_on<T>(
413 conn: &dyn SqlConn,
414 known: Known,
415 query: &str,
416 inputs: T,
417 temporal: Option<TemporalBound>,
418) -> QueryExecutionResult
419where
420 T: Into<Option<QueryInputs>>,
421{
422 let parsed = parse_find_string(query)?;
423 let algebrized = algebrize_with_inputs_and_temporal(
424 known,
425 parsed,
426 0,
427 inputs.into().unwrap_or_default(),
428 temporal,
429 )?;
430 run_algebrized_query_on(known, conn, algebrized)
431}
432
433pub fn q_once<T>(
441 sqlite: &rusqlite::Connection,
442 known: Known,
443 query: &str,
444 inputs: T,
445) -> QueryExecutionResult
446where
447 T: Into<Option<QueryInputs>>,
448{
449 let algebrized = algebrize_query_str(known, query, inputs)?;
450 run_algebrized_query(known, sqlite, algebrized)
451}
452
453pub fn q_once_value_filtered<T>(
456 sqlite: &rusqlite::Connection,
457 known: Known,
458 query: &str,
459 inputs: T,
460) -> Result<(QueryOutput, std::collections::BTreeSet<Entid>)>
461where
462 T: Into<Option<QueryInputs>>,
463{
464 let mut algebrized = algebrize_query_str(known, query, inputs)?;
465 let attrs = std::mem::take(&mut algebrized.cc.value_filtered_attributes);
466 Ok((run_algebrized_query(known, sqlite, algebrized)?, attrs))
467}
468
469pub fn q_once_temporal<T>(
473 sqlite: &rusqlite::Connection,
474 known: Known,
475 query: &str,
476 inputs: T,
477 temporal: TemporalBound,
478) -> QueryExecutionResult
479where
480 T: Into<Option<QueryInputs>>,
481{
482 let parsed = parse_find_string(query)?;
483 let algebrized = algebrize_with_inputs_and_temporal(
484 known,
485 parsed,
486 0,
487 inputs.into().unwrap_or_default(),
488 Some(temporal),
489 )?;
490 let unbound = algebrized.unbound_variables();
491 if !unbound.is_empty() {
492 bail!(MentatError::UnboundVariables(
493 unbound.into_iter().map(|v| v.to_string()).collect()
494 ));
495 }
496 run_algebrized_query(known, sqlite, algebrized)
497}
498
499pub fn q_uncached<T>(
501 sqlite: &rusqlite::Connection,
502 schema: &Schema,
503 query: &str,
504 inputs: T,
505) -> QueryExecutionResult
506where
507 T: Into<Option<QueryInputs>>,
508{
509 let known = Known::for_schema(schema);
510 let algebrized = algebrize_query_str(known, query, inputs)?;
511
512 run_algebrized_query(known, sqlite, algebrized)
513}
514
515pub fn q_prepare<'sqlite, 'schema, 'cache, 'query, T>(
516 sqlite: &'sqlite rusqlite::Connection,
517 known: Known<'schema, 'cache>,
518 query: &'query str,
519 inputs: T,
520) -> PreparedResult<'sqlite>
521where
522 T: Into<Option<QueryInputs>>,
523{
524 let algebrized = algebrize_query_str(known, query, inputs)?;
525
526 let unbound = algebrized.unbound_variables();
527 if !unbound.is_empty() {
528 bail!(MentatError::UnboundVariables(
531 unbound.into_iter().map(|v| v.to_string()).collect()
532 ));
533 }
534
535 if algebrized.is_known_empty() {
536 return Ok(PreparedQuery::Empty {
538 find_spec: algebrized.find_spec,
539 });
540 }
541
542 let select = query_to_select(known.schema, algebrized)?;
543 match select {
544 ProjectedSelect::Constant(constant) => Ok(PreparedQuery::Constant { select: constant }),
545 ProjectedSelect::Query { query, projector } => {
546 let SQLQuery { sql, args } = query.to_sql_query()?;
547 let statement = sqlite.prepare(sql.as_str())?;
548
549 Ok(PreparedQuery::Bound {
550 statement,
551 schema: known.schema.clone(),
552 connection: sqlite,
553 args,
554 projector,
555 })
556 }
557 }
558}
559
560pub fn q_explain<T>(
561 sqlite: &rusqlite::Connection,
562 known: Known,
563 query: &str,
564 inputs: T,
565) -> Result<QueryExplanation>
566where
567 T: Into<Option<QueryInputs>>,
568{
569 q_explain_temporal(sqlite, known, query, inputs, None)
570}
571
572pub fn q_explain_temporal<T>(
574 sqlite: &rusqlite::Connection,
575 known: Known,
576 query: &str,
577 inputs: T,
578 temporal: Option<TemporalBound>,
579) -> Result<QueryExplanation>
580where
581 T: Into<Option<QueryInputs>>,
582{
583 let parsed = parse_find_string(query)?;
584 let algebrized = algebrize_with_inputs_and_temporal(
585 known,
586 parsed,
587 0,
588 inputs.into().unwrap_or_default(),
589 temporal,
590 )?;
591 if algebrized.is_known_empty() {
592 return Ok(QueryExplanation::KnownEmpty(
593 algebrized.cc.empty_because.unwrap(),
594 ));
595 }
596 match query_to_select(known.schema, algebrized)? {
597 ProjectedSelect::Constant(_constant) => Ok(QueryExplanation::KnownConstant),
598 ProjectedSelect::Query {
599 query,
600 projector: _projector,
601 } => {
602 let query = query.to_sql_query()?;
603
604 let plan_sql = format!("EXPLAIN QUERY PLAN {}", query.sql);
605
606 let steps = run_sql_query(sqlite, &plan_sql, &query.args, |row| {
607 Ok(QueryPlanStep {
608 select_id: row.get(0)?,
609 order: row.get(1)?,
610 from: row.get(2)?,
611 detail: row.get(3)?,
612 })
613 })?;
614
615 Ok(QueryExplanation::ExecutionPlan { query, steps })
616 }
617 }
618}