Skip to content

Connection.copy_records_to_table does not seem to like nullable rows #153

Closed
@Rapptz

Description

@Rapptz
  • asyncpg version: 0.11
  • PostgreSQL version: 9.6
  • Python version: 3.6
  • Platform: Windows
  • Do you use pgbouncer?: No
  • Did you install asyncpg with pip?: Yes
  • If you built asyncpg locally, which version of Cython did you use?: N/A
  • Can the issue be reproduced under both asyncio and
    uvloop?
    : Yes

I have no clue if this is a limitation of the COPY protocol, if so sorry for the issue.

My problem comes from attempting to insert elements using None to denote NULL. For example, given the following table:

CREATE TABLE test (id SERIAL PRIMARY KEY, entity_id INTEGER);

Attempts to create a connection and insert NULL entity_id entries results in a TypeError:

async with pool.acquire() as con:
  await con.copy_records_to_table('test', records=[(1, 1), (2, None)], 
                                          columns=('id', 'entity_id'))

Would result in:

Traceback (most recent call last):
  File "asyncpg\connection.py", line 619, in copy_records_to_table
    copy_stmt, records, intro_ps._state, timeout)
  File "asyncpg\connection.py", line 741, in _copy_in_records
    copy_stmt, None, None, records, intro_stmt, timeout)
  File "asyncpg\protocol\protocol.pyx", line 441, in copy_in (asyncpg/protocol/protocol.c:66206)
  File "asyncpg\protocol\protocol.pyx", line 389, in asyncpg.protocol.protocol.BaseProtocol.copy_in (asyncpg/protocol/protocol.c:64677)
  File "asyncpg\protocol\codecs\base.pyx", line 150, in asyncpg.protocol.protocol.Codec.encode (asyncpg/protocol/protocol.c:14433)
  File "asyncpg\protocol\codecs\base.pyx", line 95, in asyncpg.protocol.protocol.Codec.encode_scalar (asyncpg/protocol/protocol.c:13818)
  File "asyncpg\protocol\codecs\int.pyx", line 47, in asyncpg.protocol.protocol.int8_encode (asyncpg/protocol/protocol.c:29584)
TypeError: an integer is required (got type NoneType)

The documentation for the COPY protocol seems to suggest that encoding into NULL is possible:

Each tuple begins with a 16-bit integer count of the number of fields in the tuple. (Presently, all tuples in a table will have the same count, but that might not always be true.) Then, repeated for each field in the tuple, there is a 32-bit length word followed by that many bytes of field data. (The length word does not include itself, and can be zero.) As a special case, -1 indicates a NULL field value. No value bytes follow in the NULL case.

Emphasis mine, from the documentation.

However I don't know if I'm misreading it.

Thanks for your time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions