def test_fetch_info(conn, testcomp, name, fields): info = CompositeInfo.fetch(conn, name) assert info.name == "testcomp" assert info.oid > 0 assert info.oid != info.array_oid > 0 assert len(info.field_names) == 3 assert len(info.field_types) == 3 for i, (name, t) in enumerate(fields): assert info.field_names[i] == name assert info.field_types[i] == builtins[t].oid
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") info.register(context=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 testcomp(svcconn): cur = svcconn.cursor() cur.execute(""" create schema if not exists testschema; drop type if exists testcomp cascade; drop type if exists testschema.testcomp cascade; create type testcomp as (foo text, bar int8, baz float8); create type testschema.testcomp as (foo text, bar int8, qux bool); """) return CompositeInfo.fetch(svcconn, "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_load_composite(conn, testcomp, fmt_out): info = CompositeInfo.fetch(conn, "testcomp") info.register(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_register_scope(conn, testcomp): info = CompositeInfo.fetch(conn, "testcomp") info.register() for fmt in (pq.Format.TEXT, pq.Format.BINARY): for oid in (info.oid, info.array_oid): assert global_adapters._loaders[fmt].pop(oid) cur = conn.cursor() info.register(cur) for fmt in (pq.Format.TEXT, pq.Format.BINARY): for oid in (info.oid, info.array_oid): assert oid not in global_adapters._loaders[fmt] assert oid not in conn.adapters._loaders[fmt] assert oid in cur.adapters._loaders[fmt] info.register(conn) for fmt in (pq.Format.TEXT, pq.Format.BINARY): for oid in (info.oid, info.array_oid): assert oid not in global_adapters._loaders[fmt] assert oid in conn.adapters._loaders[fmt]
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 info.register(conn, factory=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]