This is an automated email from the ASF dual-hosted git repository. maedhroz pushed a commit to branch cep-15-accord in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit ea61fcd1f1121b3653ab01d232fb7a6098e42ecd Author: Caleb Rackliffe <calebrackli...@gmail.com> AuthorDate: Fri Apr 26 12:42:45 2024 -0500 Prohibit counter column access in Accord transactions patch by Caleb Rackliffe; reviewed by David Capwell for CASSANDRA-18987 --- CHANGES.txt | 1 + .../cql3/statements/TransactionStatement.java | 16 ++++++++- .../cql3/statements/TransactionStatementTest.java | 42 +++++++++++++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 640c456496..255b8d2df2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.1 + * Prohibit counter column access in Accord transactions (CASSANDRA-18987) * Add Accord configuration stub (CASSANDRA-18221) * Improve transaction statement validation (CASSANDRA-18302) * Add support for prepared statements for accord transactions (CASSANDRA-18299) diff --git a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java index 68a8fd13f2..f2266557e9 100644 --- a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java @@ -87,6 +87,7 @@ import static accord.primitives.Txn.Kind.Read; import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull; import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue; +import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest; import static org.apache.cassandra.service.accord.txn.TxnRead.createTxnRead; import static org.apache.cassandra.service.accord.txn.TxnResult.Kind.retry_new_protocol; import static org.apache.cassandra.utils.Clock.Global.nanoTime; @@ -99,6 +100,7 @@ public class TransactionStatement implements CQLStatement.CompositeCQLStatement, public static final String INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE = "SELECT must specify either all primary key elements or all partition key elements and LIMIT 1. In both cases partition key elements must be always specified with equality operators; %s %s"; public static final String NO_CONDITIONS_IN_UPDATES_MESSAGE = "Updates within transactions may not specify their own conditions; %s statement %s"; public static final String NO_TIMESTAMPS_IN_UPDATES_MESSAGE = "Updates within transactions may not specify custom timestamps; %s statement %s"; + public static final String NO_COUNTERS_IN_TXNS_MESSAGE = "Counter columns cannot be accessed within a transaction; %s statement %s"; public static final String EMPTY_TRANSACTION_MESSAGE = "Transaction contains no reads or writes"; public static final String SELECT_REFS_NEED_COLUMN_MESSAGE = "SELECT references must specify a column."; public static final String TRANSACTIONS_DISABLED_MESSAGE = "Accord transactions are disabled. (See accord.enabled in cassandra.yaml)"; @@ -518,6 +520,10 @@ public class TransactionStatement implements CQLStatement.CompositeCQLStatement, checkTrue(selectNames.add(name), DUPLICATE_TUPLE_NAME_MESSAGE, name.name()); SelectStatement prepared = select.prepare(bindVariables); + + if (prepared.table.isCounter()) + throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE, "SELECT", prepared.source); + NamedSelect namedSelect = new NamedSelect(name, prepared); checkAtMostOneRowSpecified(namedSelect.select, "LET assignment " + name.name()); preparedAssignments.add(namedSelect); @@ -531,7 +537,12 @@ public class TransactionStatement implements CQLStatement.CompositeCQLStatement, NamedSelect returningSelect = null; if (select != null) { - returningSelect = new NamedSelect(TxnDataName.returning(), select.prepare(bindVariables)); + SelectStatement prepared = select.prepare(bindVariables); + + if (prepared.table.isCounter()) + throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE, "SELECT", prepared.source); + + returningSelect = new NamedSelect(TxnDataName.returning(), prepared); checkAtMostOneRowSpecified(returningSelect.select, "returning select"); } @@ -556,6 +567,9 @@ public class TransactionStatement implements CQLStatement.CompositeCQLStatement, checkFalse(prepared.hasConditions(), NO_CONDITIONS_IN_UPDATES_MESSAGE, prepared.type, prepared.source); checkFalse(prepared.isTimestampSet(), NO_TIMESTAMPS_IN_UPDATES_MESSAGE, prepared.type, prepared.source); + if (prepared.metadata().isCounter()) + throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE, prepared.type, prepared.source); + preparedUpdates.add(prepared); } diff --git a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java index afdc91cb17..a5dca45ae5 100644 --- a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java +++ b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java @@ -38,6 +38,7 @@ import static org.apache.cassandra.cql3.statements.TransactionStatement.EMPTY_TR import static org.apache.cassandra.cql3.statements.TransactionStatement.ILLEGAL_RANGE_QUERY_MESSAGE; import static org.apache.cassandra.cql3.statements.TransactionStatement.INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE; import static org.apache.cassandra.cql3.statements.TransactionStatement.NO_CONDITIONS_IN_UPDATES_MESSAGE; +import static org.apache.cassandra.cql3.statements.TransactionStatement.NO_COUNTERS_IN_TXNS_MESSAGE; import static org.apache.cassandra.cql3.statements.TransactionStatement.NO_TIMESTAMPS_IN_UPDATES_MESSAGE; import static org.apache.cassandra.cql3.statements.TransactionStatement.SELECT_REFS_NEED_COLUMN_MESSAGE; import static org.apache.cassandra.cql3.statements.UpdateStatement.CANNOT_SET_KEY_WITH_REFERENCE_MESSAGE; @@ -54,6 +55,7 @@ public class TransactionStatementTest private static final TableId TABLE3_ID = TableId.fromString("00000000-0000-0000-0000-000000000003"); private static final TableId TABLE4_ID = TableId.fromString("00000000-0000-0000-0000-000000000004"); private static final TableId TABLE5_ID = TableId.fromString("00000000-0000-0000-0000-000000000005"); + private static final TableId TABLE6_ID = TableId.fromString("00000000-0000-0000-0000-000000000006"); @BeforeClass public static void beforeClass() throws Exception @@ -64,7 +66,45 @@ public class TransactionStatementTest parse("CREATE TABLE tbl2 (k int, c int, v int, primary key (k, c))", "ks").id(TABLE2_ID), parse("CREATE TABLE tbl3 (k int PRIMARY KEY, \"with spaces\" int, \"with\"\"quote\" int, \"MiXeD_CaSe\" int)", "ks").id(TABLE3_ID), parse("CREATE TABLE tbl4 (k int PRIMARY KEY, int_list list<int>)", "ks").id(TABLE4_ID), - parse("CREATE TABLE tbl5 (k int PRIMARY KEY, v int)", "ks").id(TABLE5_ID)); + parse("CREATE TABLE tbl5 (k int PRIMARY KEY, v int)", "ks").id(TABLE5_ID), + parse("CREATE TABLE tbl6 (k int PRIMARY KEY, c counter)", "ks").id(TABLE6_ID)); + } + + @Test + public void shouldRejectCounterMutation() + { + String query = "BEGIN TRANSACTION\n" + + " UPDATE ks.tbl6 SET c += 100 WHERE k = 0;\n" + + "COMMIT TRANSACTION"; + + Assertions.assertThatThrownBy(() -> prepare(query)) + .isInstanceOf(InvalidRequestException.class) + .hasMessageContaining(String.format(NO_COUNTERS_IN_TXNS_MESSAGE, "UPDATE", "at [2:5]")); + } + + @Test + public void shouldRejectCounterReadInLet() + { + String query = "BEGIN TRANSACTION\n" + + " LET row1 = (SELECT * FROM ks.tbl6 WHERE k=0);\n" + + " SELECT row1.c;\n" + + "COMMIT TRANSACTION"; + + Assertions.assertThatThrownBy(() -> prepare(query)) + .isInstanceOf(InvalidRequestException.class) + .hasMessageContaining(String.format(NO_COUNTERS_IN_TXNS_MESSAGE, "SELECT", "at [2:15]")); + } + + @Test + public void shouldRejectCounterReadInSelect() + { + String query = "BEGIN TRANSACTION\n" + + " SELECT * FROM ks.tbl6 WHERE k=0;\n" + + "COMMIT TRANSACTION"; + + Assertions.assertThatThrownBy(() -> prepare(query)) + .isInstanceOf(InvalidRequestException.class) + .hasMessageContaining(String.format(NO_COUNTERS_IN_TXNS_MESSAGE, "SELECT", "at [2:3]")); } @Test --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org