]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix(composite): fix fetching composite info with invalid name or field names
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 20 Mar 2022 15:02:12 +0000 (16:02 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 10 May 2022 17:13:26 +0000 (19:13 +0200)
psycopg/psycopg/types/composite.py
tests/types/test_composite.py

index 4174854b1e8d6a83651f126eefbaed011efddb40..9093f66e8e463c4c3a7cde670536a8e7585e5b57 100644 (file)
@@ -17,6 +17,7 @@ from ..adapt import Transformer, PyFormat, RecursiveDumper, Loader
 from .._struct import pack_len, unpack_len
 from ..postgres import TEXT_OID
 from .._typeinfo import CompositeInfo as CompositeInfo  # exported here
+from .._encodings import _as_python_identifier
 
 _struct_oidlen = struct.Struct("!Ii")
 _pack_oidlen = cast(Callable[[int, int], bytes], _struct_oidlen.pack)
@@ -240,7 +241,10 @@ def register_composite(
     info.register(context)
 
     if not factory:
-        factory = namedtuple(info.name, info.field_names)  # type: ignore
+        factory = namedtuple(  # type: ignore
+            _as_python_identifier(info.name),
+            [_as_python_identifier(n) for n in info.field_names],
+        )
 
     adapters = context.adapters if context else postgres.adapters
 
index a3f7d5284340f0abe7796b920c1214b32e9ef5bb..aee61c8dbb585ae450f2fd2926d8aab58db27df5 100644 (file)
@@ -8,6 +8,8 @@ from psycopg.types.range import Range
 from psycopg.types.composite import CompositeInfo, register_composite
 from psycopg.types.composite import TupleDumper, TupleBinaryDumper
 
+eur = "\u20ac"
+
 tests_str = [
     ("", ()),
     # Funnily enough there's no way to represent (None,) in Postgres
@@ -324,3 +326,22 @@ def test_callable_dumper_not_registered(conn, testcomp):
 def test_no_info_error(conn):
     with pytest.raises(TypeError, match="composite"):
         register_composite(None, conn)  # type: ignore[arg-type]
+
+
+def test_invalid_fields_names(conn):
+    conn.execute("set client_encoding to utf8")
+    conn.execute(
+        f"""
+        create type "a-b" as ("c-d" text, "{eur}" int);
+        create type "-x-{eur}" as ("w-ww" "a-b", "0" int);
+        """
+    )
+    ab = CompositeInfo.fetch(conn, '"a-b"')
+    x = CompositeInfo.fetch(conn, f'"-x-{eur}"')
+    register_composite(ab, conn)
+    register_composite(x, conn)
+    obj = x.python_type(ab.python_type("foo", 10), 20)
+    conn.execute(f"""create table meh (wat "-x-{eur}")""")
+    conn.execute("insert into meh values (%s)", [obj])
+    got = conn.execute("select wat from meh").fetchone()[0]
+    assert obj == got