Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test trailing commas #859

Merged
merged 2 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(Default, Clone)]
aljazerzen marked this conversation as resolved.
Show resolved Hide resolved
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,
}
}
41 changes: 40 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,32 @@ 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,
}),
};

// We shouldn't verify, because trailing commas will not get generated back
aljazerzen marked this conversation as resolved.
Show resolved Hide resolved
trailing_commas
.parse_sql_statements("SELECT album_id, name, FROM track")
.unwrap();

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

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

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,
}
}