mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-04 06:18:17 +00:00
Add MySql, BigQuery to all dialects (#697)
* Add MySql, BigQuery to all dialects * move unsupported on mysql from common
This commit is contained in:
parent
f7817bc7c2
commit
0428ac742b
12 changed files with 1200 additions and 164 deletions
|
@ -35,7 +35,7 @@ pub use self::query::{
|
||||||
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
|
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
|
||||||
TableAlias, TableFactor, TableWithJoins, Top, Values, With,
|
TableAlias, TableFactor, TableWithJoins, Top, Values, With,
|
||||||
};
|
};
|
||||||
pub use self::value::{DateTimeField, TrimWhereField, Value};
|
pub use self::value::{escape_quoted_string, DateTimeField, TrimWhereField, Value};
|
||||||
|
|
||||||
mod data_type;
|
mod data_type;
|
||||||
mod ddl;
|
mod ddl;
|
||||||
|
|
|
@ -3753,6 +3753,8 @@ impl<'a> Parser<'a> {
|
||||||
// ignore the <separator> and treat the multiple strings as
|
// ignore the <separator> and treat the multiple strings as
|
||||||
// a single <literal>."
|
// a single <literal>."
|
||||||
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
|
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
|
||||||
|
// Support for MySql dialect double qouted string, `AS "HOUR"` for example
|
||||||
|
Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))),
|
||||||
not_an_ident => {
|
not_an_ident => {
|
||||||
if after_as {
|
if after_as {
|
||||||
return self.expected("an identifier after AS", not_an_ident);
|
return self.expected("an identifier after AS", not_an_ident);
|
||||||
|
@ -3836,6 +3838,7 @@ impl<'a> Parser<'a> {
|
||||||
match self.next_token() {
|
match self.next_token() {
|
||||||
Token::Word(w) => Ok(w.to_ident()),
|
Token::Word(w) => Ok(w.to_ident()),
|
||||||
Token::SingleQuotedString(s) => Ok(Ident::with_quote('\'', s)),
|
Token::SingleQuotedString(s) => Ok(Ident::with_quote('\'', s)),
|
||||||
|
Token::DoubleQuotedString(s) => Ok(Ident::with_quote('\"', s)),
|
||||||
unexpected => self.expected("identifier", unexpected),
|
unexpected => self.expected("identifier", unexpected),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,8 @@ pub fn all_dialects() -> TestedDialects {
|
||||||
Box::new(SnowflakeDialect {}),
|
Box::new(SnowflakeDialect {}),
|
||||||
Box::new(HiveDialect {}),
|
Box::new(HiveDialect {}),
|
||||||
Box::new(RedshiftSqlDialect {}),
|
Box::new(RedshiftSqlDialect {}),
|
||||||
|
Box::new(MySqlDialect {}),
|
||||||
|
Box::new(BigQueryDialect {}),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,115 @@ fn parse_cast_type() {
|
||||||
bigquery().verified_only_select(sql);
|
bigquery().verified_only_select(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = bigquery().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = bigquery().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = bigquery().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = bigquery().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = bigquery().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = bigquery().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn bigquery() -> TestedDialects {
|
fn bigquery() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(BigQueryDialect {})],
|
dialects: vec![Box::new(BigQueryDialect {})],
|
||||||
|
|
|
@ -152,6 +152,168 @@ fn parse_kill() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_delimited_identifiers() {
|
||||||
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
|
let select = clickhouse().verified_only_select(
|
||||||
|
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
||||||
|
);
|
||||||
|
// check FROM
|
||||||
|
match only(select.from).relation {
|
||||||
|
TableFactor::Table {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
args,
|
||||||
|
with_hints,
|
||||||
|
} => {
|
||||||
|
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||||
|
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||||
|
assert!(args.is_none());
|
||||||
|
assert!(with_hints.is_empty());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
|
// check SELECT
|
||||||
|
assert_eq!(3, select.projection.len());
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::CompoundIdentifier(vec![
|
||||||
|
Ident::with_quote('"', "alias"),
|
||||||
|
Ident::with_quote('"', "bar baz"),
|
||||||
|
]),
|
||||||
|
expr_from_projection(&select.projection[0]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
||||||
|
args: vec![],
|
||||||
|
over: None,
|
||||||
|
distinct: false,
|
||||||
|
special: false,
|
||||||
|
}),
|
||||||
|
expr_from_projection(&select.projection[1]),
|
||||||
|
);
|
||||||
|
match &select.projection[2] {
|
||||||
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
|
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
||||||
|
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ExprWithAlias"),
|
||||||
|
}
|
||||||
|
|
||||||
|
clickhouse().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
||||||
|
clickhouse().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
||||||
|
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = clickhouse().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn clickhouse() -> TestedDialects {
|
fn clickhouse() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(ClickHouseDialect {})],
|
dialects: vec![Box::new(ClickHouseDialect {})],
|
||||||
|
|
|
@ -886,61 +886,6 @@ fn parse_not_precedence() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_like() {
|
|
||||||
fn chk(negated: bool) {
|
|
||||||
let sql = &format!(
|
|
||||||
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
|
||||||
if negated { "NOT " } else { "" }
|
|
||||||
);
|
|
||||||
let select = verified_only_select(sql);
|
|
||||||
assert_eq!(
|
|
||||||
Expr::Like {
|
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
|
||||||
negated,
|
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
|
||||||
escape_char: None,
|
|
||||||
},
|
|
||||||
select.selection.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test with escape char
|
|
||||||
let sql = &format!(
|
|
||||||
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
|
||||||
if negated { "NOT " } else { "" }
|
|
||||||
);
|
|
||||||
let select = verified_only_select(sql);
|
|
||||||
assert_eq!(
|
|
||||||
Expr::Like {
|
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
|
||||||
negated,
|
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
|
||||||
escape_char: Some('\\'),
|
|
||||||
},
|
|
||||||
select.selection.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
|
||||||
// This was previously mishandled (#81).
|
|
||||||
let sql = &format!(
|
|
||||||
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
|
||||||
if negated { "NOT " } else { "" }
|
|
||||||
);
|
|
||||||
let select = verified_only_select(sql);
|
|
||||||
assert_eq!(
|
|
||||||
Expr::IsNull(Box::new(Expr::Like {
|
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
|
||||||
negated,
|
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
|
||||||
escape_char: None,
|
|
||||||
})),
|
|
||||||
select.selection.unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
chk(false);
|
|
||||||
chk(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_null_like() {
|
fn parse_null_like() {
|
||||||
let sql = "SELECT \
|
let sql = "SELECT \
|
||||||
|
@ -1035,60 +980,6 @@ fn parse_ilike() {
|
||||||
chk(true);
|
chk(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_similar_to() {
|
|
||||||
fn chk(negated: bool) {
|
|
||||||
let sql = &format!(
|
|
||||||
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
|
||||||
if negated { "NOT " } else { "" }
|
|
||||||
);
|
|
||||||
let select = verified_only_select(sql);
|
|
||||||
assert_eq!(
|
|
||||||
Expr::SimilarTo {
|
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
|
||||||
negated,
|
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
|
||||||
escape_char: None,
|
|
||||||
},
|
|
||||||
select.selection.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test with escape char
|
|
||||||
let sql = &format!(
|
|
||||||
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
|
||||||
if negated { "NOT " } else { "" }
|
|
||||||
);
|
|
||||||
let select = verified_only_select(sql);
|
|
||||||
assert_eq!(
|
|
||||||
Expr::SimilarTo {
|
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
|
||||||
negated,
|
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
|
||||||
escape_char: Some('\\'),
|
|
||||||
},
|
|
||||||
select.selection.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
|
||||||
let sql = &format!(
|
|
||||||
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
|
||||||
if negated { "NOT " } else { "" }
|
|
||||||
);
|
|
||||||
let select = verified_only_select(sql);
|
|
||||||
assert_eq!(
|
|
||||||
Expr::IsNull(Box::new(Expr::SimilarTo {
|
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
|
||||||
negated,
|
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
|
||||||
escape_char: Some('\\'),
|
|
||||||
})),
|
|
||||||
select.selection.unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
chk(false);
|
|
||||||
chk(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_in_list() {
|
fn parse_in_list() {
|
||||||
fn chk(negated: bool) {
|
fn chk(negated: bool) {
|
||||||
|
@ -3453,59 +3344,6 @@ fn parse_unnest() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_delimited_identifiers() {
|
|
||||||
// check that quoted identifiers in any position remain quoted after serialization
|
|
||||||
let select = verified_only_select(
|
|
||||||
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
|
||||||
);
|
|
||||||
// check FROM
|
|
||||||
match only(select.from).relation {
|
|
||||||
TableFactor::Table {
|
|
||||||
name,
|
|
||||||
alias,
|
|
||||||
args,
|
|
||||||
with_hints,
|
|
||||||
} => {
|
|
||||||
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
|
||||||
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
|
||||||
assert!(args.is_none());
|
|
||||||
assert!(with_hints.is_empty());
|
|
||||||
}
|
|
||||||
_ => panic!("Expecting TableFactor::Table"),
|
|
||||||
}
|
|
||||||
// check SELECT
|
|
||||||
assert_eq!(3, select.projection.len());
|
|
||||||
assert_eq!(
|
|
||||||
&Expr::CompoundIdentifier(vec![
|
|
||||||
Ident::with_quote('"', "alias"),
|
|
||||||
Ident::with_quote('"', "bar baz"),
|
|
||||||
]),
|
|
||||||
expr_from_projection(&select.projection[0]),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
&Expr::Function(Function {
|
|
||||||
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
|
||||||
args: vec![],
|
|
||||||
over: None,
|
|
||||||
distinct: false,
|
|
||||||
special: false,
|
|
||||||
}),
|
|
||||||
expr_from_projection(&select.projection[1]),
|
|
||||||
);
|
|
||||||
match &select.projection[2] {
|
|
||||||
SelectItem::ExprWithAlias { expr, alias } => {
|
|
||||||
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
|
||||||
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
|
||||||
}
|
|
||||||
_ => panic!("Expected ExprWithAlias"),
|
|
||||||
}
|
|
||||||
|
|
||||||
verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
|
||||||
verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
|
||||||
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_parens() {
|
fn parse_parens() {
|
||||||
use self::BinaryOperator::*;
|
use self::BinaryOperator::*;
|
||||||
|
|
|
@ -15,7 +15,10 @@
|
||||||
//! Test SQL syntax specific to Hive. The parser based on the generic dialect
|
//! Test SQL syntax specific to Hive. The parser based on the generic dialect
|
||||||
//! is also tested (on the inputs it can handle).
|
//! is also tested (on the inputs it can handle).
|
||||||
|
|
||||||
use sqlparser::ast::{CreateFunctionUsing, Expr, Ident, ObjectName, Statement, UnaryOperator};
|
use sqlparser::ast::{
|
||||||
|
CreateFunctionUsing, Expr, Function, Ident, ObjectName, SelectItem, Statement, TableFactor,
|
||||||
|
UnaryOperator, Value,
|
||||||
|
};
|
||||||
use sqlparser::dialect::{GenericDialect, HiveDialect};
|
use sqlparser::dialect::{GenericDialect, HiveDialect};
|
||||||
use sqlparser::parser::ParserError;
|
use sqlparser::parser::ParserError;
|
||||||
use sqlparser::test_utils::*;
|
use sqlparser::test_utils::*;
|
||||||
|
@ -300,6 +303,168 @@ fn filter_as_alias() {
|
||||||
println!("{}", hive().one_statement_parses_to(sql, expected));
|
println!("{}", hive().one_statement_parses_to(sql, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_delimited_identifiers() {
|
||||||
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
|
let select = hive().verified_only_select(
|
||||||
|
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
||||||
|
);
|
||||||
|
// check FROM
|
||||||
|
match only(select.from).relation {
|
||||||
|
TableFactor::Table {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
args,
|
||||||
|
with_hints,
|
||||||
|
} => {
|
||||||
|
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||||
|
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||||
|
assert!(args.is_none());
|
||||||
|
assert!(with_hints.is_empty());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
|
// check SELECT
|
||||||
|
assert_eq!(3, select.projection.len());
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::CompoundIdentifier(vec![
|
||||||
|
Ident::with_quote('"', "alias"),
|
||||||
|
Ident::with_quote('"', "bar baz"),
|
||||||
|
]),
|
||||||
|
expr_from_projection(&select.projection[0]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
||||||
|
args: vec![],
|
||||||
|
over: None,
|
||||||
|
distinct: false,
|
||||||
|
special: false,
|
||||||
|
}),
|
||||||
|
expr_from_projection(&select.projection[1]),
|
||||||
|
);
|
||||||
|
match &select.projection[2] {
|
||||||
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
|
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
||||||
|
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ExprWithAlias"),
|
||||||
|
}
|
||||||
|
|
||||||
|
hive().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
||||||
|
hive().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
||||||
|
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = hive().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = hive().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = hive().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = hive().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = hive().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = hive().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn hive() -> TestedDialects {
|
fn hive() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(HiveDialect {})],
|
dialects: vec![Box::new(HiveDialect {})],
|
||||||
|
|
|
@ -140,6 +140,168 @@ fn parse_mssql_create_role() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_delimited_identifiers() {
|
||||||
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
|
let select = ms_and_generic().verified_only_select(
|
||||||
|
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
||||||
|
);
|
||||||
|
// check FROM
|
||||||
|
match only(select.from).relation {
|
||||||
|
TableFactor::Table {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
args,
|
||||||
|
with_hints,
|
||||||
|
} => {
|
||||||
|
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||||
|
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||||
|
assert!(args.is_none());
|
||||||
|
assert!(with_hints.is_empty());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
|
// check SELECT
|
||||||
|
assert_eq!(3, select.projection.len());
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::CompoundIdentifier(vec![
|
||||||
|
Ident::with_quote('"', "alias"),
|
||||||
|
Ident::with_quote('"', "bar baz"),
|
||||||
|
]),
|
||||||
|
expr_from_projection(&select.projection[0]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
||||||
|
args: vec![],
|
||||||
|
over: None,
|
||||||
|
distinct: false,
|
||||||
|
special: false,
|
||||||
|
}),
|
||||||
|
expr_from_projection(&select.projection[1]),
|
||||||
|
);
|
||||||
|
match &select.projection[2] {
|
||||||
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
|
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
||||||
|
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ExprWithAlias"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ms_and_generic().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
||||||
|
ms_and_generic().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
||||||
|
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = ms_and_generic().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = ms_and_generic().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = ms_and_generic().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = ms_and_generic().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = ms_and_generic().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = ms_and_generic().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn ms() -> TestedDialects {
|
fn ms() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(MsSqlDialect {})],
|
dialects: vec![Box::new(MsSqlDialect {})],
|
||||||
|
|
|
@ -1966,3 +1966,165 @@ fn parse_create_role() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_delimited_identifiers() {
|
||||||
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
|
let select = pg().verified_only_select(
|
||||||
|
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
||||||
|
);
|
||||||
|
// check FROM
|
||||||
|
match only(select.from).relation {
|
||||||
|
TableFactor::Table {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
args,
|
||||||
|
with_hints,
|
||||||
|
} => {
|
||||||
|
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||||
|
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||||
|
assert!(args.is_none());
|
||||||
|
assert!(with_hints.is_empty());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
|
// check SELECT
|
||||||
|
assert_eq!(3, select.projection.len());
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::CompoundIdentifier(vec![
|
||||||
|
Ident::with_quote('"', "alias"),
|
||||||
|
Ident::with_quote('"', "bar baz"),
|
||||||
|
]),
|
||||||
|
expr_from_projection(&select.projection[0]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
||||||
|
args: vec![],
|
||||||
|
over: None,
|
||||||
|
distinct: false,
|
||||||
|
special: false,
|
||||||
|
}),
|
||||||
|
expr_from_projection(&select.projection[1]),
|
||||||
|
);
|
||||||
|
match &select.projection[2] {
|
||||||
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
|
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
||||||
|
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ExprWithAlias"),
|
||||||
|
}
|
||||||
|
|
||||||
|
pg().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
||||||
|
pg().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
||||||
|
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
|
@ -95,6 +95,168 @@ fn test_double_quotes_over_db_schema_table_name() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_delimited_identifiers() {
|
||||||
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
|
let select = redshift().verified_only_select(
|
||||||
|
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
||||||
|
);
|
||||||
|
// check FROM
|
||||||
|
match only(select.from).relation {
|
||||||
|
TableFactor::Table {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
args,
|
||||||
|
with_hints,
|
||||||
|
} => {
|
||||||
|
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||||
|
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||||
|
assert!(args.is_none());
|
||||||
|
assert!(with_hints.is_empty());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
|
// check SELECT
|
||||||
|
assert_eq!(3, select.projection.len());
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::CompoundIdentifier(vec![
|
||||||
|
Ident::with_quote('"', "alias"),
|
||||||
|
Ident::with_quote('"', "bar baz"),
|
||||||
|
]),
|
||||||
|
expr_from_projection(&select.projection[0]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
||||||
|
args: vec![],
|
||||||
|
over: None,
|
||||||
|
distinct: false,
|
||||||
|
special: false,
|
||||||
|
}),
|
||||||
|
expr_from_projection(&select.projection[1]),
|
||||||
|
);
|
||||||
|
match &select.projection[2] {
|
||||||
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
|
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
||||||
|
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ExprWithAlias"),
|
||||||
|
}
|
||||||
|
|
||||||
|
redshift().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
||||||
|
redshift().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
||||||
|
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = redshift().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = redshift().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = redshift().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = redshift().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = redshift().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = redshift().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn redshift() -> TestedDialects {
|
fn redshift() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(RedshiftSqlDialect {})],
|
dialects: vec![Box::new(RedshiftSqlDialect {})],
|
||||||
|
|
|
@ -172,6 +172,168 @@ fn parse_json_using_colon() {
|
||||||
snowflake().one_statement_parses_to("SELECT a:b::int FROM t", "SELECT CAST(a:b AS INT) FROM t");
|
snowflake().one_statement_parses_to("SELECT a:b::int FROM t", "SELECT CAST(a:b AS INT) FROM t");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_delimited_identifiers() {
|
||||||
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
|
let select = snowflake().verified_only_select(
|
||||||
|
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
|
||||||
|
);
|
||||||
|
// check FROM
|
||||||
|
match only(select.from).relation {
|
||||||
|
TableFactor::Table {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
args,
|
||||||
|
with_hints,
|
||||||
|
} => {
|
||||||
|
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
|
||||||
|
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
|
||||||
|
assert!(args.is_none());
|
||||||
|
assert!(with_hints.is_empty());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
|
// check SELECT
|
||||||
|
assert_eq!(3, select.projection.len());
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::CompoundIdentifier(vec![
|
||||||
|
Ident::with_quote('"', "alias"),
|
||||||
|
Ident::with_quote('"', "bar baz"),
|
||||||
|
]),
|
||||||
|
expr_from_projection(&select.projection[0]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&Expr::Function(Function {
|
||||||
|
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
|
||||||
|
args: vec![],
|
||||||
|
over: None,
|
||||||
|
distinct: false,
|
||||||
|
special: false,
|
||||||
|
}),
|
||||||
|
expr_from_projection(&select.projection[1]),
|
||||||
|
);
|
||||||
|
match &select.projection[2] {
|
||||||
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
|
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
|
||||||
|
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ExprWithAlias"),
|
||||||
|
}
|
||||||
|
|
||||||
|
snowflake().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
|
||||||
|
snowflake().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
|
||||||
|
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn snowflake() -> TestedDialects {
|
fn snowflake() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(SnowflakeDialect {})],
|
dialects: vec![Box::new(SnowflakeDialect {})],
|
||||||
|
|
|
@ -133,6 +133,115 @@ fn test_placeholder() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_like() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||||
|
// This was previously mishandled (#81).
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::Like {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_similar_to() {
|
||||||
|
fn chk(negated: bool) {
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: None,
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with escape char
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
},
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
|
||||||
|
let sql = &format!(
|
||||||
|
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
|
||||||
|
if negated { "NOT " } else { "" }
|
||||||
|
);
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::IsNull(Box::new(Expr::SimilarTo {
|
||||||
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
|
negated,
|
||||||
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
|
escape_char: Some('\\'),
|
||||||
|
})),
|
||||||
|
select.selection.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chk(false);
|
||||||
|
chk(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn sqlite() -> TestedDialects {
|
fn sqlite() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(SQLiteDialect {})],
|
dialects: vec![Box::new(SQLiteDialect {})],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue