11import datetime
22import uuid
33from ipaddress import IPv4Address
4- from typing import Any
4+ from typing import Any , Dict , List
55
66import pytest
7+ from pydantic import BaseModel
78from tests .conftest import DefaultPydanticModel , DefaultPythonModelClass
89
910from psqlpy import ConnectionPool
@@ -232,7 +233,7 @@ async def test_as_class(
232233 ),
233234 ),
234235)
235- async def test_deserialization_rust_into_python (
236+ async def test_deserialization_simple_into_python (
236237 psql_pool : ConnectionPool ,
237238 postgres_type : str ,
238239 py_value : Any ,
@@ -257,3 +258,170 @@ async def test_deserialization_rust_into_python(
257258 )
258259
259260 assert raw_result .result ()[0 ]["test_field" ] == expected_deserialized
261+
262+
263+ async def test_deserialization_composite_into_python (
264+ psql_pool : ConnectionPool ,
265+ ) -> None :
266+ """Test that it's possible to deserialize custom postgresql type."""
267+ await psql_pool .execute ("DROP TABLE IF EXISTS for_test" )
268+ await psql_pool .execute ("DROP TYPE IF EXISTS all_types" )
269+ create_type_query = """
270+ CREATE type all_types AS (
271+ bytea_ BYTEA,
272+ varchar_ VARCHAR,
273+ text_ TEXT,
274+ bool_ BOOL,
275+ int2_ INT2,
276+ int4_ INT4,
277+ int8_ INT8,
278+ flaot4_ FLOAT4,
279+ date_ DATE,
280+ time_ TIME,
281+ timestamp_ TIMESTAMP,
282+ timestampz_ TIMESTAMPTZ,
283+ uuid_ UUID,
284+ inet_ INET,
285+ jsonb_ JSONB,
286+ json_ JSON,
287+
288+ varchar_arr VARCHAR ARRAY,
289+ text_arr TEXT ARRAY,
290+ bool_arr BOOL ARRAY,
291+ int2_arr INT2 ARRAY,
292+ int4_arr INT4 ARRAY,
293+ int8_arr INT8 ARRAY,
294+ flaot4_arr FLOAT4 ARRAY,
295+ date_arr DATE ARRAY,
296+ time_arr TIME ARRAY,
297+ timestamp_arr TIMESTAMP ARRAY,
298+ timestampz_arr TIMESTAMPTZ ARRAY,
299+ uuid_arr UUID ARRAY,
300+ inet_arr INET ARRAY,
301+ jsonb_arr JSONB ARRAY,
302+ json_arr JSON ARRAY
303+ )
304+ """
305+ create_table_query = """
306+ CREATE table for_test (custom_type all_types)
307+ """
308+
309+ await psql_pool .execute (
310+ querystring = create_type_query ,
311+ )
312+ await psql_pool .execute (
313+ querystring = create_table_query ,
314+ )
315+ await psql_pool .execute (
316+ querystring = "INSERT INTO for_test VALUES (ROW($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31))" , # noqa: E501
317+ parameters = [
318+ b"Bytes" ,
319+ "Some String" ,
320+ PyText ("Some String" ),
321+ True ,
322+ SmallInt (123 ),
323+ Integer (199 ),
324+ BigInt (10001 ),
325+ 32.12329864501953 ,
326+ now_datetime .date (),
327+ now_datetime .time (),
328+ now_datetime ,
329+ now_datetime_with_tz ,
330+ PyUUID (str (uuid_ )),
331+ IPv4Address ("192.0.0.1" ),
332+ {
333+ "test" : ["something" , 123 , "here" ],
334+ "nested" : ["JSON" ],
335+ },
336+ PyJSON (
337+ {
338+ "test" : ["something" , 123 , "here" ],
339+ "nested" : ["JSON" ],
340+ },
341+ ),
342+ ["Some String" , "Some String" ],
343+ [PyText ("Some String" ), PyText ("Some String" )],
344+ [True , False ],
345+ [SmallInt (123 ), SmallInt (321 )],
346+ [Integer (123 ), Integer (321 )],
347+ [BigInt (10001 ), BigInt (10001 )],
348+ [32.12329864501953 , 32.12329864501953 ],
349+ [now_datetime .date (), now_datetime .date ()],
350+ [now_datetime .time (), now_datetime .time ()],
351+ [now_datetime , now_datetime ],
352+ [now_datetime_with_tz , now_datetime_with_tz ],
353+ [PyUUID (str (uuid_ )), PyUUID (str (uuid_ ))],
354+ [IPv4Address ("192.0.0.1" ), IPv4Address ("192.0.0.1" )],
355+ [
356+ {
357+ "test" : ["something" , 123 , "here" ],
358+ "nested" : ["JSON" ],
359+ },
360+ {
361+ "test" : ["something" , 123 , "here" ],
362+ "nested" : ["JSON" ],
363+ },
364+ ],
365+ [
366+ PyJSON (
367+ {
368+ "test" : ["something" , 123 , "here" ],
369+ "nested" : ["JSON" ],
370+ },
371+ ),
372+ PyJSON (
373+ {
374+ "test" : ["something" , 123 , "here" ],
375+ "nested" : ["JSON" ],
376+ },
377+ ),
378+ ],
379+ ],
380+ )
381+
382+ class ValidateModelForCustomType (BaseModel ):
383+ bytea_ : list [int ]
384+ varchar_ : str
385+ text_ : str
386+ bool_ : bool
387+ int2_ : int
388+ int4_ : int
389+ int8_ : int
390+ flaot4_ : float
391+ date_ : datetime .date
392+ time_ : datetime .time
393+ timestamp_ : datetime .datetime
394+ timestampz_ : datetime .datetime
395+ uuid_ : uuid .UUID
396+ inet_ : IPv4Address
397+ jsonb_ : Dict [str , List [str ]]
398+ json_ : Dict [str , List [str ]]
399+
400+ varchar_arr : List [str ]
401+ text_arr : List [str ]
402+ bool_arr : List [bool ]
403+ int2_arr : List [int ]
404+ int4_arr : List [int ]
405+ int8_arr : List [int ]
406+ flaot4_arr : List [float ]
407+ date_arr : List [datetime .date ]
408+ time_arr : List [datetime .time ]
409+ timestamp_arr : List [datetime .datetime ]
410+ timestampz_arr : List [datetime .datetime ]
411+ uuid_arr : List [uuid .UUID ]
412+ inet_arr : List [IPv4Address ]
413+ jsonb_arr : List [Dict [str , List [str ]]]
414+ json_arr : List [Dict [str , List [str ]]]
415+
416+ class TopLevelModel (BaseModel ):
417+ custom_type : ValidateModelForCustomType
418+
419+ query_result = await psql_pool .execute (
420+ "SELECT custom_type FROM for_test" ,
421+ )
422+
423+ model_result = query_result .as_class (
424+ as_class = TopLevelModel ,
425+ )
426+
427+ assert isinstance (model_result [0 ], TopLevelModel )
0 commit comments