def test_dump_composite_all_chars(conn, fmt_in, testcomp): cur = conn.cursor() register_composite(testcomp, cur) factory = testcomp.python_type for i in range(1, 256): obj = factory(chr(i), 1, 1.0) (res, ) = cur.execute( f"select row(chr(%s::int), 1, 1.0)::testcomp = %{fmt_in}", (i, obj)).fetchone() assert res is True
def test_dump_tuple(conn, rec, obj): cur = conn.cursor() fields = [f"f{i} text" for i in range(len(obj))] cur.execute(f""" drop type if exists tmptype; create type tmptype as ({', '.join(fields)}); """) info = CompositeInfo.fetch(conn, "tmptype") register_composite(info, conn) res = conn.execute("select %s::tmptype", [obj]).fetchone()[0] assert res == obj
def test_callable_dumper_not_registered(conn, testcomp): info = CompositeInfo.fetch(conn, "testcomp") def fac(*args): return args + (args[-1], ) register_composite(info, conn, factory=fac) assert info.python_type is None # but the loader is registered cur = conn.execute("select '(foo,42,3.14)'::testcomp") assert cur.fetchone()[0] == ("foo", 42, 3.14, 3.14)
def test_type_dumper_registered_binary(conn, testcomp): info = CompositeInfo.fetch(conn, "testcomp") register_composite(info, conn) assert issubclass(info.python_type, tuple) assert info.python_type.__name__ == "testcomp" d = conn.adapters.get_dumper(info.python_type, "b") assert issubclass(d, TupleBinaryDumper) assert d is not TupleBinaryDumper tc = info.python_type("foo", 42, 3.14) cur = conn.execute("select pg_typeof(%b)", [tc]) assert cur.fetchone()[0] == "testcomp"
def test_dump_builtin_empty_range(conn, fmt_in): conn.execute(""" drop type if exists tmptype; create type tmptype as (num integer, range daterange, nums integer[]) """) info = CompositeInfo.fetch(conn, "tmptype") register_composite(info, conn) cur = conn.execute( f"select pg_typeof(%{fmt_in})", [info.python_type(10, Range(empty=True), [])], ) assert cur.fetchone()[0] == "tmptype"
def test_dump_composite_null(conn, fmt_in, testcomp): cur = conn.cursor() register_composite(testcomp, cur) factory = testcomp.python_type obj = factory("foo", 1, None) rec = cur.execute( f""" select row('foo', 1, NULL)::testcomp = %(obj){fmt_in}, %(obj){fmt_in}::text """, { "obj": obj }, ).fetchone() assert rec[0] is True, rec[1]
def test_load_composite(conn, testcomp, fmt_out): info = CompositeInfo.fetch(conn, "testcomp") register_composite(info, conn) cur = conn.cursor(binary=fmt_out) res = cur.execute("select row('hello', 10, 20)::testcomp").fetchone()[0] assert res.foo == "hello" assert res.bar == 10 assert res.baz == 20.0 assert isinstance(res.baz, float) res = cur.execute( "select array[row('hello', 10, 30)::testcomp]").fetchone()[0] assert len(res) == 1 assert res[0].baz == 30.0 assert isinstance(res[0].baz, float)
def test_load_composite_factory(conn, testcomp, fmt_out): info = CompositeInfo.fetch(conn, "testcomp") class MyThing: def __init__(self, *args): self.foo, self.bar, self.baz = args register_composite(info, conn, factory=MyThing) assert info.python_type is MyThing cur = conn.cursor(binary=fmt_out) res = cur.execute("select row('hello', 10, 20)::testcomp").fetchone()[0] assert isinstance(res, MyThing) assert res.baz == 20.0 assert isinstance(res.baz, float) res = cur.execute( "select array[row('hello', 10, 30)::testcomp]").fetchone()[0] assert len(res) == 1 assert res[0].baz == 30.0 assert isinstance(res[0].baz, float)
def test_register_scope(conn, testcomp): info = CompositeInfo.fetch(conn, "testcomp") register_composite(info) for fmt in pq.Format: for oid in (info.oid, info.array_oid): assert postgres.adapters._loaders[fmt].pop(oid) for fmt in PyFormat: assert postgres.adapters._dumpers[fmt].pop(info.python_type) cur = conn.cursor() register_composite(info, cur) for fmt in pq.Format: for oid in (info.oid, info.array_oid): assert oid not in postgres.adapters._loaders[fmt] assert oid not in conn.adapters._loaders[fmt] assert oid in cur.adapters._loaders[fmt] register_composite(info, conn) for fmt in pq.Format: for oid in (info.oid, info.array_oid): assert oid not in postgres.adapters._loaders[fmt] assert oid in conn.adapters._loaders[fmt]
def test_no_info_error(conn): with pytest.raises(TypeError, match="composite"): register_composite(None, conn)