Skip to content

Commit 0428ac7

Browse files
authored
Add MySql, BigQuery to all dialects (#697)
* Add MySql, BigQuery to all dialects * move unsupported on mysql from common
1 parent f7817bc commit 0428ac7

12 files changed

+1200
-164
lines changed

src/ast/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub use self::query::{
3535
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
3636
TableAlias, TableFactor, TableWithJoins, Top, Values, With,
3737
};
38-
pub use self::value::{DateTimeField, TrimWhereField, Value};
38+
pub use self::value::{escape_quoted_string, DateTimeField, TrimWhereField, Value};
3939

4040
mod data_type;
4141
mod ddl;

src/parser.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3753,6 +3753,8 @@ impl<'a> Parser<'a> {
37533753
// ignore the <separator> and treat the multiple strings as
37543754
// a single <literal>."
37553755
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
3756+
// Support for MySql dialect double qouted string, `AS "HOUR"` for example
3757+
Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))),
37563758
not_an_ident => {
37573759
if after_as {
37583760
return self.expected("an identifier after AS", not_an_ident);
@@ -3836,6 +3838,7 @@ impl<'a> Parser<'a> {
38363838
match self.next_token() {
38373839
Token::Word(w) => Ok(w.to_ident()),
38383840
Token::SingleQuotedString(s) => Ok(Ident::with_quote('\'', s)),
3841+
Token::DoubleQuotedString(s) => Ok(Ident::with_quote('\"', s)),
38393842
unexpected => self.expected("identifier", unexpected),
38403843
}
38413844
}

src/test_utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ pub fn all_dialects() -> TestedDialects {
142142
Box::new(SnowflakeDialect {}),
143143
Box::new(HiveDialect {}),
144144
Box::new(RedshiftSqlDialect {}),
145+
Box::new(MySqlDialect {}),
146+
Box::new(BigQueryDialect {}),
145147
],
146148
}
147149
}

tests/sqlparser_bigquery.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,115 @@ fn parse_cast_type() {
115115
bigquery().verified_only_select(sql);
116116
}
117117

118+
#[test]
119+
fn parse_like() {
120+
fn chk(negated: bool) {
121+
let sql = &format!(
122+
"SELECT * FROM customers WHERE name {}LIKE '%a'",
123+
if negated { "NOT " } else { "" }
124+
);
125+
let select = bigquery().verified_only_select(sql);
126+
assert_eq!(
127+
Expr::Like {
128+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
129+
negated,
130+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
131+
escape_char: None,
132+
},
133+
select.selection.unwrap()
134+
);
135+
136+
// Test with escape char
137+
let sql = &format!(
138+
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
139+
if negated { "NOT " } else { "" }
140+
);
141+
let select = bigquery().verified_only_select(sql);
142+
assert_eq!(
143+
Expr::Like {
144+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
145+
negated,
146+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
147+
escape_char: Some('\\'),
148+
},
149+
select.selection.unwrap()
150+
);
151+
152+
// This statement tests that LIKE and NOT LIKE have the same precedence.
153+
// This was previously mishandled (#81).
154+
let sql = &format!(
155+
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
156+
if negated { "NOT " } else { "" }
157+
);
158+
let select = bigquery().verified_only_select(sql);
159+
assert_eq!(
160+
Expr::IsNull(Box::new(Expr::Like {
161+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
162+
negated,
163+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
164+
escape_char: None,
165+
})),
166+
select.selection.unwrap()
167+
);
168+
}
169+
chk(false);
170+
chk(true);
171+
}
172+
173+
#[test]
174+
fn parse_similar_to() {
175+
fn chk(negated: bool) {
176+
let sql = &format!(
177+
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
178+
if negated { "NOT " } else { "" }
179+
);
180+
let select = bigquery().verified_only_select(sql);
181+
assert_eq!(
182+
Expr::SimilarTo {
183+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
184+
negated,
185+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
186+
escape_char: None,
187+
},
188+
select.selection.unwrap()
189+
);
190+
191+
// Test with escape char
192+
let sql = &format!(
193+
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
194+
if negated { "NOT " } else { "" }
195+
);
196+
let select = bigquery().verified_only_select(sql);
197+
assert_eq!(
198+
Expr::SimilarTo {
199+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
200+
negated,
201+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
202+
escape_char: Some('\\'),
203+
},
204+
select.selection.unwrap()
205+
);
206+
207+
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
208+
let sql = &format!(
209+
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
210+
if negated { "NOT " } else { "" }
211+
);
212+
let select = bigquery().verified_only_select(sql);
213+
assert_eq!(
214+
Expr::IsNull(Box::new(Expr::SimilarTo {
215+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
216+
negated,
217+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
218+
escape_char: Some('\\'),
219+
})),
220+
select.selection.unwrap()
221+
);
222+
}
223+
chk(false);
224+
chk(true);
225+
}
226+
118227
fn bigquery() -> TestedDialects {
119228
TestedDialects {
120229
dialects: vec![Box::new(BigQueryDialect {})],

tests/sqlparser_clickhouse.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,168 @@ fn parse_kill() {
152152
);
153153
}
154154

155+
#[test]
156+
fn parse_delimited_identifiers() {
157+
// check that quoted identifiers in any position remain quoted after serialization
158+
let select = clickhouse().verified_only_select(
159+
r#"SELECT "alias"."bar baz", "myfun"(), "simple id" AS "column alias" FROM "a table" AS "alias""#,
160+
);
161+
// check FROM
162+
match only(select.from).relation {
163+
TableFactor::Table {
164+
name,
165+
alias,
166+
args,
167+
with_hints,
168+
} => {
169+
assert_eq!(vec![Ident::with_quote('"', "a table")], name.0);
170+
assert_eq!(Ident::with_quote('"', "alias"), alias.unwrap().name);
171+
assert!(args.is_none());
172+
assert!(with_hints.is_empty());
173+
}
174+
_ => panic!("Expecting TableFactor::Table"),
175+
}
176+
// check SELECT
177+
assert_eq!(3, select.projection.len());
178+
assert_eq!(
179+
&Expr::CompoundIdentifier(vec![
180+
Ident::with_quote('"', "alias"),
181+
Ident::with_quote('"', "bar baz"),
182+
]),
183+
expr_from_projection(&select.projection[0]),
184+
);
185+
assert_eq!(
186+
&Expr::Function(Function {
187+
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
188+
args: vec![],
189+
over: None,
190+
distinct: false,
191+
special: false,
192+
}),
193+
expr_from_projection(&select.projection[1]),
194+
);
195+
match &select.projection[2] {
196+
SelectItem::ExprWithAlias { expr, alias } => {
197+
assert_eq!(&Expr::Identifier(Ident::with_quote('"', "simple id")), expr);
198+
assert_eq!(&Ident::with_quote('"', "column alias"), alias);
199+
}
200+
_ => panic!("Expected ExprWithAlias"),
201+
}
202+
203+
clickhouse().verified_stmt(r#"CREATE TABLE "foo" ("bar" "int")"#);
204+
clickhouse().verified_stmt(r#"ALTER TABLE foo ADD CONSTRAINT "bar" PRIMARY KEY (baz)"#);
205+
//TODO verified_stmt(r#"UPDATE foo SET "bar" = 5"#);
206+
}
207+
208+
#[test]
209+
fn parse_like() {
210+
fn chk(negated: bool) {
211+
let sql = &format!(
212+
"SELECT * FROM customers WHERE name {}LIKE '%a'",
213+
if negated { "NOT " } else { "" }
214+
);
215+
let select = clickhouse().verified_only_select(sql);
216+
assert_eq!(
217+
Expr::Like {
218+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
219+
negated,
220+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
221+
escape_char: None,
222+
},
223+
select.selection.unwrap()
224+
);
225+
226+
// Test with escape char
227+
let sql = &format!(
228+
"SELECT * FROM customers WHERE name {}LIKE '%a' ESCAPE '\\'",
229+
if negated { "NOT " } else { "" }
230+
);
231+
let select = clickhouse().verified_only_select(sql);
232+
assert_eq!(
233+
Expr::Like {
234+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
235+
negated,
236+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
237+
escape_char: Some('\\'),
238+
},
239+
select.selection.unwrap()
240+
);
241+
242+
// This statement tests that LIKE and NOT LIKE have the same precedence.
243+
// This was previously mishandled (#81).
244+
let sql = &format!(
245+
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
246+
if negated { "NOT " } else { "" }
247+
);
248+
let select = clickhouse().verified_only_select(sql);
249+
assert_eq!(
250+
Expr::IsNull(Box::new(Expr::Like {
251+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
252+
negated,
253+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
254+
escape_char: None,
255+
})),
256+
select.selection.unwrap()
257+
);
258+
}
259+
chk(false);
260+
chk(true);
261+
}
262+
263+
#[test]
264+
fn parse_similar_to() {
265+
fn chk(negated: bool) {
266+
let sql = &format!(
267+
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a'",
268+
if negated { "NOT " } else { "" }
269+
);
270+
let select = clickhouse().verified_only_select(sql);
271+
assert_eq!(
272+
Expr::SimilarTo {
273+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
274+
negated,
275+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
276+
escape_char: None,
277+
},
278+
select.selection.unwrap()
279+
);
280+
281+
// Test with escape char
282+
let sql = &format!(
283+
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\'",
284+
if negated { "NOT " } else { "" }
285+
);
286+
let select = clickhouse().verified_only_select(sql);
287+
assert_eq!(
288+
Expr::SimilarTo {
289+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
290+
negated,
291+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
292+
escape_char: Some('\\'),
293+
},
294+
select.selection.unwrap()
295+
);
296+
297+
// This statement tests that SIMILAR TO and NOT SIMILAR TO have the same precedence.
298+
let sql = &format!(
299+
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE '\\' IS NULL",
300+
if negated { "NOT " } else { "" }
301+
);
302+
let select = clickhouse().verified_only_select(sql);
303+
assert_eq!(
304+
Expr::IsNull(Box::new(Expr::SimilarTo {
305+
expr: Box::new(Expr::Identifier(Ident::new("name"))),
306+
negated,
307+
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
308+
escape_char: Some('\\'),
309+
})),
310+
select.selection.unwrap()
311+
);
312+
}
313+
chk(false);
314+
chk(true);
315+
}
316+
155317
fn clickhouse() -> TestedDialects {
156318
TestedDialects {
157319
dialects: vec![Box::new(ClickHouseDialect {})],

0 commit comments

Comments
 (0)