This is an automated email from the ASF dual-hosted git repository.
dcapwell pushed a commit to branch cep-15-accord
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cep-15-accord by this push:
new 885f4362af CEP-15 (Accord): When starting a transaction in a table
where Accord is not enabled, should fail fast rather than fail with lack of
ranges
885f4362af is described below
commit 885f4362afdca43f0a98b03128b2b4993a6701e9
Author: Youki Shiraishi <[email protected]>
AuthorDate: Tue Jul 23 12:50:58 2024 -0700
CEP-15 (Accord): When starting a transaction in a table where Accord is not
enabled, should fail fast rather than fail with lack of ranges
patch by Youki Shiraishi; reviewed by Caleb Rackliffe, David Capwell for
CASSANDRA-19759
---
accord_demo.txt | 15 +++----
.../cql3/statements/TransactionStatement.java | 6 +++
.../cql3/statements/TransactionStatementTest.java | 52 +++++++++++++++++++---
.../compaction/CompactionAccordIteratorsTest.java | 2 +-
.../service/accord/AccordKeyspaceTest.java | 2 +-
5 files changed, 59 insertions(+), 18 deletions(-)
diff --git a/accord_demo.txt b/accord_demo.txt
index b883451522..63b7d21201 100644
--- a/accord_demo.txt
+++ b/accord_demo.txt
@@ -1,19 +1,14 @@
-
ccm create accord-cql-poc -n 3
ccm start
-bin/cqlsh -e "create keyspace ks with replication={'class':'SimpleStrategy',
'replication_factor':3};"
-bin/cqlsh -e "create table ks.tbl1 (k int primary key, v int);"
-bin/cqlsh -e "create table ks.tbl2 (k int primary key, v int);"
-
-bin/nodetool -h 0000:0000:0000:0000:0000:ffff:7f00:0001 -p 7100
createepochunsafe
-bin/nodetool -h 0000:0000:0000:0000:0000:ffff:7f00:0001 -p 7200
createepochunsafe
-bin/nodetool -h 0000:0000:0000:0000:0000:ffff:7f00:0001 -p 7300
createepochunsafe
+bin/cqlsh -e "CREATE KEYSPACE ks WITH replication={'class':'SimpleStrategy',
'replication_factor':3};"
+bin/cqlsh -e "CREATE TABLE ks.tbl1 (k int PRIMARY KEY, v int) WITH
transactional_mode = 'full';"
+bin/cqlsh -e "CREATE TABLE ks.tbl2 (k int PRIMARY KEY, v int) WITH
transactional_mode = 'full';"
BEGIN TRANSACTION
LET row1 = (SELECT * FROM ks.tbl1 WHERE k = 1);
SELECT row1.v;
IF row1 IS NULL THEN
- INSERT INTO ks.tbl1 (k, v) VALUES (1, 2);
+ INSERT INTO ks.tbl2 (k, v) VALUES (1, 2);
END IF
-COMMIT TRANSACTION;
\ No newline at end of file
+COMMIT TRANSACTION;
diff --git
a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
index f2266557e9..46c620ddc0 100644
--- a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
@@ -100,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 TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE =
"Accord transactions are disabled on table (See transactional_mode in table
options); %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.";
@@ -521,6 +522,8 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
SelectStatement prepared = select.prepare(bindVariables);
+ if (!prepared.table.isAccordEnabled())
+ throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE, "SELECT",
prepared.source);
if (prepared.table.isCounter())
throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE,
"SELECT", prepared.source);
@@ -539,6 +542,8 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
{
SelectStatement prepared = select.prepare(bindVariables);
+ if (!prepared.table.isAccordEnabled())
+ throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE, "SELECT",
prepared.source);
if (prepared.table.isCounter())
throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE,
"SELECT", prepared.source);
@@ -564,6 +569,7 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
ModificationStatement.Parsed parsed = updates.get(i);
ModificationStatement prepared = parsed.prepare(state,
bindVariables);
+ checkTrue(prepared.metadata().isAccordEnabled(),
TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE, prepared.type, prepared.source);
checkFalse(prepared.hasConditions(),
NO_CONDITIONS_IN_UPDATES_MESSAGE, prepared.type, prepared.source);
checkFalse(prepared.isTimestampSet(),
NO_TIMESTAMPS_IN_UPDATES_MESSAGE, prepared.type, prepared.source);
diff --git
a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
index a5dca45ae5..0f4f1aa731 100644
---
a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
+++
b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
@@ -41,6 +41,7 @@ import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_CONDI
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.TransactionStatement.TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE;
import static
org.apache.cassandra.cql3.statements.UpdateStatement.CANNOT_SET_KEY_WITH_REFERENCE_MESSAGE;
import static
org.apache.cassandra.cql3.statements.UpdateStatement.UPDATING_PRIMARY_KEY_MESSAGE;
import static
org.apache.cassandra.cql3.statements.schema.CreateTableStatement.parse;
@@ -56,18 +57,20 @@ public class TransactionStatementTest
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");
+ private static final TableId TABLE7_ID =
TableId.fromString("00000000-0000-0000-0000-000000000007");
@BeforeClass
public static void beforeClass() throws Exception
{
SchemaLoader.prepareServer();
SchemaLoader.createKeyspace("ks", KeyspaceParams.simple(1),
- parse("CREATE TABLE tbl1 (k int, c int, v
int, primary key (k, c))", "ks").id(TABLE1_ID),
- 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 tbl6 (k int PRIMARY
KEY, c counter)", "ks").id(TABLE6_ID));
+ parse("CREATE TABLE tbl1 (k int, c int, v
int, PRIMARY KEY (k, c)) WITH transactional_mode = 'full'", "ks").id(TABLE1_ID),
+ parse("CREATE TABLE tbl2 (k int, c int, v
int, primary key (k, c)) WITH transactional_mode = 'full'", "ks").id(TABLE2_ID),
+ parse("CREATE TABLE tbl3 (k int PRIMARY
KEY, \"with spaces\" int, \"with\"\"quote\" int, \"MiXeD_CaSe\" int) WITH
transactional_mode = 'full'", "ks").id(TABLE3_ID),
+ parse("CREATE TABLE tbl4 (k int PRIMARY
KEY, int_list list<int>) WITH transactional_mode = 'full'", "ks").id(TABLE4_ID),
+ parse("CREATE TABLE tbl5 (k int PRIMARY
KEY, v int) WITH transactional_mode = 'full'", "ks").id(TABLE5_ID),
+ parse("CREATE TABLE tbl6 (k int PRIMARY
KEY, c counter) WITH transactional_mode = 'full'", "ks").id(TABLE6_ID),
+ parse("CREATE TABLE tbl7 (k int PRIMARY
KEY, v int) WITH transactional_mode = 'off'", "ks").id(TABLE7_ID));
}
@Test
@@ -399,6 +402,43 @@ public class TransactionStatementTest
.hasMessageContaining(String.format(ILLEGAL_RANGE_QUERY_MESSAGE, "LET
assignment row1", "at [2:15]"));
}
+ @Test
+ public void shouldRejectLetSelectOnNonTransactionalTable()
+ {
+ String query = "BEGIN TRANSACTION\n" +
+ " LET row1 = (SELECT * FROM ks.tbl7 WHERE k = 0);\n" +
+ " INSERT INTO ks.tbl5 (k, v) VALUES (1, 2);\n" +
+ "COMMIT TRANSACTION;";
+
+ Assertions.assertThatThrownBy(() -> prepare(query))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE,
"SELECT", "at [2:15]"));
+ }
+
+ @Test
+ public void shouldRejectSelectOnNonTransactionalTable()
+ {
+ String query = "BEGIN TRANSACTION\n" +
+ " SELECT * FROM ks.tbl7 WHERE k = 0;\n" +
+ "COMMIT TRANSACTION;";
+
+ Assertions.assertThatThrownBy(() -> prepare(query))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE,
"SELECT", "at [2:3]"));
+ }
+
+ @Test
+ public void shouldRejectUpdateOnNonTransactionalTable()
+ {
+ String query = "BEGIN TRANSACTION\n" +
+ " INSERT INTO ks.tbl7 (k, v) VALUES (1, 2);\n" +
+ "COMMIT TRANSACTION;";
+
+ Assertions.assertThatThrownBy(() -> prepare(query))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE,
"INSERT", "at [2:3]"));
+ }
+
private static CQLStatement prepare(String query)
{
TransactionStatement.Parsed parsed = (TransactionStatement.Parsed)
QueryProcessor.parseStatement(query);
diff --git
a/test/unit/org/apache/cassandra/db/compaction/CompactionAccordIteratorsTest.java
b/test/unit/org/apache/cassandra/db/compaction/CompactionAccordIteratorsTest.java
index 5cbf0915b5..71594055bd 100644
---
a/test/unit/org/apache/cassandra/db/compaction/CompactionAccordIteratorsTest.java
+++
b/test/unit/org/apache/cassandra/db/compaction/CompactionAccordIteratorsTest.java
@@ -146,7 +146,7 @@ public class CompactionAccordIteratorsTest
SchemaLoader.prepareServer();
// Schema doesn't matter since this is a metadata only test
SchemaLoader.createKeyspace("ks", KeyspaceParams.simple(1),
- parse("CREATE TABLE tbl (k int, c int, v
int, primary key (k, c))", "ks"));
+ parse("CREATE TABLE tbl (k int, c int, v
int, PRIMARY KEY (k, c)) WITH transactional_mode = 'full'", "ks"));
StorageService.instance.initServer();
commands =
ColumnFamilyStore.getIfExists(SchemaConstants.ACCORD_KEYSPACE_NAME,
AccordKeyspace.COMMANDS);
diff --git
a/test/unit/org/apache/cassandra/service/accord/AccordKeyspaceTest.java
b/test/unit/org/apache/cassandra/service/accord/AccordKeyspaceTest.java
index 9c613624fe..f922c6f0ce 100644
--- a/test/unit/org/apache/cassandra/service/accord/AccordKeyspaceTest.java
+++ b/test/unit/org/apache/cassandra/service/accord/AccordKeyspaceTest.java
@@ -97,7 +97,7 @@ public class AccordKeyspaceTest extends CQLTester.InMemory
{
AtomicLong now = new AtomicLong();
- String tableName = createTable("CREATE TABLE %s (k int, c int, v int,
PRIMARY KEY (k, c))");
+ String tableName = createTable("CREATE TABLE %s (k int, c int, v int,
PRIMARY KEY (k, c)) WITH transactional_mode = 'full'");
TableId tableId = Schema.instance.getTableMetadata(KEYSPACE,
tableName).id;
Ranges scope = Ranges.of(new
TokenRange(AccordRoutingKey.SentinelKey.min(tableId),
AccordRoutingKey.SentinelKey.max(tableId)));
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]