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

Reply via email to