mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-08 01:15:00 +00:00
Test trailing commas (#859)
* test: add tests for trailing commas * tweaks
This commit is contained in:
parent
3b1076c194
commit
0113bbd924
12 changed files with 83 additions and 5 deletions
|
@ -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,
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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!(
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -7311,6 +7316,7 @@ mod tests {
|
|||
|
||||
let dialect = TestedDialects {
|
||||
dialects: vec![Box::new(GenericDialect {}), Box::new(MySqlDialect {})],
|
||||
options: None,
|
||||
};
|
||||
|
||||
test_parse_table_constraint!(
|
||||
|
|
|
@ -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
|
||||
|
@ -63,7 +73,7 @@ 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)
|
||||
})
|
||||
}
|
||||
|
@ -71,7 +81,11 @@ impl TestedDialects {
|
|||
/// 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)
|
||||
}
|
||||
|
@ -155,6 +169,7 @@ pub fn all_dialects() -> TestedDialects {
|
|||
Box::new(BigQueryDialect {}),
|
||||
Box::new(SQLiteDialect {}),
|
||||
],
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -329,5 +329,6 @@ fn parse_create_table() {
|
|||
fn clickhouse() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(ClickHouseDialect {})],
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -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!(
|
||||
|
@ -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)?;
|
||||
|
@ -1386,6 +1388,7 @@ pub fn all_dialects_but_pg() -> TestedDialects {
|
|||
.into_iter()
|
||||
.filter(|x| !x.is::<PostgreSqlDialect>())
|
||||
.collect(),
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2055,6 +2058,7 @@ fn parse_array_agg_func() {
|
|||
Box::new(AnsiDialect {}),
|
||||
Box::new(HiveDialect {}),
|
||||
],
|
||||
options: None,
|
||||
};
|
||||
|
||||
for sql in [
|
||||
|
@ -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(
|
||||
|
@ -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)";
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -267,6 +267,7 @@ fn parse_create_function() {
|
|||
|
||||
let generic = TestedDialects {
|
||||
dialects: vec![Box::new(GenericDialect {})],
|
||||
options: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
|
@ -473,5 +474,6 @@ fn parse_similar_to() {
|
|||
fn hive() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(HiveDialect {})],
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -260,6 +260,7 @@ fn parse_similar_to() {
|
|||
fn redshift() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(RedshiftSqlDialect {})],
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -245,6 +245,7 @@ fn parse_similar_to() {
|
|||
fn sqlite() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(SQLiteDialect {})],
|
||||
options: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,5 +253,6 @@ 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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue