Skip to content

Commit

Permalink
Do not crash on PREPARE statements
Browse files Browse the repository at this point in the history
Just handle the result set.
Leave the id of the prepared statement in cursor.lastrowid
so it can be used with EXEC.

See also issue #116.
  • Loading branch information
joerivanruth committed Feb 28, 2023
1 parent 3853e87 commit eda6c26
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
8 changes: 5 additions & 3 deletions pymonetdb/sql/cursors.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def _store_result(self, block): # noqa: C901
logger.info(line[1:])
self.messages.append((Warning, line[1:]))

elif line.startswith(mapi.MSG_QTABLE):
elif line.startswith(mapi.MSG_QTABLE) or line.startswith(mapi.MSG_QPREPARE):
self._query_id, rowcount, columns, tuples = line[2:].split()[:4]

columns = int(columns) # number of columns in result
Expand All @@ -368,7 +368,10 @@ def _store_result(self, block): # noqa: C901
# typesizes = [(0, 0)] * columns

self._offset = 0
self.lastrowid = None
if line.startswith(mapi.MSG_QPREPARE):
self.lastrowid = int(self._query_id)
else:
self.lastrowid = None

elif line.startswith(mapi.MSG_HEADER):
(data, identity) = line[1:].split("#")
Expand Down Expand Up @@ -401,7 +404,6 @@ def _store_result(self, block): # noqa: C901
precision[i], scale[i], null_ok[i]))
self.description = description
self._offset = 0
self.lastrowid = None

elif line.startswith(mapi.MSG_TUPLE):
values = self._parse_tuple(line)
Expand Down
29 changes: 29 additions & 0 deletions tests/test_prepare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from unittest import TestCase
from pymonetdb import connect
from tests.util import test_args


class TestPrepare(TestCase):
def setUp(self):
self.connection = connect(autocommit=False, **test_args)
self.cursor = self.connection.cursor()

def test_prepare(self):
# It would be nice to have a prepare API but in the mean time we can
# invoke PREPARE and EXEC ourselves.
#
# Note how the id of the newly prepared statement is stashed in
# cursor.lastrowid.

prepare = "PREPARE SELECT value, 10 * value, 100 * value FROM sys.generate_series(0, ?)"
self.cursor.execute(prepare)
exec_id = self.cursor.lastrowid

n = 3
expected = [
(value, 10 * value, 100 * value)
for value in range(n)
]
self.cursor.execute("EXEC %s(%s)", (exec_id, n))
received = self.cursor.fetchall()
self.assertEqual(expected, received)

0 comments on commit eda6c26

Please sign in to comment.