Skip to content

Commit 1cf6585

Browse files
authored
SupportSELECT AS VALUE and SELECT AS STRUCT for BigQuery (apache#1135)
1 parent 6a9b6f5 commit 1cf6585

File tree

10 files changed

+89
-10
lines changed

10 files changed

+89
-10
lines changed

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ pub use self::query::{
4444
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, NamedWindowDefinition,
4545
NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem, ReplaceSelectElement,
4646
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table,
47-
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, Values,
48-
WildcardAdditionalOptions, With,
47+
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
48+
Values, WildcardAdditionalOptions, With,
4949
};
5050
pub use self::value::{
5151
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,

src/ast/query.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,18 @@ pub struct Select {
245245
pub named_window: Vec<NamedWindowDefinition>,
246246
/// QUALIFY (Snowflake)
247247
pub qualify: Option<Expr>,
248+
/// BigQuery syntax: `SELECT AS VALUE | SELECT AS STRUCT`
249+
pub value_table_mode: Option<ValueTableMode>,
248250
}
249251

250252
impl fmt::Display for Select {
251253
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252254
write!(f, "SELECT")?;
255+
256+
if let Some(value_table_mode) = self.value_table_mode {
257+
write!(f, " {value_table_mode}")?;
258+
}
259+
253260
if let Some(ref distinct) = self.distinct {
254261
write!(f, " {distinct}")?;
255262
}
@@ -1574,3 +1581,24 @@ impl fmt::Display for JsonTableColumnErrorHandling {
15741581
}
15751582
}
15761583
}
1584+
1585+
/// BigQuery supports ValueTables which have 2 modes:
1586+
/// `SELECT AS STRUCT`
1587+
/// `SELECT AS VALUE`
1588+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#value_tables>
1589+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1590+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1591+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1592+
pub enum ValueTableMode {
1593+
AsStruct,
1594+
AsValue,
1595+
}
1596+
1597+
impl fmt::Display for ValueTableMode {
1598+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1599+
match self {
1600+
ValueTableMode::AsStruct => write!(f, "AS STRUCT"),
1601+
ValueTableMode::AsValue => write!(f, "AS VALUE"),
1602+
}
1603+
}
1604+
}

src/parser/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6826,6 +6826,19 @@ impl<'a> Parser<'a> {
68266826
/// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`),
68276827
/// assuming the initial `SELECT` was already consumed
68286828
pub fn parse_select(&mut self) -> Result<Select, ParserError> {
6829+
let value_table_mode =
6830+
if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS) {
6831+
if self.parse_keyword(Keyword::VALUE) {
6832+
Some(ValueTableMode::AsValue)
6833+
} else if self.parse_keyword(Keyword::STRUCT) {
6834+
Some(ValueTableMode::AsStruct)
6835+
} else {
6836+
self.expected("VALUE or STRUCT", self.peek_token())?
6837+
}
6838+
} else {
6839+
None
6840+
};
6841+
68296842
let distinct = self.parse_all_or_distinct()?;
68306843

68316844
let top = if self.parse_keyword(Keyword::TOP) {
@@ -6962,6 +6975,7 @@ impl<'a> Parser<'a> {
69626975
having,
69636976
named_window: named_windows,
69646977
qualify,
6978+
value_table_mode,
69656979
})
69666980
}
69676981

tests/sqlparser_bigquery.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,3 +1352,19 @@ fn test_bigquery_trim() {
13521352
bigquery().parse_sql_statements(error_sql).unwrap_err()
13531353
);
13541354
}
1355+
1356+
#[test]
1357+
fn test_select_as_struct() {
1358+
bigquery().verified_only_select("SELECT * FROM (SELECT AS VALUE STRUCT(123 AS a, false AS b))");
1359+
let select = bigquery().verified_only_select("SELECT AS STRUCT 1 AS a, 2 AS b");
1360+
assert_eq!(Some(ValueTableMode::AsStruct), select.value_table_mode);
1361+
}
1362+
1363+
#[test]
1364+
fn test_select_as_value() {
1365+
bigquery().verified_only_select(
1366+
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
1367+
);
1368+
let select = bigquery().verified_only_select("SELECT AS VALUE STRUCT(1 AS a, 2 AS b) AS xyz");
1369+
assert_eq!(Some(ValueTableMode::AsValue), select.value_table_mode);
1370+
}

tests/sqlparser_clickhouse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ fn parse_map_access_expr() {
110110
having: None,
111111
named_window: vec![],
112112
qualify: None,
113+
value_table_mode: None,
113114
},
114115
select
115116
);

tests/sqlparser_common.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ fn parse_update_set_from() {
400400
sort_by: vec![],
401401
having: None,
402402
named_window: vec![],
403-
qualify: None
403+
qualify: None,
404+
value_table_mode: None,
404405
}))),
405406
order_by: vec![],
406407
limit: None,
@@ -4212,6 +4213,7 @@ fn test_parse_named_window() {
42124213
),
42134214
],
42144215
qualify: None,
4216+
value_table_mode: None,
42154217
};
42164218
assert_eq!(actual_select_only, expected);
42174219
}
@@ -4567,6 +4569,7 @@ fn parse_interval_and_or_xor() {
45674569
having: None,
45684570
named_window: vec![],
45694571
qualify: None,
4572+
value_table_mode: None,
45704573
}))),
45714574
order_by: vec![],
45724575
limit: None,
@@ -6550,6 +6553,7 @@ fn lateral_function() {
65506553
having: None,
65516554
named_window: vec![],
65526555
qualify: None,
6556+
value_table_mode: None,
65536557
};
65546558
assert_eq!(actual_select_only, expected);
65556559
}
@@ -7193,6 +7197,7 @@ fn parse_merge() {
71937197
having: None,
71947198
named_window: vec![],
71957199
qualify: None,
7200+
value_table_mode: None,
71967201
}))),
71977202
order_by: vec![],
71987203
limit: None,

tests/sqlparser_duckdb.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ fn test_select_union_by_name() {
177177
having: None,
178178
named_window: vec![],
179179
qualify: None,
180+
value_table_mode: None,
180181
}))),
181182
right: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
182183
distinct: None,
@@ -211,6 +212,7 @@ fn test_select_union_by_name() {
211212
having: None,
212213
named_window: vec![],
213214
qualify: None,
215+
value_table_mode: None,
214216
}))),
215217
});
216218
assert_eq!(ast.body, expected);

tests/sqlparser_mssql.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ fn parse_create_procedure() {
112112
sort_by: vec![],
113113
having: None,
114114
named_window: vec![],
115-
qualify: None
115+
qualify: None,
116+
value_table_mode: None,
116117
})))
117118
}))],
118119
params: Some(vec![
@@ -595,7 +596,8 @@ fn parse_substring_in_select() {
595596
sort_by: vec![],
596597
having: None,
597598
named_window: vec![],
598-
qualify: None
599+
qualify: None,
600+
value_table_mode: None,
599601
}))),
600602
order_by: vec![],
601603
limit: None,

tests/sqlparser_mysql.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,8 @@ fn parse_escaped_quote_identifiers_with_escape() {
785785
sort_by: vec![],
786786
having: None,
787787
named_window: vec![],
788-
qualify: None
788+
qualify: None,
789+
value_table_mode: None,
789790
}))),
790791
order_by: vec![],
791792
limit: None,
@@ -829,7 +830,8 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
829830
sort_by: vec![],
830831
having: None,
831832
named_window: vec![],
832-
qualify: None
833+
qualify: None,
834+
value_table_mode: None,
833835
}))),
834836
order_by: vec![],
835837
limit: None,
@@ -870,7 +872,8 @@ fn parse_escaped_backticks_with_escape() {
870872
sort_by: vec![],
871873
having: None,
872874
named_window: vec![],
873-
qualify: None
875+
qualify: None,
876+
value_table_mode: None,
874877
}))),
875878
order_by: vec![],
876879
limit: None,
@@ -911,7 +914,8 @@ fn parse_escaped_backticks_with_no_escape() {
911914
sort_by: vec![],
912915
having: None,
913916
named_window: vec![],
914-
qualify: None
917+
qualify: None,
918+
value_table_mode: None,
915919
}))),
916920
order_by: vec![],
917921
limit: None,
@@ -1581,6 +1585,7 @@ fn parse_select_with_numeric_prefix_column_name() {
15811585
having: None,
15821586
named_window: vec![],
15831587
qualify: None,
1588+
value_table_mode: None,
15841589
})))
15851590
);
15861591
}
@@ -1631,6 +1636,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() {
16311636
having: None,
16321637
named_window: vec![],
16331638
qualify: None,
1639+
value_table_mode: None,
16341640
})))
16351641
);
16361642
}
@@ -1841,7 +1847,8 @@ fn parse_substring_in_select() {
18411847
sort_by: vec![],
18421848
having: None,
18431849
named_window: vec![],
1844-
qualify: None
1850+
qualify: None,
1851+
value_table_mode: None,
18451852
}))),
18461853
order_by: vec![],
18471854
limit: None,
@@ -2143,6 +2150,7 @@ fn parse_hex_string_introducer() {
21432150
having: None,
21442151
named_window: vec![],
21452152
qualify: None,
2153+
value_table_mode: None,
21462154
into: None
21472155
}))),
21482156
order_by: vec![],

tests/sqlparser_postgres.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,7 @@ fn parse_copy_to() {
10821082
distribute_by: vec![],
10831083
sort_by: vec![],
10841084
qualify: None,
1085+
value_table_mode: None,
10851086
}))),
10861087
order_by: vec![],
10871088
limit: None,
@@ -2139,6 +2140,7 @@ fn parse_array_subquery_expr() {
21392140
having: None,
21402141
named_window: vec![],
21412142
qualify: None,
2143+
value_table_mode: None,
21422144
}))),
21432145
right: Box::new(SetExpr::Select(Box::new(Select {
21442146
distinct: None,
@@ -2155,6 +2157,7 @@ fn parse_array_subquery_expr() {
21552157
having: None,
21562158
named_window: vec![],
21572159
qualify: None,
2160+
value_table_mode: None,
21582161
}))),
21592162
}),
21602163
order_by: vec![],

0 commit comments

Comments
 (0)