6464import junit .framework .TestCase ;
6565import org .assertj .core .api .AbstractThrowableAssert ;
6666import org .junit .Before ;
67+ import org .junit .ClassRule ;
6768import org .junit .Rule ;
6869import org .junit .Test ;
6970import org .junit .experimental .categories .Category ;
@@ -84,9 +85,9 @@ public class PreparedStatementIT {
8485 private static final Version SCYLLA_METADATA_ID_SUPPORT_VERSION =
8586 Objects .requireNonNull (Version .parse ("2025.3" ));
8687
87- private final CcmRule ccmRule = CcmRule .getInstance ();
88+ private static final CcmRule ccmRule = CcmRule .getInstance ();
8889
89- private final SessionRule <CqlSession > sessionRule =
90+ private static final SessionRule <CqlSession > sessionRule =
9091 SessionRule .builder (ccmRule )
9192 .withConfigLoader (
9293 SessionUtils .configLoaderBuilder ()
@@ -95,19 +96,31 @@ public class PreparedStatementIT {
9596 .build ())
9697 .build ();
9798
98- @ Rule public TestRule chain = RuleChain .outerRule (ccmRule ).around (sessionRule );
99+ @ ClassRule public static TestRule classChain = RuleChain .outerRule (ccmRule ).around (sessionRule );
100+
101+ // CcmRule as @Rule for per-method @ScyllaOnly / @BackendRequirement annotation checking
102+ @ Rule public TestRule methodChain = ccmRule ;
103+
104+ // When true, @Before will DROP+CREATE the table (needed after tests that ALTER TABLE).
105+ // Starts as true so the table is created on first run.
106+ private static boolean needsTableRecreate = true ;
99107
100108 @ Before
101109 public void setupSchema () {
110+ if (needsTableRecreate ) {
111+ executeDdl ("DROP TABLE IF EXISTS prepared_statement_test" );
112+ executeDdl ("CREATE TABLE prepared_statement_test (a int PRIMARY KEY, b int, c int)" );
113+ needsTableRecreate = false ;
114+ } else {
115+ executeDdl ("TRUNCATE prepared_statement_test" );
116+ }
102117 for (String query :
103118 ImmutableList .of (
104- "DROP TABLE IF EXISTS prepared_statement_test" ,
105- "CREATE TABLE prepared_statement_test (a int PRIMARY KEY, b int, c int)" ,
106119 "INSERT INTO prepared_statement_test (a, b, c) VALUES (1, 1, 1)" ,
107120 "INSERT INTO prepared_statement_test (a, b, c) VALUES (2, 2, 2)" ,
108121 "INSERT INTO prepared_statement_test (a, b, c) VALUES (3, 3, 3)" ,
109122 "INSERT INTO prepared_statement_test (a, b, c) VALUES (4, 4, 4)" )) {
110- executeDdl (query );
123+ sessionRule . session (). execute (query );
111124 }
112125 }
113126
@@ -176,6 +189,7 @@ public void should_update_metadata_when_schema_changed_across_executions() {
176189 }
177190
178191 // When
192+ needsTableRecreate = true ;
179193 session .execute (
180194 SimpleStatement .builder ("ALTER TABLE prepared_statement_test ADD d int" )
181195 .setExecutionProfile (sessionRule .slowProfile ())
@@ -239,6 +253,7 @@ public void should_update_metadata_when_schema_changed_across_pages() {
239253 }
240254
241255 // When
256+ needsTableRecreate = true ;
242257 session .execute (
243258 SimpleStatement .builder ("ALTER TABLE prepared_statement_test ADD d int" )
244259 .setExecutionProfile (sessionRule .slowProfile ())
@@ -271,71 +286,71 @@ public void should_update_metadata_when_schema_changed_across_pages() {
271286 @ BackendRequirement (type = BackendType .CASSANDRA , minInclusive = "4.0" )
272287 @ BackendRequirement (type = BackendType .SCYLLA )
273288 public void should_update_metadata_when_schema_changed_across_sessions () {
274- // Given
275- CqlSession session1 = sessionRule .session ();
276- CqlSession session2 = SessionUtils .newSession (ccmRule , sessionRule .keyspace ());
277-
278- PreparedStatement ps1 = session1 .prepare ("SELECT * FROM prepared_statement_test WHERE a = ?" );
279- PreparedStatement ps2 = session2 .prepare ("SELECT * FROM prepared_statement_test WHERE a = ?" );
280-
281- ByteBuffer id1a = ps1 .getResultMetadataId ();
282- ByteBuffer id2a = ps2 .getResultMetadataId ();
283- if (hasNoScyllaMetadataIdSupport ()) {
284- // Scylla does not support CQL5 extensions and metadata id
285- assertThat (id1a ).isNull ();
286- assertThat (id2a ).isNull ();
287- }
289+ // Use fresh sessions to avoid prepare cache interference from other tests that use the
290+ // same query string on the shared session.
291+ try (CqlSession session1 = SessionUtils .newSession (ccmRule , sessionRule .keyspace ());
292+ CqlSession session2 = SessionUtils .newSession (ccmRule , sessionRule .keyspace ())) {
293+ // Given
294+ PreparedStatement ps1 = session1 .prepare ("SELECT * FROM prepared_statement_test WHERE a = ?" );
295+ PreparedStatement ps2 = session2 .prepare ("SELECT * FROM prepared_statement_test WHERE a = ?" );
296+
297+ ByteBuffer id1a = ps1 .getResultMetadataId ();
298+ ByteBuffer id2a = ps2 .getResultMetadataId ();
299+ if (hasNoScyllaMetadataIdSupport ()) {
300+ // Scylla does not support CQL5 extensions and metadata id
301+ assertThat (id1a ).isNull ();
302+ assertThat (id2a ).isNull ();
303+ }
288304
289- ResultSet rows1 = session1 .execute (ps1 .bind (1 ));
290- ResultSet rows2 = session2 .execute (ps2 .bind (1 ));
305+ ResultSet rows1 = session1 .execute (ps1 .bind (1 ));
306+ ResultSet rows2 = session2 .execute (ps2 .bind (1 ));
291307
292- assertThat (rows1 .getColumnDefinitions ()).hasSize (3 );
293- assertThat (rows1 .getColumnDefinitions ().contains ("d" )).isFalse ();
294- assertThat (rows2 .getColumnDefinitions ()).hasSize (3 );
295- assertThat (rows2 .getColumnDefinitions ().contains ("d" )).isFalse ();
308+ assertThat (rows1 .getColumnDefinitions ()).hasSize (3 );
309+ assertThat (rows1 .getColumnDefinitions ().contains ("d" )).isFalse ();
310+ assertThat (rows2 .getColumnDefinitions ()).hasSize (3 );
311+ assertThat (rows2 .getColumnDefinitions ().contains ("d" )).isFalse ();
296312
297- // When
298- session1 .execute ("ALTER TABLE prepared_statement_test ADD d int" );
313+ // When
314+ needsTableRecreate = true ;
315+ session1 .execute ("ALTER TABLE prepared_statement_test ADD d int" );
299316
300- rows1 = session1 .execute (ps1 .bind (1 ));
301- rows2 = session2 .execute (ps2 .bind (1 ));
317+ rows1 = session1 .execute (ps1 .bind (1 ));
318+ rows2 = session2 .execute (ps2 .bind (1 ));
302319
303- ByteBuffer id1b = ps1 .getResultMetadataId ();
304- ByteBuffer id2b = ps2 .getResultMetadataId ();
320+ ByteBuffer id1b = ps1 .getResultMetadataId ();
321+ ByteBuffer id2b = ps2 .getResultMetadataId ();
305322
306- // Then
307- if (hasNoScyllaMetadataIdSupport ()) {
308- // Scylla does not support CQL5 extensions and metadata id
309- assertThat (id1b ).isNull ();
310- assertThat (id2b ).isNull ();
323+ // Then
324+ if (hasNoScyllaMetadataIdSupport ()) {
325+ // Scylla does not support CQL5 extensions and metadata id
326+ assertThat (id1b ).isNull ();
327+ assertThat (id2b ).isNull ();
328+
329+ assertThat (ps1 .getResultSetDefinitions ()).hasSize (3 );
330+ assertThat (ps1 .getResultSetDefinitions ().contains ("d" )).isFalse ();
331+ assertThat (ps2 .getResultSetDefinitions ()).hasSize (3 );
332+ assertThat (ps2 .getResultSetDefinitions ().contains ("d" )).isFalse ();
333+
334+ assertThat (rows1 .getColumnDefinitions ()).hasSize (4 );
335+ assertThat (rows1 .getColumnDefinitions ().contains ("d" )).isTrue ();
336+ assertThat (rows2 .getColumnDefinitions ()).hasSize (4 );
337+ assertThat (rows2 .getColumnDefinitions ().contains ("d" )).isTrue ();
338+
339+ return ;
340+ }
341+ assertThat (Bytes .toHexString (id1b )).isNotEqualTo (Bytes .toHexString (id1a ));
342+ assertThat (Bytes .toHexString (id2b )).isNotEqualTo (Bytes .toHexString (id2a ));
311343
312- assertThat (ps1 .getResultSetDefinitions ()).hasSize (3 );
313- assertThat (ps1 .getResultSetDefinitions ().contains ("d" )).isFalse ();
314- assertThat (ps2 .getResultSetDefinitions ()).hasSize (3 );
315- assertThat (ps2 .getResultSetDefinitions ().contains ("d" )).isFalse ();
344+ assertThat (ps1 .getResultSetDefinitions ()).hasSize (4 );
345+ assertThat (ps1 .getResultSetDefinitions ().contains ("d" )).isTrue ();
346+ assertThat (ps2 .getResultSetDefinitions ()).hasSize (4 );
347+ assertThat (ps2 .getResultSetDefinitions ().contains ("d" )).isTrue ();
316348
317349 assertThat (rows1 .getColumnDefinitions ()).hasSize (4 );
318350 assertThat (rows1 .getColumnDefinitions ().contains ("d" )).isTrue ();
319351 assertThat (rows2 .getColumnDefinitions ()).hasSize (4 );
320352 assertThat (rows2 .getColumnDefinitions ().contains ("d" )).isTrue ();
321-
322- session2 .close ();
323- return ;
324353 }
325- assertThat (Bytes .toHexString (id1b )).isNotEqualTo (Bytes .toHexString (id1a ));
326- assertThat (Bytes .toHexString (id2b )).isNotEqualTo (Bytes .toHexString (id2a ));
327-
328- assertThat (ps1 .getResultSetDefinitions ()).hasSize (4 );
329- assertThat (ps1 .getResultSetDefinitions ().contains ("d" )).isTrue ();
330- assertThat (ps2 .getResultSetDefinitions ()).hasSize (4 );
331- assertThat (ps2 .getResultSetDefinitions ().contains ("d" )).isTrue ();
332-
333- assertThat (rows1 .getColumnDefinitions ()).hasSize (4 );
334- assertThat (rows1 .getColumnDefinitions ().contains ("d" )).isTrue ();
335- assertThat (rows2 .getColumnDefinitions ()).hasSize (4 );
336- assertThat (rows2 .getColumnDefinitions ().contains ("d" )).isTrue ();
337-
338- session2 .close ();
339354 }
340355
341356 @ Test
@@ -344,6 +359,7 @@ public void should_update_metadata_when_schema_changed_across_sessions() {
344359 public void should_fail_to_reprepare_if_query_becomes_invalid () {
345360 // Given
346361 CqlSession session = sessionRule .session ();
362+ needsTableRecreate = true ;
347363 session .execute ("ALTER TABLE prepared_statement_test ADD d int" );
348364 PreparedStatement ps =
349365 session .prepare ("SELECT a, b, c, d FROM prepared_statement_test WHERE a = ?" );
@@ -439,6 +455,7 @@ private void should_not_store_metadata_for_conditional_updates(CqlSession sessio
439455 assertThat (Bytes .toHexString (ps .getResultMetadataId ())).isEqualTo (Bytes .toHexString (idBefore ));
440456
441457 // When
458+ needsTableRecreate = true ;
442459 session .execute ("ALTER TABLE prepared_statement_test ADD d int" );
443460 rs = session .execute (ps .bind (5 , 5 , 5 ));
444461
@@ -565,6 +582,7 @@ public void should_create_separate_instances_for_different_statement_parameters(
565582 session .execute ("USE " + sessionRule .keyspace ().asCql (false ));
566583
567584 // Drop and recreate the table to invalidate the prepared statement server-side
585+ needsTableRecreate = true ;
568586 executeDdl ("DROP TABLE prepared_statement_test" );
569587 executeDdl ("CREATE TABLE prepared_statement_test (a int PRIMARY KEY, b int, c int)" );
570588
0 commit comments