Skip to content

Commit 8850ad3

Browse files
committed
make sure prepared result metadata is replaced on reprepare
PYTHON-621
1 parent e8c638a commit 8850ad3

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

cassandra/cluster.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3549,6 +3549,10 @@ def _execute_after_prepare(self, response):
35493549

35503550
if isinstance(response, ResultMessage):
35513551
if response.kind == RESULT_KIND_PREPARED:
3552+
# result metadata is the only thing that could have changed from an alter
3553+
_, _, _, result_metadata = response.results
3554+
self.prepared_statement.result_metadata = result_metadata
3555+
35523556
# use self._query to re-use the same host and
35533557
# at the same time properly borrow the connection
35543558
request_id = self._query(self._current_host)

tests/integration/standard/test_prepared_statements.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
from cassandra import ConsistencyLevel
2525
from cassandra.cluster import Cluster
26-
from cassandra.query import PreparedStatement, UNSET_VALUE
26+
from cassandra.query import PreparedStatement, UNSET_VALUE, tuple_factory
2727
from tests.integration import get_server_versions
2828

2929

@@ -385,3 +385,39 @@ def test_raise_error_on_prepared_statement_execution_dropped_table(self):
385385

386386
with self.assertRaises(InvalidRequest):
387387
self.session.execute(prepared, [0])
388+
389+
def test_invalidated_result_metadata(self):
390+
"""
391+
Tests to make sure cached metadata is updated when an invalidated prepared statement is reprepared.
392+
393+
@since 2.7.0
394+
@jira_ticket PYTHON-621
395+
396+
Prior to this fix, the request would blow up with a protocol error when the result was decoded expecting a different
397+
number of columns.
398+
"""
399+
s = self.session
400+
s.result_factory = tuple_factory
401+
402+
table = "test1rf.%s" % self._testMethodName.lower()
403+
404+
s.execute("DROP TABLE IF EXISTS %s" % table)
405+
s.execute("CREATE TABLE %s (k int PRIMARY KEY, a int, b int, c int)" % table)
406+
s.execute("INSERT INTO %s (k, a, b, c) VALUES (0, 0, 0, 0)" % table)
407+
408+
wildcard_prepared = s.prepare("SELECT * FROM %s" % table)
409+
original_result_metadata = wildcard_prepared.result_metadata
410+
self.assertEqual(len(original_result_metadata), 4)
411+
412+
r = s.execute(wildcard_prepared)
413+
self.assertEqual(r[0], (0, 0, 0, 0))
414+
415+
s.execute("ALTER TABLE %s DROP c" % table)
416+
417+
# Get a bunch of requests in the pipeline with varying states of result_meta, reprepare, resolved
418+
futures = set(s.execute_async(wildcard_prepared.bind(None)) for _ in range(200))
419+
for f in futures:
420+
self.assertEqual(f.result()[0], (0, 0, 0))
421+
self.assertIsNot(wildcard_prepared.result_metadata, original_result_metadata)
422+
s.execute("DROP TABLE %s" % table)
423+

0 commit comments

Comments
 (0)