- Leave the connection working after interrupting a query with Ctrl-C
(currently only for sync connections, :ticket:`#231`).
+- Fix `Cursor.description` after a COPY ... TO STDOUT operation
+ (:ticket:`#235`).
Current release
from typing import Any, NamedTuple, Optional, Sequence, TYPE_CHECKING
from operator import attrgetter
-from . import errors as e
-
if TYPE_CHECKING:
from .cursor import BaseCursor
assert res
fname = res.fname(index)
- if not fname:
- raise e.InterfaceError(f"no name available for column {index}")
-
- self._name = fname.decode(cursor._encoding)
+ if fname:
+ self._name = fname.decode(cursor._encoding)
+ else:
+ # COPY_OUT results have columns but no name
+ self._name = f"column_{index + 1}"
self._data = ColumnData(
ftype=res.ftype(index),
assert "[INTRANS]" in str(copy)
+def test_description(conn):
+ with conn.cursor() as cur:
+ with cur.copy("copy (select 'This', 'Is', 'Text') to stdout") as copy:
+ len(cur.description) == 3
+ assert cur.description[0].name == "column_1"
+ assert cur.description[2].name == "column_3"
+ list(copy.rows())
+
+ len(cur.description) == 3
+ assert cur.description[0].name == "column_1"
+ assert cur.description[2].name == "column_3"
+
+
@pytest.mark.parametrize(
"format, buffer",
[(Format.TEXT, "sample_text"), (Format.BINARY, "sample_binary")],
assert "[INTRANS]" in str(copy)
+async def test_description(aconn):
+ async with aconn.cursor() as cur:
+ async with cur.copy("copy (select 'This', 'Is', 'Text') to stdout") as copy:
+ len(cur.description) == 3
+ assert cur.description[0].name == "column_1"
+ assert cur.description[2].name == "column_3"
+ await alist(copy.rows())
+
+ len(cur.description) == 3
+ assert cur.description[0].name == "column_1"
+ assert cur.description[2].name == "column_3"
+
+
@pytest.mark.parametrize(
"format, buffer",
[(Format.TEXT, "sample_text"), (Format.BINARY, "sample_binary")],