Module mz_sql::ast::visit

source ·
Expand description

Traversal of an immutable AST.

Each method of the Visit trait is a hook that can be overridden to customize the behavior when visiting the corresponding type of node. By default, every method recursively visits the substructure of the input by invoking the right visitor method of each of its fields.

pub trait Visit<'ast, T: AstInfo> {
    /* ... */

    fn visit_function(&mut self, node: &'ast Function<T>) {
        visit_function(self, node);
    }

    /* ... */
}

pub fn visit_function<'ast, V, T: AstInfo>(visitor: &mut V, node: &'ast Function<T>)
where
    V: Visit<'ast, T> + ?Sized,
{
    visitor.visit_item_name(&node.name);
    visitor.visit_function_args(&node.args);
    if let Some(filter) = &node.filter {
        visitor.visit_expr(&*filter);
    }
    if let Some(over) = &node.over {
        visitor.visit_window_spec(over);
    }
}

See also the visit_mut module for traversing mutable ASTs.

§Examples

This visitor will count the number of subqueries in a SQL statement.

use std::error::Error;

use mz_sql_parser::ast::{AstInfo, Query, Raw};
use mz_sql_parser::ast::visit::{self, Visit};

struct SubqueryCounter {
    count: usize,
}

impl<'ast> Visit<'ast, Raw> for SubqueryCounter {
    fn visit_query(&mut self, query: &'ast Query<Raw>) {
        self.count += 1;

        // Delegate to the default implementation to visit any nested
        // subqueries. Placing this call at the end of the method results
        // in a pre-order traversal. Place it at the beginning for a
        // post-order traversal instead.
        visit::visit_query(self, query);
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let sql = "SELECT (SELECT 1) FROM (SELECT 1) WHERE EXISTS (SELECT (SELECT 1))";
    let stmts = mz_sql_parser::parser::parse_statements(sql.into())?;

    let mut counter = SubqueryCounter { count: 0 };
    for stmt in &stmts {
        counter.visit_statement(&stmt.ast);
    }
    assert_eq!(counter.count, 5);
    Ok(())
}

The 'ast lifetime on the input references means that the syntax tree outlives the complete recursive visit call, so the visitor is allowed to hold on to references into the syntax tree.

use std::error::Error;

use mz_sql_parser::ast::{Ident, Raw, AstInfo, RawItemName};
use mz_sql_parser::ast::visit::{self, Visit};

struct IdentCollector<'ast> {
    idents: Vec<&'ast Ident>,
}

impl<'ast> Visit<'ast, Raw> for IdentCollector<'ast> {
    fn visit_ident(&mut self, node: &'ast Ident) {
        self.idents.push(node);
        visit::visit_ident(self, node);
    }
    fn visit_item_name(&mut self, name: &'ast <Raw as AstInfo>::ItemName) {
        match name {
            RawItemName::Name(n) | RawItemName::Id(_, n, _) => {
                for node in &n.0 {
                    self.idents.push(node);
                    visit::visit_ident(self, node);
                }
            }
        }
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let sql = "SELECT a FROM b.c WHERE 1 + d(e)";
    let stmts = mz_sql_parser::parser::parse_statements(sql.into())?;

    let mut collector = IdentCollector { idents: vec![] };
    for stmt in &stmts {
        collector.visit_statement(&stmt.ast);
    }
    assert_eq!(collector.idents, &[
        &Ident::new_unchecked("a"), &Ident::new_unchecked("b"),
        &Ident::new_unchecked("c"), &Ident::new_unchecked("d"),
        &Ident::new_unchecked("e"),
    ]);
    Ok(())
}

The VisitNode trait is implemented for every node in the AST and can be used to write generic functions that apply a Visit implementation to any node in the AST.

§Implementation notes

This module is automatically generated by the crate’s build script. Changes to the AST will be automatically propagated to the visitor.

This approach to AST visitors is inspired by the syn crate. These module docs are directly derived from the syn::visit module docs.

Traits§

Functions§