Skip to content

Commit 84a0e12

Browse files
committed
more tests
1 parent c679d96 commit 84a0e12

3 files changed

Lines changed: 226 additions & 6 deletions

File tree

src/ast/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4362,6 +4362,7 @@ pub enum Statement {
43624362
/// EXPORT DATA OPTIONS(uri='gs://bucket/folder/*', format='PARQUET', overwrite=true) AS
43634363
/// SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10
43644364
/// ```
4365+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/export-statements)
43654366
ExportData(ExportData),
43664367
/// ```sql
43674368
/// CREATE [OR REPLACE] USER <user> [IF NOT EXISTS]

src/parser/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,10 @@ impl<'a> Parser<'a> {
645645
Keyword::COMMENT if self.dialect.supports_comment_on() => self.parse_comment(),
646646
Keyword::PRINT => self.parse_print(),
647647
Keyword::RETURN => self.parse_return(),
648-
Keyword::EXPORT => self.parse_export(),
648+
Keyword::EXPORT => {
649+
self.prev_token();
650+
self.parse_export_data()
651+
}
649652
_ => self.expected("an SQL statement", next_token),
650653
},
651654
Token::LParen => {
@@ -16524,8 +16527,11 @@ impl<'a> Parser<'a> {
1652416527
}
1652516528
}
1652616529

16527-
fn parse_export(&mut self) -> Result<Statement, ParserError> {
16528-
self.expect_keyword(Keyword::DATA)?;
16530+
/// /// Parse a `EXPORT DATA` statement.
16531+
///
16532+
/// See [Statement::ExportData]
16533+
fn parse_export_data(&mut self) -> Result<Statement, ParserError> {
16534+
self.expect_keywords(&[Keyword::EXPORT, Keyword::DATA])?;
1652916535

1653016536
let connection = if self.parse_keywords(&[Keyword::WITH, Keyword::CONNECTION]) {
1653116537
Some(self.parse_object_name(false)?)

tests/sqlparser_bigquery.rs

Lines changed: 216 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ mod test_utils;
2020

2121
use std::ops::Deref;
2222

23+
use sqlparser::ast::helpers::attached_token::AttachedToken;
2324
use sqlparser::ast::*;
2425
use sqlparser::dialect::{BigQueryDialect, GenericDialect};
26+
use sqlparser::keywords::Keyword;
2527
use sqlparser::parser::{ParserError, ParserOptions};
26-
use sqlparser::tokenizer::{Location, Span};
28+
use sqlparser::tokenizer::{Location, Span, Token, TokenWithSpan, Word};
2729
use test_utils::*;
2830

2931
#[test]
@@ -2569,23 +2571,234 @@ fn test_struct_trailing_and_nested_bracket() {
25692571

25702572
#[test]
25712573
fn test_export_data() {
2572-
bigquery().verified_stmt(concat!(
2574+
let stmt = bigquery().verified_stmt(concat!(
25732575
"EXPORT DATA OPTIONS(",
25742576
"uri = 'gs://bucket/folder/*', ",
25752577
"format = 'PARQUET', ",
25762578
"overwrite = true",
25772579
") AS ",
25782580
"SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10",
25792581
));
2582+
assert_eq!(
2583+
stmt,
2584+
Statement::ExportData(ExportData {
2585+
options: vec![
2586+
SqlOption::KeyValue {
2587+
key: Ident::new("uri"),
2588+
value: Expr::Value(
2589+
Value::SingleQuotedString("gs://bucket/folder/*".to_owned())
2590+
.with_empty_span()
2591+
),
2592+
},
2593+
SqlOption::KeyValue {
2594+
key: Ident::new("format"),
2595+
value: Expr::Value(
2596+
Value::SingleQuotedString("PARQUET".to_owned()).with_empty_span()
2597+
),
2598+
},
2599+
SqlOption::KeyValue {
2600+
key: Ident::new("overwrite"),
2601+
value: Expr::Value(Value::Boolean(true).with_empty_span()),
2602+
},
2603+
],
2604+
connection: None,
2605+
query: Box::new(Query {
2606+
with: None,
2607+
body: Box::new(SetExpr::Select(Box::new(Select {
2608+
select_token: AttachedToken(TokenWithSpan::new(
2609+
Token::Word(Word {
2610+
value: "SELECT".to_string(),
2611+
quote_style: None,
2612+
keyword: Keyword::SELECT,
2613+
}),
2614+
Span::empty()
2615+
)),
2616+
distinct: None,
2617+
top: None,
2618+
top_before_distinct: false,
2619+
projection: vec![
2620+
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("field1"))),
2621+
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("field2"))),
2622+
],
2623+
exclude: None,
2624+
into: None,
2625+
from: vec![TableWithJoins {
2626+
relation: table_from_name(ObjectName::from(vec![
2627+
Ident::new("mydataset"),
2628+
Ident::new("table1")
2629+
])),
2630+
joins: vec![],
2631+
}],
2632+
lateral_views: vec![],
2633+
prewhere: None,
2634+
selection: None,
2635+
group_by: GroupByExpr::Expressions(vec![], vec![]),
2636+
cluster_by: vec![],
2637+
distribute_by: vec![],
2638+
sort_by: vec![],
2639+
having: None,
2640+
named_window: vec![],
2641+
qualify: None,
2642+
window_before_qualify: false,
2643+
value_table_mode: None,
2644+
connect_by: None,
2645+
flavor: SelectFlavor::Standard,
2646+
}))),
2647+
order_by: Some(OrderBy {
2648+
kind: OrderByKind::Expressions(vec![OrderByExpr {
2649+
expr: Expr::Identifier(Ident::new("field1")),
2650+
options: OrderByOptions {
2651+
asc: None,
2652+
nulls_first: None,
2653+
},
2654+
with_fill: None,
2655+
},]),
2656+
interpolate: None,
2657+
}),
2658+
limit_clause: Some(LimitClause::LimitOffset {
2659+
limit: Some(Expr::Value(
2660+
Value::Number("10".to_string(), false).with_empty_span()
2661+
)),
2662+
offset: None,
2663+
limit_by: vec![],
2664+
}),
2665+
fetch: None,
2666+
locks: vec![],
2667+
for_clause: None,
2668+
settings: None,
2669+
format_clause: None,
2670+
pipe_operators: vec![],
2671+
})
2672+
})
2673+
);
25802674

2581-
bigquery().verified_stmt(concat!(
2675+
let stmt = bigquery().verified_stmt(concat!(
25822676
"EXPORT DATA WITH CONNECTION myconnection.myproject.us OPTIONS(",
25832677
"uri = 'gs://bucket/folder/*', ",
25842678
"format = 'PARQUET', ",
25852679
"overwrite = true",
25862680
") AS ",
25872681
"SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10",
25882682
));
2683+
2684+
assert_eq!(
2685+
stmt,
2686+
Statement::ExportData(ExportData {
2687+
options: vec![
2688+
SqlOption::KeyValue {
2689+
key: Ident::new("uri"),
2690+
value: Expr::Value(
2691+
Value::SingleQuotedString("gs://bucket/folder/*".to_owned())
2692+
.with_empty_span()
2693+
),
2694+
},
2695+
SqlOption::KeyValue {
2696+
key: Ident::new("format"),
2697+
value: Expr::Value(
2698+
Value::SingleQuotedString("PARQUET".to_owned()).with_empty_span()
2699+
),
2700+
},
2701+
SqlOption::KeyValue {
2702+
key: Ident::new("overwrite"),
2703+
value: Expr::Value(Value::Boolean(true).with_empty_span()),
2704+
},
2705+
],
2706+
connection: Some(ObjectName::from(vec![
2707+
Ident::new("myconnection"),
2708+
Ident::new("myproject"),
2709+
Ident::new("us")
2710+
])),
2711+
query: Box::new(Query {
2712+
with: None,
2713+
body: Box::new(SetExpr::Select(Box::new(Select {
2714+
select_token: AttachedToken(TokenWithSpan::new(
2715+
Token::Word(Word {
2716+
value: "SELECT".to_string(),
2717+
quote_style: None,
2718+
keyword: Keyword::SELECT,
2719+
}),
2720+
Span::empty()
2721+
)),
2722+
distinct: None,
2723+
top: None,
2724+
top_before_distinct: false,
2725+
projection: vec![
2726+
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("field1"))),
2727+
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("field2"))),
2728+
],
2729+
exclude: None,
2730+
into: None,
2731+
from: vec![TableWithJoins {
2732+
relation: table_from_name(ObjectName::from(vec![
2733+
Ident::new("mydataset"),
2734+
Ident::new("table1")
2735+
])),
2736+
joins: vec![],
2737+
}],
2738+
lateral_views: vec![],
2739+
prewhere: None,
2740+
selection: None,
2741+
group_by: GroupByExpr::Expressions(vec![], vec![]),
2742+
cluster_by: vec![],
2743+
distribute_by: vec![],
2744+
sort_by: vec![],
2745+
having: None,
2746+
named_window: vec![],
2747+
qualify: None,
2748+
window_before_qualify: false,
2749+
value_table_mode: None,
2750+
connect_by: None,
2751+
flavor: SelectFlavor::Standard,
2752+
}))),
2753+
order_by: Some(OrderBy {
2754+
kind: OrderByKind::Expressions(vec![OrderByExpr {
2755+
expr: Expr::Identifier(Ident::new("field1")),
2756+
options: OrderByOptions {
2757+
asc: None,
2758+
nulls_first: None,
2759+
},
2760+
with_fill: None,
2761+
},]),
2762+
interpolate: None,
2763+
}),
2764+
limit_clause: Some(LimitClause::LimitOffset {
2765+
limit: Some(Expr::Value(
2766+
Value::Number("10".to_string(), false).with_empty_span()
2767+
)),
2768+
offset: None,
2769+
limit_by: vec![],
2770+
}),
2771+
fetch: None,
2772+
locks: vec![],
2773+
for_clause: None,
2774+
settings: None,
2775+
format_clause: None,
2776+
pipe_operators: vec![],
2777+
})
2778+
})
2779+
);
2780+
2781+
// at least one option (uri) is required
2782+
let err = bigquery()
2783+
.parse_sql_statements(concat!(
2784+
"EXPORT DATA OPTIONS() AS ",
2785+
"SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10",
2786+
))
2787+
.unwrap_err();
2788+
assert_eq!(
2789+
err.to_string(),
2790+
"sql parser error: Expected: identifier, found: )"
2791+
);
2792+
2793+
let err = bigquery()
2794+
.parse_sql_statements(concat!(
2795+
"EXPORT DATA AS SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10",
2796+
))
2797+
.unwrap_err();
2798+
assert_eq!(
2799+
err.to_string(),
2800+
"sql parser error: Expected: OPTIONS, found: AS"
2801+
);
25892802
}
25902803

25912804
#[test]

0 commit comments

Comments
 (0)