Skip to content

DataError and PostgresSyntaxError on queries which work on psycopg2 #1016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
negcx opened this issue Mar 18, 2023 · 2 comments
Closed

DataError and PostgresSyntaxError on queries which work on psycopg2 #1016

negcx opened this issue Mar 18, 2023 · 2 comments

Comments

@negcx
Copy link

negcx commented Mar 18, 2023

  • asyncpg version: 0.27.0
  • PostgreSQL version: 14.7
  • Do you use a PostgreSQL SaaS? If so, which? Can you reproduce
    the issue with a local PostgreSQL install?
    : Local
  • Python version: 3.10.7, 3.11
  • Platform: macOS
  • Do you use pgbouncer?: No
  • Did you install asyncpg with pip?: No (Poetry)
  • If you built asyncpg locally, which version of Cython did you use?: n/a
  • Can the issue be reproduced under both asyncio and
    uvloop?
    : n/a

asyncpg throws a DataError when I pass a string and try to cast it as an integer within the query.
asyncpg throws a PostgresSyntaxError when I try to use a parameter with a string and an interval, such as interval $1 where $1 is '2 weeks ago'.

I have created a repository with tests to reproduce the errors here: https://github.com/negcx/asyncpg-type-issue. This also includes passing tests when calling psycopg2. As the code is relatively short, I've also added it here below.

Thank you!


import pytest
import asyncpg
import psycopg2

TABLE = """
CREATE TABLE IF NOT EXISTS asyncpg_issue (
    id SERIAL PRIMARY KEY
    , date DATE
);
"""

DSN = "postgresql://localhost/asyncpg-type-issue"

@pytest.mark.asyncio
async def test_asyncpg_integer_cast():
    """
    asyncpg.exceptions.DataError: invalid input for query argument $1:
    '1' ('str' object cannot be interpreted as an integer)
    """
    conn = await asyncpg.connect(DSN)
    await conn.execute(TABLE)
    await conn.execute(
        "select id, date from asyncpg_issue where id = $1::integer",
        "1"
    )
    await conn.close()

@pytest.mark.asyncio
async def test_asyncpg_interval():
    """
    asyncpg.exceptions.PostgresSyntaxError: syntax error at or near "$1"
    """
    conn = await asyncpg.connect(DSN)
    await conn.execute(TABLE)
    await conn.execute(
        "select id, date from asyncpg_issue where date > now() + interval $1",
        '2 weeks ago'
    )
    conn.close()

def test_psycopg_integer_cast():
    conn = psycopg2.connect(dsn=DSN)
    cur = conn.cursor()

    cur.execute(TABLE)
    cur.execute("select id, date from asyncpg_issue where id = (%s)::integer", ("1"))

    cur.close()
    conn.close()

def test_psycopg_interval():
    conn = psycopg2.connect(dsn=DSN)
    cur = conn.cursor()

    cur.execute(TABLE)
    cur.execute(
        "select id, date from asyncpg_issue where date > now() + interval %s",
        ("2 weeks ago",)
    )

    cur.close()
    conn.close()
    
@elprans
Copy link
Member

elprans commented Mar 18, 2023

Unlike psycopg2, asyncpg does uses the binary data I/O and never does parameter substitution on the client side. Server-side arguments are used instead, so you should expect certain differences in behavior.

These should work:

@pytest.mark.asyncio
async def test_asyncpg_integer_cast():
    """
    asyncpg.exceptions.DataError: invalid input for query argument $1:
    '1' ('str' object cannot be interpreted as an integer)
    """
    conn = await asyncpg.connect(DSN)
    await conn.execute(TABLE)
    await conn.execute(
        "select id, date from asyncpg_issue where id = $1::integer",
        1  # <- pass an actual Python integer
           # if you really need to pass a string, cast $1 to text first:
           # $1::text::integer.
    )
    await conn.close()

@pytest.mark.asyncio
async def test_asyncpg_interval():
    """
    asyncpg.exceptions.PostgresSyntaxError: syntax error at or near "$1"
    """
    conn = await asyncpg.connect(DSN)
    await conn.execute(TABLE)
    await conn.execute(
        "select id, date from asyncpg_issue where date > now() + $1::text::interval",
        '2 weeks ago'
    )
    conn.close()

@negcx
Copy link
Author

negcx commented Mar 18, 2023

Thank you - that makes a lot of sense. These do work and I figured there was something like that. I appreciate your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants