Skip to content

Commit

Permalink
Feat(bigquery): support the full syntax of ANY_VALUE (#1860)
Browse files Browse the repository at this point in the history
  • Loading branch information
georgesittas authored Jun 29, 2023
1 parent f837b17 commit 08c3074
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 1 deletion.
2 changes: 1 addition & 1 deletion sqlglot/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3922,7 +3922,7 @@ class Avg(AggFunc):


class AnyValue(AggFunc):
pass
arg_types = {"this": True, "having": False, "max": False}


class Case(Func):
Expand Down
9 changes: 9 additions & 0 deletions sqlglot/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2423,6 +2423,15 @@ def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
buckets = self.sql(expression, "buckets")
return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

def anyvalue_sql(self, expression: exp.AnyValue) -> str:
this = self.sql(expression, "this")
having = self.sql(expression, "having")

if having:
this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"

return self.func("ANY_VALUE", this)


def cached_generator(
cache: t.Optional[t.Dict[int, str]] = None
Expand Down
13 changes: 13 additions & 0 deletions sqlglot/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ class Parser(metaclass=_Parser):
FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}

FUNCTION_PARSERS: t.Dict[str, t.Callable] = {
"ANY_VALUE": lambda self: self._parse_any_value(),
"CAST": lambda self: self._parse_cast(self.STRICT_CAST),
"CONCAT": lambda self: self._parse_concat(),
"CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
Expand Down Expand Up @@ -3678,6 +3679,18 @@ def _parse_extract(self) -> exp.Extract:

return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())

def _parse_any_value(self) -> exp.AnyValue:
this = self._parse_lambda()
is_max = None
having = None

if self._match(TokenType.HAVING):
self._match_texts(("MAX", "MIN"))
is_max = self._prev.text == "MAX"
having = self._parse_column()

return self.expression(exp.AnyValue, this=this, having=having, max=is_max)

def _parse_cast(self, strict: bool) -> exp.Expression:
this = self._parse_conjunction()

Expand Down
2 changes: 2 additions & 0 deletions tests/dialects/test_bigquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def test_bigquery(self):
with self.assertRaises(ParseError):
transpile("SELECT * FROM UNNEST(x) AS x(y)", read="bigquery")

self.validate_identity("SELECT ANY_VALUE(fruit HAVING MAX sold) FROM fruits")
self.validate_identity("SELECT ANY_VALUE(fruit HAVING MIN sold) FROM fruits")
self.validate_identity("SELECT `project-id`.udfs.func(call.dir)")
self.validate_identity("SELECT CAST(CURRENT_DATE AS STRING FORMAT 'DAY') AS current_day")
self.validate_identity("SAFE_CAST(encrypted_value AS STRING FORMAT 'BASE64')")
Expand Down

0 comments on commit 08c3074

Please sign in to comment.