fix for maybe_parse preventing parser from erroring on recursion limit (#1464)

This commit is contained in:
tomershaniii 2024-10-21 22:41:34 +03:00 committed by GitHub
parent 38f1e573fe
commit 8e0d26abb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 422 additions and 570 deletions

View file

@ -40,10 +40,10 @@ fn parse_literal_string() {
r#""""triple-double\"escaped""", "#,
r#""""triple-double"unescaped""""#,
);
let dialect = TestedDialects {
dialects: vec![Box::new(BigQueryDialect {})],
options: Some(ParserOptions::new().with_unescape(false)),
};
let dialect = TestedDialects::new_with_options(
vec![Box::new(BigQueryDialect {})],
ParserOptions::new().with_unescape(false),
);
let select = dialect.verified_only_select(sql);
assert_eq!(10, select.projection.len());
assert_eq!(
@ -1936,17 +1936,14 @@ fn parse_big_query_declare() {
}
fn bigquery() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(BigQueryDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(BigQueryDialect {})])
}
fn bigquery_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(BigQueryDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(BigQueryDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]

View file

@ -1613,15 +1613,12 @@ fn parse_explain_table() {
}
fn clickhouse() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(ClickHouseDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(ClickHouseDialect {})])
}
fn clickhouse_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(ClickHouseDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(ClickHouseDialect {}),
Box::new(GenericDialect {}),
])
}

View file

@ -341,19 +341,16 @@ fn parse_update() {
#[test]
fn parse_update_set_from() {
let sql = "UPDATE t1 SET name = t2.name FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 WHERE t1.id = t2.id";
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(SQLiteDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(SQLiteDialect {}),
]);
let stmt = dialects.verified_stmt(sql);
assert_eq!(
stmt,
@ -1051,10 +1048,7 @@ fn test_eof_after_as() {
#[test]
fn test_no_infix_error() {
let dialects = TestedDialects {
dialects: vec![Box::new(ClickHouseDialect {})],
options: None,
};
let dialects = TestedDialects::new(vec![Box::new(ClickHouseDialect {})]);
let res = dialects.parse_sql_statements("ASSERT-URA<<");
assert_eq!(
@ -1182,23 +1176,20 @@ fn parse_null_in_select() {
#[test]
fn parse_exponent_in_select() -> Result<(), ParserError> {
// all except Hive, as it allows numbers to start an identifier
let dialects = TestedDialects {
dialects: vec![
Box::new(AnsiDialect {}),
Box::new(BigQueryDialect {}),
Box::new(ClickHouseDialect {}),
Box::new(DuckDbDialect {}),
Box::new(GenericDialect {}),
// Box::new(HiveDialect {}),
Box::new(MsSqlDialect {}),
Box::new(MySqlDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(SQLiteDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(AnsiDialect {}),
Box::new(BigQueryDialect {}),
Box::new(ClickHouseDialect {}),
Box::new(DuckDbDialect {}),
Box::new(GenericDialect {}),
// Box::new(HiveDialect {}),
Box::new(MsSqlDialect {}),
Box::new(MySqlDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(SQLiteDialect {}),
]);
let sql = "SELECT 10e-20, 1e3, 1e+3, 1e3a, 1e, 0.5e2";
let mut select = dialects.parse_sql_statements(sql)?;
@ -1271,14 +1262,12 @@ fn parse_escaped_single_quote_string_predicate_with_no_escape() {
let sql = "SELECT id, fname, lname FROM customer \
WHERE salary <> 'Jim''s salary'";
let ast = TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: Some(
ParserOptions::new()
.with_trailing_commas(true)
.with_unescape(false),
),
}
let ast = TestedDialects::new_with_options(
vec![Box::new(MySqlDialect {})],
ParserOptions::new()
.with_trailing_commas(true)
.with_unescape(false),
)
.verified_only_select(sql);
assert_eq!(
@ -1400,10 +1389,10 @@ fn parse_mod() {
}
fn pg_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(PostgreSqlDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]
@ -1868,14 +1857,13 @@ fn parse_string_agg() {
/// selects all dialects but PostgreSQL
pub fn all_dialects_but_pg() -> TestedDialects {
TestedDialects {
dialects: all_dialects()
TestedDialects::new(
all_dialects()
.dialects
.into_iter()
.filter(|x| !x.is::<PostgreSqlDialect>())
.collect(),
options: None,
}
)
}
#[test]
@ -2691,17 +2679,14 @@ fn parse_listagg() {
#[test]
fn parse_array_agg_func() {
let supported_dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
],
options: None,
};
let supported_dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
]);
for sql in [
"SELECT ARRAY_AGG(x ORDER BY x) AS a FROM T",
@ -2716,16 +2701,13 @@ fn parse_array_agg_func() {
#[test]
fn parse_agg_with_order_by() {
let supported_dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
],
options: None,
};
let supported_dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
]);
for sql in [
"SELECT FIRST_VALUE(x ORDER BY x) AS a FROM T",
@ -2739,17 +2721,14 @@ fn parse_agg_with_order_by() {
#[test]
fn parse_window_rank_function() {
let supported_dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
Box::new(SnowflakeDialect {}),
],
options: None,
};
let supported_dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(HiveDialect {}),
Box::new(SnowflakeDialect {}),
]);
for sql in [
"SELECT column1, column2, FIRST_VALUE(column2) OVER (PARTITION BY column1 ORDER BY column2 NULLS LAST) AS column2_first FROM t1",
@ -2761,10 +2740,10 @@ fn parse_window_rank_function() {
supported_dialects.verified_stmt(sql);
}
let supported_dialects_nulls = TestedDialects {
dialects: vec![Box::new(MsSqlDialect {}), Box::new(SnowflakeDialect {})],
options: None,
};
let supported_dialects_nulls = TestedDialects::new(vec![
Box::new(MsSqlDialect {}),
Box::new(SnowflakeDialect {}),
]);
for sql in [
"SELECT column1, column2, FIRST_VALUE(column2) IGNORE NULLS OVER (PARTITION BY column1 ORDER BY column2 NULLS LAST) AS column2_first FROM t1",
@ -3321,10 +3300,7 @@ fn parse_create_table_hive_array() {
true,
),
] {
let dialects = TestedDialects {
dialects,
options: None,
};
let dialects = TestedDialects::new(dialects);
let sql = format!(
"CREATE TABLE IF NOT EXISTS something (name INT, val {})",
@ -3374,14 +3350,11 @@ fn parse_create_table_hive_array() {
}
// SnowflakeDialect using array different
let dialects = TestedDialects {
dialects: vec![
Box::new(PostgreSqlDialect {}),
Box::new(HiveDialect {}),
Box::new(MySqlDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(PostgreSqlDialect {}),
Box::new(HiveDialect {}),
Box::new(MySqlDialect {}),
]);
let sql = "CREATE TABLE IF NOT EXISTS something (name int, val array<int)";
assert_eq!(
@ -3586,10 +3559,7 @@ fn parse_create_table_as_table() {
#[test]
fn parse_create_table_on_cluster() {
let generic = TestedDialects {
dialects: vec![Box::new(GenericDialect {})],
options: None,
};
let generic = TestedDialects::new(vec![Box::new(GenericDialect {})]);
// Using single-quote literal to define current cluster
let sql = "CREATE TABLE t ON CLUSTER '{cluster}' (a INT, b INT)";
@ -3655,10 +3625,7 @@ fn parse_create_table_with_on_delete_on_update_2in_any_order() -> Result<(), Par
#[test]
fn parse_create_table_with_options() {
let generic = TestedDialects {
dialects: vec![Box::new(GenericDialect {})],
options: None,
};
let generic = TestedDialects::new(vec![Box::new(GenericDialect {})]);
let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)";
match generic.verified_stmt(sql) {
@ -3695,10 +3662,7 @@ fn parse_create_table_clone() {
#[test]
fn parse_create_table_trailing_comma() {
let dialect = TestedDialects {
dialects: vec![Box::new(DuckDbDialect {})],
options: None,
};
let dialect = TestedDialects::new(vec![Box::new(DuckDbDialect {})]);
let sql = "CREATE TABLE foo (bar int,);";
dialect.one_statement_parses_to(sql, "CREATE TABLE foo (bar INT)");
@ -4040,15 +4004,12 @@ fn parse_alter_table_add_column() {
#[test]
fn parse_alter_table_add_column_if_not_exists() {
let dialects = TestedDialects {
dialects: vec![
Box::new(PostgreSqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(PostgreSqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
]);
match alter_table_op(dialects.verified_stmt("ALTER TABLE tab ADD IF NOT EXISTS foo TEXT")) {
AlterTableOperation::AddColumn { if_not_exists, .. } => {
@ -4191,10 +4152,7 @@ fn parse_alter_table_alter_column_type() {
_ => unreachable!(),
}
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {})],
options: None,
};
let dialect = TestedDialects::new(vec![Box::new(GenericDialect {})]);
let res =
dialect.parse_sql_statements(&format!("{alter_stmt} ALTER COLUMN is_active TYPE TEXT"));
@ -4611,15 +4569,12 @@ fn parse_window_functions() {
#[test]
fn parse_named_window_functions() {
let supported_dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MySqlDialect {}),
Box::new(BigQueryDialect {}),
],
options: None,
};
let supported_dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MySqlDialect {}),
Box::new(BigQueryDialect {}),
]);
let sql = "SELECT row_number() OVER (w ORDER BY dt DESC), \
sum(foo) OVER (win PARTITION BY a, b ORDER BY c, d \
@ -5684,10 +5639,10 @@ fn parse_unnest_in_from_clause() {
let select = dialects.verified_only_select(sql);
assert_eq!(select.from, want);
}
let dialects = TestedDialects {
dialects: vec![Box::new(BigQueryDialect {}), Box::new(GenericDialect {})],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(BigQueryDialect {}),
Box::new(GenericDialect {}),
]);
// 1. both Alias and WITH OFFSET clauses.
chk(
"expr",
@ -6670,22 +6625,20 @@ fn parse_trim() {
);
//keep Snowflake/BigQuery TRIM syntax failing
let all_expected_snowflake = TestedDialects {
dialects: vec![
//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 {}),
],
options: None,
};
let all_expected_snowflake = TestedDialects::new(vec![
//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 {}),
]);
assert_eq!(
ParserError::ParserError("Expected: ), found: 'a'".to_owned()),
all_expected_snowflake
@ -8582,20 +8535,17 @@ fn test_lock_nonblock() {
#[test]
fn test_placeholder() {
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
// Note: `$` is the starting word for the HiveDialect identifier
// Box::new(sqlparser::dialect::HiveDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
// Note: `$` is the starting word for the HiveDialect identifier
// Box::new(sqlparser::dialect::HiveDialect {}),
]);
let sql = "SELECT * FROM student WHERE id = $Id1";
let ast = dialects.verified_only_select(sql);
assert_eq!(
@ -8621,21 +8571,18 @@ fn test_placeholder() {
}),
);
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
// Note: `?` is for jsonb operators in PostgreSqlDialect
// Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
// Note: `$` is the starting word for the HiveDialect identifier
// Box::new(sqlparser::dialect::HiveDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
// Note: `?` is for jsonb operators in PostgreSqlDialect
// Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
// Note: `$` is the starting word for the HiveDialect identifier
// Box::new(sqlparser::dialect::HiveDialect {}),
]);
let sql = "SELECT * FROM student WHERE id = ?";
let ast = dialects.verified_only_select(sql);
assert_eq!(
@ -9023,7 +8970,7 @@ fn parse_cache_table() {
value: Expr::Value(number("0.88")),
},
],
query: Some(query.clone()),
query: Some(query.clone().into()),
}
);
@ -9048,7 +8995,7 @@ fn parse_cache_table() {
value: Expr::Value(number("0.88")),
},
],
query: Some(query.clone()),
query: Some(query.clone().into()),
}
);
@ -9059,7 +9006,7 @@ fn parse_cache_table() {
table_name: ObjectName(vec![Ident::with_quote('\'', cache_table_name)]),
has_as: false,
options: vec![],
query: Some(query.clone()),
query: Some(query.clone().into()),
}
);
@ -9070,7 +9017,7 @@ fn parse_cache_table() {
table_name: ObjectName(vec![Ident::with_quote('\'', cache_table_name)]),
has_as: true,
options: vec![],
query: Some(query),
query: Some(query.into()),
}
);
@ -9243,14 +9190,11 @@ fn parse_with_recursion_limit() {
#[test]
fn parse_escaped_string_with_unescape() {
fn assert_mysql_query_value(sql: &str, quoted: &str) {
let stmt = TestedDialects {
dialects: vec![
Box::new(MySqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
],
options: None,
}
let stmt = TestedDialects::new(vec![
Box::new(MySqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
])
.one_statement_parses_to(sql, "");
match stmt {
@ -9283,14 +9227,14 @@ fn parse_escaped_string_with_unescape() {
#[test]
fn parse_escaped_string_without_unescape() {
fn assert_mysql_query_value(sql: &str, quoted: &str) {
let stmt = TestedDialects {
dialects: vec![
let stmt = TestedDialects::new_with_options(
vec![
Box::new(MySqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
],
options: Some(ParserOptions::new().with_unescape(false)),
}
ParserOptions::new().with_unescape(false),
)
.one_statement_parses_to(sql, "");
match stmt {
@ -9558,17 +9502,14 @@ fn make_where_clause(num: usize) -> String {
#[test]
fn parse_non_latin_identifiers() {
let supported_dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(MySqlDialect {}),
],
options: None,
};
let supported_dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(MySqlDialect {}),
]);
supported_dialects.verified_stmt("SELECT a.説明 FROM test.public.inter01 AS a");
supported_dialects.verified_stmt("SELECT a.説明 FROM inter01 AS a, inter01_transactions AS b WHERE a.説明 = b.取引 GROUP BY a.説明");
@ -9582,10 +9523,7 @@ fn parse_non_latin_identifiers() {
fn parse_trailing_comma() {
// At the moment, DuckDB is the only dialect that allows
// trailing commas anywhere in the query
let trailing_commas = TestedDialects {
dialects: vec![Box::new(DuckDbDialect {})],
options: None,
};
let trailing_commas = TestedDialects::new(vec![Box::new(DuckDbDialect {})]);
trailing_commas.one_statement_parses_to(
"SELECT album_id, name, FROM track",
@ -9624,10 +9562,7 @@ fn parse_trailing_comma() {
trailing_commas.verified_stmt(r#"SELECT "from" FROM "from""#);
// doesn't allow any trailing commas
let trailing_commas = TestedDialects {
dialects: vec![Box::new(GenericDialect {})],
options: None,
};
let trailing_commas = TestedDialects::new(vec![Box::new(GenericDialect {})]);
assert_eq!(
trailing_commas
@ -9656,10 +9591,10 @@ fn parse_trailing_comma() {
#[test]
fn parse_projection_trailing_comma() {
// Some dialects allow trailing commas only in the projection
let trailing_commas = TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {}), Box::new(BigQueryDialect {})],
options: None,
};
let trailing_commas = TestedDialects::new(vec![
Box::new(SnowflakeDialect {}),
Box::new(BigQueryDialect {}),
]);
trailing_commas.one_statement_parses_to(
"SELECT album_id, name, FROM track",
@ -9946,14 +9881,11 @@ fn test_release_savepoint() {
#[test]
fn test_comment_hash_syntax() {
let dialects = TestedDialects {
dialects: vec![
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(MySqlDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(BigQueryDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(MySqlDialect {}),
]);
let sql = r#"
# comment
SELECT a, b, c # , d, e
@ -10013,10 +9945,10 @@ fn test_buffer_reuse() {
#[test]
fn parse_map_access_expr() {
let sql = "users[-1][safe_offset(2)]";
let dialects = TestedDialects {
dialects: vec![Box::new(BigQueryDialect {}), Box::new(ClickHouseDialect {})],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(BigQueryDialect {}),
Box::new(ClickHouseDialect {}),
]);
let expr = dialects.verified_expr(sql);
let expected = Expr::MapAccess {
column: Expr::Identifier(Ident::new("users")).into(),
@ -10591,16 +10523,13 @@ fn test_match_recognize_patterns() {
#[test]
fn test_select_wildcard_with_replace() {
let sql = r#"SELECT * REPLACE (lower(city) AS city) FROM addresses"#;
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(BigQueryDialect {}),
Box::new(ClickHouseDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(DuckDbDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(BigQueryDialect {}),
Box::new(ClickHouseDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(DuckDbDialect {}),
]);
let select = dialects.verified_only_select(sql);
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_replace: Some(ReplaceSelectItem {
@ -10657,14 +10586,11 @@ fn test_select_wildcard_with_replace() {
#[test]
fn parse_sized_list() {
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(DuckDbDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(DuckDbDialect {}),
]);
let sql = r#"CREATE TABLE embeddings (data FLOAT[1536])"#;
dialects.verified_stmt(sql);
let sql = r#"CREATE TABLE embeddings (data FLOAT[1536][3])"#;
@ -10675,14 +10601,11 @@ fn parse_sized_list() {
#[test]
fn insert_into_with_parentheses() {
let dialects = TestedDialects {
dialects: vec![
Box::new(SnowflakeDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(GenericDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(SnowflakeDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(GenericDialect {}),
]);
dialects.verified_stmt("INSERT INTO t1 (id, name) (SELECT t2.id, t2.name FROM t2)");
}
@ -10850,14 +10773,11 @@ fn parse_within_group() {
#[test]
fn tests_select_values_without_parens() {
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(DatabricksDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(DatabricksDialect {}),
]);
let sql = "SELECT * FROM VALUES (1, 2), (2,3) AS tbl (id, val)";
let canonical = "SELECT * FROM (VALUES (1, 2), (2, 3)) AS tbl (id, val)";
dialects.verified_only_select_with_canonical(sql, canonical);
@ -10865,14 +10785,12 @@ fn tests_select_values_without_parens() {
#[test]
fn tests_select_values_without_parens_and_set_op() {
let dialects = TestedDialects {
dialects: vec![
Box::new(GenericDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(DatabricksDialect {}),
],
options: None,
};
let dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(DatabricksDialect {}),
]);
let sql = "SELECT id + 1, name FROM VALUES (1, 'Apple'), (2, 'Banana'), (3, 'Orange') AS fruits (id, name) UNION ALL SELECT 5, 'Strawberry'";
let canonical = "SELECT id + 1, name FROM (VALUES (1, 'Apple'), (2, 'Banana'), (3, 'Orange')) AS fruits (id, name) UNION ALL SELECT 5, 'Strawberry'";
let query = dialects.verified_query_with_canonical(sql, canonical);

View file

@ -24,17 +24,14 @@ use test_utils::*;
mod test_utils;
fn databricks() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(DatabricksDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(DatabricksDialect {})])
}
fn databricks_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(DatabricksDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(DatabricksDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]

View file

@ -24,17 +24,14 @@ use sqlparser::ast::*;
use sqlparser::dialect::{DuckDbDialect, GenericDialect};
fn duckdb() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(DuckDbDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(DuckDbDialect {})])
}
fn duckdb_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(DuckDbDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(DuckDbDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]
@ -242,7 +239,7 @@ fn test_create_table_macro() {
MacroArg::new("col1_value"),
MacroArg::new("col2_value"),
]),
definition: MacroDefinition::Table(duckdb().verified_query(query)),
definition: MacroDefinition::Table(duckdb().verified_query(query).into()),
};
assert_eq!(expected, macro_);
}

View file

@ -418,10 +418,7 @@ fn parse_create_function() {
}
// Test error in dialect that doesn't support parsing CREATE FUNCTION
let unsupported_dialects = TestedDialects {
dialects: vec![Box::new(MsSqlDialect {})],
options: None,
};
let unsupported_dialects = TestedDialects::new(vec![Box::new(MsSqlDialect {})]);
assert_eq!(
unsupported_dialects.parse_sql_statements(sql).unwrap_err(),
@ -538,15 +535,9 @@ fn parse_use() {
}
fn hive() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(HiveDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(HiveDialect {})])
}
fn hive_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(HiveDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(HiveDialect {}), Box::new(GenericDialect {})])
}

View file

@ -1030,14 +1030,8 @@ fn parse_create_table_with_identity_column() {
}
fn ms() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MsSqlDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
}
fn ms_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MsSqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(MsSqlDialect {}), Box::new(GenericDialect {})])
}

View file

@ -944,11 +944,7 @@ fn parse_quote_identifiers() {
fn parse_escaped_quote_identifiers_with_escape() {
let sql = "SELECT `quoted `` identifier`";
assert_eq!(
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: None,
}
.verified_stmt(sql),
TestedDialects::new(vec![Box::new(MySqlDialect {})]).verified_stmt(sql),
Statement::Query(Box::new(Query {
with: None,
body: Box::new(SetExpr::Select(Box::new(Select {
@ -991,13 +987,13 @@ fn parse_escaped_quote_identifiers_with_escape() {
fn parse_escaped_quote_identifiers_with_no_escape() {
let sql = "SELECT `quoted `` identifier`";
assert_eq!(
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: Some(ParserOptions {
TestedDialects::new_with_options(
vec![Box::new(MySqlDialect {})],
ParserOptions {
trailing_commas: false,
unescape: false,
}),
}
}
)
.verified_stmt(sql),
Statement::Query(Box::new(Query {
with: None,
@ -1041,11 +1037,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
fn parse_escaped_backticks_with_escape() {
let sql = "SELECT ```quoted identifier```";
assert_eq!(
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: None,
}
.verified_stmt(sql),
TestedDialects::new(vec![Box::new(MySqlDialect {})]).verified_stmt(sql),
Statement::Query(Box::new(Query {
with: None,
body: Box::new(SetExpr::Select(Box::new(Select {
@ -1088,10 +1080,10 @@ fn parse_escaped_backticks_with_escape() {
fn parse_escaped_backticks_with_no_escape() {
let sql = "SELECT ```quoted identifier```";
assert_eq!(
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: Some(ParserOptions::new().with_unescape(false)),
}
TestedDialects::new_with_options(
vec![Box::new(MySqlDialect {})],
ParserOptions::new().with_unescape(false)
)
.verified_stmt(sql),
Statement::Query(Box::new(Query {
with: None,
@ -1144,55 +1136,26 @@ fn parse_unterminated_escape() {
#[test]
fn check_roundtrip_of_escaped_string() {
let options = Some(ParserOptions::new().with_unescape(false));
let options = ParserOptions::new().with_unescape(false);
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r"SELECT 'I\'m fine'");
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r#"SELECT 'I''m fine'"#);
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r"SELECT 'I\\\'m fine'");
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r"SELECT 'I\\\'m fine'");
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r#"SELECT "I\"m fine""#);
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r#"SELECT "I""m fine""#);
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r#"SELECT "I\\\"m fine""#);
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: options.clone(),
}
.verified_stmt(r#"SELECT "I\\\"m fine""#);
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options,
}
.verified_stmt(r#"SELECT "I'm ''fine''""#);
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r"SELECT 'I\'m fine'");
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r#"SELECT 'I''m fine'"#);
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r"SELECT 'I\\\'m fine'");
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r"SELECT 'I\\\'m fine'");
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r#"SELECT "I\"m fine""#);
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r#"SELECT "I""m fine""#);
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r#"SELECT "I\\\"m fine""#);
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r#"SELECT "I\\\"m fine""#);
TestedDialects::new_with_options(vec![Box::new(MySqlDialect {})], options.clone())
.verified_stmt(r#"SELECT "I'm ''fine''""#);
}
#[test]
@ -2624,17 +2587,11 @@ fn parse_create_table_with_fulltext_definition_should_not_accept_constraint_name
}
fn mysql() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MySqlDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(MySqlDialect {})])
}
fn mysql_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(MySqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(MySqlDialect {}), Box::new(GenericDialect {})])
}
#[test]

View file

@ -2973,17 +2973,14 @@ fn parse_on_commit() {
}
fn pg() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(PostgreSqlDialect {})])
}
fn pg_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(PostgreSqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(PostgreSqlDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]

View file

@ -171,17 +171,14 @@ fn parse_delimited_identifiers() {
}
fn redshift() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(RedshiftSqlDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(RedshiftSqlDialect {})])
}
fn redshift_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(RedshiftSqlDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(RedshiftSqlDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]

View file

@ -854,10 +854,8 @@ fn parse_sf_create_or_replace_view_with_comment_missing_equal() {
#[test]
fn parse_sf_create_or_replace_with_comment_for_snowflake() {
let sql = "CREATE OR REPLACE VIEW v COMMENT = 'hello, world' AS SELECT 1";
let dialect = test_utils::TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {}) as Box<dyn Dialect>],
options: None,
};
let dialect =
test_utils::TestedDialects::new(vec![Box::new(SnowflakeDialect {}) as Box<dyn Dialect>]);
match dialect.verified_stmt(sql) {
Statement::CreateView {
@ -1250,24 +1248,25 @@ fn test_array_agg_func() {
}
fn snowflake() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(SnowflakeDialect {})])
}
fn snowflake_with_recursion_limit(recursion_limit: usize) -> TestedDialects {
TestedDialects::new(vec![Box::new(SnowflakeDialect {})]).with_recursion_limit(recursion_limit)
}
fn snowflake_without_unescape() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {})],
options: Some(ParserOptions::new().with_unescape(false)),
}
TestedDialects::new_with_options(
vec![Box::new(SnowflakeDialect {})],
ParserOptions::new().with_unescape(false),
)
}
fn snowflake_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(SnowflakeDialect {}),
Box::new(GenericDialect {}),
])
}
#[test]
@ -2759,3 +2758,26 @@ fn parse_view_column_descriptions() {
_ => unreachable!(),
};
}
#[test]
fn test_parentheses_overflow() {
let max_nesting_level: usize = 30;
// Verify the recursion check is not too wasteful... (num of parentheses - 2 is acceptable)
let slack = 2;
let l_parens = "(".repeat(max_nesting_level - slack);
let r_parens = ")".repeat(max_nesting_level - slack);
let sql = format!("SELECT * FROM {l_parens}a.b.c{r_parens}");
let parsed =
snowflake_with_recursion_limit(max_nesting_level).parse_sql_statements(sql.as_str());
assert_eq!(parsed.err(), None);
// Verify the recursion check triggers... (num of parentheses - 1 is acceptable)
let slack = 1;
let l_parens = "(".repeat(max_nesting_level - slack);
let r_parens = ")".repeat(max_nesting_level - slack);
let sql = format!("SELECT * FROM {l_parens}a.b.c{r_parens}");
let parsed =
snowflake_with_recursion_limit(max_nesting_level).parse_sql_statements(sql.as_str());
assert_eq!(parsed.err(), Some(ParserError::RecursionLimitExceeded));
}

View file

@ -529,14 +529,13 @@ fn parse_start_transaction_with_modifier() {
sqlite_and_generic().one_statement_parses_to("BEGIN IMMEDIATE", "BEGIN IMMEDIATE TRANSACTION");
sqlite_and_generic().one_statement_parses_to("BEGIN EXCLUSIVE", "BEGIN EXCLUSIVE TRANSACTION");
let unsupported_dialects = TestedDialects {
dialects: all_dialects()
let unsupported_dialects = TestedDialects::new(
all_dialects()
.dialects
.into_iter()
.filter(|x| !(x.is::<SQLiteDialect>() || x.is::<GenericDialect>()))
.collect(),
options: None,
};
);
let res = unsupported_dialects.parse_sql_statements("BEGIN DEFERRED");
assert_eq!(
ParserError::ParserError("Expected: end of statement, found: DEFERRED".to_string()),
@ -571,22 +570,16 @@ fn test_dollar_identifier_as_placeholder() {
}
fn sqlite() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SQLiteDialect {})],
options: None,
}
TestedDialects::new(vec![Box::new(SQLiteDialect {})])
}
fn sqlite_with_options(options: ParserOptions) -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SQLiteDialect {})],
options: Some(options),
}
TestedDialects::new_with_options(vec![Box::new(SQLiteDialect {})], options)
}
fn sqlite_and_generic() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SQLiteDialect {}), Box::new(GenericDialect {})],
options: None,
}
TestedDialects::new(vec![
Box::new(SQLiteDialect {}),
Box::new(GenericDialect {}),
])
}