-
-
Notifications
You must be signed in to change notification settings - Fork 219
Description
Hello, again :). With binary loading fixed in #545 🎉, next up is... binary dumping.
(aside: I suspect since the binary protocol isn't default, it just doesn't get tested as much.)
Observations
I have only observed this issue with the binary protocol, text protocol seems fine.
When serializing something of the form: Composite( Array[ Composite( Enum ) ] ),
The stack trace shows
- Composite Dumper
abc.CollectionBinaryDumper - Array Dumper
abc.ItemListBinaryDumper - Composite Dumper
abc.ItemBinaryDumper - Array Dumper
abc.ItemListBinaryDumper(Expected Enum Dumper)
And the following error is thrown due to the enum, Kind, being passed to the array dumper.
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 270, in dump_list
if len(L) != dims[dim]:
TypeError: object of type 'Kind' has no len()Note: this can occur without an array.
In a custom BinaryDumper, adding self._tx.set_dumper_types(self.info.field_types, self.format) to
the top of the dump method seemed to workaround the problem.
However, the fix was only intermittent, sometimes still failing.
Env
- Python: v3.10
- OS: Arch Linux
- DB: Postgres 15.2
- psycopg 3.1.8 both with and without
binaryextra - Don't know if it is used but I have
libpq.so.5.15on my system.
Reproduction
SQL to initialize the state.
DROP SCHEMA IF EXISTS test CASCADE;
CREATE SCHEMA IF NOT EXISTS test;
CREATE TYPE test.kind AS ENUM('A', 'B');
CREATE TYPE test.item AS (kind test.Kind, time timestamptz);
CREATE TYPE test.collection AS (items test.item[]);Python to trigger the problem.
import psycopg
from enum import Enum, auto
from datetime import datetime, timezone
from psycopg.types.enum import EnumInfo, register_enum
from psycopg.types.composite import CompositeInfo, register_composite
class Kind(Enum):
A = auto()
B = auto()
with psycopg.connect("host=localhost user=postgres password=postgres") as conn:
info = CompositeInfo.fetch(conn, "test.collection")
register_composite(info, conn)
Collection = info.python_type
info = CompositeInfo.fetch(conn, "test.item")
register_composite(info, conn)
Item = info.python_type
info = EnumInfo.fetch(conn, "test.kind")
register_enum(info, conn, Kind)
# Successful: Array of Items
print(
conn.execute(
"SELECT %b",
[[Item(kind=Kind.A, time=datetime.now(tz=timezone.utc))]],
).fetchall()
)
# Successful: Collection with empty array
print(
conn.execute(
"SELECT %b",
[Collection(items=[])],
).fetchall()
)
# Failure: Collection with non-empty array
print(
conn.execute(
"SELECT %b",
[Collection(items=[Item(kind=Kind.A, time=datetime.now(tz=timezone.utc))])],
).fetchall()
)Error Traceback Logs
Traceback when using psycopg[binary,pool] version 3.1.8
Traceback (most recent call last):
File "/home/user/bugs/python/src/repro.py", line 43, in <module>
conn.execute(
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/connection.py", line 876, in execute
return cur.execute(query, params, prepare=prepare)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/cursor.py", line 719, in execute
self._conn.wait(
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/connection.py", line 957, in wait
return waiting.wait(gen, self.pgconn.socket, timeout=timeout)
File "psycopg_binary/_psycopg/waiting.pyx", line 172, in psycopg_binary._psycopg.wait_c
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/cursor.py", line 195, in _execute_gen
pgq = self._convert_query(query, params)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/cursor.py", line 468, in _convert_query
pgq.convert(query, params)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_queries.py", line 80, in convert
self.dump(vars)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_queries.py", line 91, in dump
self.params = self._tx.dump_sequence(params, self._want_formats)
File "psycopg_binary/_psycopg/transform.pyx", line 353, in psycopg_binary._psycopg.Transformer.dump_sequence
File "psycopg_binary/_psycopg/transform.pyx", line 404, in psycopg_binary._psycopg.Transformer.dump_sequence
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/composite.py", line 85, in dump
adapted = self._tx.dump_sequence(obj, self._formats)
File "psycopg_binary/_psycopg/transform.pyx", line 353, in psycopg_binary._psycopg.Transformer.dump_sequence
File "psycopg_binary/_psycopg/transform.pyx", line 379, in psycopg_binary._psycopg.Transformer.dump_sequence
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 289, in dump
dump_list(obj, 0)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 277, in dump_list
ad = self.sub_dumper.dump(item) # type: ignore[union-attr]
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/composite.py", line 85, in dump
adapted = self._tx.dump_sequence(obj, self._formats)
File "psycopg_binary/_psycopg/transform.pyx", line 353, in psycopg_binary._psycopg.Transformer.dump_sequence
File "psycopg_binary/_psycopg/transform.pyx", line 379, in psycopg_binary._psycopg.Transformer.dump_sequence
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 289, in dump
dump_list(obj, 0)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 270, in dump_list
if len(L) != dims[dim]:
TypeError: object of type 'Kind' has no len()
Traceback when using only psycopg[pool] version 3.1.8
Traceback (most recent call last):
File "/home/user/bugs/python/src/repro.py", line 43, in <module>
conn.execute(
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/connection.py", line 876, in execute
return cur.execute(query, params, prepare=prepare)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/cursor.py", line 719, in execute
self._conn.wait(
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/connection.py", line 957, in wait
return waiting.wait(gen, self.pgconn.socket, timeout=timeout)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/waiting.py", line 214, in wait_select
s = next(gen)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/cursor.py", line 195, in _execute_gen
pgq = self._convert_query(query, params)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/cursor.py", line 468, in _convert_query
pgq.convert(query, params)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_queries.py", line 80, in convert
self.dump(vars)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_queries.py", line 91, in dump
self.params = self._tx.dump_sequence(params, self._want_formats)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_transform.py", line 188, in dump_sequence
out[i] = dumper.dump(param)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/composite.py", line 85, in dump
adapted = self._tx.dump_sequence(obj, self._formats)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_transform.py", line 177, in dump_sequence
out[i] = self._row_dumpers[i].dump(param)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 289, in dump
dump_list(obj, 0)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 277, in dump_list
ad = self.sub_dumper.dump(item) # type: ignore[union-attr]
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/composite.py", line 85, in dump
adapted = self._tx.dump_sequence(obj, self._formats)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/_transform.py", line 177, in dump_sequence
out[i] = self._row_dumpers[i].dump(param)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 289, in dump
dump_list(obj, 0)
File "/home/user/bugs/python/.venv/lib/python3.10/site-packages/psycopg/types/array.py", line 270, in dump_list
if len(L) != dims[dim]:
TypeError: object of type 'Kind' has no len()