risingwave_sqlparser/
test_utils.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13/// This module contains internal utilities used for testing the library.
14/// While technically public, the library's users are not supposed to rely
15/// on this module, as it will change without notice.
16// Integration tests (i.e. everything under `tests/`) import this
17// via `tests/test_utils/executor`.
18use std::fmt::Debug;
19
20use crate::ast::*;
21use crate::parser::{Parser, ParserError};
22use crate::tokenizer::Tokenizer;
23
24pub fn run_parser_method<F, T: Debug + PartialEq>(sql: &str, f: F) -> T
25where
26    F: Fn(&mut Parser<'_>) -> T,
27{
28    let mut tokenizer = Tokenizer::new(sql);
29    let tokens = tokenizer.tokenize_with_location().unwrap();
30    f(&mut Parser(&tokens))
31}
32
33pub fn parse_sql_statements(sql: &str) -> Result<Vec<Statement>, ParserError> {
34    Parser::parse_sql(sql)
35    // To fail the `ensure_multiple_dialects_are_tested` test:
36    // Parser::parse_sql(&**self.dialects.first().unwrap(), sql)
37}
38
39/// Ensures that `sql` parses as a single statement and returns it.
40///
41/// If non-empty `canonical` SQL representation is provided,
42/// additionally asserts that parsing `sql` results in the same parse
43/// tree as parsing `canonical`, and that serializing it back to string
44/// results in the `canonical` representation.
45#[track_caller]
46pub fn one_statement_parses_to(sql: &str, canonical: &str) -> Statement {
47    let mut statements = parse_sql_statements(sql).unwrap();
48    assert_eq!(statements.len(), 1);
49
50    if !canonical.is_empty() && sql != canonical {
51        assert_eq!(parse_sql_statements(canonical).unwrap(), statements);
52    }
53
54    let only_statement = statements.pop().unwrap();
55    if !canonical.is_empty() {
56        assert_eq!(canonical, only_statement.to_string())
57    }
58    only_statement
59}
60
61/// Ensures that `sql` parses as a single [Statement], and is not modified
62/// after a serialization round-trip.
63#[track_caller]
64pub fn verified_stmt(query: &str) -> Statement {
65    one_statement_parses_to(query, query)
66}
67
68/// Ensures that `sql` parses as a single [Query], and is not modified
69/// after a serialization round-trip.
70#[track_caller]
71pub fn verified_query(sql: &str) -> Query {
72    match verified_stmt(sql) {
73        Statement::Query(query) => *query,
74        _ => panic!("Expected Query"),
75    }
76}
77
78#[track_caller]
79pub fn query(sql: &str, canonical: &str) -> Query {
80    match one_statement_parses_to(sql, canonical) {
81        Statement::Query(query) => *query,
82        _ => panic!("Expected Query"),
83    }
84}
85
86/// Ensures that `sql` parses as a single [Select], and is not modified
87/// after a serialization round-trip.
88#[track_caller]
89pub fn verified_only_select(query: &str) -> Select {
90    match verified_query(query).body {
91        SetExpr::Select(s) => *s,
92        _ => panic!("Expected SetExpr::Select"),
93    }
94}
95
96/// Ensures that `sql` parses as an expression, and is not modified
97/// after a serialization round-trip.
98pub fn verified_expr(sql: &str) -> Expr {
99    let ast = run_parser_method(sql, |parser| parser.parse_expr()).unwrap();
100    assert_eq!(sql, &ast.to_string(), "round-tripping without changes");
101    ast
102}
103
104pub fn only<T>(v: impl IntoIterator<Item = T>) -> T {
105    let mut iter = v.into_iter();
106    match (iter.next(), iter.next()) {
107        (Some(item), None) => item,
108        _ => {
109            panic!("only called on collection without exactly one item")
110        }
111    }
112}
113
114pub fn expr_from_projection(item: &SelectItem) -> &Expr {
115    match item {
116        SelectItem::UnnamedExpr(expr) => expr,
117        _ => panic!("Expected UnnamedExpr"),
118    }
119}
120
121pub fn number(n: &'static str) -> Value {
122    Value::Number(n.parse().unwrap())
123}
124
125pub fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
126    Some(TableAlias {
127        name: Ident::new_unchecked(name),
128        columns: vec![],
129    })
130}
131
132pub fn table(name: impl Into<String>) -> TableFactor {
133    TableFactor::Table {
134        name: ObjectName(vec![Ident::new_unchecked(name.into())]),
135        as_of: None,
136        alias: None,
137    }
138}
139
140pub fn join(relation: TableFactor) -> Join {
141    Join {
142        relation,
143        join_operator: JoinOperator::Inner(JoinConstraint::Natural),
144    }
145}