diff --git a/ibis/backends/__init__.py b/ibis/backends/__init__.py index b3c6a6e301076..08657da4412c4 100644 --- a/ibis/backends/__init__.py +++ b/ibis/backends/__init__.py @@ -87,6 +87,17 @@ def _ipython_key_completions_(self) -> list[str]: return self._backend.list_tables() +class DdlAccessor: + """ddl accessor list views.""" + + def __init__(self, backend: BaseBackend): + self._backend = backend + + def list_views(self) -> list[str]: + """List the names of views in the database.""" + return self._backend.list_views() + + class _FileIOHandler: @staticmethod def _import_pyarrow(): @@ -895,6 +906,14 @@ def _filter_with_like(values: Iterable[str], like: str | None = None) -> list[st pattern = re.compile(like) return sorted(filter(pattern.findall, values)) + @abc.abstractmethod + def list_views( + self, + like: str | None = None, + database: str | None = None, + ) -> list[str]: + """List the names of views in the database.""" + @abc.abstractmethod def list_tables( self, like: str | None = None, database: tuple[str, str] | str | None = None @@ -983,6 +1002,11 @@ def tables(self): """ return TablesAccessor(self) + @functools.cached_property + def ddl(self): + """A ddl accessor.""" + return DdlAccessor(self) + @property @abc.abstractmethod def version(self) -> str: diff --git a/ibis/backends/duckdb/__init__.py b/ibis/backends/duckdb/__init__.py index 747dba0d25a8e..fa143c3aa1cb0 100644 --- a/ibis/backends/duckdb/__init__.py +++ b/ibis/backends/duckdb/__init__.py @@ -1037,6 +1037,32 @@ def list_tables( return self._filter_with_like(out[col].to_pylist(), like) + def list_views( + self, + like: str | None = None, + database: tuple[str, str] | str | None = None, + schema: str | None = None, + ) -> list[str]: + """List views.""" + # return ["test1", "test2"] + table_loc = self._warn_and_create_table_loc(database, schema) + + database = self.compiler.f.current_schema() + if table_loc is not None: + database = table_loc.db or database + + col = "view_name" + sql = ( + sg.select(col) + .from_(sg.table("duckdb_views")) + .distinct() + .where(C.schema_name.eq(database)) + .sql(self.name, pretty=True) + ) + out = self.con.execute(sql).fetch_arrow_table() + + return self._filter_with_like(out[col].to_pylist(), like) + def read_postgres( self, uri: str, *, table_name: str | None = None, database: str = "public" ) -> ir.Table: diff --git a/ibis/backends/tests/test_api.py b/ibis/backends/tests/test_api.py index 4b71bdb4ffee8..e1413478be0fa 100644 --- a/ibis/backends/tests/test_api.py +++ b/ibis/backends/tests/test_api.py @@ -142,3 +142,15 @@ def test_unbind(alltypes, expr_fn): assert "Unbound" not in repr(expr) assert "Unbound" in repr(expr.unbind()) + + +###for now let's just try duckdb +def test_list_views(ddl_con, temp_view): + expr = ddl_con.table("functional_alltypes") + ddl_con.create_view(temp_view, expr) + + views = ddl_con.list_views() + assert isinstance(views, list) + assert temp_view in views + + assert temp_view not in ddl_con.list_tables()