From 4437fa4fd42f76106493965e400cc5b79510c00a Mon Sep 17 00:00:00 2001 From: Pranav Shenoy Date: Thu, 26 Jun 2025 09:32:25 +0800 Subject: [PATCH] Ensuring Row reference is only used for IS Null/IS Not Null expressions patch by Pranav Shenoy; reviewed by David Capwell and Maxwell Guo for CASSANDRA-20596 --- .../cql3/transactions/ConditionStatement.java | 4 ++ .../statements/TransactionStatementTest.java | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/java/org/apache/cassandra/cql3/transactions/ConditionStatement.java b/src/java/org/apache/cassandra/cql3/transactions/ConditionStatement.java index 25715b34e1..2acf247724 100644 --- a/src/java/org/apache/cassandra/cql3/transactions/ConditionStatement.java +++ b/src/java/org/apache/cassandra/cql3/transactions/ConditionStatement.java @@ -103,12 +103,16 @@ public class ConditionStatement if (lhs instanceof RowDataReference.Raw) { + if (((RowDataReference.Raw) lhs).column() == null) + throw new IllegalStateException(String.format("Row reference (%s) can only be used with IS NULL/IS NOT NULL conditions", lhs.getText())); reference = ((RowDataReference.Raw) lhs).prepareAsReceiver(); ColumnSpecification receiver = reference.getValueReceiver(); value = rhs.prepare(keyspace, receiver); } else if (rhs instanceof RowDataReference.Raw) { + if (((RowDataReference.Raw) rhs).column() == null) + throw new IllegalStateException(String.format("Row reference (%s) can only be used with IS NULL/IS NOT NULL conditions", rhs.getText())); reference = ((RowDataReference.Raw) rhs).prepareAsReceiver(); ColumnSpecification receiver = reference.getValueReceiver(); value = lhs.prepare(keyspace, receiver); diff --git a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java index dd4c816e84..3395dadb1d 100644 --- a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java +++ b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java @@ -561,6 +561,55 @@ public class TransactionStatementTest .hasMessageContaining(String.format(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE, "INSERT", "at [2:3]")); } + @Test + public void shouldRejectRowReferenceOnLHSExceptIsNullAndIsNotNull() + { + String query = "BEGIN TRANSACTION\n" + + " LET row1 = (SELECT * FROM ks.tbl1 WHERE k=1 AND c=1);\n" + + " IF row1 = 1 THEN\n" + + " UPDATE ks.tbl1 SET v=1 WHERE k=1 AND c=1;\n" + + " END IF\n" + + "COMMIT TRANSACTION"; + + Assertions.assertThatThrownBy(() -> prepare(query)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Row reference (row1) can only be used with IS NULL/IS NOT NULL conditions"); + } + + @Test + public void shouldRejectRowReferenceOnRHSExceptIsNullAndIsNotNull() + { + String query = "BEGIN TRANSACTION\n" + + " LET row1 = (SELECT * FROM ks.tbl1 WHERE k=1 AND c=1);\n" + + " IF 100 = row1 THEN\n" + + " UPDATE ks.tbl1 SET v=1 WHERE k=1 AND c=1;\n" + + " END IF\n" + + "COMMIT TRANSACTION"; + + Assertions.assertThatThrownBy(() -> prepare(query)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Row reference (row1) can only be used with IS NULL/IS NOT NULL conditions"); + } + + @Test + public void shouldAcceptRowReferenceWithIsNullAndIsNotNull() + { + String query = "BEGIN TRANSACTION\n" + + " LET row1 = (SELECT * FROM ks.tbl1 WHERE k=1 AND c=1);\n" + + " IF row1 IS NULL THEN\n" + + " UPDATE ks.tbl1 SET v=1 WHERE k=1 AND c=1;\n" + + " END IF\n" + + "COMMIT TRANSACTION"; + Assertions.assertThat(prepare(query)).isNotNull(); + query = "BEGIN TRANSACTION\n" + + " LET row1 = (SELECT * FROM ks.tbl1 WHERE k=1 AND c=1);\n" + + " IF row1 IS NOT NULL THEN\n" + + " UPDATE ks.tbl1 SET v=1 WHERE k=1 AND c=1;\n" + + " END IF\n" + + "COMMIT TRANSACTION"; + Assertions.assertThat(prepare(query)).isNotNull(); + } + private static CQLStatement prepare(String query) { TransactionStatement.Parsed parsed = (TransactionStatement.Parsed) QueryProcessor.parseStatement(query);