@@ -625,6 +625,86 @@ class TestStrEnum(str, Enum):
625625 assert qs_result .result ()[0 ]["test_mood2" ] == TestStrEnum .OK
626626
627627
628+ async def test_char_internal_type_pg_type_reproduction (
629+ psql_pool : ConnectionPool ,
630+ ) -> None :
631+ """Regression for issue #165.
632+
633+ The original repro queried system catalog columns of the internal
634+ ``"char"`` type (OID 18). Prior to the fix this raised
635+ ``RustToPyValueMappingError`` even when ``custom_decoders`` was supplied,
636+ because the type had no native decoder.
637+ """
638+ pg_type_limit = 5
639+ async with psql_pool .acquire () as conn :
640+ result = await conn .execute (
641+ f"SELECT typname, typtype FROM pg_type LIMIT { pg_type_limit } " ,
642+ )
643+ rows = result .result ()
644+ assert len (rows ) == pg_type_limit
645+ for row in rows :
646+ assert isinstance (row ["typname" ], str )
647+ assert isinstance (row ["typtype" ], str )
648+ assert len (row ["typtype" ]) == 1
649+
650+
651+ async def test_char_internal_type_byte_spectrum (
652+ psql_pool : ConnectionPool ,
653+ ) -> None :
654+ """Round-trip representative ASCII bytes through a ``"char"`` column.
655+
656+ The internal ``"char"`` type holds a single byte. SQL ``chr(N)`` rejects
657+ NUL (0x00) with "null character not permitted", and ``chr(N)`` for N >= 128
658+ produces multi-byte UTF-8 whose cast to ``"char"`` keeps only the first byte
659+ (e.g. chr(128)::"char" stores 0xC2, not 0x80). So this integration test
660+ covers the reachable ASCII slice. The full 0..=255 byte mapping is verified
661+ by the Rust unit test in models/internal_char.rs.
662+ """
663+ bytes_under_test = [0x20 , 0x41 , 0x61 , 0x7E ]
664+
665+ async with psql_pool .acquire () as conn :
666+ await conn .execute ("DROP TABLE IF EXISTS for_char_test" )
667+ await conn .execute (
668+ 'CREATE TABLE for_char_test (id INT, c "char")' ,
669+ )
670+ for i , b in enumerate (bytes_under_test ):
671+ await conn .execute (
672+ 'INSERT INTO for_char_test (id, c) VALUES ($1, chr($2)::"char")' ,
673+ [i , b ],
674+ )
675+ await conn .execute (
676+ "INSERT INTO for_char_test (id, c) VALUES ($1, NULL)" ,
677+ [len (bytes_under_test )],
678+ )
679+
680+ result = await conn .execute (
681+ "SELECT id, c FROM for_char_test ORDER BY id" ,
682+ )
683+ rows = result .result ()
684+
685+ decoded = {row ["id" ]: row ["c" ] for row in rows }
686+ for i , b in enumerate (bytes_under_test ):
687+ value = decoded [i ]
688+ assert isinstance (value , str )
689+ assert len (value ) == 1
690+ assert (
691+ ord (value ) == b
692+ ), f"byte 0x{ b :02x} round-tripped to ord(value)=0x{ ord (value ):02x} "
693+ assert decoded [len (bytes_under_test )] is None
694+
695+
696+ async def test_char_internal_type_array (
697+ psql_pool : ConnectionPool ,
698+ ) -> None :
699+ """Decode an array of ``"char"`` (OID 1002) into a list of one-character strs."""
700+ async with psql_pool .acquire () as conn :
701+ result = await conn .execute (
702+ "SELECT ARRAY['a'::\" char\" , 'b'::\" char\" , 'c'::\" char\" ] AS chars" ,
703+ )
704+ rows = result .result ()
705+ assert rows [0 ]["chars" ] == ["a" , "b" , "c" ]
706+
707+
628708async def test_custom_type_as_parameter (
629709 psql_pool : ConnectionPool ,
630710) -> None :
0 commit comments