Repository navigation
Expand file tree
/
Copy pathoverrides.rs
More file actions
161 lines (148 loc) · 6.02 KB
/
Copy pathoverrides.rs
File metadata and controls
161 lines (148 loc) · 6.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Which query keywords may override the database's settings (#862, #869).
//!
//! The embedder lists them in [`OpenOptions::allow_overrides`]; a query that
//! uses a keyword the list leaves out is refused with `API-025` before it runs.
//!
//! [`OpenOptions::allow_overrides`]: crate::OpenOptions::allow_overrides
use anyhow::Result;
use crate::error::{ErrorCode, bail_coded};
use crate::query::datalog::types::DatalogQuery;
/// A setting that query text may override (#862).
///
/// `#[non_exhaustive]`: later releases add variants for new query keywords.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Overridable {
/// `:max-derived-facts N`, which overrides
/// [`OpenOptions::max_derived_facts`](crate::OpenOptions::max_derived_facts).
MaxDerivedFacts,
/// `:max-results N`, the deprecated alias of `:max-derived-facts`.
MaxResults,
/// `:query-memory-limit N`, which overrides
/// [`OpenOptions::query_memory_limit`](crate::OpenOptions::query_memory_limit)
/// (#870). Not in [`DEFAULTS`](Self::DEFAULTS): query text can raise the
/// limit that keeps it from exhausting memory.
QueryMemoryLimit,
/// `:max-iterations N`, which overrides
/// [`OpenOptions::max_iterations`](crate::OpenOptions::max_iterations)
/// (#861).
MaxIterations,
}
impl Overridable {
/// The list a database uses when
/// [`OpenOptions::allow_overrides`](crate::OpenOptions::allow_overrides)
/// is not set: every keyword that could override a setting before #862,
/// and `:max-iterations` (#861).
pub const DEFAULTS: &'static [Overridable] = &[
Overridable::MaxDerivedFacts,
Overridable::MaxResults,
Overridable::MaxIterations,
];
/// The query keyword this variant gates, e.g. `":max-derived-facts"`.
pub fn keyword(self) -> &'static str {
match self {
Overridable::MaxDerivedFacts => ":max-derived-facts",
Overridable::MaxResults => ":max-results",
Overridable::QueryMemoryLimit => &quo
9E9D
t;:query-memory-limit",
Overridable::MaxIterations => ":max-iterations",
}
}
fn name(self) -> &'static str {
match self {
Overridable::MaxDerivedFacts => "MaxDerivedFacts",
Overridable::MaxResults => "MaxResults",
Overridable::QueryMemoryLimit => "QueryMemoryLimit",
Overridable::MaxIterations => "MaxIterations",
}
}
}
/// Refuse a query that uses a keyword `allowed` does not list (`API-025`).
///
/// Checks the keywords in a fixed order and reports the first one refused.
/// Only the top-level query takes keywords; rule bodies take none.
pub(crate) fn check_overrides(query: &DatalogQuery, allowed: &[Overridable]) -> Result<()> {
let present = [
(
Overridable::MaxDerivedFacts,
query.max_derived_facts.is_some(),
),
(Overridable::MaxResults, query.max_results.is_some()),
(Overridable::MaxIterations, query.max_iterations.is_some()),
(
Overridable::QueryMemoryLimit,
query.query_memory_limit.is_some(),
),
];
for (setting, used) in present {
if used && !allowed.contains(&setting) {
bail_coded!(ErrorCode::Api025, setting.keyword(), setting.name());
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query::datalog::parser::parse_datalog_command;
use crate::query::datalog::types::DatalogCommand;
fn query(text: &str) -> DatalogQuery {
match parse_datalog_command(text).expect("parse") {
DatalogCommand::Query(q) => q,
_ => panic!("expected a query"),
}
}
fn code(r: Result<()>) -> String {
crate::MinigrafError::from(r.expect_err("refused"))
.code()
.to_string()
}
#[test]
fn reports_max_derived_facts_first() {
let q = query("(query [:find ?e :where [?e :a ?v] :max-derived-facts 5 :max-results 5])");
let err = crate::MinigrafError::from(check_overrides(&q, &[]).expect_err("refused"));
assert_eq!(err.code(), "API-025");
assert!(err.to_string().contains(":max-derived-facts"));
assert!(err.to_string().contains("Overridable::MaxDerivedFacts"));
assert!(!err.to_string().contains(":max-results"));
}
#[test]
fn checks_in_a_fixed_order() {
let q = query(
"(query [:find ?e :where [?e :a ?v] :query-memory-limit 9 \
:max-iterations 3 :max-results 5 :max-derived-facts 5])",
);
let first = |allowed: &[Overridable]| {
crate::MinigrafError::from(check_overrides(&q, allowed).expect_err("refused"))
.to_string()
};
assert!(first(&[]).contains(":max-derived-facts"));
assert!(first(&[Overridable::MaxDerivedFacts]).contains(":max-results"));
assert!(
first(&[Overridable::MaxDerivedFacts, Overridable::MaxResults])
.contains(":max-iterations")
);
let last = first(Overridable::DEFAULTS);
assert!(last.contains(":query-memory-limit"));
assert!(last.contains("Overridable::QueryMemoryLimit"));
let all: Vec<_> = Overridable::DEFAULTS
.iter()
.copied()
.chain([Overridable::QueryMemoryLimit])
.collect();
check_overrides(&q, &all).expect("all listed");
assert!(!Overridable::DEFAULTS.contains(&Overridable::QueryMemoryLimit));
}
#[test]
fn listed_and_absent_keywords_pass() {
let both =
query("(query [:find ?e :where [?e :a ?v] :max-derived-facts 5 :max-results 5])");
check_overrides(&both, Overridable::DEFAULTS).expect("defaults allow both");
let plain = query("(query [:find ?e :where [?e :a ?v]])");
check_overrides(&plain, &[]).expect("no keyword");
let results = query("(query [:find ?e :where [?e :a ?v] :max-results 5])");
assert_eq!(
code(check_overrides(&results, &[Overridable::MaxDerivedFacts])),
"API-025"
);
}
}