Skip to content

Commit 47c0236

Browse files
authored
fix(bigquery): Refactor EXPORT DATA statement (#4693)
1 parent 9ea15c7 commit 47c0236

File tree

6 files changed

+20
-61
lines changed

6 files changed

+20
-61
lines changed

sqlglot/dialects/bigquery.py

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -491,11 +491,6 @@ class Parser(parser.Parser):
491491
LOG_DEFAULTS_TO_LN = True
492492
SUPPORTS_IMPLICIT_UNNEST = True
493493

494-
ID_VAR_TOKENS = {
495-
*parser.Parser.ID_VAR_TOKENS,
496-
TokenType.EXPORT,
497-
}
498-
499494
FUNCTIONS = {
500495
**parser.Parser.FUNCTIONS,
501496
"CONTAINS_SUBSTR": _build_contains_substring,
@@ -837,47 +832,13 @@ def _parse_features_at_time(self) -> exp.FeaturesAtTime:
837832
return expr
838833

839834
def _parse_export_data(self) -> exp.Export:
840-
# https://cloud.google.com/bigquery/docs/reference/standard-sql/export-statements
841-
if not self._match_text_seq("DATA"):
842-
self.raise_error("Expected 'DATA' after 'EXPORT'")
843-
844-
with_connection = None
845-
options = None
846-
847-
if self._match_text_seq("WITH", "CONNECTION"):
848-
parts = []
849-
while True:
850-
part = self._parse_var()
851-
if not part:
852-
break
853-
parts.append(part.name)
854-
if not self._match(TokenType.DOT):
855-
break
856-
857-
if not parts:
858-
self.raise_error("Expected connection name after WITH CONNECTION")
859-
860-
with_connection = exp.Identifier(this=".".join(parts))
861-
862-
if self._match_text_seq("OPTIONS"):
863-
self._match(TokenType.L_PAREN)
864-
options = self._parse_properties()
865-
self._match(TokenType.R_PAREN)
866-
else:
867-
self.raise_error("Expected 'OPTIONS' after 'EXPORT DATA'")
868-
869-
self._match_text_seq("AS")
870-
871-
# Parse the full SELECT statement
872-
query = self._parse_statement()
873-
if not isinstance(query, exp.Select):
874-
self.raise_error("Expected SELECT statement in EXPORT DATA")
835+
self._match_text_seq("DATA")
875836

876837
return self.expression(
877838
exp.Export,
878-
this=query,
879-
with_connection=with_connection,
880-
options=options,
839+
connection=self._match_text_seq("WITH", "CONNECTION") and self._parse_table_parts(),
840+
options=self._parse_properties(),
841+
this=self._match_text_seq("AS") and self._parse_select(),
881842
)
882843

883844
class Generator(generator.Generator):
@@ -1288,11 +1249,3 @@ def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) ->
12881249
return f"{self.sql(expression, 'to')}{self.sql(this)}"
12891250

12901251
return super().cast_sql(expression, safe_prefix=safe_prefix)
1291-
1292-
def export_sql(self, expression: exp.Export) -> str:
1293-
this = self.sql(expression, "this")
1294-
with_connection = self.sql(expression, "with_connection")
1295-
with_connection = f"WITH CONNECTION {with_connection} " if with_connection else ""
1296-
options = self.sql(expression, "options")
1297-
options = f"{options} " if options else ""
1298-
return f"EXPORT DATA {with_connection}{options}{this}"

sqlglot/expressions.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,6 +2064,11 @@ def kind(self) -> t.Optional[str]:
20642064
return kind and kind.upper()
20652065

20662066

2067+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/export-statements
2068+
class Export(Expression):
2069+
arg_types = {"this": True, "connection": False, "options": True}
2070+
2071+
20672072
class Filter(Expression):
20682073
arg_types = {"this": True, "expression": True}
20692074

@@ -8650,11 +8655,3 @@ def null() -> Null:
86508655
Boolean,
86518656
Null,
86528657
)
8653-
8654-
8655-
class Export(Expression):
8656-
arg_types = {
8657-
"this": True,
8658-
"with_connection": False,
8659-
"options": False,
8660-
}

sqlglot/generator.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4752,3 +4752,10 @@ def xmltable_sql(self, expression: exp.XMLTable) -> str:
47524752
def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
47534753
this = self.sql(expression, "this")
47544754
return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
4755+
4756+
def export_sql(self, expression: exp.Export) -> str:
4757+
this = self.sql(expression, "this")
4758+
connection = self.sql(expression, "connection")
4759+
connection = f"WITH CONNECTION {connection} " if connection else ""
4760+
options = self.sql(expression, "options")
4761+
return f"EXPORT DATA {connection}{options} AS {this}"

sqlglot/parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ class Parser(metaclass=_Parser):
497497
TokenType.DIV,
498498
TokenType.END,
499499
TokenType.EXECUTE,
500+
TokenType.EXPORT,
500501
TokenType.ESCAPE,
501502
TokenType.FALSE,
502503
TokenType.FIRST,

tests/dialects/test_bigquery.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,10 +1662,10 @@ def test_bigquery(self):
16621662
self.validate_identity("SELECT * FROM ML.FEATURES_AT_TIME((SELECT 1), num_rows => 1)")
16631663

16641664
self.validate_identity(
1665-
"EXPORT DATA OPTIONS (URI='gs://path*.csv.gz', FORMAT='CSV') SELECT * FROM all_rows"
1665+
"EXPORT DATA OPTIONS (URI='gs://path*.csv.gz', FORMAT='CSV') AS SELECT * FROM all_rows"
16661666
)
16671667
self.validate_identity(
1668-
"EXPORT DATA WITH CONNECTION myproject.us.myconnection OPTIONS (URI='gs://path*.csv.gz', FORMAT='CSV') SELECT * FROM all_rows"
1668+
"EXPORT DATA WITH CONNECTION myproject.us.myconnection OPTIONS (URI='gs://path*.csv.gz', FORMAT='CSV') AS SELECT * FROM all_rows"
16691669
)
16701670

16711671
def test_errors(self):

tests/fixtures/identity.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,3 +888,4 @@ SELECT 1 LIMIT 1
888888
CAST(x AS INT128)
889889
CAST(x AS UINT128)
890890
CAST(x AS UINT256)
891+
SELECT export

0 commit comments

Comments
 (0)