promql_parser/util/
visitor.rsuse crate::parser::{
AggregateExpr, BinaryExpr, Expr, Extension, ParenExpr, SubqueryExpr, UnaryExpr,
};
pub trait ExprVisitor {
type Error;
fn pre_visit(&mut self, plan: &Expr) -> Result<bool, Self::Error>;
fn post_visit(&mut self, _plan: &Expr) -> Result<bool, Self::Error> {
Ok(true)
}
}
pub fn walk_expr<V: ExprVisitor>(visitor: &mut V, expr: &Expr) -> Result<bool, V::Error> {
if !visitor.pre_visit(expr)? {
return Ok(false);
}
let recurse = match expr {
Expr::Aggregate(AggregateExpr { expr, .. }) => walk_expr(visitor, expr)?,
Expr::Unary(UnaryExpr { expr }) => walk_expr(visitor, expr)?,
Expr::Binary(BinaryExpr { lhs, rhs, .. }) => {
walk_expr(visitor, lhs)? && walk_expr(visitor, rhs)?
}
Expr::Paren(ParenExpr { expr }) => walk_expr(visitor, expr)?,
Expr::Subquery(SubqueryExpr { expr, .. }) => walk_expr(visitor, expr)?,
Expr::Extension(Extension { expr }) => {
for child in expr.children() {
if !walk_expr(visitor, child)? {
return Ok(false);
}
}
true
}
Expr::Call(call) => {
for func_argument_expr in &call.args.args {
if !walk_expr(visitor, func_argument_expr)? {
return Ok(false);
}
}
true
}
Expr::NumberLiteral(_)
| Expr::StringLiteral(_)
| Expr::VectorSelector(_)
| Expr::MatrixSelector(_) => true,
};
if !recurse {
return Ok(false);
}
if !visitor.post_visit(expr)? {
return Ok(false);
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::label::MatchOp;
use crate::parser;
use crate::parser::VectorSelector;
struct NamespaceVisitor {
namespace: String,
}
fn vector_selector_includes_namespace(
namespace: &str,
vector_selector: &VectorSelector,
) -> bool {
let mut includes_namespace = false;
for filters in &vector_selector.matchers.matchers {
if filters.name.eq("namespace")
&& filters.value.eq(namespace)
&& filters.op == MatchOp::Equal
{
includes_namespace = true;
break;
}
}
includes_namespace
}
impl ExprVisitor for NamespaceVisitor {
type Error = &'static str;
fn pre_visit(&mut self, expr: &Expr) -> Result<bool, Self::Error> {
match expr {
Expr::VectorSelector(vector_selector) => {
let included = vector_selector_includes_namespace(
self.namespace.as_str(),
vector_selector,
);
return Ok(included);
}
Expr::MatrixSelector(matrix_selector) => {
let included = vector_selector_includes_namespace(
self.namespace.as_str(),
&matrix_selector.vs,
);
return Ok(included);
}
Expr::NumberLiteral(_) | Expr::StringLiteral(_) => return Ok(false),
_ => (),
}
Ok(true)
}
}
#[test]
fn test_check_for_namespace_basic_query() {
let expr = "pg_stat_activity_count{namespace=\"sample\"}";
let ast = parser::parse(expr).unwrap();
let mut visitor = NamespaceVisitor {
namespace: "sample".to_string(),
};
assert!(walk_expr(&mut visitor, &ast).unwrap());
}
#[test]
fn test_check_for_namespace_label_present() {
let expr = "(sum by (namespace) (max_over_time(pg_stat_activity_count{namespace=\"sample\"}[1h])))";
let ast = parser::parse(expr).unwrap();
let mut visitor = NamespaceVisitor {
namespace: "sample".to_string(),
};
assert!(walk_expr(&mut visitor, &ast).unwrap());
}
#[test]
fn test_check_for_namespace_label_wrong_namespace() {
let expr = "(sum by (namespace) (max_over_time(pg_stat_activity_count{namespace=\"sample\"}[1h])))";
let ast = parser::parse(expr).unwrap();
let mut visitor = NamespaceVisitor {
namespace: "foobar".to_string(),
};
assert!(!walk_expr(&mut visitor, &ast).unwrap());
}
#[test]
fn test_check_for_namespace_label_missing_namespace() {
let expr = "(sum by (namespace) (max_over_time(pg_stat_activity_count{}[1h])))";
let ast = parser::parse(expr).unwrap();
let mut visitor = NamespaceVisitor {
namespace: "sample".to_string(),
};
assert!(!walk_expr(&mut visitor, &ast).unwrap());
}
#[test]
fn test_literal_expr() {
let mut visitor = NamespaceVisitor {
namespace: "sample".to_string(),
};
let ast = parser::parse("1").unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse("1 + 1").unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse(r#""1""#).unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
}
#[test]
fn test_binary_expr() {
let mut visitor = NamespaceVisitor {
namespace: "sample".to_string(),
};
let ast = parser::parse(
"pg_stat_activity_count{namespace=\"sample\"} + pg_stat_activity_count{}",
)
.unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse(
"pg_stat_activity_count{} - pg_stat_activity_count{namespace=\"sample\"}",
)
.unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse("pg_stat_activity_count{} * pg_stat_activity_count{}").unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse("pg_stat_activity_count{namespace=\"sample\"} / 1").unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse("1 % pg_stat_activity_count{namespace=\"sample\"}").unwrap();
assert!(!walk_expr(&mut visitor, &ast).unwrap());
let ast = parser::parse("pg_stat_activity_count{namespace=\"sample\"} ^ pg_stat_activity_count{namespace=\"sample\"}").unwrap();
assert!(walk_expr(&mut visitor, &ast).unwrap());
}
}