diff --git a/ci/schema/postgresql.sql b/ci/schema/postgresql.sql index 8be637c8d645..67d208744e3b 100644 --- a/ci/schema/postgresql.sql +++ b/ci/schema/postgresql.sql @@ -1,6 +1,7 @@ CREATE EXTENSION IF NOT EXISTS hstore; CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS plpython3u; +CREATE EXTENSION IF NOT EXISTS vector; DROP TABLE IF EXISTS diamonds CASCADE; @@ -214,3 +215,7 @@ ADD search tsvector GENERATED always AS ( setweight(to_tsvector('simple', notes), 'A') :: tsvector ) stored; + +ALTER TABLE awards_players +ADD simvec vector +GENERATED always AS ('[1,2,3]' :: vector) stored; diff --git a/docker/postgres/Dockerfile b/docker/postgres/Dockerfile index ada998b23162..d2de900cbbea 100644 --- a/docker/postgres/Dockerfile +++ b/docker/postgres/Dockerfile @@ -1,2 +1,7 @@ FROM postgis/postgis:15-3.3-alpine -RUN apk add postgresql15-plpython3 +RUN apk add --no-cache build-base clang15 llvm15 postgresql15-plpython3 python3 py3-pip && \ + python3 -m pip install pgxnclient && \ + pgxn install vector && \ + python3 -m pip uninstall -y pgxnclient && \ + rm -rf ~/.cache/pip && \ + apk del build-base clang15 llvm15 python3 py3-pip diff --git a/ibis/backends/postgres/__init__.py b/ibis/backends/postgres/__init__.py index d9c2f7664618..88ce6af7d23c 100644 --- a/ibis/backends/postgres/__init__.py +++ b/ibis/backends/postgres/__init__.py @@ -246,6 +246,8 @@ def _metadata(self, query: str) -> Iterable[tuple[str, dt.DataType]]: AND attnum > 0 AND NOT attisdropped ORDER BY attnum""" + if self.inspector.has_table(query): + query = f"TABLE {query}" with self.begin() as con: con.exec_driver_sql(f"CREATE TEMPORARY VIEW {name} AS {query}") type_info = con.execute( diff --git a/ibis/backends/postgres/datatypes.py b/ibis/backends/postgres/datatypes.py index ae64e8aba937..668d30a96d84 100644 --- a/ibis/backends/postgres/datatypes.py +++ b/ibis/backends/postgres/datatypes.py @@ -46,7 +46,14 @@ def _get_type(typestr: str) -> dt.DataType: is_array = typestr.endswith(_BRACKETS) if (typ := _type_mapping.get(typestr.replace(_BRACKETS, ""))) is not None: return dt.Array(typ) if is_array else typ - return _parse_numeric(typestr) + try: + return _parse_numeric(typestr) + except parsy.ParseError: + # postgres can have arbitrary types unknown to ibis, so we just + # consider them null since we can't know what to do with them without + # explicit support, return null to effectively give no public API to + # such columns + return dt.null _type_mapping = { diff --git a/ibis/backends/postgres/tests/test_client.py b/ibis/backends/postgres/tests/test_client.py index c1afd12a7b34..fd6777eb3b87 100644 --- a/ibis/backends/postgres/tests/test_client.py +++ b/ibis/backends/postgres/tests/test_client.py @@ -207,3 +207,11 @@ def test_get_schema_from_query(con, pg_type, expected_type): expected_schema = ibis.schema(dict(x=expected_type, y=dt.Array(expected_type))) result_schema = con._get_schema_using_query(f"SELECT x, y FROM {name}") assert result_schema == expected_schema + + +@pytest.mark.parametrize( + ("col", "expected_type"), [("search", dt.string), ("simvec", dt.null)] +) +def test_unknown_column_type(con, col, expected_type): + awards_players = con.table("awards_players") + assert awards_players[col].type() == expected_type diff --git a/ibis/backends/postgres/tests/test_string.py b/ibis/backends/postgres/tests/test_string.py index b194fd682df3..97803651a59d 100644 --- a/ibis/backends/postgres/tests/test_string.py +++ b/ibis/backends/postgres/tests/test_string.py @@ -4,7 +4,6 @@ from pytest import param import ibis -import ibis.expr.datatypes as dt @pytest.mark.parametrize( @@ -17,9 +16,3 @@ def test_special_strings(alltypes, data, data_type): expr = alltypes[[alltypes.id, lit]].head(1) df = expr.execute() assert df['tmp'].iloc[0] == uuid.UUID(data) - - -def test_load_tsvector_table(con): - awards_players = con.table("awards_players") - assert "search" in awards_players.columns - assert awards_players.schema()["search"] == dt.String(nullable=True)