Skip to content

Commit

Permalink
Test trailing commas (#859)
Browse files Browse the repository at this point in the history
* test: add tests for trailing commas

* tweaks
  • Loading branch information
aljazerzen authored May 1, 2023
1 parent 3b1076c commit 0113bbd
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 5 deletions.
8 changes: 7 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl std::error::Error for ParserError {}
// By default, allow expressions up to this deep before erroring
const DEFAULT_REMAINING_DEPTH: usize = 50;

#[derive(Default)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct ParserOptions {
pub trailing_commas: bool,
}
Expand Down Expand Up @@ -6958,6 +6958,7 @@ mod tests {
// Character string types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#character-string-type>
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
options: None,
};

test_parse_data_type!(dialect, "CHARACTER", DataType::Character(None));
Expand Down Expand Up @@ -7087,6 +7088,7 @@ mod tests {
// Character large object types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#character-large-object-length>
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
options: None,
};

test_parse_data_type!(
Expand Down Expand Up @@ -7119,6 +7121,7 @@ mod tests {
fn test_parse_custom_types() {
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
options: None,
};
test_parse_data_type!(
dialect,
Expand Down Expand Up @@ -7150,6 +7153,7 @@ mod tests {
// Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type>
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
options: None,
};

test_parse_data_type!(dialect, "NUMERIC", DataType::Numeric(ExactNumberInfo::None));
Expand Down Expand Up @@ -7200,6 +7204,7 @@ mod tests {
// Datetime types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#datetime-type>
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
options: None,
};

test_parse_data_type!(dialect, "DATE", DataType::Date);
Expand Down Expand Up @@ -7311,6 +7316,7 @@ mod tests {

let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(MySqlDialect {})],
options: None,
};

test_parse_table_constraint!(
Expand Down
21 changes: 18 additions & 3 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,27 @@ use alloc::{
};
use core::fmt::Debug;

use crate::ast::*;
use crate::dialect::*;
use crate::parser::{Parser, ParserError};
use crate::{ast::*, parser::ParserOptions};

/// Tests use the methods on this struct to invoke the parser on one or
/// multiple dialects.
pub struct TestedDialects {
pub dialects: Vec<Box<dyn Dialect>>,
pub options: Option<ParserOptions>,
}

impl TestedDialects {
fn new_parser<'a>(&self, dialect: &'a dyn Dialect) -> Parser<'a> {
let parser = Parser::new(dialect);
if let Some(options) = &self.options {
parser.with_options(options.clone())
} else {
parser
}
}

/// Run the given function for all of `self.dialects`, assert that they
/// return the same result, and return that result.
pub fn one_of_identical_results<F, T: Debug + PartialEq>(&self, f: F) -> T
Expand All @@ -63,15 +73,19 @@ impl TestedDialects {
F: Fn(&mut Parser) -> T,
{
self.one_of_identical_results(|dialect| {
let mut parser = Parser::new(dialect).try_with_sql(sql).unwrap();
let mut parser = self.new_parser(dialect).try_with_sql(sql).unwrap();
f(&mut parser)
})
}

/// Parses a single SQL string into multiple statements, ensuring
/// the result is the same for all tested dialects.
pub fn parse_sql_statements(&self, sql: &str) -> Result<Vec<Statement>, ParserError> {
self.one_of_identical_results(|dialect| Parser::parse_sql(dialect, sql))
self.one_of_identical_results(|dialect| {
self.new_parser(dialect)
.try_with_sql(sql)?
.parse_statements()
})
// To fail the `ensure_multiple_dialects_are_tested` test:
// Parser::parse_sql(&**self.dialects.first().unwrap(), sql)
}
Expand Down Expand Up @@ -155,6 +169,7 @@ pub fn all_dialects() -> TestedDialects {
Box::new(BigQueryDialect {}),
Box::new(SQLiteDialect {}),
],
options: None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,12 +406,14 @@ fn test_select_wildcard_with_replace() {
fn bigquery() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(BigQueryDialect {})],
options: None,
}
}

fn bigquery_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(BigQueryDialect {}), Box::new(GenericDialect {})],
options: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,5 +329,6 @@ fn parse_create_table() {
fn clickhouse() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(ClickHouseDialect {})],
options: None,
}
}
43 changes: 42 additions & 1 deletion tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use sqlparser::dialect::{
MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect,
};
use sqlparser::keywords::ALL_KEYWORDS;
use sqlparser::parser::{Parser, ParserError};
use sqlparser::parser::{Parser, ParserError, ParserOptions};
use test_utils::{
all_dialects, assert_eq_vec, expr_from_projection, join, number, only, table, table_alias,
TestedDialects,
Expand Down Expand Up @@ -201,6 +201,7 @@ fn parse_update_set_from() {
Box::new(RedshiftSqlDialect {}),
Box::new(MsSqlDialect {}),
],
options: None,
};
let stmt = dialects.verified_stmt(sql);
assert_eq!(
Expand Down Expand Up @@ -949,6 +950,7 @@ fn parse_exponent_in_select() -> Result<(), ParserError> {
Box::new(SnowflakeDialect {}),
Box::new(SQLiteDialect {}),
],
options: None,
};
let sql = "SELECT 10e-20, 1e3, 1e+3, 1e3a, 1e, 0.5e2";
let mut select = dialects.parse_sql_statements(sql)?;
Expand Down Expand Up @@ -1386,6 +1388,7 @@ pub fn all_dialects_but_pg() -> TestedDialects {
.into_iter()
.filter(|x| !x.is::<PostgreSqlDialect>())
.collect(),
options: None,
}
}

Expand Down Expand Up @@ -2055,6 +2058,7 @@ fn parse_array_agg_func() {
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
],
options: None,
};

for sql in [
Expand Down Expand Up @@ -2254,6 +2258,7 @@ fn parse_create_table_hive_array() {
// Parsing [] type arrays does not work in MsSql since [ is used in is_delimited_identifier_start
let dialects = TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {}), Box::new(HiveDialect {})],
options: None,
};
let sql = "CREATE TABLE IF NOT EXISTS something (name int, val array<int>)";
match dialects.one_statement_parses_to(
Expand Down Expand Up @@ -2296,6 +2301,7 @@ fn parse_create_table_hive_array() {
Box::new(HiveDialect {}),
Box::new(MySqlDialect {}),
],
options: None,
};
let sql = "CREATE TABLE IF NOT EXISTS something (name int, val array<int)";

Expand Down Expand Up @@ -2841,6 +2847,7 @@ fn parse_alter_table_add_column_if_not_exists() {
Box::new(BigQueryDialect {}),
Box::new(GenericDialect {}),
],
options: None,
};

match dialects.verified_stmt("ALTER TABLE tab ADD IF NOT EXISTS foo TEXT") {
Expand Down Expand Up @@ -3898,6 +3905,7 @@ fn parse_unnest() {
}
let dialects = TestedDialects {
dialects: vec![Box::new(BigQueryDialect {}), Box::new(GenericDialect {})],
options: None,
};
// 1. both Alias and WITH OFFSET clauses.
chk(
Expand Down Expand Up @@ -6137,6 +6145,7 @@ fn test_placeholder() {
// Note: `$` is the starting word for the HiveDialect identifier
// Box::new(sqlparser::dialect::HiveDialect {}),
],
options: None,
};
let sql = "SELECT * FROM student WHERE id = $Id1";
let ast = dialects.verified_only_select(sql);
Expand Down Expand Up @@ -6867,6 +6876,7 @@ fn parse_non_latin_identifiers() {
Box::new(RedshiftSqlDialect {}),
Box::new(MySqlDialect {}),
],
options: None,
};

supported_dialects.verified_stmt("SELECT a.説明 FROM test.public.inter01 AS a");
Expand All @@ -6876,3 +6886,34 @@ fn parse_non_latin_identifiers() {
.parse_sql_statements("SELECT 💝 FROM table1")
.is_err());
}

#[test]
fn parse_trailing_comma() {
let trailing_commas = TestedDialects {
dialects: vec![Box::new(GenericDialect {})],
options: Some(ParserOptions {
trailing_commas: true,
}),
};

trailing_commas.one_statement_parses_to(
"SELECT album_id, name, FROM track",
"SELECT album_id, name FROM track",
);

trailing_commas.one_statement_parses_to(
"SELECT * FROM track ORDER BY milliseconds,",
"SELECT * FROM track ORDER BY milliseconds",
);

trailing_commas.one_statement_parses_to(
"SELECT DISTINCT ON (album_id,) name FROM track",
"SELECT DISTINCT ON (album_id) name FROM track",
);

trailing_commas.verified_stmt("SELECT album_id, name FROM track");

trailing_commas.verified_stmt("SELECT * FROM track ORDER BY milliseconds");

trailing_commas.verified_stmt("SELECT DISTINCT ON (album_id) name FROM track");
}
2 changes: 2 additions & 0 deletions tests/sqlparser_hive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ fn parse_create_function() {

let generic = TestedDialects {
dialects: vec![Box::new(GenericDialect {})],
options: None,
};

assert_eq!(
Expand Down Expand Up @@ -473,5 +474,6 @@ fn parse_similar_to() {
fn hive() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(HiveDialect {})],
options: None,
}
}
2 changes: 2 additions & 0 deletions tests/sqlparser_mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,12 @@ fn parse_similar_to() {
fn ms() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MsSqlDialect {})],
options: None,
}
}
fn ms_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MsSqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
}
2 changes: 2 additions & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,12 +1350,14 @@ fn parse_create_table_with_fulltext_definition_should_not_accept_constraint_name
fn mysql() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: None,
}
}

fn mysql_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MySqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2052,12 +2052,14 @@ fn parse_on_commit() {
fn pg() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {})],
options: None,
}
}

fn pg_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_redshift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ fn parse_similar_to() {
fn redshift() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(RedshiftSqlDialect {})],
options: None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,14 @@ fn test_array_agg_func() {
fn snowflake() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {})],
options: None,
}
}

fn snowflake_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {}), Box::new(GenericDialect {})],
options: None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,14 @@ fn parse_similar_to() {
fn sqlite() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SQLiteDialect {})],
options: None,
}
}

fn sqlite_and_generic() -> TestedDialects {
TestedDialects {
// we don't have a separate SQLite dialect, so test only the generic dialect for now
dialects: vec![Box::new(SQLiteDialect {}), Box::new(GenericDialect {})],
options: None,
}
}

0 comments on commit 0113bbd

Please sign in to comment.