- asyncpg version: 0.28.0
- PostgreSQL version: 15 (latest docker container)
- Do you use a PostgreSQL SaaS? If so, which? Can you reproduce
the issue with a local PostgreSQL install?: - Python version: 3.10
- Platform: windows or debian in WSL
- Do you use pgbouncer?: no
- Did you install asyncpg with pip?: yes
- If you built asyncpg locally, which version of Cython did you use?:
- Can the issue be reproduced under both asyncio and
uvloop?: yes
Hi,
I need for a project to store complex numbers in a database (numpy.complex64 exactly) and for that I created a postgresql composite datatype.
I was able to make it work with asyncpg in text format for simple INSERT, SELECT... after some works and patches found on previous issues report.
However for the binary format I have weird errors : asyncpg.exceptions.DatatypeMismatchError: wrong number of columns: 1082549862, expected 2
I made a "simple" python script to reproduce the issue :
from __future__ importannotationsimportasyncpgimportasyncioimportnumpyasnpfromastimportliteral_evalimportstructSQL_CREATE="""DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'complex') THEN CREATE TYPE complex AS ( r float4, i float4 ); END IF; CREATE TABLE dummy_table (dummy_column complex); DROP TABLE dummy_table;END $$;DROP TABLE IF EXISTS "poc_asyncpg";CREATE TABLE "poc_asyncpg" ( "id" SERIAL PRIMARY KEY, "float_value" float4 NULL, "complex_value" complex NULL, "complex_array" complex[] NULL)"""def_cplx_decode(val) ->np.complex64:
cplx=complex(*literal_eval(val))
returnnp.complex64(cplx)
def_cplx_encode(val: np.complex64|complex) ->str:
returnstr((np.float32(val.real), np.float32(val.imag)))
asyncdefset_type_codec(conn):
""" had to use this patch since the conn.set_type_codec does not work for scalar variables """schema='public'format='text'conn._check_open()
typenames= ('complex',)
fortypenameintypenames:
typeinfo=awaitconn.fetchrow(
asyncpg.introspection.TYPE_BY_NAME, typename, schema)
ifnottypeinfo:
raiseValueError('unknown type: {}.{}'.format(schema, typename))
oid=typeinfo['oid']
conn._protocol.get_settings().add_python_codec(
oid, typename, schema, 'scalar',
lambdaa: _cplx_encode(a), lambdaa: _cplx_decode(a), format)
# if this part is commented, error message is : # asyncpg.exceptions._base.InternalClientError: no binary format encoder for type complex conn._protocol.get_settings().add_python_codec(
oid, typename, schema, 'scalar',
encoder=lambdax: struct.pack('!2f', x.real, x.imag),
decoder=lambdax: np.frombuffer(x, dtype=np.complex64)[0],
format="binary")
# Statement cache is no longer valid due to codec changes.conn._drop_local_statement_cache()
asyncdefinit_connection(conn):
awaitset_type_codec(conn)
awaitconn.set_type_codec(
'numeric', encoder=str, decoder=np.float32,
schema='pg_catalog', format='text'
)
awaitconn.set_type_codec(
'float4', encoder=str, decoder=np.float32,
schema='pg_catalog', format='text'
)
awaitconn.set_type_codec(
'float4', encoder=struct.Struct("!f").pack, decoder=struct.Struct("!f").unpack,
schema='pg_catalog', format='binary'
)
asyncdeftrunc(pool):
asyncwithpool.acquire() asconn:
asyncwithconn.transaction():
query="TRUNCATE poc_asyncpg"awaitconn.execute(query)
asyncdefworker_copy(pool, column_name, data):
awaittrunc(pool)
asyncwithpool.acquire() asconn:
asyncwithconn.transaction():
awaitconn.copy_records_to_table("poc_asyncpg",
records=[(data,)],
columns=(column_name,)
)
try:
fromcommonimportdbinfoexceptImportErrorase:
classDB:
user="user"password="password"database="db"dbinfo=DB()
defcreate_pool(db_info):
pool=asyncpg.create_pool(
user=db_info.user,
password=db_info.password,
database=dbinfo.database,
host="127.0.0.1",
init=init_connection
)
returnpoolasyncdefmain(info):
pool=awaitcreate_pool(info)
asyncwithpool.acquire() asconn:
asyncwithconn.transaction():
awaitconn.execute(SQL_CREATE)
awaitworker_copy(pool, "float_value", np.float32(4.2))
awaitworker_copy(pool, "complex_value", np.complex64(4.2+1j*4.2))
if__name__=='__main__':
loop=asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main(dbinfo))
the issue with a local PostgreSQL install?:
uvloop?: yes
Hi,
I need for a project to store complex numbers in a database (numpy.complex64 exactly) and for that I created a postgresql composite datatype.
I was able to make it work with asyncpg in text format for simple INSERT, SELECT... after some works and patches found on previous issues report.
However for the binary format I have weird errors : asyncpg.exceptions.DatatypeMismatchError: wrong number of columns: 1082549862, expected 2
I made a "simple" python script to reproduce the issue :