Skip to content

Commit 97a148a

Browse files
authored
Add BigQuery dialect (apache#490)
1 parent 484a7b6 commit 97a148a

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

examples/cli.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ $ cargo run --feature json_example --example cli FILENAME.sql [--dialectname]
3838

3939
let dialect: Box<dyn Dialect> = match std::env::args().nth(2).unwrap_or_default().as_ref() {
4040
"--ansi" => Box::new(AnsiDialect {}),
41+
"--bigquery" => Box::new(BigQueryDialect {}),
4142
"--postgres" => Box::new(PostgreSqlDialect {}),
4243
"--ms" => Box::new(MsSqlDialect {}),
4344
"--mysql" => Box::new(MySqlDialect {}),

src/dialect/bigquery.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
use crate::dialect::Dialect;
14+
15+
#[derive(Debug, Default)]
16+
pub struct BigQueryDialect;
17+
18+
impl Dialect for BigQueryDialect {
19+
// See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#identifiers
20+
fn is_delimited_identifier_start(&self, ch: char) -> bool {
21+
ch == '`'
22+
}
23+
24+
fn is_identifier_start(&self, ch: char) -> bool {
25+
('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) || ch == '_'
26+
}
27+
28+
fn is_identifier_part(&self, ch: char) -> bool {
29+
('a'..='z').contains(&ch)
30+
|| ('A'..='Z').contains(&ch)
31+
|| ('0'..='9').contains(&ch)
32+
|| ch == '_'
33+
|| ch == '-'
34+
}
35+
}

src/dialect/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// limitations under the License.
1212

1313
mod ansi;
14+
mod bigquery;
1415
mod clickhouse;
1516
mod generic;
1617
mod hive;
@@ -27,6 +28,7 @@ use core::iter::Peekable;
2728
use core::str::Chars;
2829

2930
pub use self::ansi::AnsiDialect;
31+
pub use self::bigquery::BigQueryDialect;
3032
pub use self::clickhouse::ClickHouseDialect;
3133
pub use self::generic::GenericDialect;
3234
pub use self::hive::HiveDialect;

tests/sqlparser_bigquery.rs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
#[macro_use]
14+
mod test_utils;
15+
16+
use test_utils::*;
17+
18+
use sqlparser::ast::*;
19+
use sqlparser::dialect::BigQueryDialect;
20+
21+
#[test]
22+
fn parse_table_identifiers() {
23+
fn test_table_ident(ident: &str, expected: Vec<Ident>) {
24+
let sql = format!("SELECT 1 FROM {}", ident);
25+
let select = bigquery().verified_only_select(&sql);
26+
assert_eq!(
27+
select.from,
28+
vec![TableWithJoins {
29+
relation: TableFactor::Table {
30+
name: ObjectName(expected),
31+
alias: None,
32+
args: vec![],
33+
with_hints: vec![],
34+
},
35+
joins: vec![]
36+
},]
37+
);
38+
}
39+
fn test_table_ident_err(ident: &str) {
40+
let sql = format!("SELECT 1 FROM {}", ident);
41+
assert!(bigquery().parse_sql_statements(&sql).is_err());
42+
}
43+
44+
test_table_ident("da-sh-es", vec![Ident::new("da-sh-es")]);
45+
46+
test_table_ident("`spa ce`", vec![Ident::with_quote('`', "spa ce")]);
47+
48+
test_table_ident(
49+
"`!@#$%^&*()-=_+`",
50+
vec![Ident::with_quote('`', "!@#$%^&*()-=_+")],
51+
);
52+
53+
test_table_ident(
54+
"_5abc.dataField",
55+
vec![Ident::new("_5abc"), Ident::new("dataField")],
56+
);
57+
test_table_ident(
58+
"`5abc`.dataField",
59+
vec![Ident::with_quote('`', "5abc"), Ident::new("dataField")],
60+
);
61+
62+
test_table_ident_err("5abc.dataField");
63+
64+
test_table_ident(
65+
"abc5.dataField",
66+
vec![Ident::new("abc5"), Ident::new("dataField")],
67+
);
68+
69+
test_table_ident_err("abc5!.dataField");
70+
71+
test_table_ident(
72+
"`GROUP`.dataField",
73+
vec![Ident::with_quote('`', "GROUP"), Ident::new("dataField")],
74+
);
75+
76+
// TODO: this should be error
77+
// test_table_ident_err("GROUP.dataField");
78+
79+
test_table_ident("abc5.GROUP", vec![Ident::new("abc5"), Ident::new("GROUP")]);
80+
}
81+
82+
fn bigquery() -> TestedDialects {
83+
TestedDialects {
84+
dialects: vec![Box::new(BigQueryDialect {})],
85+
}
86+
}

tests/sqlparser_common.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ mod test_utils;
2323
use matches::assert_matches;
2424
use sqlparser::ast::*;
2525
use sqlparser::dialect::{
26-
AnsiDialect, GenericDialect, MsSqlDialect, PostgreSqlDialect, SQLiteDialect, SnowflakeDialect,
26+
AnsiDialect, BigQueryDialect, GenericDialect, MsSqlDialect, PostgreSqlDialect, SQLiteDialect,
27+
SnowflakeDialect,
2728
};
2829
use sqlparser::keywords::ALL_KEYWORDS;
2930
use sqlparser::parser::{Parser, ParserError};
@@ -4556,6 +4557,7 @@ fn test_placeholder() {
45564557
Box::new(PostgreSqlDialect {}),
45574558
Box::new(MsSqlDialect {}),
45584559
Box::new(AnsiDialect {}),
4560+
Box::new(BigQueryDialect {}),
45594561
Box::new(SnowflakeDialect {}),
45604562
// Note: `$` is the starting word for the HiveDialect identifier
45614563
// Box::new(sqlparser::dialect::HiveDialect {}),

0 commit comments

Comments
 (0)