Skip to main content

sqlparser/
test_utils.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18/// This module contains internal utilities used for testing the library.
19/// While technically public, the library's users are not supposed to rely
20/// on this module, as it will change without notice.
21//
22// Integration tests (i.e. everything under `tests/`) import this
23// via `tests/test_utils/helpers`.
24
25#[cfg(not(feature = "std"))]
26use alloc::{
27    boxed::Box,
28    string::{String, ToString},
29    vec,
30    vec::Vec,
31};
32use core::fmt::Debug;
33
34use crate::dialect::*;
35use crate::parser::{Parser, ParserError};
36use crate::tokenizer::{Token, Tokenizer};
37use crate::{ast::*, parser::ParserOptions};
38
39#[cfg(test)]
40use pretty_assertions::assert_eq;
41
42/// Tests use the methods on this struct to invoke the parser on one or
43/// multiple dialects.
44pub struct TestedDialects {
45    pub dialects: Vec<Box<dyn Dialect>>,
46    pub options: Option<ParserOptions>,
47    pub recursion_limit: Option<usize>,
48}
49
50impl TestedDialects {
51    /// Create a TestedDialects with default options and the given dialects.
52    pub fn new(dialects: Vec<Box<dyn Dialect>>) -> Self {
53        Self {
54            dialects,
55            options: None,
56            recursion_limit: None,
57        }
58    }
59
60    pub fn new_with_options(dialects: Vec<Box<dyn Dialect>>, options: ParserOptions) -> Self {
61        Self {
62            dialects,
63            options: Some(options),
64            recursion_limit: None,
65        }
66    }
67
68    pub fn with_recursion_limit(mut self, recursion_limit: usize) -> Self {
69        self.recursion_limit = Some(recursion_limit);
70        self
71    }
72
73    fn new_parser<'a>(&self, dialect: &'a dyn Dialect) -> Parser<'a> {
74        let parser = Parser::new(dialect);
75        let parser = if let Some(options) = &self.options {
76            parser.with_options(options.clone())
77        } else {
78            parser
79        };
80
81        let parser = if let Some(recursion_limit) = &self.recursion_limit {
82            parser.with_recursion_limit(*recursion_limit)
83        } else {
84            parser
85        };
86
87        parser
88    }
89
90    /// Run the given function for all of `self.dialects`, assert that they
91    /// return the same result, and return that result.
92    pub fn one_of_identical_results<F, T: Debug + PartialEq>(&self, f: F) -> T
93    where
94        F: Fn(&dyn Dialect) -> T,
95    {
96        let parse_results = self.dialects.iter().map(|dialect| (dialect, f(&**dialect)));
97        parse_results
98            .fold(None, |s, (dialect, parsed)| {
99                if let Some((prev_dialect, prev_parsed)) = s {
100                    match (&prev_parsed, &parsed) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::Some(format_args!("Parse results with {0:?} are different from {1:?}",
                        prev_dialect, dialect)));
        }
    }
};assert_eq!(
101                        prev_parsed, parsed,
102                        "Parse results with {prev_dialect:?} are different from {dialect:?}"
103                    );
104                }
105                Some((dialect, parsed))
106            })
107            .expect("tested dialects cannot be empty")
108            .1
109    }
110
111    pub fn run_parser_method<F, T: Debug + PartialEq>(&self, sql: &str, f: F) -> T
112    where
113        F: Fn(&mut Parser) -> T,
114    {
115        self.one_of_identical_results(|dialect| {
116            let mut parser = self.new_parser(dialect).try_with_sql(sql).unwrap();
117            f(&mut parser)
118        })
119    }
120
121    /// Parses a single SQL string into multiple statements, ensuring
122    /// the result is the same for all tested dialects.
123    pub fn parse_sql_statements(&self, sql: &str) -> Result<Vec<Statement>, ParserError> {
124        self.one_of_identical_results(|dialect| {
125            let mut tokenizer = Tokenizer::new(dialect, sql);
126            if let Some(options) = &self.options {
127                tokenizer = tokenizer.with_unescape(options.unescape);
128            }
129            let tokens = tokenizer.tokenize()?;
130            self.new_parser(dialect)
131                .with_tokens(tokens)
132                .parse_statements()
133        })
134        // To fail the `ensure_multiple_dialects_are_tested` test:
135        // Parser::parse_sql(&**self.dialects.first().unwrap(), sql)
136    }
137
138    /// Ensures that `sql` parses as a single [Statement] for all tested
139    /// dialects.
140    ///
141    /// In general, the canonical SQL should be the same (see crate
142    /// documentation for rationale) and you should prefer the `verified_`
143    /// variants in testing, such as  [`verified_statement`] or
144    /// [`verified_query`].
145    ///
146    /// If `canonical` is non empty,this function additionally asserts
147    /// that:
148    ///
149    /// 1. parsing `sql` results in the same [`Statement`] as parsing
150    ///    `canonical`.
151    ///
152    /// 2. re-serializing the result of parsing `sql` produces the same
153    ///    `canonical` sql string
154    ///
155    ///  For multiple statements, use [`statements_parse_to`].
156    pub fn one_statement_parses_to(&self, sql: &str, canonical: &str) -> Statement {
157        let mut statements = self.parse_sql_statements(sql).expect(sql);
158        match (&statements.len(), &1) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(statements.len(), 1);
159        if !canonical.is_empty() && sql != canonical {
160            match (&self.parse_sql_statements(canonical).unwrap(), &statements) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(self.parse_sql_statements(canonical).unwrap(), statements);
161        }
162
163        let only_statement = statements.pop().unwrap();
164
165        if !canonical.is_empty() {
166            match (&canonical, &only_statement.to_string()) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
}assert_eq!(canonical, only_statement.to_string())
167        }
168        only_statement
169    }
170
171    /// The same as [`one_statement_parses_to`] but it works for a multiple statements
172    pub fn statements_parse_to(&self, sql: &str, canonical: &str) -> Vec<Statement> {
173        let statements = self.parse_sql_statements(sql).expect(sql);
174        if !canonical.is_empty() && sql != canonical {
175            match (&self.parse_sql_statements(canonical).unwrap(), &statements) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(self.parse_sql_statements(canonical).unwrap(), statements);
176        } else {
177            match (&sql,
        &statements.iter().map(|s|
                            s.to_string()).collect::<Vec<_>>().join("; ")) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(
178                sql,
179                statements
180                    .iter()
181                    .map(|s| s.to_string())
182                    .collect::<Vec<_>>()
183                    .join("; ")
184            );
185        }
186        statements
187    }
188
189    /// Ensures that `sql` parses as an [`Expr`], and that
190    /// re-serializing the parse result produces canonical
191    pub fn expr_parses_to(&self, sql: &str, canonical: &str) -> Expr {
192        let ast = self
193            .run_parser_method(sql, |parser| parser.parse_expr())
194            .unwrap();
195        match (&canonical, &&ast.to_string()) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(canonical, &ast.to_string());
196        ast
197    }
198
199    /// Ensures that `sql` parses as a single [Statement], and that
200    /// re-serializing the parse result produces the same `sql`
201    /// string (is not modified after a serialization round-trip).
202    pub fn verified_stmt(&self, sql: &str) -> Statement {
203        self.one_statement_parses_to(sql, sql)
204    }
205
206    /// Ensures that `sql` parses as a single [Query], and that
207    /// re-serializing the parse result produces the same `sql`
208    /// string (is not modified after a serialization round-trip).
209    pub fn verified_query(&self, sql: &str) -> Query {
210        match self.verified_stmt(sql) {
211            Statement::Query(query) => *query,
212            _ => { ::core::panicking::panic_fmt(format_args!("Expected Query")); }panic!("Expected Query"),
213        }
214    }
215
216    /// Ensures that `sql` parses as a single [Query], and that
217    /// re-serializing the parse result matches the given canonical
218    /// sql string.
219    pub fn verified_query_with_canonical(&self, query: &str, canonical: &str) -> Query {
220        match self.one_statement_parses_to(query, canonical) {
221            Statement::Query(query) => *query,
222            _ => { ::core::panicking::panic_fmt(format_args!("Expected Query")); }panic!("Expected Query"),
223        }
224    }
225
226    /// Ensures that `sql` parses as a single [Select], and that
227    /// re-serializing the parse result produces the same `sql`
228    /// string (is not modified after a serialization round-trip).
229    pub fn verified_only_select(&self, query: &str) -> Select {
230        match *self.verified_query(query).body {
231            SetExpr::Select(s) => *s,
232            _ => { ::core::panicking::panic_fmt(format_args!("Expected SetExpr::Select")); }panic!("Expected SetExpr::Select"),
233        }
234    }
235
236    /// Ensures that `sql` parses as a single [`Select`], and that additionally:
237    ///
238    /// 1. parsing `sql` results in the same [`Statement`] as parsing
239    ///    `canonical`.
240    ///
241    /// 2. re-serializing the result of parsing `sql` produces the same
242    ///    `canonical` sql string
243    pub fn verified_only_select_with_canonical(&self, query: &str, canonical: &str) -> Select {
244        let q = match self.one_statement_parses_to(query, canonical) {
245            Statement::Query(query) => *query,
246            _ => { ::core::panicking::panic_fmt(format_args!("Expected Query")); }panic!("Expected Query"),
247        };
248        match *q.body {
249            SetExpr::Select(s) => *s,
250            _ => { ::core::panicking::panic_fmt(format_args!("Expected SetExpr::Select")); }panic!("Expected SetExpr::Select"),
251        }
252    }
253
254    /// Ensures that `sql` parses as an [`Expr`], and that
255    /// re-serializing the parse result produces the same `sql`
256    /// string (is not modified after a serialization round-trip).
257    pub fn verified_expr(&self, sql: &str) -> Expr {
258        self.expr_parses_to(sql, sql)
259    }
260
261    /// Check that the tokenizer returns the expected tokens for the given SQL.
262    pub fn tokenizes_to(&self, sql: &str, expected: Vec<Token>) {
263        if self.dialects.is_empty() {
264            { ::core::panicking::panic_fmt(format_args!("No dialects to test")); };panic!("No dialects to test");
265        }
266
267        self.dialects.iter().for_each(|dialect| {
268            let mut tokenizer = Tokenizer::new(&**dialect, sql);
269            if let Some(options) = &self.options {
270                tokenizer = tokenizer.with_unescape(options.unescape);
271            }
272            let tokens = tokenizer.tokenize().unwrap();
273            match (&expected, &tokens) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::Some(format_args!("Tokenized differently for {0:?}",
                        dialect)));
        }
    }
};assert_eq!(expected, tokens, "Tokenized differently for {dialect:?}");
274        });
275    }
276}
277
278/// Returns all available dialects.
279pub fn all_dialects() -> TestedDialects {
280    TestedDialects::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Box::new(GenericDialect {}), Box::new(PostgreSqlDialect {}),
                Box::new(MsSqlDialect {}), Box::new(AnsiDialect {}),
                Box::new(SnowflakeDialect {}), Box::new(HiveDialect {}),
                Box::new(RedshiftSqlDialect {}), Box::new(MySqlDialect {}),
                Box::new(BigQueryDialect {}), Box::new(SQLiteDialect {}),
                Box::new(DuckDbDialect {}), Box::new(DatabricksDialect {}),
                Box::new(ClickHouseDialect {}), Box::new(OracleDialect {})]))vec![
281        Box::new(GenericDialect {}),
282        Box::new(PostgreSqlDialect {}),
283        Box::new(MsSqlDialect {}),
284        Box::new(AnsiDialect {}),
285        Box::new(SnowflakeDialect {}),
286        Box::new(HiveDialect {}),
287        Box::new(RedshiftSqlDialect {}),
288        Box::new(MySqlDialect {}),
289        Box::new(BigQueryDialect {}),
290        Box::new(SQLiteDialect {}),
291        Box::new(DuckDbDialect {}),
292        Box::new(DatabricksDialect {}),
293        Box::new(ClickHouseDialect {}),
294        Box::new(OracleDialect {}),
295    ])
296}
297
298// Returns all available dialects with the specified parser options
299pub fn all_dialects_with_options(options: ParserOptions) -> TestedDialects {
300    TestedDialects::new_with_options(all_dialects().dialects, options)
301}
302
303/// Returns all dialects matching the given predicate.
304pub fn all_dialects_where<F>(predicate: F) -> TestedDialects
305where
306    F: Fn(&dyn Dialect) -> bool,
307{
308    let mut dialects = all_dialects();
309    dialects.dialects.retain(|d| predicate(&**d));
310    dialects
311}
312
313/// Returns available dialects. The `except` predicate is used
314/// to filter out specific dialects.
315pub fn all_dialects_except<F>(except: F) -> TestedDialects
316where
317    F: Fn(&dyn Dialect) -> bool,
318{
319    all_dialects_where(|d| !except(d))
320}
321
322pub fn assert_eq_vec<T: ToString>(expected: &[&str], actual: &[T]) {
323    match (&expected, &actual.iter().map(ToString::to_string).collect::<Vec<_>>())
    {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(
324        expected,
325        actual.iter().map(ToString::to_string).collect::<Vec<_>>()
326    );
327}
328
329pub fn only<T>(v: impl IntoIterator<Item = T>) -> T {
330    let mut iter = v.into_iter();
331    if let (Some(item), None) = (iter.next(), iter.next()) {
332        item
333    } else {
334        {
    ::core::panicking::panic_fmt(format_args!("only called on collection without exactly one item"));
}panic!("only called on collection without exactly one item")
335    }
336}
337
338pub fn expr_from_projection(item: &SelectItem) -> &Expr {
339    match item {
340        SelectItem::UnnamedExpr(expr) => expr,
341        _ => { ::core::panicking::panic_fmt(format_args!("Expected UnnamedExpr")); }panic!("Expected UnnamedExpr"),
342    }
343}
344
345pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTableOperation {
346    match stmt {
347        Statement::AlterTable(alter_table) => {
348            match (&alter_table.name.to_string(), &expected_name) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(alter_table.name.to_string(), expected_name);
349            if !!alter_table.if_exists {
    ::core::panicking::panic("assertion failed: !alter_table.if_exists")
};assert!(!alter_table.if_exists);
350            if !!alter_table.only {
    ::core::panicking::panic("assertion failed: !alter_table.only")
};assert!(!alter_table.only);
351            match (&alter_table.table_type, &None) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(alter_table.table_type, None);
352            only(alter_table.operations)
353        }
354        _ => {
    ::core::panicking::panic_fmt(format_args!("Expected ALTER TABLE statement"));
}panic!("Expected ALTER TABLE statement"),
355    }
356}
357
358pub fn alter_table_op(stmt: Statement) -> AlterTableOperation {
359    alter_table_op_with_name(stmt, "tab")
360}
361
362/// Creates a `Value::Number`, panic'ing if n is not a number
363pub fn number(n: &str) -> Value {
364    Value::Number(n.parse().unwrap(), false)
365}
366
367/// Creates a [Value::SingleQuotedString]
368pub fn single_quoted_string(s: impl Into<String>) -> Value {
369    Value::SingleQuotedString(s.into())
370}
371
372pub fn table_alias(explicit: bool, name: impl Into<String>) -> Option<TableAlias> {
373    Some(TableAlias {
374        explicit,
375        name: Ident::new(name),
376        columns: ::alloc::vec::Vec::new()vec![],
377    })
378}
379
380pub fn table(name: impl Into<String>) -> TableFactor {
381    TableFactor::Table {
382        name: ObjectName::from(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ident::new(name.into())]))vec![Ident::new(name.into())]),
383        alias: None,
384        args: None,
385        with_hints: ::alloc::vec::Vec::new()vec![],
386        version: None,
387        partitions: ::alloc::vec::Vec::new()vec![],
388        with_ordinality: false,
389        json_path: None,
390        sample: None,
391        index_hints: ::alloc::vec::Vec::new()vec![],
392    }
393}
394
395pub fn table_from_name(name: ObjectName) -> TableFactor {
396    TableFactor::Table {
397        name,
398        alias: None,
399        args: None,
400        with_hints: ::alloc::vec::Vec::new()vec![],
401        version: None,
402        partitions: ::alloc::vec::Vec::new()vec![],
403        with_ordinality: false,
404        json_path: None,
405        sample: None,
406        index_hints: ::alloc::vec::Vec::new()vec![],
407    }
408}
409
410pub fn table_with_alias(
411    name: impl Into<String>,
412    with_as_keyword: bool,
413    alias: impl Into<String>,
414) -> TableFactor {
415    TableFactor::Table {
416        name: ObjectName::from(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ident::new(name)]))vec![Ident::new(name)]),
417        alias: table_alias(with_as_keyword, alias),
418        args: None,
419        with_hints: ::alloc::vec::Vec::new()vec![],
420        version: None,
421        partitions: ::alloc::vec::Vec::new()vec![],
422        with_ordinality: false,
423        json_path: None,
424        sample: None,
425        index_hints: ::alloc::vec::Vec::new()vec![],
426    }
427}
428
429pub fn join(relation: TableFactor) -> Join {
430    Join {
431        relation,
432        global: false,
433        join_operator: JoinOperator::Join(JoinConstraint::Natural),
434    }
435}
436
437pub fn call(function: &str, args: impl IntoIterator<Item = Expr>) -> Expr {
438    Expr::Function(Function {
439        name: ObjectName::from(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ident::new(function)]))vec![Ident::new(function)]),
440        uses_odbc_syntax: false,
441        parameters: FunctionArguments::None,
442        args: FunctionArguments::List(FunctionArgumentList {
443            duplicate_treatment: None,
444            args: args
445                .into_iter()
446                .map(|arg| FunctionArg::Unnamed(FunctionArgExpr::Expr(arg)))
447                .collect(),
448            clauses: ::alloc::vec::Vec::new()vec![],
449        }),
450        filter: None,
451        null_treatment: None,
452        over: None,
453        within_group: ::alloc::vec::Vec::new()vec![],
454    })
455}
456
457/// Gets the first index column (mysql calls it a key part) of the first index found in a
458/// [`Statement::CreateIndex`], [`Statement::CreateTable`], or [`Statement::AlterTable`].
459pub fn index_column(stmt: Statement) -> Expr {
460    match stmt {
461        Statement::CreateIndex(CreateIndex { columns, .. }) => {
462            columns.first().unwrap().column.expr.clone()
463        }
464        Statement::CreateTable(CreateTable { constraints, .. }) => {
465            match constraints.first().unwrap() {
466                TableConstraint::Index(constraint) => {
467                    constraint.columns.first().unwrap().column.expr.clone()
468                }
469                TableConstraint::Unique(constraint) => {
470                    constraint.columns.first().unwrap().column.expr.clone()
471                }
472                TableConstraint::PrimaryKey(constraint) => {
473                    constraint.columns.first().unwrap().column.expr.clone()
474                }
475                TableConstraint::FulltextOrSpatial(constraint) => {
476                    constraint.columns.first().unwrap().column.expr.clone()
477                }
478                _ => {
    ::core::panicking::panic_fmt(format_args!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"));
}panic!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"),
479            }
480        }
481        Statement::AlterTable(alter_table) => match alter_table.operations.first().unwrap() {
482            AlterTableOperation::AddConstraint { constraint, .. } => {
483                match constraint {
484                    TableConstraint::Index(constraint) => {
485                        constraint.columns.first().unwrap().column.expr.clone()
486                    }
487                    TableConstraint::Unique(constraint) => {
488                        constraint.columns.first().unwrap().column.expr.clone()
489                    }
490                    TableConstraint::PrimaryKey(constraint) => {
491                        constraint.columns.first().unwrap().column.expr.clone()
492                    }
493                    TableConstraint::FulltextOrSpatial(constraint) => {
494                        constraint.columns.first().unwrap().column.expr.clone()
495                    }
496                    _ => {
    ::core::panicking::panic_fmt(format_args!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"));
}panic!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"),
497                }
498            }
499            _ => { ::core::panicking::panic_fmt(format_args!("Expected a constraint")); }panic!("Expected a constraint"),
500        },
501        _ => {
    ::core::panicking::panic_fmt(format_args!("Expected CREATE INDEX, ALTER TABLE, or CREATE TABLE, got: {0:?}",
            stmt));
}panic!("Expected CREATE INDEX, ALTER TABLE, or CREATE TABLE, got: {stmt:?}"),
502    }
503}