From 0ab34cb36cef98a58b1e04254f3a92cdf09af389 Mon Sep 17 00:00:00 2001 From: Chelsea Lin Date: Tue, 6 Feb 2024 00:19:46 +0000 Subject: [PATCH] prototype as_table --- ibis/backends/base/sqlglot/compiler.py | 8 +++++++- ibis/backends/tests/test_array.py | 8 ++++++++ ibis/expr/operations/arrays.py | 18 ++++++++++++++++++ ibis/expr/types/arrays.py | 16 ++++++++++++++-- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/ibis/backends/base/sqlglot/compiler.py b/ibis/backends/base/sqlglot/compiler.py index f54188c5daba..4599cb4d4965 100644 --- a/ibis/backends/base/sqlglot/compiler.py +++ b/ibis/backends/base/sqlglot/compiler.py @@ -276,7 +276,7 @@ def fn(node, _, **kwargs): try: return result.subquery(alias) except AttributeError: - return result.as_(alias, quoted=self.quoted) + return result.as_(alias, quoted=self.quoted, table=[alias.name]) # apply translate rules in topological order results = op.map(fn) @@ -1011,6 +1011,12 @@ def visit_DatabaseTable( name, db=namespace.schema, catalog=namespace.database, quoted=self.quoted ) + @visit_node.register(ops.ArrayTable) + def visit_ArrayTable( + self, op, *, col + ) -> sg.Table: + return sge.Unnest(expressions=[col], offset=None) + @visit_node.register(ops.SelfReference) def visit_SelfReference(self, op, *, parent, identifier): return parent diff --git a/ibis/backends/tests/test_array.py b/ibis/backends/tests/test_array.py index 2d203c2c9f92..6623e532e6d0 100644 --- a/ibis/backends/tests/test_array.py +++ b/ibis/backends/tests/test_array.py @@ -325,6 +325,14 @@ def test_unnest_idempotent(backend): tm.assert_frame_equal(result, expected) +@builtin_array +@pytest.mark.notimpl("dask", raises=ValueError) +@pytest.mark.notimpl(["datafusion"], raises=com.OperationNotDefinedError) +def test_as_table(backend): + array_types = backend.array_types + print(ibis.to_sql(array_types.cross_join(array_types.x.as_table())).__repr__()) + + @builtin_array @pytest.mark.notimpl(["datafusion", "flink"], raises=com.OperationNotDefinedError) @pytest.mark.broken( diff --git a/ibis/expr/operations/arrays.py b/ibis/expr/operations/arrays.py index 68ee711a2da6..9a585ea5be21 100644 --- a/ibis/expr/operations/arrays.py +++ b/ibis/expr/operations/arrays.py @@ -8,8 +8,11 @@ import ibis.expr.datatypes as dt import ibis.expr.rules as rlz from ibis.common.annotations import attribute +from ibis.common.collections import FrozenDict from ibis.common.typing import VarTuple # noqa: TCH001 from ibis.expr.operations.core import Unary, Value +from ibis.expr.operations.relations import Relation, Simple +from ibis.expr.schema import Schema @public @@ -111,6 +114,21 @@ def dtype(self): return self.arg.dtype.value_type +@public +class ArrayTable(Relation): + # -> DatabaseTable + """A table sourced from the result set of a select query.""" + col: Value[dt.Array] + + @attribute + def values(self): + return FrozenDict() + + @property + def schema(self) -> Schema: + return Schema.from_tuples(zip([self.col.name], [self.col.dtype.value_type])) + + @public class ArrayContains(Value): arg: Value[dt.Array] diff --git a/ibis/expr/types/arrays.py b/ibis/expr/types/arrays.py index 5a390b2919b1..79e4fa3954ec 100644 --- a/ibis/expr/types/arrays.py +++ b/ibis/expr/types/arrays.py @@ -1,5 +1,6 @@ from __future__ import annotations +from typing import TYPE_CHECKING, Callable, Iterable, Optional import inspect from typing import TYPE_CHECKING, Callable @@ -305,12 +306,23 @@ def unnest(self) -> ir.Value: ir.Value Unnested array """ - expr = ops.Unnest(self).to_expr() + expr = ops.Unnest(self).to_expr() # -> UNNEST(t0.x) AS AsTable_x try: - return expr.name(self.get_name()) + return expr.name(self.get_name()) # -> UNNEST(t0.x) AS x except com.ExpressionError: return expr + def as_table( + self, + offset_name: Optional[str] = None, + ) -> ir.Table: + """TODO(chelsealin) + """ + + # TODO(chelsealin) add offset_name + expr = ops.ArrayTable(self).to_expr() + return expr + def join(self, sep: str | ir.StringValue) -> ir.StringValue: """Join the elements of this array expression with `sep`.