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 56ca3032e4 Accord should block currently unsafe operations
56ca3032e4 is described below
commit 56ca3032e4009204356887509a39a56fbcbd9cf7
Author: David Capwell <[email protected]>
AuthorDate: Wed Oct 23 09:56:01 2024 -0700
Accord should block currently unsafe operations
patch by David Capwell; reviewed by Ariel Weisberg for CASSANDRA-20020
---
.../cassandra/cql3/statements/SelectStatement.java | 7 +-
.../cql3/statements/TransactionStatement.java | 50 ++++---
.../org/apache/cassandra/cql3/ast/Conditional.java | 37 ++++++
.../org/apache/cassandra/cql3/ast/Mutation.java | 53 ++++++++
.../unit/org/apache/cassandra/cql3/ast/Select.java | 8 ++
test/unit/org/apache/cassandra/cql3/ast/Txn.java | 11 ++
.../cql3/statements/TransactionStatementTest.java | 143 ++++++++++++++++++++-
7 files changed, 292 insertions(+), 17 deletions(-)
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 66a4797637..bfe6589eb4 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -420,6 +420,11 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
return aggregationSpecFactory == null ? null :
aggregationSpecFactory.newInstance(options);
}
+ public boolean hasAggregation()
+ {
+ return aggregationSpecFactory != null;
+ }
+
public ReadQuery getQuery(QueryOptions options, long nowInSec) throws
RequestValidationException
{
Selectors selectors = selection.newSelectors(options);
@@ -1226,7 +1231,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
public final Term.Raw limit;
public final Term.Raw perPartitionLimit;
private ClientState state;
- private final StatementSource source;
+ public final StatementSource source;
public RawStatement(QualifiedName cfName,
Parameters parameters,
diff --git
a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
index 992d3af0f0..1a6b3ee782 100644
--- a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
@@ -57,6 +57,7 @@ import
org.apache.cassandra.cql3.transactions.RowDataReference;
import org.apache.cassandra.cql3.transactions.SelectReferenceSource;
import org.apache.cassandra.db.SinglePartitionReadCommand;
import org.apache.cassandra.db.SinglePartitionReadQuery;
+import org.apache.cassandra.db.filter.DataLimits;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.partitions.FilteredPartition;
import org.apache.cassandra.exceptions.InvalidRequestException;
@@ -90,6 +91,7 @@ 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.service.accord.txn.TxnResult.Kind.values;
public class TransactionStatement implements
CQLStatement.CompositeCQLStatement, CQLStatement.ReturningCQLStatement
{
@@ -98,14 +100,19 @@ 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_TTLS_IN_UPDATES_MESSAGE = "Updates within
transactions may not specify custom ttls; %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
TRANSACTIONS_DISABLED_ON_TABLE_BEING_DROPPED_MESSAGE = "Accord transactions are
disabled on table (table is being dropped); %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 NO_AGGREGATION_IN_TXNS_MESSAGE = "No
aggregation functions allowed within a transaction; %s statement %s";
+ public static final String NO_ORDER_BY_IN_TXNS_MESSAGE = "No ORDER BY
clause allowed within a transaction; %s statement %s";
+ public static final String NO_GROUP_BY_IN_TXNS_MESSAGE = "No GROUP BY
clause allowed 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)";
public static final String ILLEGAL_RANGE_QUERY_MESSAGE = "Range queries
are not allowed for reads within a transaction; %s %s";
public static final String UNSUPPORTED_MIGRATION = "Transaction Statement
is unsupported when migrating away from Accord or before migration to Accord is
complete for a range";
+ public static final String NO_PARTITION_IN_CLAUSE_WITH_LIMIT = "Partition
key is present in IN clause and there is a LIMIT... this is currently not
supported; %s statement %s";
static class NamedSelect
{
@@ -464,6 +471,29 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
return false;
}
+ private static void validate(SelectStatement.RawStatement select)
+ {
+ if (select.parameters.orderings != null &&
!select.parameters.orderings.isEmpty())
+ throw invalidRequest(NO_ORDER_BY_IN_TXNS_MESSAGE, "SELECT",
select.source);
+ if (select.parameters.groups != null &&
!select.parameters.groups.isEmpty())
+ throw invalidRequest(NO_GROUP_BY_IN_TXNS_MESSAGE, "SELECT",
select.source);
+ }
+
+ private static void validate(SelectStatement prepared)
+ {
+ if (!prepared.table.isAccordEnabled())
+ throw invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE,
"SELECT", prepared.source);
+ if (prepared.table.params.pendingDrop)
+ throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_BEING_DROPPED_MESSAGE, "SELECT",
prepared.source);
+ if (prepared.table.isCounter())
+ throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE, "SELECT",
prepared.source);
+ if (prepared.hasAggregation())
+ throw invalidRequest(NO_AGGREGATION_IN_TXNS_MESSAGE, "SELECT",
prepared.source);
+
+ if (prepared.getRestrictions().keyIsInRelation())
+ checkTrue(prepared.getLimit(null) == DataLimits.NO_LIMIT,
NO_PARTITION_IN_CLAUSE_WITH_LIMIT, "SELECT", prepared.source);
+ }
+
public static class Parsed extends QualifiedStatement.Composite
{
private final List<SelectStatement.RawStatement> assignments;
@@ -514,15 +544,10 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
checkNotNull(select.parameters.refName, "Assignments must be
named");
TxnDataName name = TxnDataName.user(select.parameters.refName);
checkTrue(selectNames.add(name), DUPLICATE_TUPLE_NAME_MESSAGE,
name.name());
+ validate(select);
SelectStatement prepared = select.prepare(bindVariables);
-
- if (!prepared.table.isAccordEnabled())
- throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE, "SELECT",
prepared.source);
- if (prepared.table.params.pendingDrop)
- throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_BEING_DROPPED_MESSAGE, "SELECT",
prepared.source);
- if (prepared.table.isCounter())
- throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE,
"SELECT", prepared.source);
+ validate(prepared);
NamedSelect namedSelect = new NamedSelect(name, prepared);
checkAtMostOneRowSpecified(namedSelect.select, "LET assignment
" + name.name());
@@ -537,15 +562,9 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
NamedSelect returningSelect = null;
if (select != null)
{
+ validate(select);
SelectStatement prepared = select.prepare(bindVariables);
-
- if (!prepared.table.isAccordEnabled())
- throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_MESSAGE, "SELECT",
prepared.source);
- if (prepared.table.params.pendingDrop)
- throw
invalidRequest(TRANSACTIONS_DISABLED_ON_TABLE_BEING_DROPPED_MESSAGE, "SELECT",
prepared.source);
- if (prepared.table.isCounter())
- throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE,
"SELECT", prepared.source);
-
+ validate(prepared);
returningSelect = new NamedSelect(TxnDataName.returning(),
prepared);
checkAtMostOnePartitionSpecified(returningSelect.select,
"returning select");
}
@@ -572,6 +591,7 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
checkFalse(prepared.metadata().params.pendingDrop,
TRANSACTIONS_DISABLED_ON_TABLE_BEING_DROPPED_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);
+ checkFalse(prepared.attrs.isTimeToLiveSet(),
NO_TTLS_IN_UPDATES_MESSAGE, prepared.type, prepared.source);
if (prepared.metadata().isCounter())
throw invalidRequest(NO_COUNTERS_IN_TXNS_MESSAGE,
prepared.type, prepared.source);
diff --git a/test/unit/org/apache/cassandra/cql3/ast/Conditional.java
b/test/unit/org/apache/cassandra/cql3/ast/Conditional.java
index ffce162450..4cc0026f97 100644
--- a/test/unit/org/apache/cassandra/cql3/ast/Conditional.java
+++ b/test/unit/org/apache/cassandra/cql3/ast/Conditional.java
@@ -62,6 +62,43 @@ public interface Conditional extends Expression
}
}
+ class Is implements Conditional
+ {
+ public enum Kind
+ {
+ Null("NULL"),
+ NotNull("NOT NULL");
+
+ private final String cql;
+
+ Kind(String s)
+ {
+ this.cql = s;
+ }
+ }
+
+ public final Kind kind;
+ public final Reference reference;
+
+ public Is(String symbol, Kind kind)
+ {
+ this(Reference.of(Symbol.unknownType(symbol)), kind);
+ }
+
+ public Is(Reference reference, Kind kind)
+ {
+ this.kind = kind;
+ this.reference = reference;
+ }
+
+ @Override
+ public void toCQL(StringBuilder sb, int indent)
+ {
+ reference.toCQL(sb, indent);
+ sb.append(" IS ").append(kind.cql);
+ }
+ }
+
class Builder
{
private final List<Conditional> sub = new ArrayList<>();
diff --git a/test/unit/org/apache/cassandra/cql3/ast/Mutation.java
b/test/unit/org/apache/cassandra/cql3/ast/Mutation.java
index e9134ddee7..68ed84db49 100644
--- a/test/unit/org/apache/cassandra/cql3/ast/Mutation.java
+++ b/test/unit/org/apache/cassandra/cql3/ast/Mutation.java
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -35,6 +36,7 @@ import java.util.stream.StreamSupport;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
@@ -103,6 +105,11 @@ public class Mutation implements Statement
this.casCondition = casCondition;
}
+ public static Builder builder(TableMetadata table)
+ {
+ return new Builder(table);
+ }
+
@Override
public Statement.Kind kind()
{
@@ -355,4 +362,50 @@ WHERE PK_column_conditions
{
return StreamSupport.stream(columns.spliterator(), false).map(m -> new
Symbol(m)).collect(Collectors.toSet());
}
+
+ public static class Builder
+ {
+ private final TableMetadata metadata;
+ private Kind kind;
+ private Map<Symbol, Expression> values = new LinkedHashMap<>();
+ private OptionalInt ttl = OptionalInt.empty();
+ private OptionalLong timestamp = OptionalLong.empty();
+ private Optional<? extends CasCondition> casCondition =
Optional.empty();
+
+ public Builder(TableMetadata metadata)
+ {
+ this.metadata = metadata;
+ }
+
+ public Builder kind(Kind kind)
+ {
+ this.kind = kind;
+ return this;
+ }
+
+ public Builder value(String col, int value)
+ {
+ return value(new Symbol(col, Int32Type.instance), value);
+ }
+
+ public Builder value(Symbol symbol, Object value)
+ {
+ values.put(symbol, new Literal(value, symbol.type()));
+ return this;
+ }
+
+ public Builder ttl(int ttl)
+ {
+ this.ttl = OptionalInt.of(ttl);
+ return this;
+ }
+
+ public Mutation build()
+ {
+ if (kind == null)
+ throw new IllegalStateException("Kind is not defined, must
call kind method before build");
+ // don't need to check values as Mutation does
+ return new Mutation(kind, metadata, new LinkedHashMap<>(values),
ttl, timestamp, casCondition);
+ }
+ }
}
diff --git a/test/unit/org/apache/cassandra/cql3/ast/Select.java
b/test/unit/org/apache/cassandra/cql3/ast/Select.java
index 9ac541ee74..806f27f187 100644
--- a/test/unit/org/apache/cassandra/cql3/ast/Select.java
+++ b/test/unit/org/apache/cassandra/cql3/ast/Select.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@@ -343,6 +345,12 @@ FROM [keyspace_name.] table_name
return this;
}
+ public Builder withIn(String name, int... values)
+ {
+ where.in(new Symbol(name, Int32Type.instance),
IntStream.of(values).mapToObj(i -> new Literal(i,
Int32Type.instance)).collect(Collectors.toList()));
+ return this;
+ }
+
/**
* When the column type/value type isn't known, this will fall back to
byte type
*/
diff --git a/test/unit/org/apache/cassandra/cql3/ast/Txn.java
b/test/unit/org/apache/cassandra/cql3/ast/Txn.java
index 3ce90f5ce8..1e4097d07a 100644
--- a/test/unit/org/apache/cassandra/cql3/ast/Txn.java
+++ b/test/unit/org/apache/cassandra/cql3/ast/Txn.java
@@ -19,6 +19,7 @@
package org.apache.cassandra.cql3.ast;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -244,6 +245,16 @@ public class Txn implements Statement
return this;
}
+ public Builder addIf(Conditional conditional, Mutation... mutations)
+ {
+ return addIf(conditional, Arrays.asList(mutations));
+ }
+
+ public Builder addIf(Conditional conditional, List<Mutation> mutations)
+ {
+ return addIf(new If(conditional, mutations));
+ }
+
public Builder addUpdate(Mutation mutation)
{
this.mutations.add(Objects.requireNonNull(mutation));
diff --git
a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
index da7d9213e8..c7df283216 100644
---
a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
+++
b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
@@ -24,23 +24,37 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.cql3.CQLStatement;
import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.ast.Conditional.Is;
+import org.apache.cassandra.cql3.ast.FunctionCall;
+import org.apache.cassandra.cql3.ast.Mutation;
+import org.apache.cassandra.cql3.ast.Select;
+import org.apache.cassandra.cql3.ast.Txn;
+import org.apache.cassandra.cql3.ast.Where;
+import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.TableId;
+import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.assertj.core.api.Assertions;
+import static org.apache.cassandra.cql3.ast.Where.Inequalities.EQUAL;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.DUPLICATE_TUPLE_NAME_MESSAGE;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.EMPTY_TRANSACTION_MESSAGE;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.ILLEGAL_RANGE_QUERY_MESSAGE;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.INCOMPLETE_PARTITION_KEY_SELECT_MESSAGE;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE;
+import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_AGGREGATION_IN_TXNS_MESSAGE;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_CONDITIONS_IN_UPDATES_MESSAGE;
+import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_GROUP_BY_IN_TXNS_MESSAGE;
+import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_ORDER_BY_IN_TXNS_MESSAGE;
+import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_PARTITION_IN_CLAUSE_WITH_LIMIT;
import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_TIMESTAMPS_IN_UPDATES_MESSAGE;
+import static
org.apache.cassandra.cql3.statements.TransactionStatement.NO_TTLS_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;
@@ -58,6 +72,7 @@ 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
@@ -69,7 +84,18 @@ public class TransactionStatementTest
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, v int) WITH transactional_mode = 'off'", "ks").id(TABLE6_ID));
+ parse("CREATE TABLE tbl6 (k int PRIMARY
KEY, v int) WITH transactional_mode = 'off'", "ks").id(TABLE6_ID),
+ parse("CREATE TABLE tbl7 (k int PRIMARY
KEY, v vector<float, 1>) WITH transactional_mode = 'full'",
"ks").id(TABLE7_ID));
+ }
+
+ private static TableMetadata tbl(int num)
+ {
+ return Keyspace.open("ks").getColumnFamilyStore("tbl" +
num).metadata();
+ }
+
+ private static TableMetadata tbl5()
+ {
+ return tbl(5);
}
@Test
@@ -342,6 +368,121 @@ public class TransactionStatementTest
.hasMessageContaining(String.format(ILLEGAL_RANGE_QUERY_MESSAGE, "LET
assignment row1", "at [2:15]"));
}
+ @Test
+ public void shouldRejectTTL()
+ {
+ Mutation.Builder builder = Mutation.builder(tbl5())
+ .value("k", 1)
+ .value("v", 2)
+ .ttl(42);
+ for (Mutation.Kind kind : Mutation.Kind.values())
+ {
+ if (kind == Mutation.Kind.DELETE) continue; // deletes don't
support TTL
+ Mutation mutation = builder.kind(kind).build();
+ String query = Txn.wrap(mutation).toCQL();
+ Assertions.assertThatThrownBy(() -> prepare(query))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_TTLS_IN_UPDATES_MESSAGE, kind.name(),
"at"));
+
+ var txn = Txn.builder()
+ .addLet("a", Select.builder()
+ .withTable(tbl5())
+ .withWhere("k",
Where.Inequalities.EQUAL, 1)
+ .build())
+ .addIf(new Is("a", Is.Kind.Null), mutation)
+ .build();
+ Assertions.assertThatThrownBy(() -> prepare(txn.toCQL()))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_TTLS_IN_UPDATES_MESSAGE, kind.name(),
"at"));
+ }
+ }
+
+ @Test
+ public void shouldRejectAggFunctions()
+ {
+ var select = Select.builder()
+ .withSelection(FunctionCall.count("v"))
+ .withTable(tbl5())
+ .withWhere("k", EQUAL, 0)
+ .build();
+
+ Assertions.assertThatThrownBy(() -> prepare(Txn.wrap(select).toCQL()))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_AGGREGATION_IN_TXNS_MESSAGE, "SELECT",
"at"));
+
+ var txn = Txn.builder()
+ .addLet("a", select)
+ .addReturnReferences("a.count")
+ .build();
+
+ Assertions.assertThatThrownBy(() -> prepare(txn.toCQL()))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_AGGREGATION_IN_TXNS_MESSAGE, "SELECT",
"at"));
+ }
+
+ @Test
+ public void shouldRejectOrderBy()
+ {
+ String query = "BEGIN TRANSACTION\n" +
+ " SELECT * FROM ks.tbl7 WHERE k=0 ORDER BY v ANN OF
[42] LIMIT 1;" +
+ "COMMIT TRANSACTION;";
+ Assertions.assertThatThrownBy(() -> prepare(query))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_ORDER_BY_IN_TXNS_MESSAGE, "SELECT",
"at"));
+
+ // The below code is left commented out as a reminder to think about
this case... As of this writing ORDER BY does not parse in a LET clause... if
that is ever fixed we should block it right away!
+// String query2 = "BEGIN TRANSACTION\n" +
+// " LET a = (SELECT * FROM ks.tbl7 WHERE k=0 ORDER BY
v ANN OF [42] LIMIT 1;)" +
+// " SELECT a.v" +
+// "COMMIT TRANSACTION;";
+// Assertions.assertThatThrownBy(() -> prepare(query2))
+// .isInstanceOf(InvalidRequestException.class)
+//
.hasMessageContaining(String.format(NO_ORDER_BY_IN_TXNS_MESSAGE, "SELECT",
"at"));
+ }
+
+ @Test
+ public void shouldRejectGroupBy()
+ {
+ String query = "BEGIN TRANSACTION\n" +
+ " SELECT * FROM ks.tbl1 WHERE k=0 GROUP BY c LIMIT 1;"
+
+ "COMMIT TRANSACTION;";
+ Assertions.assertThatThrownBy(() -> prepare(query))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_GROUP_BY_IN_TXNS_MESSAGE, "SELECT",
"at"));
+
+ // The below code is left commented out as a reminder to think about
this case... As of this writing GROUP BY does not parse in a LET clause... if
that is ever fixed we should block it right away!
+// String query2 = "BEGIN TRANSACTION\n" +
+// " LET a = (SELECT * FROM ks.tbl1 WHERE k=0 GROUP BY
c LIMIT 1;)" +
+// " SELECT a.v" +
+// "COMMIT TRANSACTION;";
+// Assertions.assertThatThrownBy(() -> prepare(query2))
+// .isInstanceOf(InvalidRequestException.class)
+//
.hasMessageContaining(String.format(NO_GROUP_BY_IN_TXNS_MESSAGE, "SELECT",
"at"));
+ }
+
+ @Test
+ public void shouldRejectInClauseInLet()
+ {
+ // this is blocked not because this isn't safe, but that the logic to
handle this is currently in the read coordinator, which Accord doesn't call.
+ // So rather than return bad results to users, IN w/ LIMIT is
blocked... until we can fix
+ var select = Select.builder()
+ .withTable(tbl(1))
+ .withIn("k", 0, 1)
+ .withLimit(1)
+ .build();
+
+ Assertions.assertThatThrownBy(() -> prepare(Txn.wrap(select).toCQL()))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_PARTITION_IN_CLAUSE_WITH_LIMIT,
"SELECT", "at"));
+
+ Assertions.assertThatThrownBy(() -> prepare(Txn.builder()
+ .addLet("a", select)
+ .addReturnReferences("a.k")
+ .build().toCQL()))
+ .isInstanceOf(InvalidRequestException.class)
+
.hasMessageContaining(String.format(NO_PARTITION_IN_CLAUSE_WITH_LIMIT,
"SELECT", "at"));
+ }
+
@Test
public void shouldRejectLetSelectOnNonTransactionalTable()
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]