This is an automated email from the ASF dual-hosted git repository.
jlewandowski 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 ef87a5ae22 Improve transaction statement validation
ef87a5ae22 is described below
commit ef87a5ae224b12350a248e469bc3b42471490540
Author: Jacek Lewandowski <[email protected]>
AuthorDate: Thu Mar 16 18:43:20 2023 +0100
Improve transaction statement validation
patch by Jacek Lewandowski; reviewed by David Capwell and Caleb Rackliffe
for CASSANDRA-18302
---
CHANGES.txt | 1 +
src/antlr/Parser.g | 38 +++++--
.../org/apache/cassandra/cql3/StatementSource.java | 76 ++++++++++++++
.../cassandra/cql3/statements/BatchStatement.java | 50 +++++++---
.../cassandra/cql3/statements/DeleteStatement.java | 28 ++++--
.../cql3/statements/ModificationStatement.java | 55 ++++++++---
.../cassandra/cql3/statements/SelectStatement.java | 109 ++++++++++++++++-----
.../cql3/statements/TransactionStatement.java | 65 ++++++------
.../cassandra/cql3/statements/UpdateStatement.java | 47 ++++++---
src/java/org/apache/cassandra/db/view/View.java | 22 +++--
.../apache/cassandra/cql3/StatementSourceTest.java | 61 ++++++++++++
.../cql3/statements/TransactionStatementTest.java | 28 +++---
12 files changed, 451 insertions(+), 129 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 2937ec238a..9ff6764a47 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
accord
+ * Improve transaction statement validation (CASSANDRA-18302)
* Add support for prepared statements for accord transactions
(CASSANDRA-18299)
* Fix statement validation against partition range queries (CASSANDRA-18240)
* Fix null value handling for static columns (CASSANDRA-18241)
diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g
index d69c7c7fd5..a076b204ff 100644
--- a/src/antlr/Parser.g
+++ b/src/antlr/Parser.g
@@ -34,6 +34,8 @@ options {
protected List<RowDataReference.Raw> references;
+ private Token statementBeginMarker;
+
public static final Set<String> reservedTypeNames = new HashSet<String>()
{{
add("byte");
@@ -230,6 +232,19 @@ options {
{
// Do nothing.
}
+
+ public Token stmtBegins()
+ {
+ statementBeginMarker = input.LT(1);
+ return statementBeginMarker;
+ }
+
+ public StatementSource stmtSrc()
+ {
+ StatementSource stmtSrc = StatementSource.create(statementBeginMarker);
+ statementBeginMarker = null;
+ return stmtSrc;
+ }
}
/** STATEMENTS **/
@@ -302,6 +317,7 @@ selectStatement returns [SelectStatement.RawStatement expr]
List<Selectable.Raw> groups = new ArrayList<>();
boolean allowFiltering = false;
boolean isJson = false;
+ stmtBegins();
}
: K_SELECT
// json is a valid column name. By consequence, we need to resolve the
ambiguity for "json - json"
@@ -321,7 +337,7 @@ selectStatement returns [SelectStatement.RawStatement expr]
isJson,
null);
WhereClause where = wclause == null ? WhereClause.empty() :
wclause.build();
- $expr = new SelectStatement.RawStatement(cf, params,
$sclause.selectors, where, limit, perPartitionLimit);
+ $expr = new SelectStatement.RawStatement(cf, params,
$sclause.selectors, where, limit, perPartitionLimit, stmtSrc());
}
;
@@ -334,11 +350,12 @@ letStatement returns [SelectStatement.RawStatement expr]
Term.Raw limit = null;
}
: K_LET txnVar=IDENT '='
- '(' K_SELECT assignments=letSelectors K_FROM cf=columnFamilyName K_WHERE
wclause=whereClause ( K_LIMIT rows=intValue { limit = rows; } )? ')'
+ '(' { stmtBegins(); } K_SELECT assignments=letSelectors K_FROM
cf=columnFamilyName K_WHERE wclause=whereClause ( K_LIMIT rows=intValue { limit
= rows; } )? ')'
{
SelectStatement.Parameters params = new
SelectStatement.Parameters(Collections.emptyMap(), Collections.emptyList(),
false, false, false, $txnVar.text);
WhereClause where = wclause == null ? WhereClause.empty() :
wclause.build();
- $expr = new SelectStatement.RawStatement(cf, params, assignments,
where, limit, null);
+
+ $expr = new SelectStatement.RawStatement(cf, params, assignments,
where, limit, null, stmtSrc());
}
;
@@ -535,6 +552,9 @@ groupByClause[List<Selectable.Raw> groups]
*
*/
insertStatement returns [ModificationStatement.Parsed expr]
+ @init {
+ stmtBegins();
+ }
: K_INSERT K_INTO cf=columnFamilyName
( st1=normalInsertStatement[cf] { $expr = st1; }
| K_JSON st2=jsonInsertStatement[cf] { $expr = st2; })
@@ -553,7 +573,7 @@ normalInsertStatement [QualifiedName qn] returns
[UpdateStatement.ParsedInsert e
( K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
( usingClause[attrs] )?
{
- $expr = new UpdateStatement.ParsedInsert(qn, attrs, columnNames,
values, ifNotExists);
+ $expr = new UpdateStatement.ParsedInsert(qn, attrs, columnNames,
values, ifNotExists, stmtSrc());
}
;
@@ -573,7 +593,7 @@ jsonInsertStatement [QualifiedName qn] returns
[UpdateStatement.ParsedInsertJson
( K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
( usingClause[attrs] )?
{
- $expr = new UpdateStatement.ParsedInsertJson(qn, attrs, val,
defaultUnset, ifNotExists);
+ $expr = new UpdateStatement.ParsedInsertJson(qn, attrs, val,
defaultUnset, ifNotExists, stmtSrc());
}
;
@@ -604,6 +624,7 @@ updateStatement returns [UpdateStatement.ParsedUpdate expr]
Attributes.Raw attrs = new Attributes.Raw();
UpdateStatement.OperationCollector operations = new
UpdateStatement.OperationCollector();
boolean ifExists = false;
+ stmtBegins();
}
: K_UPDATE cf=columnFamilyName
( usingClause[attrs] )?
@@ -617,7 +638,8 @@ updateStatement returns [UpdateStatement.ParsedUpdate expr]
wclause.build(),
conditions == null ?
Collections.<Pair<ColumnIdentifier, ColumnCondition.Raw>>emptyList() :
conditions,
ifExists,
- isParsingTxn);
+ isParsingTxn,
+ stmtSrc());
}
;
@@ -639,6 +661,7 @@ deleteStatement returns [DeleteStatement.Parsed expr]
Attributes.Raw attrs = new Attributes.Raw();
List<Operation.RawDeletion> columnDeletions = Collections.emptyList();
boolean ifExists = false;
+ stmtBegins();
}
: K_DELETE ( dels=deleteSelection { columnDeletions = dels; } )?
K_FROM cf=columnFamilyName
@@ -651,7 +674,8 @@ deleteStatement returns [DeleteStatement.Parsed expr]
columnDeletions,
wclause.build(),
conditions == null ?
Collections.<Pair<ColumnIdentifier, ColumnCondition.Raw>>emptyList() :
conditions,
- ifExists);
+ ifExists,
+ stmtSrc());
}
;
diff --git a/src/java/org/apache/cassandra/cql3/StatementSource.java
b/src/java/org/apache/cassandra/cql3/StatementSource.java
new file mode 100644
index 0000000000..2f07ec4f53
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/StatementSource.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.cql3;
+
+import java.util.Objects;
+
+import org.antlr.runtime.Token;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+public class StatementSource
+{
+ public static final StatementSource INTERNAL = new StatementSource(0, 0);
+
+ public final int line;
+ public final int charPositionInLine;
+
+ public StatementSource(int line, int charPositionInLine)
+ {
+ this.line = line;
+ this.charPositionInLine = charPositionInLine;
+ }
+
+ @Override
+ public String toString()
+ {
+ if (this == INTERNAL)
+ {
+ return "<<<internal statement>>>";
+ }
+ else
+ {
+ if (!isEmpty())
+ return String.format("at [%d:%d]", line + 1,
charPositionInLine + 1);
+ else
+ return "";
+ }
+ }
+
+ public boolean isEmpty()
+ {
+ return line > Character.MAX_VALUE || line == Character.MAX_VALUE &&
charPositionInLine > Character.MAX_VALUE;
+ }
+
+ // note - this can also reproduce the original statement raw text by
getting TokenStream and calling toString(startToken, endToken)
+ public static StatementSource create(Token startToken)
+ {
+ Objects.requireNonNull(startToken);
+
+ if (startToken.getType() == Token.EOF)
+ return new StatementSource(Character.MAX_VALUE + 1, 0);
+
+ int startLine = min(max(startToken.getLine(), 1) - 1,
Character.MAX_VALUE);
+ int startChar = min(max(startToken.getCharPositionInLine(), 0),
Character.MAX_VALUE);
+
+ return new StatementSource(startLine, startChar);
+ }
+
+}
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index eb77c33127..df173a66cc 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -18,7 +18,16 @@
package org.apache.cassandra.cql3.statements;
import java.nio.ByteBuffer;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
@@ -31,18 +40,36 @@ import org.slf4j.helpers.MessageFormatter;
import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.audit.AuditLogEntryType;
-import org.apache.cassandra.db.guardrails.Guardrails;
-import org.apache.cassandra.schema.TableId;
-import org.apache.cassandra.schema.TableMetadata;
-import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.cql3.*;
-import org.apache.cassandra.db.*;
+import org.apache.cassandra.cql3.Attributes;
+import org.apache.cassandra.cql3.BatchQueryOptions;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.ColumnSpecification;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.ResultSet;
+import org.apache.cassandra.cql3.VariableSpecifications;
+import org.apache.cassandra.db.Clustering;
+import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.IMutation;
+import org.apache.cassandra.db.RegularAndStaticColumns;
+import org.apache.cassandra.db.Slice;
+import org.apache.cassandra.db.Slices;
+import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.db.rows.RowIterator;
-import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.metrics.BatchMetrics;
-import org.apache.cassandra.service.*;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableId;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.ClientWarn;
+import org.apache.cassandra.service.QueryState;
+import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.FBUtilities;
@@ -50,7 +77,6 @@ import org.apache.cassandra.utils.NoSpamLogger;
import org.apache.cassandra.utils.Pair;
import static java.util.function.Predicate.isEqual;
-
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
@@ -194,7 +220,7 @@ public class BatchStatement implements
CQLStatement.CompositeCQLStatement
for (ModificationStatement statement : statements)
{
if (timestampSet && statement.isTimestampSet())
- throw new InvalidRequestException("Timestamp must be set
either on BATCH or individual statements");
+ throw new InvalidRequestException("Timestamp must be set
either on BATCH or individual statements: " + statement.source);
if (statement.isCounter())
hasCounters = true;
@@ -235,7 +261,7 @@ public class BatchStatement implements
CQLStatement.CompositeCQLStatement
for (ModificationStatement stmt : statements)
{
if (ksName != null && (!stmt.keyspace().equals(ksName) ||
!stmt.table().equals(cfName)))
- throw new InvalidRequestException("Batch with conditions
cannot span multiple tables");
+ throw new InvalidRequestException("Batch with conditions
cannot span multiple tables: " + stmt.source);
ksName = stmt.keyspace();
cfName = stmt.table();
}
diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
index a8d99e588b..b37800992c 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
@@ -19,9 +19,20 @@ package org.apache.cassandra.cql3.statements;
import java.util.List;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.audit.AuditLogEntryType;
-import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Attributes;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.Operation;
+import org.apache.cassandra.cql3.Operations;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.cql3.StatementSource;
+import org.apache.cassandra.cql3.UpdateParameters;
+import org.apache.cassandra.cql3.VariableSpecifications;
+import org.apache.cassandra.cql3.WhereClause;
import org.apache.cassandra.cql3.conditions.ColumnCondition;
import org.apache.cassandra.cql3.conditions.Conditions;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
@@ -33,8 +44,6 @@ import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.utils.Pair;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
@@ -49,9 +58,10 @@ public class DeleteStatement extends ModificationStatement
Operations operations,
StatementRestrictions restrictions,
Conditions conditions,
- Attributes attrs)
+ Attributes attrs,
+ StatementSource source)
{
- super(StatementType.DELETE, bindVariables, cfm, operations,
restrictions, conditions, attrs);
+ super(StatementType.DELETE, bindVariables, cfm, operations,
restrictions, conditions, attrs, source);
}
@Override
@@ -132,9 +142,10 @@ public class DeleteStatement extends ModificationStatement
List<Operation.RawDeletion> deletions,
WhereClause whereClause,
List<Pair<ColumnIdentifier, ColumnCondition.Raw>>
conditions,
- boolean ifExists)
+ boolean ifExists,
+ StatementSource source)
{
- super(name, StatementType.DELETE, attrs, conditions, false,
ifExists);
+ super(name, StatementType.DELETE, attrs, conditions, false,
ifExists, source);
this.deletions = deletions;
this.whereClause = whereClause;
}
@@ -174,7 +185,8 @@ public class DeleteStatement extends ModificationStatement
operations,
restrictions,
conditions,
- attrs);
+ attrs,
+ source);
if (stmt.hasConditions() &&
!restrictions.hasAllPrimaryKeyColumnsRestrictedByEqualities())
{
diff --git
a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index a62921bf11..5d5dba79d7 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -35,7 +35,6 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Iterables;
-
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,6 +51,7 @@ import org.apache.cassandra.cql3.QualifiedName;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.ResultSet;
+import org.apache.cassandra.cql3.StatementSource;
import org.apache.cassandra.cql3.UpdateParameters;
import org.apache.cassandra.cql3.Validation;
import org.apache.cassandra.cql3.VariableSpecifications;
@@ -65,16 +65,41 @@ import org.apache.cassandra.cql3.selection.ResultSetBuilder;
import org.apache.cassandra.cql3.selection.Selection;
import org.apache.cassandra.cql3.selection.Selection.Selectors;
import org.apache.cassandra.cql3.transactions.ReferenceOperation;
-import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.filter.*;
+import org.apache.cassandra.db.CBuilder;
+import org.apache.cassandra.db.Clustering;
+import org.apache.cassandra.db.ClusteringBound;
+import org.apache.cassandra.db.ClusteringComparator;
+import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.IMutation;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.ReadExecutionController;
+import org.apache.cassandra.db.RegularAndStaticColumns;
+import org.apache.cassandra.db.SinglePartitionReadCommand;
+import org.apache.cassandra.db.SinglePartitionReadQuery;
+import org.apache.cassandra.db.Slice;
+import org.apache.cassandra.db.Slices;
+import org.apache.cassandra.db.filter.ClusteringIndexFilter;
+import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter;
+import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter;
+import org.apache.cassandra.db.filter.ColumnFilter;
+import org.apache.cassandra.db.filter.DataLimits;
+import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.db.marshal.BooleanType;
import org.apache.cassandra.db.marshal.ValueAccessor;
-import org.apache.cassandra.db.partitions.*;
+import org.apache.cassandra.db.partitions.FilteredPartition;
+import org.apache.cassandra.db.partitions.Partition;
+import org.apache.cassandra.db.partitions.PartitionIterator;
+import org.apache.cassandra.db.partitions.PartitionIterators;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.db.rows.RowIterator;
import org.apache.cassandra.db.view.View;
import org.apache.cassandra.dht.Token;
-import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.locator.Replica;
import org.apache.cassandra.locator.ReplicaLayout;
import org.apache.cassandra.schema.ColumnMetadata;
@@ -84,13 +109,13 @@ import org.apache.cassandra.schema.ViewMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageProxy;
+import org.apache.cassandra.service.accord.txn.TxnReferenceOperation;
+import org.apache.cassandra.service.accord.txn.TxnReferenceOperations;
+import org.apache.cassandra.service.accord.txn.TxnWrite;
import org.apache.cassandra.service.disk.usage.DiskUsageBroadcaster;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.service.paxos.BallotGenerator;
import org.apache.cassandra.service.paxos.Commit.Proposal;
-import org.apache.cassandra.service.accord.txn.TxnReferenceOperation;
-import org.apache.cassandra.service.accord.txn.TxnReferenceOperations;
-import org.apache.cassandra.service.accord.txn.TxnWrite;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.utils.ByteBufferUtil;
@@ -136,13 +161,16 @@ public abstract class ModificationStatement implements
CQLStatement.SingleKeyspa
private final RegularAndStaticColumns requiresRead;
+ public final StatementSource source;
+
public ModificationStatement(StatementType type,
VariableSpecifications bindVariables,
TableMetadata metadata,
Operations operations,
StatementRestrictions restrictions,
Conditions conditions,
- Attributes attrs)
+ Attributes attrs,
+ StatementSource source)
{
this.type = type;
this.bindVariables = bindVariables;
@@ -151,6 +179,7 @@ public abstract class ModificationStatement implements
CQLStatement.SingleKeyspa
this.operations = operations;
this.conditions = conditions;
this.attrs = attrs;
+ this.source = source;
if (!conditions.isEmpty())
{
@@ -1019,13 +1048,15 @@ public abstract class ModificationStatement implements
CQLStatement.SingleKeyspa
private final List<Pair<ColumnIdentifier, ColumnCondition.Raw>>
conditions;
private final boolean ifNotExists;
private final boolean ifExists;
+ protected final StatementSource source;
protected Parsed(QualifiedName name,
StatementType type,
Attributes.Raw attrs,
List<Pair<ColumnIdentifier, ColumnCondition.Raw>>
conditions,
boolean ifNotExists,
- boolean ifExists)
+ boolean ifExists,
+ StatementSource source)
{
super(name);
this.type = type;
@@ -1033,6 +1064,7 @@ public abstract class ModificationStatement implements
CQLStatement.SingleKeyspa
this.conditions = conditions == null ? Collections.emptyList() :
conditions;
this.ifNotExists = ifNotExists;
this.ifExists = ifExists;
+ this.source = source;
}
public ModificationStatement prepare(ClientState state)
@@ -1160,6 +1192,7 @@ public abstract class ModificationStatement implements
CQLStatement.SingleKeyspa
null,
null,
ONE,
- null);
+ null,
+ StatementSource.INTERNAL);
}
}
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 43f803782d..b54abd3fa1 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -18,27 +18,45 @@
package org.apache.cassandra.cql3.statements;
import java.nio.ByteBuffer;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
-
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.audit.AuditLogEntryType;
import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.db.guardrails.Guardrails;
-import org.apache.cassandra.schema.ColumnMetadata;
-import org.apache.cassandra.schema.Schema;
-import org.apache.cassandra.schema.SchemaConstants;
-import org.apache.cassandra.schema.TableMetadata;
-import org.apache.cassandra.schema.TableMetadataRef;
-import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.ColumnSpecification;
+import org.apache.cassandra.cql3.Constants;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.ResultSet;
+import org.apache.cassandra.cql3.StatementSource;
+import org.apache.cassandra.cql3.Term;
+import org.apache.cassandra.cql3.VariableSpecifications;
+import org.apache.cassandra.cql3.WhereClause;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.cql3.selection.RawSelector;
@@ -48,10 +66,29 @@ import
org.apache.cassandra.cql3.selection.Selectable.WithFunction;
import org.apache.cassandra.cql3.selection.Selection;
import org.apache.cassandra.cql3.selection.Selection.Selectors;
import org.apache.cassandra.cql3.selection.Selector;
-import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.Clustering;
+import org.apache.cassandra.db.ClusteringBound;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.db.DataRange;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.PartitionPosition;
+import org.apache.cassandra.db.PartitionRangeReadQuery;
+import org.apache.cassandra.db.ReadExecutionController;
+import org.apache.cassandra.db.ReadQuery;
+import org.apache.cassandra.db.SinglePartitionReadCommand;
+import org.apache.cassandra.db.SinglePartitionReadQuery;
+import org.apache.cassandra.db.Slice;
+import org.apache.cassandra.db.Slices;
import org.apache.cassandra.db.aggregation.AggregationSpecification;
import org.apache.cassandra.db.aggregation.GroupMaker;
-import org.apache.cassandra.db.filter.*;
+import org.apache.cassandra.db.filter.ClusteringIndexFilter;
+import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter;
+import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter;
+import org.apache.cassandra.db.filter.ColumnFilter;
+import org.apache.cassandra.db.filter.DataLimits;
+import org.apache.cassandra.db.filter.RowFilter;
+import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.partitions.PartitionIterator;
@@ -59,8 +96,18 @@ import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.db.rows.RowIterator;
import org.apache.cassandra.db.view.View;
import org.apache.cassandra.dht.AbstractBounds;
-import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.ReadSizeAbortException;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestFailureReason;
+import org.apache.cassandra.exceptions.RequestValidationException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.index.IndexRegistry;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.schema.SchemaConstants;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.ClientWarn;
@@ -74,9 +121,6 @@ import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
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.checkNull;
@@ -121,6 +165,8 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
*/
private final Comparator<List<ByteBuffer>> orderingComparator;
+ public final StatementSource source;
+
// Used by forSelection below
public static final Parameters defaultParameters = new
Parameters(Collections.emptyMap(),
Collections.emptyList(),
@@ -137,7 +183,8 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
AggregationSpecification.Factory
aggregationSpecFactory,
Comparator<List<ByteBuffer>> orderingComparator,
Term limit,
- Term perPartitionLimit)
+ Term perPartitionLimit,
+ StatementSource source)
{
this.table = table;
this.bindVariables = bindVariables;
@@ -149,6 +196,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
this.parameters = parameters;
this.limit = limit;
this.perPartitionLimit = perPartitionLimit;
+ this.source = source;
}
@Override
@@ -209,7 +257,8 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
null,
null,
null,
- null);
+ null,
+ StatementSource.INTERNAL);
}
@Override
@@ -306,7 +355,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
int pageSize,
AggregationSpecification aggregationSpec)
{
- boolean isPartitionRangeQuery = restrictions.isKeyRange() ||
restrictions.usesSecondaryIndexing();
+ boolean isPartitionRangeQuery = isPartitionRangeQuery();
DataLimits limit = getDataLimits(userLimit, perPartitionLimit,
pageSize, aggregationSpec);
@@ -602,6 +651,11 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
return restrictions;
}
+ public boolean isPartitionRangeQuery()
+ {
+ return isForPartitionRange(restrictions);
+ }
+
private ReadQuery getSliceCommands(QueryOptions options, ClientState
state, ColumnFilter columnFilter,
DataLimits limit, int nowInSec)
{
@@ -1053,6 +1107,11 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
Collections.sort(cqlRows.rows, orderingComparator);
}
+ private static boolean isForPartitionRange(StatementRestrictions
restrictions)
+ {
+ return restrictions.isKeyRange() ||
restrictions.usesSecondaryIndexing();
+ }
+
public static class RawStatement extends QualifiedStatement
{
public final Parameters parameters;
@@ -1061,13 +1120,15 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
public final Term.Raw limit;
public final Term.Raw perPartitionLimit;
private ClientState state;
+ private final StatementSource source;
public RawStatement(QualifiedName cfName,
Parameters parameters,
List<RawSelector> selectClause,
WhereClause whereClause,
Term.Raw limit,
- Term.Raw perPartitionLimit)
+ Term.Raw perPartitionLimit,
+ StatementSource source)
{
super(cfName);
this.parameters = parameters;
@@ -1075,6 +1136,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
this.whereClause = whereClause;
this.limit = limit;
this.perPartitionLimit = perPartitionLimit;
+ this.source = source;
}
public SelectStatement prepare(ClientState state)
@@ -1163,7 +1225,8 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
aggregationSpecFactory,
orderingComparator,
prepareLimit(variableSpecifications,
limit, keyspace(), limitReceiver()),
- prepareLimit(variableSpecifications,
perPartitionLimit, keyspace(), perPartitionLimitReceiver()));
+ prepareLimit(variableSpecifications,
perPartitionLimit, keyspace(), perPartitionLimitReceiver()),
+ source);
}
private Selection prepareSelection(TableMetadata table,
@@ -1460,7 +1523,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
private void checkNeedsFiltering(StatementRestrictions restrictions)
throws InvalidRequestException
{
// non-key-range non-indexed queries cannot involve filtering
underneath
- if (!parameters.allowFiltering && (restrictions.isKeyRange() ||
restrictions.usesSecondaryIndexing()))
+ if (!parameters.allowFiltering &&
isForPartitionRange(restrictions))
{
// We will potentially filter data if either:
// - Have more than one IndexExpression
@@ -1596,7 +1659,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
private String loggableTokens(QueryOptions options, ClientState state)
{
- if (restrictions.isKeyRange() || restrictions.usesSecondaryIndexing())
+ if (isPartitionRangeQuery())
{
AbstractBounds<PartitionPosition> bounds =
restrictions.getPartitionKeyBounds(options);
return "token range: " + (bounds.inclusiveLeft() ? '[' : '(') +
@@ -1633,7 +1696,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement,
sb.append("SELECT ").append(queriedColumns().toCQLString());
sb.append(" FROM
").append(table.keyspace).append('.').append(table.name);
- if (restrictions.isKeyRange() || restrictions.usesSecondaryIndexing())
+ if (isPartitionRangeQuery())
{
// partition range
ClusteringIndexFilter clusteringIndexFilter =
makeClusteringIndexFilter(options, state, columnFilter);
diff --git
a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
index 33e5803a96..4ffad56358 100644
--- a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java
@@ -57,7 +57,6 @@ import
org.apache.cassandra.cql3.transactions.ConditionStatement;
import org.apache.cassandra.cql3.transactions.ReferenceOperation;
import org.apache.cassandra.cql3.transactions.RowDataReference;
import org.apache.cassandra.cql3.transactions.SelectReferenceSource;
-import org.apache.cassandra.db.ReadQuery;
import org.apache.cassandra.db.SinglePartitionReadCommand;
import org.apache.cassandra.db.SinglePartitionReadQuery;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -77,7 +76,6 @@ import org.apache.cassandra.service.accord.txn.TxnUpdate;
import org.apache.cassandra.service.accord.txn.TxnWrite;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.LazyToString;
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
@@ -89,14 +87,13 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
private static final Logger logger =
LoggerFactory.getLogger(TransactionStatement.class);
public static final String DUPLICATE_TUPLE_NAME_MESSAGE = "The name '%s'
has already been used by a LET assignment.";
- public static final String INCOMPLETE_PRIMARY_KEY_LET_MESSAGE = "SELECT in
LET assignment 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; CQL %s";
- public static final String INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE = "Normal
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; CQL %s";
- public static final String NO_CONDITIONS_IN_UPDATES_MESSAGE = "Updates
within transactions may not specify their own conditions.";
- public static final String NO_TIMESTAMPS_IN_UPDATES_MESSAGE = "Updates
within transactions may not specify custom timestamps.";
+ 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 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_transactions_enabled in cassandra.yaml)";
- public static final String ILLEGAL_RANGE_QUERY_MESSAGE = "Range queries
are not allowed for reads within a transaction";
+ public static final String ILLEGAL_RANGE_QUERY_MESSAGE = "Range queries
are not allowed for reads within a transaction; %s %s";
static class NamedSelect
{
@@ -207,12 +204,9 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
TxnNamedRead createNamedRead(NamedSelect namedSelect, QueryOptions
options, ClientState state)
{
SelectStatement select = namedSelect.select;
- ReadQuery readQuery = select.getQuery(options, 0);
- checkTrue(readQuery instanceof SinglePartitionReadQuery.Group,
ILLEGAL_RANGE_QUERY_MESSAGE, select.asCQL(options, state));
-
// We reject reads from both LET and SELECT that do not specify a
single row.
@SuppressWarnings("unchecked")
- SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery
= (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>) readQuery;
+ SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery
= (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>)
select.getQuery(options, 0);
if (selectQuery.queries.size() != 1)
throw new IllegalArgumentException("Within a transaction, SELECT
statements must select a single partition; found " + selectQuery.queries.size()
+ " partitions");
@@ -223,12 +217,9 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
List<TxnNamedRead> createNamedReads(NamedSelect namedSelect, QueryOptions
options, ClientState state)
{
SelectStatement select = namedSelect.select;
- ReadQuery readQuery = select.getQuery(options, 0);
- checkTrue(readQuery instanceof SinglePartitionReadQuery.Group,
ILLEGAL_RANGE_QUERY_MESSAGE, select.asCQL(options, state));
-
// We reject reads from both LET and SELECT that do not specify a
single row.
@SuppressWarnings("unchecked")
- SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery
= (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>) readQuery;
+ SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery
= (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>)
select.getQuery(options, 0);
if (selectQuery.queries.size() == 1)
return Collections.singletonList(new
TxnNamedRead(namedSelect.name, Iterables.getOnlyElement(selectQuery.queries)));
@@ -339,24 +330,24 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
}
}
- private static void checkAtMostOneRowSpecified(ClientState clientState,
@Nullable QueryOptions options, SelectStatement select, String failureMessage)
+ /**
+ * Returns {@code true} only if the statement selects multiple clusterings
in a partition
+ */
+ private static boolean isSelectingMultipleClusterings(SelectStatement
select, @Nullable QueryOptions options)
{
if
(select.getRestrictions().hasAllPrimaryKeyColumnsRestrictedByEqualities())
- return;
+ return false;
if (options == null)
{
- // If the limit is a non-terminal marker (because we're
preparing), defer validation until execution.
+ // if the limit is a non-terminal marker (because we're
preparing), defer validation until execution (when options != null)
if (select.isLimitMarker())
- return;
+ return false;
- // The limit is already defined, so proceed with validation...
options = QueryOptions.DEFAULT;
}
- int limit = select.getLimit(options);
- QueryOptions finalOptions = options; // javac thinks this is mutable
so requires a copy
- checkTrue(limit == 1 &&
select.getRestrictions().hasAllPartitionKeyColumnsRestrictedByEqualities(),
failureMessage, LazyToString.lazy(() -> select.asCQL(finalOptions,
clientState)));
+ return select.getLimit(options) != 1;
}
@Override
@@ -366,21 +357,19 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
try
{
+ // check again since now we have query options; note that
statements are quaranted to be single partition reads at this point
for (NamedSelect assignment : assignments)
- checkAtMostOneRowSpecified(state.getClientState(), options,
assignment.select, INCOMPLETE_PRIMARY_KEY_LET_MESSAGE);
+ checkFalse(isSelectingMultipleClusterings(assignment.select,
options), INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, "LET assignment",
assignment.select.source);
if (returningSelect != null)
- checkAtMostOneRowSpecified(state.getClientState(), options,
returningSelect.select, INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE);
+
checkFalse(isSelectingMultipleClusterings(returningSelect.select, options),
INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, "returning SELECT",
returningSelect.select.source);
TxnData data =
AccordService.instance().coordinate(createTxn(state.getClientState(), options),
options.getConsistency());
if (returningSelect != null)
{
- ReadQuery readQuery = returningSelect.select.getQuery(options,
0);
- checkTrue(readQuery instanceof
SinglePartitionReadQuery.Group, ILLEGAL_RANGE_QUERY_MESSAGE,
returningSelect.select.asCQL(options, state.getClientState()));
-
@SuppressWarnings("unchecked")
- SinglePartitionReadQuery.Group<SinglePartitionReadCommand>
selectQuery = (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>)
readQuery;
+ SinglePartitionReadQuery.Group<SinglePartitionReadCommand>
selectQuery = (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>)
returningSelect.select.getQuery(options, 0);
Selection.Selectors selectors =
returningSelect.select.getSelection().newSelectors(options);
ResultSetBuilder result = new ResultSetBuilder(resultMetadata,
selectors, null);
if (selectQuery.queries.size() == 1)
@@ -505,7 +494,7 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
SelectStatement prepared = select.prepare(bindVariables);
NamedSelect namedSelect = new NamedSelect(name, prepared);
- checkAtMostOneRowSpecified(state, null, namedSelect.select,
INCOMPLETE_PRIMARY_KEY_LET_MESSAGE);
+ checkAtMostOneRowSpecified(namedSelect.select, "LET assignment
" + name.name());
preparedAssignments.add(namedSelect);
refSources.put(name, new SelectReferenceSource(prepared));
}
@@ -518,7 +507,7 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
if (select != null)
{
returningSelect = new NamedSelect(TxnDataName.returning(),
select.prepare(bindVariables));
- checkAtMostOneRowSpecified(state, null,
returningSelect.select, INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE);
+ checkAtMostOneRowSpecified(returningSelect.select, "returning
select");
}
List<RowDataReference> returningReferences = null;
@@ -539,8 +528,8 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
ModificationStatement.Parsed parsed = updates.get(i);
ModificationStatement prepared = parsed.prepare(state,
bindVariables);
- checkFalse(prepared.hasConditions(),
NO_CONDITIONS_IN_UPDATES_MESSAGE);
- checkFalse(prepared.isTimestampSet(),
NO_TIMESTAMPS_IN_UPDATES_MESSAGE);
+ checkFalse(prepared.hasConditions(),
NO_CONDITIONS_IN_UPDATES_MESSAGE, prepared.type, prepared.source);
+ checkFalse(prepared.isTimestampSet(),
NO_TIMESTAMPS_IN_UPDATES_MESSAGE, prepared.type, prepared.source);
preparedUpdates.add(prepared);
}
@@ -552,5 +541,15 @@ public class TransactionStatement implements
CQLStatement.CompositeCQLStatement,
return new TransactionStatement(preparedAssignments,
returningSelect, returningReferences, preparedUpdates, preparedConditions,
bindVariables);
}
+
+ /**
+ * Do not use this method in execution!!! It is only allowed during
prepare because it outputs a query raw text.
+ * We don't want it print it for a user who provided an identifier of
someone's else prepared statement.
+ */
+ private static void checkAtMostOneRowSpecified(SelectStatement select,
String name)
+ {
+ checkFalse(select.isPartitionRangeQuery(),
ILLEGAL_RANGE_QUERY_MESSAGE, name, select.source);
+ checkFalse(isSelectingMultipleClusterings(select, null),
INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, name, select.source);
+ }
}
}
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index 9531679f0f..da25ef7a22 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -23,10 +23,25 @@ import java.util.Collections;
import java.util.List;
import com.google.common.base.Preconditions;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.audit.AuditLogEntryType;
-import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Attributes;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.Constants;
+import org.apache.cassandra.cql3.Json;
+import org.apache.cassandra.cql3.Operation;
+import org.apache.cassandra.cql3.Operations;
+import org.apache.cassandra.cql3.Operator;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.cql3.SingleColumnRelation;
+import org.apache.cassandra.cql3.StatementSource;
+import org.apache.cassandra.cql3.Term;
+import org.apache.cassandra.cql3.UpdateParameters;
+import org.apache.cassandra.cql3.VariableSpecifications;
+import org.apache.cassandra.cql3.WhereClause;
import org.apache.cassandra.cql3.conditions.ColumnCondition;
import org.apache.cassandra.cql3.conditions.Conditions;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
@@ -41,8 +56,6 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.accord.txn.TxnReferenceOperation;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkContainsNoDuplicates;
import static
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
@@ -64,9 +77,10 @@ public class UpdateStatement extends ModificationStatement
Operations operations,
StatementRestrictions restrictions,
Conditions conditions,
- Attributes attrs)
+ Attributes attrs,
+ StatementSource source)
{
- super(type, bindVariables, metadata, operations, restrictions,
conditions, attrs);
+ super(type, bindVariables, metadata, operations, restrictions,
conditions, attrs, source);
}
@Override
@@ -137,9 +151,10 @@ public class UpdateStatement extends ModificationStatement
Attributes.Raw attrs,
List<ColumnIdentifier> columnNames,
List<Term.Raw> columnValues,
- boolean ifNotExists)
+ boolean ifNotExists,
+ StatementSource source)
{
- super(name, StatementType.INSERT, attrs, null, ifNotExists, false);
+ super(name, StatementType.INSERT, attrs, null, ifNotExists, false,
source);
this.columnNames = columnNames;
this.columnValues = columnValues;
}
@@ -210,7 +225,8 @@ public class UpdateStatement extends ModificationStatement
operations,
restrictions,
conditions,
- attrs);
+ attrs,
+ source);
}
}
@@ -222,9 +238,9 @@ public class UpdateStatement extends ModificationStatement
private final Json.Raw jsonValue;
private final boolean defaultUnset;
- public ParsedInsertJson(QualifiedName name, Attributes.Raw attrs,
Json.Raw jsonValue, boolean defaultUnset, boolean ifNotExists)
+ public ParsedInsertJson(QualifiedName name, Attributes.Raw attrs,
Json.Raw jsonValue, boolean defaultUnset, boolean ifNotExists, StatementSource
source)
{
- super(name, StatementType.INSERT, attrs, null, ifNotExists, false);
+ super(name, StatementType.INSERT, attrs, null, ifNotExists, false,
source);
this.jsonValue = jsonValue;
this.defaultUnset = defaultUnset;
}
@@ -280,7 +296,8 @@ public class UpdateStatement extends ModificationStatement
operations,
restrictions,
conditions,
- attrs);
+ attrs,
+ source);
}
}
@@ -354,9 +371,10 @@ public class UpdateStatement extends ModificationStatement
WhereClause whereClause,
List<Pair<ColumnIdentifier, ColumnCondition.Raw>>
conditions,
boolean ifExists,
- boolean isForTxn)
+ boolean isForTxn,
+ StatementSource source)
{
- super(name, StatementType.UPDATE, attrs, conditions, false,
ifExists);
+ super(name, StatementType.UPDATE, attrs, conditions, false,
ifExists, source);
this.updates = updates;
this.whereClause = whereClause;
this.isForTxn = isForTxn;
@@ -402,7 +420,8 @@ public class UpdateStatement extends ModificationStatement
operations,
restrictions,
conditions,
- attrs);
+ attrs,
+ source);
}
}
diff --git a/src/java/org/apache/cassandra/db/view/View.java
b/src/java/org/apache/cassandra/db/view/View.java
index a3ecc33d79..5603ede19a 100644
--- a/src/java/org/apache/cassandra/db/view/View.java
+++ b/src/java/org/apache/cassandra/db/view/View.java
@@ -17,19 +17,26 @@
*/
package org.apache.cassandra.db.view;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.stream.Collectors;
-
import javax.annotation.Nullable;
import com.google.common.collect.Iterables;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.StatementSource;
import org.apache.cassandra.cql3.selection.RawSelector;
import org.apache.cassandra.cql3.selection.Selectable;
import org.apache.cassandra.cql3.statements.SelectStatement;
-import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.ReadQuery;
+import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.Schema;
@@ -37,8 +44,6 @@ import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.schema.ViewMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.utils.FBUtilities;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* A View copies data from a base table into a view table which can be queried
independently from the
@@ -174,7 +179,8 @@ public class View
selectClause(),
definition.whereClause,
null,
- null);
+ null,
+ StatementSource.INTERNAL);
rawSelect.setBindVariables(Collections.emptyList());
diff --git a/test/unit/org/apache/cassandra/cql3/StatementSourceTest.java
b/test/unit/org/apache/cassandra/cql3/StatementSourceTest.java
new file mode 100644
index 0000000000..b6362747d5
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/StatementSourceTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.cql3;
+
+import org.junit.Test;
+
+import org.antlr.runtime.Token;
+import org.mockito.Mockito;
+
+import static org.apache.cassandra.cql3.StatementSource.create;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+public class StatementSourceTest
+{
+ private static Token token(int line, int pos)
+ {
+ Token token = Mockito.mock(Token.class);
+ when(token.getLine()).thenReturn(line);
+ when(token.getCharPositionInLine()).thenReturn(pos);
+ when(token.getType()).thenReturn(1);
+ return token;
+ }
+
+ private static Token eof()
+ {
+ Token token = Mockito.mock(Token.class);
+ when(token.getLine()).thenThrow(UnsupportedOperationException.class);
+
when(token.getCharPositionInLine()).thenThrow(UnsupportedOperationException.class);
+ when(token.getType()).thenReturn(Token.EOF);
+ return token;
+ }
+
+ @Test
+ public void test()
+ {
+ assertThat(create(token(1, 4))).hasToString("at [1:5]");
+ assertThat(create(token(3, 8))).hasToString("at [3:9]");
+ assertThat(create(token(6, 8))).hasToString("at [6:9]");
+ assertThat(create(token(1, 0))).hasToString("at [1:1]");
+ assertThat(create(eof()).toString()).isEmpty();
+
+ assertThat(StatementSource.INTERNAL).hasToString("<<<internal
statement>>>");
+ }
+}
diff --git
a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
index d8062a39ec..afdc91cb17 100644
---
a/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
+++
b/test/unit/org/apache/cassandra/cql3/statements/TransactionStatementTest.java
@@ -18,7 +18,6 @@
package org.apache.cassandra.cql3.statements;
-import org.assertj.core.api.Assertions;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -32,10 +31,11 @@ import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.messages.ResultMessage;
+import org.assertj.core.api.Assertions;
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.INCOMPLETE_PRIMARY_KEY_LET_MESSAGE;
+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_TIMESTAMPS_IN_UPDATES_MESSAGE;
@@ -161,7 +161,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_LET_MESSAGE,
letSelect));
+
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, "LET
assignment row1", "at [2:15]"));
}
@Test
@@ -175,7 +175,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> execute(query, 2))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_LET_MESSAGE,
letSelect.replace("?", "2")));
+
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, "LET
assignment", "at [2:15]"));
}
@Test
@@ -189,7 +189,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_LET_MESSAGE,
letSelect));
+
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE, "LET
assignment row1", "at [2:15]"));
}
@Test
@@ -200,7 +200,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE,
select));
+
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE,
"returning select", "at [2:1]"));
}
@Test
@@ -211,7 +211,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE,
select));
+
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE,
"returning select", "at [2:1]"));
}
@Test
@@ -223,7 +223,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
- .hasMessageContaining(NO_CONDITIONS_IN_UPDATES_MESSAGE);
+ .hasMessageContaining(NO_CONDITIONS_IN_UPDATES_MESSAGE,
"INSERT", "at [2:3]");
}
@Test
@@ -235,7 +235,7 @@ public class TransactionStatementTest
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
- .hasMessageContaining(NO_TIMESTAMPS_IN_UPDATES_MESSAGE);
+ .hasMessageContaining(NO_TIMESTAMPS_IN_UPDATES_MESSAGE,
"INSERT", "at [2:3]");
}
@Test
@@ -335,26 +335,28 @@ public class TransactionStatementTest
@Test
public void shouldRejectNormalSelectWithIncompletePartitionKey()
{
+ String select = "SELECT k, v FROM ks.tbl5 LIMIT 1";
String query = "BEGIN TRANSACTION\n" +
- " SELECT k, v FROM ks.tbl5 LIMIT 1;\n" +
+ select + ";\n" +
"COMMIT TRANSACTION;\n";
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE,
"SELECT v FROM ks.tbl5 LIMIT 1"));
+
.hasMessageContaining(String.format(ILLEGAL_RANGE_QUERY_MESSAGE, "returning
select", "at [2:1]"));
}
@Test
public void shouldRejectLetSelectWithIncompletePartitionKey()
{
+ String select = "SELECT k, v FROM ks.tbl5 WHERE token(k) > token(123)
LIMIT 1";
String query = "BEGIN TRANSACTION\n" +
- " LET row1 = (SELECT k, v FROM ks.tbl5 WHERE token(k)
> token(123) LIMIT 1); \n" +
+ " LET row1 = (" + select + "); \n" +
" SELECT row1.k, row1.v;\n" +
"COMMIT TRANSACTION;\n";
Assertions.assertThatThrownBy(() -> prepare(query))
.isInstanceOf(InvalidRequestException.class)
-
.hasMessageContaining(String.format(INCOMPLETE_PRIMARY_KEY_LET_MESSAGE, "SELECT
v FROM ks.tbl5 WHERE token(k) > 0000007b LIMIT 1"));
+
.hasMessageContaining(String.format(ILLEGAL_RANGE_QUERY_MESSAGE, "LET
assignment row1", "at [2:15]"));
}
private static CQLStatement prepare(String query)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]