Skip to content

Commit

Permalink
Raise proper error on anonymous composite input (tuple arguments) (#664)
Browse files Browse the repository at this point in the history
Currently asyncpg would crash with an arcane "could not resolve query
result and/or argument types in 6 attempts", which isn't helpful.  Do
the right thing by raising an `UnsupportedClientFeatureError`
explicitly instead.

Fixes #476.
  • Loading branch information
elprans committed Dec 2, 2020
1 parent 50f964f commit 7252dbe
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 4 deletions.
9 changes: 9 additions & 0 deletions asyncpg/exceptions/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,15 @@ def __init__(self, msg, *, detail=None, hint=None):
InterfaceMessage.__init__(self, detail=detail, hint=hint)
Exception.__init__(self, msg)

def with_msg(self, msg):
return type(self)(
msg,
detail=self.detail,
hint=self.hint,
).with_traceback(
self.__traceback__
)


class DataError(InterfaceError, ValueError):
"""An error caused by invalid query input."""
Expand Down
13 changes: 12 additions & 1 deletion asyncpg/protocol/codecs/record.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,20 @@ cdef anonymous_record_decode(ConnectionSettings settings, FRBuffer *buf):
return result


cdef anonymous_record_encode(ConnectionSettings settings, WriteBuffer buf, obj):
raise exceptions.UnsupportedClientFeatureError(
'input of anonymous composite types is not supported',
hint=(
'Consider declaring an explicit composite type and '
'using it to cast the argument.'
),
detail='PostgreSQL does not implement anonymous composite type input.'
)


cdef init_record_codecs():
register_core_codec(RECORDOID,
<encode_func>NULL,
<encode_func>anonymous_record_encode,
<decode_func>anonymous_record_decode,
PG_FORMAT_BINARY)

Expand Down
8 changes: 5 additions & 3 deletions asyncpg/protocol/prepared_stmt.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ cdef class PreparedStatementState:
except (AssertionError, exceptions.InternalClientError):
# These are internal errors and should raise as-is.
raise
except exceptions.InterfaceError:
# This is already a descriptive error.
raise
except exceptions.InterfaceError as e:
# This is already a descriptive error, but annotate
# with argument name for clarity.
raise e.with_msg(
f'query argument ${idx + 1}: {e.args[0]}') from None
except Exception as e:
# Everything else is assumed to be an encoding error
# due to invalid input.
Expand Down
7 changes: 7 additions & 0 deletions tests/test_codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,13 @@ async def test_composites(self):

self.assertEqual(res, (None, 1234, '5678', (42, '42')))

with self.assertRaisesRegex(
asyncpg.UnsupportedClientFeatureError,
'query argument \\$1: input of anonymous '
'composite types is not supported',
):
await self.con.fetchval("SELECT (1, 'foo') = $1", (1, 'foo'))

try:
st = await self.con.prepare('''
SELECT ROW(
Expand Down

0 comments on commit 7252dbe

Please sign in to comment.