Skip to content

Clarify the argument/parameter count mismatch exception #194

New issue

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

By clicking “No 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? No Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions asyncpg/protocol/prepared_stmt.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0


from asyncpg import exceptions


@cython.final
cdef class PreparedStatementState:

Expand Down Expand Up @@ -92,18 +95,34 @@ cdef class PreparedStatementState:
Codec codec

if len(args) > 32767:
raise ValueError('number of arguments cannot exceed 32767')
raise exceptions.InterfaceError(
'the number of query arguments cannot exceed 32767')

self._ensure_args_encoder()
self._ensure_rows_decoder()

writer = WriteBuffer.new()

if self.args_num != len(args):
raise ValueError(
'number of arguments ({}) does not match '
'number of parameters ({})'.format(
len(args), self.args_num))
num_args_passed = len(args)
if self.args_num != num_args_passed:
hint = 'Check the query against the passed list of arguments.'

if self.args_num == 0:
# If the server was expecting zero arguments, it is likely
# that the user tried to parametrize a statement that does
# not support parameters.
hint += (r' Note that parameters are supported only in'
r' SELECT, INSERT, UPDATE, DELETE, and VALUES'
r' statements, and will *not* work in statements '
r' like CREATE VIEW or DECLARE CURSOR.')

raise exceptions.InterfaceError(
'the server expects {x} argument{s} for this query, '
'{y} {w} passed'.format(
x=self.args_num, s='s' if self.args_num != 1 else '',
y=num_args_passed,
w='was' if num_args_passed == 1 else 'were'),
hint=hint)

if self.have_text_args:
writer.write_int16(self.args_num)
Expand Down
17 changes: 15 additions & 2 deletions tests/test_prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import unittest

from asyncpg import _testbase as tb
from asyncpg import exceptions


class TestPrepare(tb.ConnectedTestCase):
Expand Down Expand Up @@ -540,8 +541,9 @@ async def test_prepare_28_max_args(self):
args = ','.join('${}'.format(i) for i in range(1, N + 1))
query = 'SELECT ARRAY[{}]'.format(args)

with self.assertRaisesRegex(ValueError,
'number of arguments cannot exceed 32767'):
with self.assertRaisesRegex(
exceptions.InterfaceError,
'the number of query arguments cannot exceed 32767'):
await self.con.fetchval(query, *range(1, N + 1))

async def test_prepare_29_duplicates(self):
Expand All @@ -556,3 +558,14 @@ async def test_prepare_29_duplicates(self):
self.assertEqual(r[0], 1)
self.assertEqual(r[1], 2)
self.assertEqual(r[2], 3)

async def test_prepare_30_invalid_arg_count(self):
with self.assertRaisesRegex(
exceptions.InterfaceError,
'the server expects 1 argument for this query, 0 were passed'):
await self.con.fetchval('SELECT $1::int')

with self.assertRaisesRegex(
exceptions.InterfaceError,
'the server expects 0 arguments for this query, 1 was passed'):
await self.con.fetchval('SELECT 1', 1)