We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug(postgres): cannot connect to postgres table with `tsvector` column
### What happened?

Reposting from https://stackoverflow.com/questions/74520302/why-cant-i-connect-ibis-to-a-postgres-table-with-a-tsvector-column

Implementing whatever postgres functionality exists around tsvector may not be something we want to do, but I think we should at least allow connecting to the table.

### What version of ibis are you using?

3.2.0

### What backend(s) are you using, if any?

postgres

### Relevant log output

```sh
KeyError                                  Traceback (most recent call last)
File ~/anaconda3/envs/ec_extract/lib/python3.9/site-packages/multipledispatch/dispatcher.py:269, in Dispatcher.__call__(self, *args, **kwargs)
    268 try:
--> 269     func = self._cache[types]
    270 except KeyError:

KeyError: (<class 'sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2'>, <class 'sqlalchemy.dialects.postgresql.base.TSVECTOR'>)

During handling of the above exception, another exception occurred:

NotImplementedError                       Traceback (most recent call last)
*** 
----> 29 main_table = con.table(table_name)[columns['column_list']]

File ~/anaconda3/envs/ec_extract/lib/python3.9/site-packages/ibis/backends/base/sql/alchemy/__init__.py:438, in BaseAlchemyBackend.table(self, name, database, schema)
    428     return self.database(database=database).table(
    429         name=name,
    430         database=database,
    431         schema=schema,
    432     )
    433 sqla_table = self._get_sqla_table(
    434     name,
...
    275             (self.name, str_signature(types)))
    276     self._cache[types] = func
    277 try:

NotImplementedError: Could not find signature for dtype: <PGDialect_psycopg2, TSVECTOR>
```


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ibis/backends/postgres/datatypes.py`
Content:
```
1 from __future__ import annotations
2 
3 import parsy
4 import sqlalchemy as sa
5 import toolz
6 from sqlalchemy.dialects import postgresql
7 from sqlalchemy.dialects.postgresql.base import PGDialect
8 
9 import ibis.expr.datatypes as dt
10 from ibis.backends.base.sql.alchemy import to_sqla_type
11 from ibis.common.parsing import (
12     COMMA,
13     LBRACKET,
14     LPAREN,
15     PRECISION,
16     RBRACKET,
17     RPAREN,
18     SCALE,
19     spaceless,
20     spaceless_string,
21 )
22 
23 _BRACKETS = "[]"
24 
25 
26 def _parse_numeric(
27     text: str, default_decimal_parameters: tuple[int | None, int | None] = (None, None)
28 ) -> dt.DataType:
29     decimal = spaceless_string("decimal", "numeric").then(
30         parsy.seq(LPAREN.then(PRECISION.skip(COMMA)), SCALE.skip(RPAREN))
31         .optional(default_decimal_parameters)
32         .combine(dt.Decimal)
33     )
34 
35     brackets = spaceless(LBRACKET).then(spaceless(RBRACKET))
36 
37     pg_array = parsy.seq(decimal, brackets.at_least(1).map(len)).combine(
38         lambda value_type, n: toolz.nth(n, toolz.iterate(dt.Array, value_type))
39     )
40 
41     ty = pg_array | decimal
42     return ty.parse(text)
43 
44 
45 def _get_type(typestr: str) -> dt.DataType:
46     is_array = typestr.endswith(_BRACKETS)
47     if (typ := _type_mapping.get(typestr.replace(_BRACKETS, ""))) is not None:
48         return dt.Array(typ) if is_array else typ
49     return _parse_numeric(typestr)
50 
51 
52 _type_mapping = {
53     "bigint": dt.int64,
54     "boolean": dt.bool,
55     "bytea": dt.binary,
56     "character varying": dt.string,
57     "character": dt.string,
58     "character(1)": dt.string,
59     "date": dt.date,
60     "double precision": dt.float64,
61     "geography": dt.geography,
62     "geometry": dt.geometry,
63     "inet": dt.inet,
64     "integer": dt.int32,
65     "interval": dt.interval,
66     "json": dt.json,
67     "jsonb": dt.json,
68     "line": dt.linestring,
69     "macaddr": dt.macaddr,
70     "macaddr8": dt.macaddr,
71     "numeric": dt.decimal,
72     "point": dt.point,
73     "polygon": dt.polygon,
74     "real": dt.float32,
75     "smallint": dt.int16,
76     "text": dt.string,
77     # NB: this isn't correct because we're losing the "with time zone"
78     # information (ibis doesn't have time type that is time-zone aware), but we
79     # try to do _something_ here instead of failing
80     "time with time zone": dt.time,
81     "time without time zone": dt.time,
82     "timestamp with time zone": dt.Timestamp("UTC"),
83     "timestamp without time zone": dt.timestamp,
84     "uuid": dt.uuid,
85 }
86 
87 
88 @to_sqla_type.register(PGDialect, dt.Array)
89 def _pg_array(dialect, itype):
90     # Unwrap the array element type because sqlalchemy doesn't allow arrays of
91     # arrays. This doesn't affect the underlying data.
92     while itype.is_array():
93         itype = itype.value_type
94     return sa.ARRAY(to_sqla_type(dialect, itype))
95 
96 
97 @to_sqla_type.register(PGDialect, dt.Map)
98 def _pg_map(dialect, itype):
99     if not (itype.key_type.is_string() and itype.value_type.is_string()):
100         raise TypeError(f"PostgreSQL only supports map<string, string>, got: {itype}")
101     return postgresql.HSTORE()
102 
103 
104 @dt.dtype.register(PGDialect, postgresql.DOUBLE_PRECISION)
105 def sa_double(_, satype, nullable=True):
106     return dt.Float64(nullable=nullable)
107 
108 
109 @dt.dtype.register(PGDialect, postgresql.UUID)
110 def sa_uuid(_, satype, nullable=True):
111     return dt.UUID(nullable=nullable)
112 
113 
114 @dt.dtype.register(PGDialect, postgresql.MACADDR)
115 def sa_macaddr(_, satype, nullable=True):
116     return dt.MACADDR(nullable=nullable)
117 
118 
119 @dt.dtype.register(PGDialect, postgresql.HSTORE)
120 def sa_hstore(_, satype, nullable=True):
121     return dt.Map(dt.string, dt.string, nullable=nullable)
122 
123 
124 @dt.dtype.register(PGDialect, postgresql.INET)
125 def sa_inet(_, satype, nullable=True):
126     return dt.INET(nullable=nullable)
127 
128 
129 @dt.dtype.register(PGDialect, postgresql.JSONB)
130 def sa_json(_, satype, nullable=True):
131     return dt.JSON(nullable=nullable)
132 
133 
134 _POSTGRES_FIELD_TO_IBIS_UNIT = {
135     "YEAR": "Y",
136     "MONTH": "M",
137     "DAY": "D",
138     "HOUR": "h",
139     "MINUTE": "m",
140     "SECOND": "s",
141     "YEAR TO MONTH": "M",
142     "DAY TO HOUR": "h",
143     "DAY TO MINUTE": "m",
144     "DAY TO SECOND": "s",
145     "HOUR TO MINUTE": "m",
146     "HOUR TO SECOND": "s",
147     "MINUTE TO SECOND": "s",
148 }
149 
150 
151 @dt.dtype.register(PGDialect, postgresql.INTERVAL)
152 def sa_postgres_interval(_, satype, nullable=True):
153     field = satype.fields.upper()
154     if (unit := _POSTGRES_FIELD_TO_IBIS_UNIT.get(field, None)) is None:
155         raise ValueError(f"Unknown PostgreSQL interval field {field!r}")
156     elif unit in {"Y", "M"}:
157         raise ValueError(
158             "Variable length intervals are not yet supported with PostgreSQL"
159         )
160     return dt.Interval(unit=unit, nullable=nullable)
161 
162 
163 @dt.dtype.register(PGDialect, sa.ARRAY)
164 def sa_pg_array(dialect, satype, nullable=True):
165     dimensions = satype.dimensions
166     if dimensions is not None and dimensions != 1:
167         raise NotImplementedError(
168             f"Nested array types not yet supported for {dialect.name} dialect"
169         )
170 
171     value_dtype = dt.dtype(dialect, satype.item_type)
172     return dt.Array(value_dtype, nullable=nullable)
173
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ibis/backends/postgres/datatypes.py b/ibis/backends/postgres/datatypes.py
--- a/ibis/backends/postgres/datatypes.py
+++ b/ibis/backends/postgres/datatypes.py
@@ -170,3 +170,8 @@
 
     value_dtype = dt.dtype(dialect, satype.item_type)
     return dt.Array(value_dtype, nullable=nullable)
+
+
+@dt.dtype.register(PGDialect, postgresql.TSVECTOR)
+def sa_postgres_tsvector(_, satype, nullable=True):
+    return dt.String(nullable=nullable)

{"golden_diff": "diff --git a/ibis/backends/postgres/datatypes.py b/ibis/backends/postgres/datatypes.py\n--- a/ibis/backends/postgres/datatypes.py\n+++ b/ibis/backends/postgres/datatypes.py\n@@ -170,3 +170,8 @@\n \n     value_dtype = dt.dtype(dialect, satype.item_type)\n     return dt.Array(value_dtype, nullable=nullable)\n+\n+\n+@dt.dtype.register(PGDialect, postgresql.TSVECTOR)\n+def sa_postgres_tsvector(_, satype, nullable=True):\n+    return dt.String(nullable=nullable)\n", "issue": "bug(postgres): cannot connect to postgres table with `tsvector` column\n### What happened?\n\nReposting from https://stackoverflow.com/questions/74520302/why-cant-i-connect-ibis-to-a-postgres-table-with-a-tsvector-column\r\n\r\nImplementing whatever postgres functionality exists around tsvector may not be something we want to do, but I think we should at least allow connecting to the table.\n\n### What version of ibis are you using?\n\n3.2.0\n\n### What backend(s) are you using, if any?\n\npostgres\n\n### Relevant log output\n\n```sh\nKeyError                                  Traceback (most recent call last)\r\nFile ~/anaconda3/envs/ec_extract/lib/python3.9/site-packages/multipledispatch/dispatcher.py:269, in Dispatcher.__call__(self, *args, **kwargs)\r\n    268 try:\r\n--> 269     func = self._cache[types]\r\n    270 except KeyError:\r\n\r\nKeyError: (<class 'sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2'>, <class 'sqlalchemy.dialects.postgresql.base.TSVECTOR'>)\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nNotImplementedError                       Traceback (most recent call last)\r\n*** \r\n----> 29 main_table = con.table(table_name)[columns['column_list']]\r\n\r\nFile ~/anaconda3/envs/ec_extract/lib/python3.9/site-packages/ibis/backends/base/sql/alchemy/__init__.py:438, in BaseAlchemyBackend.table(self, name, database, schema)\r\n    428     return self.database(database=database).table(\r\n    429         name=name,\r\n    430         database=database,\r\n    431         schema=schema,\r\n    432     )\r\n    433 sqla_table = self._get_sqla_table(\r\n    434     name,\r\n...\r\n    275             (self.name, str_signature(types)))\r\n    276     self._cache[types] = func\r\n    277 try:\r\n\r\nNotImplementedError: Could not find signature for dtype: <PGDialect_psycopg2, TSVECTOR>\n```\n\n\n### Code of Conduct\n\n- [X] I agree to follow this project's Code of Conduct\n", "before_files": [{"content": "from __future__ import annotations\n\nimport parsy\nimport sqlalchemy as sa\nimport toolz\nfrom sqlalchemy.dialects import postgresql\nfrom sqlalchemy.dialects.postgresql.base import PGDialect\n\nimport ibis.expr.datatypes as dt\nfrom ibis.backends.base.sql.alchemy import to_sqla_type\nfrom ibis.common.parsing import (\n    COMMA,\n    LBRACKET,\n    LPAREN,\n    PRECISION,\n    RBRACKET,\n    RPAREN,\n    SCALE,\n    spaceless,\n    spaceless_string,\n)\n\n_BRACKETS = \"[]\"\n\n\ndef _parse_numeric(\n    text: str, default_decimal_parameters: tuple[int | None, int | None] = (None, None)\n) -> dt.DataType:\n    decimal = spaceless_string(\"decimal\", \"numeric\").then(\n        parsy.seq(LPAREN.then(PRECISION.skip(COMMA)), SCALE.skip(RPAREN))\n        .optional(default_decimal_parameters)\n        .combine(dt.Decimal)\n    )\n\n    brackets = spaceless(LBRACKET).then(spaceless(RBRACKET))\n\n    pg_array = parsy.seq(decimal, brackets.at_least(1).map(len)).combine(\n        lambda value_type, n: toolz.nth(n, toolz.iterate(dt.Array, value_type))\n    )\n\n    ty = pg_array | decimal\n    return ty.parse(text)\n\n\ndef _get_type(typestr: str) -> dt.DataType:\n    is_array = typestr.endswith(_BRACKETS)\n    if (typ := _type_mapping.get(typestr.replace(_BRACKETS, \"\"))) is not None:\n        return dt.Array(typ) if is_array else typ\n    return _parse_numeric(typestr)\n\n\n_type_mapping = {\n    \"bigint\": dt.int64,\n    \"boolean\": dt.bool,\n    \"bytea\": dt.binary,\n    \"character varying\": dt.string,\n    \"character\": dt.string,\n    \"character(1)\": dt.string,\n    \"date\": dt.date,\n    \"double precision\": dt.float64,\n    \"geography\": dt.geography,\n    \"geometry\": dt.geometry,\n    \"inet\": dt.inet,\n    \"integer\": dt.int32,\n    \"interval\": dt.interval,\n    \"json\": dt.json,\n    \"jsonb\": dt.json,\n    \"line\": dt.linestring,\n    \"macaddr\": dt.macaddr,\n    \"macaddr8\": dt.macaddr,\n    \"numeric\": dt.decimal,\n    \"point\": dt.point,\n    \"polygon\": dt.polygon,\n    \"real\": dt.float32,\n    \"smallint\": dt.int16,\n    \"text\": dt.string,\n    # NB: this isn't correct because we're losing the \"with time zone\"\n    # information (ibis doesn't have time type that is time-zone aware), but we\n    # try to do _something_ here instead of failing\n    \"time with time zone\": dt.time,\n    \"time without time zone\": dt.time,\n    \"timestamp with time zone\": dt.Timestamp(\"UTC\"),\n    \"timestamp without time zone\": dt.timestamp,\n    \"uuid\": dt.uuid,\n}\n\n\n@to_sqla_type.register(PGDialect, dt.Array)\ndef _pg_array(dialect, itype):\n    # Unwrap the array element type because sqlalchemy doesn't allow arrays of\n    # arrays. This doesn't affect the underlying data.\n    while itype.is_array():\n        itype = itype.value_type\n    return sa.ARRAY(to_sqla_type(dialect, itype))\n\n\n@to_sqla_type.register(PGDialect, dt.Map)\ndef _pg_map(dialect, itype):\n    if not (itype.key_type.is_string() and itype.value_type.is_string()):\n        raise TypeError(f\"PostgreSQL only supports map<string, string>, got: {itype}\")\n    return postgresql.HSTORE()\n\n\n@dt.dtype.register(PGDialect, postgresql.DOUBLE_PRECISION)\ndef sa_double(_, satype, nullable=True):\n    return dt.Float64(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.UUID)\ndef sa_uuid(_, satype, nullable=True):\n    return dt.UUID(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.MACADDR)\ndef sa_macaddr(_, satype, nullable=True):\n    return dt.MACADDR(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.HSTORE)\ndef sa_hstore(_, satype, nullable=True):\n    return dt.Map(dt.string, dt.string, nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.INET)\ndef sa_inet(_, satype, nullable=True):\n    return dt.INET(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.JSONB)\ndef sa_json(_, satype, nullable=True):\n    return dt.JSON(nullable=nullable)\n\n\n_POSTGRES_FIELD_TO_IBIS_UNIT = {\n    \"YEAR\": \"Y\",\n    \"MONTH\": \"M\",\n    \"DAY\": \"D\",\n    \"HOUR\": \"h\",\n    \"MINUTE\": \"m\",\n    \"SECOND\": \"s\",\n    \"YEAR TO MONTH\": \"M\",\n    \"DAY TO HOUR\": \"h\",\n    \"DAY TO MINUTE\": \"m\",\n    \"DAY TO SECOND\": \"s\",\n    \"HOUR TO MINUTE\": \"m\",\n    \"HOUR TO SECOND\": \"s\",\n    \"MINUTE TO SECOND\": \"s\",\n}\n\n\n@dt.dtype.register(PGDialect, postgresql.INTERVAL)\ndef sa_postgres_interval(_, satype, nullable=True):\n    field = satype.fields.upper()\n    if (unit := _POSTGRES_FIELD_TO_IBIS_UNIT.get(field, None)) is None:\n        raise ValueError(f\"Unknown PostgreSQL interval field {field!r}\")\n    elif unit in {\"Y\", \"M\"}:\n        raise ValueError(\n            \"Variable length intervals are not yet supported with PostgreSQL\"\n        )\n    return dt.Interval(unit=unit, nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, sa.ARRAY)\ndef sa_pg_array(dialect, satype, nullable=True):\n    dimensions = satype.dimensions\n    if dimensions is not None and dimensions != 1:\n        raise NotImplementedError(\n            f\"Nested array types not yet supported for {dialect.name} dialect\"\n        )\n\n    value_dtype = dt.dtype(dialect, satype.item_type)\n    return dt.Array(value_dtype, nullable=nullable)\n", "path": "ibis/backends/postgres/datatypes.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport parsy\nimport sqlalchemy as sa\nimport toolz\nfrom sqlalchemy.dialects import postgresql\nfrom sqlalchemy.dialects.postgresql.base import PGDialect\n\nimport ibis.expr.datatypes as dt\nfrom ibis.backends.base.sql.alchemy import to_sqla_type\nfrom ibis.common.parsing import (\n    COMMA,\n    LBRACKET,\n    LPAREN,\n    PRECISION,\n    RBRACKET,\n    RPAREN,\n    SCALE,\n    spaceless,\n    spaceless_string,\n)\n\n_BRACKETS = \"[]\"\n\n\ndef _parse_numeric(\n    text: str, default_decimal_parameters: tuple[int | None, int | None] = (None, None)\n) -> dt.DataType:\n    decimal = spaceless_string(\"decimal\", \"numeric\").then(\n        parsy.seq(LPAREN.then(PRECISION.skip(COMMA)), SCALE.skip(RPAREN))\n        .optional(default_decimal_parameters)\n        .combine(dt.Decimal)\n    )\n\n    brackets = spaceless(LBRACKET).then(spaceless(RBRACKET))\n\n    pg_array = parsy.seq(decimal, brackets.at_least(1).map(len)).combine(\n        lambda value_type, n: toolz.nth(n, toolz.iterate(dt.Array, value_type))\n    )\n\n    ty = pg_array | decimal\n    return ty.parse(text)\n\n\ndef _get_type(typestr: str) -> dt.DataType:\n    is_array = typestr.endswith(_BRACKETS)\n    if (typ := _type_mapping.get(typestr.replace(_BRACKETS, \"\"))) is not None:\n        return dt.Array(typ) if is_array else typ\n    return _parse_numeric(typestr)\n\n\n_type_mapping = {\n    \"bigint\": dt.int64,\n    \"boolean\": dt.bool,\n    \"bytea\": dt.binary,\n    \"character varying\": dt.string,\n    \"character\": dt.string,\n    \"character(1)\": dt.string,\n    \"date\": dt.date,\n    \"double precision\": dt.float64,\n    \"geography\": dt.geography,\n    \"geometry\": dt.geometry,\n    \"inet\": dt.inet,\n    \"integer\": dt.int32,\n    \"interval\": dt.interval,\n    \"json\": dt.json,\n    \"jsonb\": dt.json,\n    \"line\": dt.linestring,\n    \"macaddr\": dt.macaddr,\n    \"macaddr8\": dt.macaddr,\n    \"numeric\": dt.decimal,\n    \"point\": dt.point,\n    \"polygon\": dt.polygon,\n    \"real\": dt.float32,\n    \"smallint\": dt.int16,\n    \"text\": dt.string,\n    # NB: this isn't correct because we're losing the \"with time zone\"\n    # information (ibis doesn't have time type that is time-zone aware), but we\n    # try to do _something_ here instead of failing\n    \"time with time zone\": dt.time,\n    \"time without time zone\": dt.time,\n    \"timestamp with time zone\": dt.Timestamp(\"UTC\"),\n    \"timestamp without time zone\": dt.timestamp,\n    \"uuid\": dt.uuid,\n}\n\n\n@to_sqla_type.register(PGDialect, dt.Array)\ndef _pg_array(dialect, itype):\n    # Unwrap the array element type because sqlalchemy doesn't allow arrays of\n    # arrays. This doesn't affect the underlying data.\n    while itype.is_array():\n        itype = itype.value_type\n    return sa.ARRAY(to_sqla_type(dialect, itype))\n\n\n@to_sqla_type.register(PGDialect, dt.Map)\ndef _pg_map(dialect, itype):\n    if not (itype.key_type.is_string() and itype.value_type.is_string()):\n        raise TypeError(f\"PostgreSQL only supports map<string, string>, got: {itype}\")\n    return postgresql.HSTORE()\n\n\n@dt.dtype.register(PGDialect, postgresql.DOUBLE_PRECISION)\ndef sa_double(_, satype, nullable=True):\n    return dt.Float64(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.UUID)\ndef sa_uuid(_, satype, nullable=True):\n    return dt.UUID(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.MACADDR)\ndef sa_macaddr(_, satype, nullable=True):\n    return dt.MACADDR(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.HSTORE)\ndef sa_hstore(_, satype, nullable=True):\n    return dt.Map(dt.string, dt.string, nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.INET)\ndef sa_inet(_, satype, nullable=True):\n    return dt.INET(nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.JSONB)\ndef sa_json(_, satype, nullable=True):\n    return dt.JSON(nullable=nullable)\n\n\n_POSTGRES_FIELD_TO_IBIS_UNIT = {\n    \"YEAR\": \"Y\",\n    \"MONTH\": \"M\",\n    \"DAY\": \"D\",\n    \"HOUR\": \"h\",\n    \"MINUTE\": \"m\",\n    \"SECOND\": \"s\",\n    \"YEAR TO MONTH\": \"M\",\n    \"DAY TO HOUR\": \"h\",\n    \"DAY TO MINUTE\": \"m\",\n    \"DAY TO SECOND\": \"s\",\n    \"HOUR TO MINUTE\": \"m\",\n    \"HOUR TO SECOND\": \"s\",\n    \"MINUTE TO SECOND\": \"s\",\n}\n\n\n@dt.dtype.register(PGDialect, postgresql.INTERVAL)\ndef sa_postgres_interval(_, satype, nullable=True):\n    field = satype.fields.upper()\n    if (unit := _POSTGRES_FIELD_TO_IBIS_UNIT.get(field, None)) is None:\n        raise ValueError(f\"Unknown PostgreSQL interval field {field!r}\")\n    elif unit in {\"Y\", \"M\"}:\n        raise ValueError(\n            \"Variable length intervals are not yet supported with PostgreSQL\"\n        )\n    return dt.Interval(unit=unit, nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, sa.ARRAY)\ndef sa_pg_array(dialect, satype, nullable=True):\n    dimensions = satype.dimensions\n    if dimensions is not None and dimensions != 1:\n        raise NotImplementedError(\n            f\"Nested array types not yet supported for {dialect.name} dialect\"\n        )\n\n    value_dtype = dt.dtype(dialect, satype.item_type)\n    return dt.Array(value_dtype, nullable=nullable)\n\n\n@dt.dtype.register(PGDialect, postgresql.TSVECTOR)\ndef sa_postgres_tsvector(_, satype, nullable=True):\n    return dt.String(nullable=nullable)\n", "path": "ibis/backends/postgres/datatypes.py"}]}