///|
fn validate_expression_depth(
root : Expr,
maximum : Int32,
) -> Unit raise QueryError {
let stack = [(root, 1)]
while stack.pop() is Some((expression, depth)) {
if depth > maximum {
raise Resource("expression tree depth")
}
match expression {
Unary(_, e) => stack.push((e, depth + 1))
Binary(_, a, b) => {
stack.push((a, depth + 1))
stack.push((b, depth + 1))
}
Call(_, args) =>
for e in args {
stack.push((e, depth + 1))
}
Aggregate(_, e, _, _) => if e is Some(e) { stack.push((e, depth + 1)) }
Exists(g, _) => validate_group_depth(g, maximum - depth)
_ => ()
}
}
}
///|
fn validate_path_depth(root : Path, maximum : Int32) -> Unit raise QueryError {
let stack = [(root, 1)]
while stack.pop() is Some((path, depth)) {
if depth > maximum {
raise Resource("property path tree depth")
}
match path {
Inverse(p) | ZeroMore(p) | OneMore(p) | ZeroOne(p) =>
stack.push((p, depth + 1))
Sequence(a, b) | Alternative(a, b) => {
stack.push((a, depth + 1))
stack.push((b, depth + 1))
}
_ => ()
}
}
}
///|
fn validate_group_depth(
group : Group,
maximum : Int32,
) -> Unit raise QueryError {
if maximum <= 0 {
raise Resource("graph pattern depth")
}
for pattern in group.patterns {
match pattern {
Triple(_, path, _) => validate_path_depth(path, maximum - 1)
Filter(e) | Bind(e, _) => validate_expression_depth(e, maximum - 1)
Nested(g) | Optional(g) | Graph(_, g) | Minus(g) =>
validate_group_depth(g, maximum - 1)
Subselect(q) => validate_depth(q, maximum - 1)
Union(groups) =>
for g in groups {
validate_group_depth(g, maximum - 1)
}
_ => ()
}
}
}
///|
fn validate_depth(q : Query, maximum : Int32) -> Unit raise QueryError {
validate_group_depth(q.group, maximum)
for p in q.projections {
validate_expression_depth(p.expression, maximum)
}
for (e, _) in q.group_by {
validate_expression_depth(e, maximum)
}
for e in q.having {
validate_expression_depth(e, maximum)
}
for (e, _) in q.order {
validate_expression_depth(e, maximum)
}
}