You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ad...@apache.org on 2021/07/06 12:21:25 UTC
[cassandra] branch trunk updated: Fix
AbstractReadQuery::toCQLString not returning valid CQL
This is an automated email from the ASF dual-hosted git repository.
adelapena pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new e6a311f Fix AbstractReadQuery::toCQLString not returning valid CQL
e6a311f is described below
commit e6a311f6898b1184d7d58826021a82cbda2f9bc0
Author: Andrés de la Peña <a....@gmail.com>
AuthorDate: Tue Jul 6 13:20:34 2021 +0100
Fix AbstractReadQuery::toCQLString not returning valid CQL
patch by Andrés de la Peña; reviewed by Benjamin Lerer for CASSANDRA-16510
---
CHANGES.txt | 1 +
.../org/apache/cassandra/db/AbstractReadQuery.java | 9 +-
src/java/org/apache/cassandra/db/Clustering.java | 2 +-
src/java/org/apache/cassandra/db/DataRange.java | 24 +-
src/java/org/apache/cassandra/db/DecoratedKey.java | 32 +
.../cassandra/db/PartitionRangeReadCommand.java | 18 +-
src/java/org/apache/cassandra/db/ReadCommand.java | 15 +-
.../cassandra/db/SinglePartitionReadCommand.java | 18 +-
src/java/org/apache/cassandra/db/Slices.java | 54 +-
.../db/VirtualTablePartitionRangeReadQuery.java | 16 +-
.../db/VirtualTableSinglePartitionReadQuery.java | 18 +-
.../db/filter/AbstractClusteringIndexFilter.java | 9 +-
.../cassandra/db/filter/ClusteringIndexFilter.java | 3 +-
.../db/filter/ClusteringIndexNamesFilter.java | 31 +-
.../db/filter/ClusteringIndexSliceFilter.java | 8 +-
.../apache/cassandra/db/filter/ColumnFilter.java | 2 +-
.../cassandra/db/filter/ColumnSubselection.java | 18 +-
.../org/apache/cassandra/db/filter/RowFilter.java | 74 +-
.../apache/cassandra/db/marshal/AbstractType.java | 5 +
.../apache/cassandra/schema/ColumnMetadata.java | 5 +-
.../db/AbstractReadQueryToCQLStringTest.java | 818 +++++++++++++++++++++
.../cassandra/index/sasi/plan/OperationTest.java | 10 +
22 files changed, 1076 insertions(+), 114 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 0744d6a..dda7b90 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
4.1
+ * Fix AbstractReadQuery::toCQLString not returning valid CQL (CASSANDRA-16510)
* Log when compacting many tombstones (CASSANDRA-16780)
* Display bytes per level in tablestats for LCS tables (CASSANDRA-16799)
* Add isolated flush timer to CommitLogMetrics and ensure writes correspond to single WaitingOnCommit data points (CASSANDRA-16701)
diff --git a/src/java/org/apache/cassandra/db/AbstractReadQuery.java b/src/java/org/apache/cassandra/db/AbstractReadQuery.java
index ec1a6b1..374d2b2 100644
--- a/src/java/org/apache/cassandra/db/AbstractReadQuery.java
+++ b/src/java/org/apache/cassandra/db/AbstractReadQuery.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.db;
+import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.db.filter.ColumnFilter;
import org.apache.cassandra.db.filter.DataLimits;
import org.apache.cassandra.db.filter.RowFilter;
@@ -102,13 +103,17 @@ abstract class AbstractReadQuery extends MonitorableImpl implements ReadQuery
StringBuilder sb = new StringBuilder().append("SELECT ")
.append(columnFilter().toCQLString())
.append(" FROM ")
- .append(metadata().keyspace)
+ .append(ColumnIdentifier.maybeQuote(metadata().keyspace))
.append('.')
- .append(metadata().name);
+ .append(ColumnIdentifier.maybeQuote(metadata().name));
appendCQLWhereClause(sb);
if (limits() != DataLimits.NONE)
sb.append(' ').append(limits());
+
+ // ALLOW FILTERING might not be strictly necessary
+ sb.append(" ALLOW FILTERING");
+
return sb.toString();
}
diff --git a/src/java/org/apache/cassandra/db/Clustering.java b/src/java/org/apache/cassandra/db/Clustering.java
index c685638..f5184e9 100644
--- a/src/java/org/apache/cassandra/db/Clustering.java
+++ b/src/java/org/apache/cassandra/db/Clustering.java
@@ -72,7 +72,7 @@ public interface Clustering<V> extends ClusteringPrefix<V>
for (int i = 0; i < size(); i++)
{
ColumnMetadata c = metadata.clusteringColumns().get(i);
- sb.append(i == 0 ? "" : ", ").append(c.type.getString(get(i), accessor()));
+ sb.append(i == 0 ? "" : ", ").append(c.type.toCQLString(bufferAt(i)));
}
return sb.toString();
}
diff --git a/src/java/org/apache/cassandra/db/DataRange.java b/src/java/org/apache/cassandra/db/DataRange.java
index 91a62b3..b322912 100644
--- a/src/java/org/apache/cassandra/db/DataRange.java
+++ b/src/java/org/apache/cassandra/db/DataRange.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
-import org.apache.cassandra.db.marshal.ByteArrayAccessor;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.db.filter.*;
@@ -186,9 +185,10 @@ public class DataRange
*
* @return Whether this {@code DataRange} queries everything.
*/
- public boolean isUnrestricted()
+ public boolean isUnrestricted(TableMetadata metadata)
{
- return startKey().isMinimum() && stopKey().isMinimum() && clusteringIndexFilter.selectsAllPartition();
+ return startKey().isMinimum() && stopKey().isMinimum() &&
+ (clusteringIndexFilter.selectsAllPartition() || metadata.clusteringColumns().isEmpty());
}
public boolean selectsAllPartition()
@@ -257,10 +257,10 @@ public class DataRange
return String.format("range=%s pfilter=%s", keyRange.getString(metadata.partitionKeyType), clusteringIndexFilter.toString(metadata));
}
- public String toCQLString(TableMetadata metadata)
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter)
{
- if (isUnrestricted())
- return "UNRESTRICTED";
+ if (isUnrestricted(metadata))
+ return rowFilter.toCQLString();
StringBuilder sb = new StringBuilder();
@@ -278,7 +278,7 @@ public class DataRange
needAnd = true;
}
- String filterString = clusteringIndexFilter.toCQLString(metadata);
+ String filterString = clusteringIndexFilter.toCQLString(metadata, rowFilter);
if (!filterString.isEmpty())
sb.append(needAnd ? " AND " : "").append(filterString);
@@ -312,20 +312,18 @@ public class DataRange
: (isInclusive ? "<=" : "<");
}
- // TODO: this is reused in SinglePartitionReadCommand but this should not really be here. Ideally
- // we need a more "native" handling of composite partition keys.
- public static void appendKeyString(StringBuilder sb, AbstractType<?> type, ByteBuffer key)
+ private static void appendKeyString(StringBuilder sb, AbstractType<?> type, ByteBuffer key)
{
if (type instanceof CompositeType)
{
CompositeType ct = (CompositeType)type;
ByteBuffer[] values = ct.split(key);
for (int i = 0; i < ct.types.size(); i++)
- sb.append(i == 0 ? "" : ", ").append(ct.types.get(i).getString(values[i]));
+ sb.append(i == 0 ? "" : ", ").append(ct.types.get(i).toCQLString(values[i]));
}
else
{
- sb.append(type.getString(key));
+ sb.append(type.toCQLString(key));
}
}
@@ -393,7 +391,7 @@ public class DataRange
}
@Override
- public boolean isUnrestricted()
+ public boolean isUnrestricted(TableMetadata metadata)
{
return false;
}
diff --git a/src/java/org/apache/cassandra/db/DecoratedKey.java b/src/java/org/apache/cassandra/db/DecoratedKey.java
index 92d6414..4dd87d0 100644
--- a/src/java/org/apache/cassandra/db/DecoratedKey.java
+++ b/src/java/org/apache/cassandra/db/DecoratedKey.java
@@ -19,10 +19,15 @@ package org.apache.cassandra.db;
import java.nio.ByteBuffer;
import java.util.Comparator;
+import java.util.List;
+import java.util.StringJoiner;
+import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.dht.Token.KeyBound;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.MurmurHash;
import org.apache.cassandra.utils.IFilter.FilterKey;
@@ -125,6 +130,33 @@ public abstract class DecoratedKey implements PartitionPosition, FilterKey
return "DecoratedKey(" + getToken() + ", " + keystring + ")";
}
+ /**
+ * Returns a CQL representation of this key.
+ *
+ * @param metadata the metadata of the table that this key belogs to
+ * @return a CQL representation of this key
+ */
+ public String toCQLString(TableMetadata metadata)
+ {
+ List<ColumnMetadata> columns = metadata.partitionKeyColumns();
+
+ if (columns.size() == 1)
+ return toCQLString(columns.get(0), getKey());
+
+ ByteBuffer[] values = ((CompositeType) metadata.partitionKeyType).split(getKey());
+ StringJoiner joiner = new StringJoiner(" AND ");
+
+ for (int i = 0; i < columns.size(); i++)
+ joiner.add(toCQLString(columns.get(i), values[i]));
+
+ return joiner.toString();
+ }
+
+ private static String toCQLString(ColumnMetadata metadata, ByteBuffer key)
+ {
+ return String.format("%s = %s", metadata.name.toCQLString(), metadata.type.toCQLString(key));
+ }
+
public Token getToken()
{
return token;
diff --git a/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java b/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java
index 82b6e8a..45cd308 100644
--- a/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java
+++ b/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java
@@ -22,7 +22,6 @@ import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
-import org.apache.cassandra.net.MessageFlag;
import org.apache.cassandra.net.Verb;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.config.DatabaseDescriptor;
@@ -41,7 +40,6 @@ import org.apache.cassandra.io.sstable.format.SSTableReadsListener;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.metrics.TableMetrics;
-import org.apache.cassandra.net.Message;
import org.apache.cassandra.schema.IndexMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageProxy;
@@ -360,19 +358,9 @@ public class PartitionRangeReadCommand extends ReadCommand implements PartitionR
protected void appendCQLWhereClause(StringBuilder sb)
{
- if (dataRange.isUnrestricted() && rowFilter().isEmpty())
- return;
-
- sb.append(" WHERE ");
- // We put the row filter first because the data range can end by "ORDER BY"
- if (!rowFilter().isEmpty())
- {
- sb.append(rowFilter());
- if (!dataRange.isUnrestricted())
- sb.append(" AND ");
- }
- if (!dataRange.isUnrestricted())
- sb.append(dataRange.toCQLString(metadata()));
+ String filterString = dataRange().toCQLString(metadata(), rowFilter());
+ if (!filterString.isEmpty())
+ sb.append(" WHERE ").append(filterString);
}
/**
diff --git a/src/java/org/apache/cassandra/db/ReadCommand.java b/src/java/org/apache/cassandra/db/ReadCommand.java
index 7b889d1..71bce0b 100644
--- a/src/java/org/apache/cassandra/db/ReadCommand.java
+++ b/src/java/org/apache/cassandra/db/ReadCommand.java
@@ -35,6 +35,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.db.filter.*;
import org.apache.cassandra.net.MessageFlag;
import org.apache.cassandra.net.Verb;
@@ -727,13 +728,21 @@ public abstract class ReadCommand extends AbstractReadQuery
*/
public String toCQLString()
{
- StringBuilder sb = new StringBuilder();
- sb.append("SELECT ").append(columnFilter().toCQLString());
- sb.append(" FROM ").append(metadata().keyspace).append('.').append(metadata().name);
+ StringBuilder sb = new StringBuilder().append("SELECT ")
+ .append(columnFilter().toCQLString())
+ .append(" FROM ")
+ .append(ColumnIdentifier.maybeQuote(metadata().keyspace))
+ .append('.')
+ .append(ColumnIdentifier.maybeQuote(metadata().name));
+
appendCQLWhereClause(sb);
if (limits() != DataLimits.NONE)
sb.append(' ').append(limits());
+
+ // ALLOW FILTERING might not be strictly necessary
+ sb.append(" ALLOW FILTERING");
+
return sb.toString();
}
diff --git a/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java b/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java
index b17506f..026a795 100644
--- a/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java
+++ b/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java
@@ -1054,20 +1054,18 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
return Verb.READ_REQ;
}
+ @Override
protected void appendCQLWhereClause(StringBuilder sb)
{
- sb.append(" WHERE ");
-
- sb.append(ColumnMetadata.toCQLString(metadata().partitionKeyColumns())).append(" = ");
- DataRange.appendKeyString(sb, metadata().partitionKeyType, partitionKey().getKey());
-
- // We put the row filter first because the clustering index filter can end by "ORDER BY"
- if (!rowFilter().isEmpty())
- sb.append(" AND ").append(rowFilter());
+ sb.append(" WHERE ").append(partitionKey().toCQLString(metadata()));
- String filterString = clusteringIndexFilter().toCQLString(metadata());
+ String filterString = clusteringIndexFilter().toCQLString(metadata(), rowFilter());
if (!filterString.isEmpty())
- sb.append(" AND ").append(filterString);
+ {
+ if (!clusteringIndexFilter().selectsAllPartition() || !rowFilter().isEmpty())
+ sb.append(" AND ");
+ sb.append(filterString);
+ }
}
protected void serializeSelection(DataOutputPlus out, int version) throws IOException
diff --git a/src/java/org/apache/cassandra/db/Slices.java b/src/java/org/apache/cassandra/db/Slices.java
index 441a5d3..b3f5681 100644
--- a/src/java/org/apache/cassandra/db/Slices.java
+++ b/src/java/org/apache/cassandra/db/Slices.java
@@ -24,6 +24,8 @@ import java.util.*;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators;
+import org.apache.cassandra.cql3.Operator;
+import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -141,7 +143,7 @@ public abstract class Slices implements Iterable<Slice>
*/
public abstract boolean intersects(List<ByteBuffer> minClusteringValues, List<ByteBuffer> maxClusteringValues);
- public abstract String toCQLString(TableMetadata metadata);
+ public abstract String toCQLString(TableMetadata metadata, RowFilter rowFilter);
/**
* Checks if this <code>Slices</code> is empty.
@@ -549,7 +551,8 @@ public abstract class Slices implements Iterable<Slice>
return sb.append("}").toString();
}
- public String toCQLString(TableMetadata metadata)
+ @Override
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter)
{
StringBuilder sb = new StringBuilder();
@@ -593,7 +596,7 @@ public abstract class Slices implements Iterable<Slice>
sb.append(" AND ");
needAnd = true;
- sb.append(column.name);
+ sb.append(column.name.toCQLString());
Set<ByteBuffer> values = new LinkedHashSet<>();
for (int j = 0; j < componentInfo.size(); j++)
@@ -601,20 +604,25 @@ public abstract class Slices implements Iterable<Slice>
if (values.size() == 1)
{
- sb.append(" = ").append(column.type.getString(first.startValue));
+ sb.append(" = ").append(column.type.toCQLString(first.startValue));
+ rowFilter = rowFilter.without(column, Operator.EQ, first.startValue);
}
else
{
sb.append(" IN (");
int j = 0;
for (ByteBuffer value : values)
- sb.append(j++ == 0 ? "" : ", ").append(column.type.getString(value));
+ {
+ sb.append(j++ == 0 ? "" : ", ").append(column.type.toCQLString(value));
+ rowFilter = rowFilter.without(column, Operator.EQ, value);
+ }
sb.append(")");
}
}
else
{
boolean isReversed = column.isReversedType();
+ Operator operator;
// As said above, we assume (without checking) that this means all ComponentOfSlice for this column
// are the same, so we only bother about the first.
@@ -623,27 +631,39 @@ public abstract class Slices implements Iterable<Slice>
if (needAnd)
sb.append(" AND ");
needAnd = true;
- sb.append(column.name);
+ sb.append(column.name.toCQLString());
if (isReversed)
- sb.append(first.startInclusive ? " <= " : " < ");
+ operator = first.startInclusive ? Operator.LTE : Operator.LT;
else
- sb.append(first.startInclusive ? " >= " : " > ");
- sb.append(column.type.getString(first.startValue));
+ operator = first.startInclusive ? Operator.GTE : Operator.GT;
+ sb.append(' ').append(operator.toString()).append(' ')
+ .append(column.type.toCQLString(first.startValue));
+ rowFilter = rowFilter.without(column, operator, first.startValue);
}
if (first.endValue != null)
{
if (needAnd)
sb.append(" AND ");
needAnd = true;
- sb.append(column.name);
+ sb.append(column.name.toCQLString());
if (isReversed)
- sb.append(first.endInclusive ? " >= " : " > ");
+ operator = first.endInclusive ? Operator.GTE : Operator.GT;
else
- sb.append(first.endInclusive ? " <= " : " < ");
- sb.append(column.type.getString(first.endValue));
+ operator = first.endInclusive ? Operator.LTE : Operator.LT;
+ sb.append(' ').append(operator.toString()).append(' ')
+ .append(column.type.toCQLString(first.endValue));
+ rowFilter = rowFilter.without(column, operator, first.endValue);
}
}
}
+
+ if (!rowFilter.isEmpty())
+ {
+ if (needAnd)
+ sb.append(" AND ");
+ sb.append(rowFilter.toCQLString());
+ }
+
return sb.toString();
}
@@ -764,9 +784,10 @@ public abstract class Slices implements Iterable<Slice>
return "ALL";
}
- public String toCQLString(TableMetadata metadata)
+ @Override
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter)
{
- return "";
+ return rowFilter.toCQLString();
}
}
@@ -839,7 +860,8 @@ public abstract class Slices implements Iterable<Slice>
return "NONE";
}
- public String toCQLString(TableMetadata metadata)
+ @Override
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter)
{
return "";
}
diff --git a/src/java/org/apache/cassandra/db/VirtualTablePartitionRangeReadQuery.java b/src/java/org/apache/cassandra/db/VirtualTablePartitionRangeReadQuery.java
index 48cafa1..b24a670 100644
--- a/src/java/org/apache/cassandra/db/VirtualTablePartitionRangeReadQuery.java
+++ b/src/java/org/apache/cassandra/db/VirtualTablePartitionRangeReadQuery.java
@@ -96,18 +96,8 @@ public class VirtualTablePartitionRangeReadQuery extends VirtualTableReadQuery i
@Override
protected void appendCQLWhereClause(StringBuilder sb)
{
- if (dataRange.isUnrestricted() && rowFilter().isEmpty())
- return;
-
- sb.append(" WHERE ");
- // We put the row filter first because the data range can end by "ORDER BY"
- if (!rowFilter().isEmpty())
- {
- sb.append(rowFilter());
- if (!dataRange.isUnrestricted())
- sb.append(" AND ");
- }
- if (!dataRange.isUnrestricted())
- sb.append(dataRange.toCQLString(metadata()));
+ String filterString = dataRange.toCQLString(metadata(), rowFilter());
+ if (!filterString.isEmpty())
+ sb.append(" WHERE ").append(filterString);
}
}
diff --git a/src/java/org/apache/cassandra/db/VirtualTableSinglePartitionReadQuery.java b/src/java/org/apache/cassandra/db/VirtualTableSinglePartitionReadQuery.java
index ba9441a..f96f652 100644
--- a/src/java/org/apache/cassandra/db/VirtualTableSinglePartitionReadQuery.java
+++ b/src/java/org/apache/cassandra/db/VirtualTableSinglePartitionReadQuery.java
@@ -32,7 +32,6 @@ import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.db.virtual.VirtualKeyspaceRegistry;
import org.apache.cassandra.db.virtual.VirtualTable;
import org.apache.cassandra.exceptions.RequestExecutionException;
-import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
@@ -77,18 +76,15 @@ public class VirtualTableSinglePartitionReadQuery extends VirtualTableReadQuery
@Override
protected void appendCQLWhereClause(StringBuilder sb)
{
- sb.append(" WHERE ");
+ sb.append(" WHERE ").append(partitionKey().toCQLString(metadata()));
- sb.append(ColumnMetadata.toCQLString(metadata().partitionKeyColumns())).append(" = ");
- DataRange.appendKeyString(sb, metadata().partitionKeyType, partitionKey().getKey());
-
- // We put the row filter first because the clustering index filter can end by "ORDER BY"
- if (!rowFilter().isEmpty())
- sb.append(" AND ").append(rowFilter());
-
- String filterString = clusteringIndexFilter().toCQLString(metadata());
+ String filterString = clusteringIndexFilter().toCQLString(metadata(), rowFilter());
if (!filterString.isEmpty())
- sb.append(" AND ").append(filterString);
+ {
+ if (!clusteringIndexFilter().selectsAllPartition() || !rowFilter().isEmpty())
+ sb.append(" AND ");
+ sb.append(filterString);
+ }
}
@Override
diff --git a/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java b/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java
index 63c2783..ddcaaed 100644
--- a/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java
@@ -54,11 +54,14 @@ public abstract class AbstractClusteringIndexFilter implements ClusteringIndexFi
{
if (reversed)
{
- sb.append(" ORDER BY (");
+ sb.append(" ORDER BY ");
int i = 0;
for (ColumnMetadata column : metadata.clusteringColumns())
- sb.append(i++ == 0 ? "" : ", ").append(column.name).append(column.type instanceof ReversedType ? " ASC" : " DESC");
- sb.append(')');
+ {
+ sb.append(i++ == 0 ? "" : ", ")
+ .append(column.name.toCQLString())
+ .append(column.type instanceof ReversedType ? " ASC" : " DESC");
+ }
}
}
diff --git a/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java b/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java
index 6ea0435..08e9eb5 100644
--- a/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java
@@ -23,6 +23,7 @@ import org.apache.cassandra.db.*;
import org.apache.cassandra.db.partitions.CachedPartition;
import org.apache.cassandra.db.partitions.Partition;
import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.index.Index;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
@@ -153,7 +154,7 @@ public interface ClusteringIndexFilter
public Kind kind();
public String toString(TableMetadata metadata);
- public String toCQLString(TableMetadata metadata);
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter);
public interface Serializer
{
diff --git a/src/java/org/apache/cassandra/db/filter/ClusteringIndexNamesFilter.java b/src/java/org/apache/cassandra/db/filter/ClusteringIndexNamesFilter.java
index ef9ceff..f937c12 100644
--- a/src/java/org/apache/cassandra/db/filter/ClusteringIndexNamesFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ClusteringIndexNamesFilter.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
+import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.partitions.*;
import org.apache.cassandra.db.rows.*;
@@ -167,18 +168,36 @@ public class ClusteringIndexNamesFilter extends AbstractClusteringIndexFilter
return sb.append(')').toString();
}
- public String toCQLString(TableMetadata metadata)
+ @Override
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter)
{
if (metadata.clusteringColumns().isEmpty() || clusterings.isEmpty())
- return "";
+ return rowFilter.toCQLString();
+
+ boolean isSingleColumn = metadata.clusteringColumns().size() == 1;
+ boolean isSingleClustering = clusterings.size() == 1;
StringBuilder sb = new StringBuilder();
- sb.append('(').append(ColumnMetadata.toCQLString(metadata.clusteringColumns())).append(')');
- sb.append(clusterings.size() == 1 ? " = " : " IN (");
+ sb.append(isSingleColumn ? "" : '(')
+ .append(ColumnMetadata.toCQLString(metadata.clusteringColumns()))
+ .append(isSingleColumn ? "" : ')');
+
+ sb.append(isSingleClustering ? " = " : " IN (");
int i = 0;
for (Clustering<?> clustering : clusterings)
- sb.append(i++ == 0 ? "" : ", ").append('(').append(clustering.toCQLString(metadata)).append(')');
- sb.append(clusterings.size() == 1 ? "" : ")");
+ {
+ sb.append(i++ == 0 ? "" : ", ")
+ .append(isSingleColumn ? "" : '(')
+ .append(clustering.toCQLString(metadata))
+ .append(isSingleColumn ? "" : ')');
+
+ for (int j = 0; j < clustering.size(); j++)
+ rowFilter = rowFilter.without(metadata.clusteringColumns().get(j), Operator.EQ, clustering.bufferAt(j));
+ }
+ sb.append(isSingleClustering ? "" : ")");
+
+ if (!rowFilter.isEmpty())
+ sb.append(" AND ").append(rowFilter.toCQLString());
appendOrderByToCQLString(metadata, sb);
return sb.toString();
diff --git a/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java b/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java
index 5df98c3..b96888d 100644
--- a/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java
@@ -30,6 +30,7 @@ import org.apache.cassandra.db.transform.Transformation;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
+import org.stringtemplate.v4.ST;
/**
* A filter over a single partition.
@@ -142,13 +143,12 @@ public class ClusteringIndexSliceFilter extends AbstractClusteringIndexFilter
return String.format("slice(slices=%s, reversed=%b)", slices, reversed);
}
- public String toCQLString(TableMetadata metadata)
+ @Override
+ public String toCQLString(TableMetadata metadata, RowFilter rowFilter)
{
StringBuilder sb = new StringBuilder();
- if (!selectsAllPartition())
- sb.append(slices.toCQLString(metadata));
-
+ sb.append(slices.toCQLString(metadata, rowFilter));
appendOrderByToCQLString(metadata, sb);
return sb.toString();
diff --git a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java
index d9a1b9d..0ed6237 100644
--- a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java
@@ -940,7 +940,7 @@ public abstract class ColumnFilter
if (s.isEmpty())
joiner.add(columnName);
else
- s.forEach(subSel -> joiner.add(String.format("%s%s", columnName, subSel)));
+ s.forEach(subSel -> joiner.add(String.format("%s%s", columnName, subSel.toString(cql))));
}
return joiner.toString();
}
diff --git a/src/java/org/apache/cassandra/db/filter/ColumnSubselection.java b/src/java/org/apache/cassandra/db/filter/ColumnSubselection.java
index dbb415a..c53c43a 100644
--- a/src/java/org/apache/cassandra/db/filter/ColumnSubselection.java
+++ b/src/java/org/apache/cassandra/db/filter/ColumnSubselection.java
@@ -88,6 +88,14 @@ public abstract class ColumnSubselection implements Comparable<ColumnSubselectio
*/
public abstract int compareInclusionOf(CellPath path);
+ @Override
+ public String toString()
+ {
+ return toString(false);
+ }
+
+ protected abstract String toString(boolean cql);
+
private static class Slice extends ColumnSubselection
{
private final CellPath from;
@@ -122,11 +130,13 @@ public abstract class ColumnSubselection implements Comparable<ColumnSubselectio
}
@Override
- public String toString()
+ protected String toString(boolean cql)
{
// This assert we're dealing with a collection since that's the only thing it's used for so far.
AbstractType<?> type = ((CollectionType<?>)column().type).nameComparator();
- return String.format("[%s:%s]", from == CellPath.BOTTOM ? "" : type.getString(from.get(0)), to == CellPath.TOP ? "" : type.getString(to.get(0)));
+ return String.format("[%s:%s]",
+ from == CellPath.BOTTOM ? "" : (cql ? type.toCQLString(from.get(0)) : type.getString(from.get(0))),
+ to == CellPath.TOP ? "" : (cql ? type.toCQLString(to.get(0)) : type.getString(to.get(0))));
}
}
@@ -156,11 +166,11 @@ public abstract class ColumnSubselection implements Comparable<ColumnSubselectio
}
@Override
- public String toString()
+ protected String toString(boolean cql)
{
// This assert we're dealing with a collection since that's the only thing it's used for so far.
AbstractType<?> type = ((CollectionType<?>)column().type).nameComparator();
- return String.format("[%s]", type.getString(element.get(0)));
+ return String.format("[%s]", cql ? type.toCQLString(element.get(0)) : type.getString(element.get(0)));
}
}
diff --git a/src/java/org/apache/cassandra/db/filter/RowFilter.java b/src/java/org/apache/cassandra/db/filter/RowFilter.java
index 68a1d57..68cc194 100644
--- a/src/java/org/apache/cassandra/db/filter/RowFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/RowFilter.java
@@ -28,6 +28,7 @@ import com.google.common.base.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.context.*;
@@ -238,6 +239,23 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
return withNewExpressions(newExpressions);
}
+ /**
+ * Returns a copy of this filter but without the provided expression. If this filter doesn't contain the specified
+ * expression this method will just return an identical copy of this filter.
+ */
+ public RowFilter without(ColumnMetadata column, Operator op, ByteBuffer value)
+ {
+ if (isEmpty())
+ return this;
+
+ List<Expression> newExpressions = new ArrayList<>(expressions.size() - 1);
+ for (Expression e : expressions)
+ if (!e.column().equals(column) || e.operator() != op || !e.value.equals(value))
+ newExpressions.add(e);
+
+ return withNewExpressions(newExpressions);
+ }
+
public RowFilter withoutExpressions()
{
return withNewExpressions(Collections.emptyList());
@@ -258,12 +276,27 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
@Override
public String toString()
{
+ return toString(false);
+ }
+
+ /**
+ * Returns a CQL representation of this row filter.
+ *
+ * @return a CQL representation of this row filter
+ */
+ public String toCQLString()
+ {
+ return toString(true);
+ }
+
+ private String toString(boolean cql)
+ {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expressions.size(); i++)
{
if (i > 0)
sb.append(" AND ");
- sb.append(expressions.get(i));
+ sb.append(expressions.get(i).toString(cql));
}
return sb.toString();
}
@@ -478,6 +511,24 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
return Objects.hashCode(column.name, operator, value);
}
+ @Override
+ public String toString()
+ {
+ return toString(false);
+ }
+
+ /**
+ * Returns a CQL representation of this expression.
+ *
+ * @return a CQL representation of this expression
+ */
+ public String toCQLString()
+ {
+ return toString(true);
+ }
+
+ protected abstract String toString(boolean cql);
+
private static class Serializer
{
public void serialize(Expression expression, DataOutputPlus out, int version) throws IOException
@@ -705,7 +756,7 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
}
@Override
- public String toString()
+ protected String toString(boolean cql)
{
AbstractType<?> type = column.type;
switch (operator)
@@ -725,7 +776,9 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
default:
break;
}
- return String.format("%s %s %s", column.name, operator, type.getString(value));
+ return cql
+ ? String.format("%s %s %s", column.name.toCQLString(), operator, type.toCQLString(value) )
+ : String.format("%s %s %s", column.name.toString(), operator, type.getString(value));
}
@Override
@@ -793,10 +846,14 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
}
@Override
- public String toString()
+ protected String toString(boolean cql)
{
- MapType<?, ?> mt = (MapType<?, ?>)column.type;
- return String.format("%s[%s] = %s", column.name, mt.nameComparator().getString(key), mt.valueComparator().getString(value));
+ MapType<?, ?> mt = (MapType<?, ?>) column.type;
+ AbstractType<?> nt = mt.nameComparator();
+ AbstractType<?> vt = mt.valueComparator();
+ return cql
+ ? String.format("%s[%s] = %s", column.name.toCQLString(), nt.toCQLString(key), vt.toCQLString(value))
+ : String.format("%s[%s] = %s", column.name.toString(), nt.getString(key), vt.getString(value));
}
@Override
@@ -863,10 +920,11 @@ public abstract class RowFilter implements Iterable<RowFilter.Expression>
return value;
}
- public String toString()
+ @Override
+ protected String toString(boolean cql)
{
return String.format("expr(%s, %s)",
- targetIndex.name,
+ cql ? ColumnIdentifier.maybeQuote(targetIndex.name) : targetIndex.name,
Keyspace.openAndGetStore(table)
.indexManager
.getIndex(targetIndex)
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 19cf849..f171e7f 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -148,6 +148,11 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>, Assignm
return getString(bytes, ByteBufferAccessor.instance);
}
+ public String toCQLString(ByteBuffer bytes)
+ {
+ return asCQL3Type().toCQLLiteral(bytes, ProtocolVersion.CURRENT);
+ }
+
/** get a byte representation of the given string. */
public abstract ByteBuffer fromString(String source) throws MarshalException;
diff --git a/src/java/org/apache/cassandra/schema/ColumnMetadata.java b/src/java/org/apache/cassandra/schema/ColumnMetadata.java
index d48ca06..fdbd166 100644
--- a/src/java/org/apache/cassandra/schema/ColumnMetadata.java
+++ b/src/java/org/apache/cassandra/schema/ColumnMetadata.java
@@ -33,7 +33,6 @@ import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.MarshalException;
-import org.apache.cassandra.utils.ByteBufferUtil;
import org.github.jamm.Unmetered;
@Unmetered
@@ -447,9 +446,9 @@ public final class ColumnMetadata extends ColumnSpecification implements Selecta
return "";
StringBuilder sb = new StringBuilder();
- sb.append(defs.next().name);
+ sb.append(defs.next().name.toCQLString());
while (defs.hasNext())
- sb.append(", ").append(defs.next().name);
+ sb.append(", ").append(defs.next().name.toCQLString());
return sb.toString();
}
diff --git a/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java b/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java
new file mode 100644
index 0000000..4d0adea
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java
@@ -0,0 +1,818 @@
+/*
+ * 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.db;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.statements.SelectStatement;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.virtual.AbstractVirtualTable;
+import org.apache.cassandra.db.virtual.SimpleDataSet;
+import org.apache.cassandra.db.virtual.VirtualKeyspace;
+import org.apache.cassandra.db.virtual.VirtualKeyspaceRegistry;
+import org.apache.cassandra.db.virtual.VirtualTable;
+import org.apache.cassandra.exceptions.RequestValidationException;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.utils.FBUtilities;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link AbstractReadQuery#toCQLString()}.
+ */
+public class AbstractReadQueryToCQLStringTest extends CQLTester
+{
+ @Test
+ public void testSkinnyTable() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, v1 int, v2 int)");
+
+ // column selection on unrestricted partition range query
+ test("SELECT * FROM %s");
+ test("SELECT k FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT v1 FROM %s");
+ test("SELECT v2 FROM %s");
+ test("SELECT k, v1, v2 FROM %s",
+ "SELECT v1, v2 FROM %s");
+
+ // column selection on partition directed query
+ test("SELECT * FROM %s WHERE k = 0");
+ test("SELECT k FROM %s WHERE k = 0",
+ "SELECT * FROM %s WHERE k = 0");
+ test("SELECT v1 FROM %s WHERE k = 0");
+ test("SELECT v2 FROM %s WHERE k = 0");
+ test("SELECT k, v1, v2 FROM %s WHERE k = 0",
+ "SELECT v1, v2 FROM %s WHERE k = 0");
+
+ // token restrictions
+ test("SELECT * FROM %s WHERE token(k) > 0");
+ test("SELECT * FROM %s WHERE token(k) < 0");
+ test("SELECT * FROM %s WHERE token(k) >= 0");
+ test("SELECT * FROM %s WHERE token(k) <= 0");
+ test("SELECT * FROM %s WHERE token(k) = 0",
+ "SELECT * FROM %s WHERE token(k) >= 0 AND token(k) <= 0");
+
+ // row filter without indexed column
+ test("SELECT * FROM %s WHERE v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 < 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 > 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 <= 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 >= 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+
+ // row filter with indexed column
+ createIndex("CREATE INDEX ON %s (v1)");
+ test("SELECT * FROM %s WHERE v1 = 1");
+ test("SELECT * FROM %s WHERE v1 < 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 > 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 <= 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 >= 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1",
+ "SELECT * FROM %s WHERE token(k) >= token(0) AND token(k) <= token(0) AND v1 = 1");
+
+ // grouped partition-directed queries, maybe producing multiple queries
+ test("SELECT * FROM %s WHERE k IN (0)",
+ "SELECT * FROM %s WHERE k = 0");
+ test("SELECT * FROM %s WHERE k IN (0, 1)",
+ "SELECT * FROM %s WHERE k = 0",
+ "SELECT * FROM %s WHERE k = 1");
+ }
+
+ @Test
+ public void testSkinnyTableWithMulticolumnKey() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k1 int, k2 int, v1 int, v2 int, PRIMARY KEY((k1, k2)))");
+
+ // column selection on unrestricted partition range query
+ test("SELECT * FROM %s");
+ test("SELECT k1 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT k2 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT v1 FROM %s");
+ test("SELECT v2 FROM %s");
+ test("SELECT k1, k2, v1, v2 FROM %s",
+ "SELECT v1, v2 FROM %s");
+
+ // column selection on partition directed query
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT k1 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT k2 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT v1 FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT v2 FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT k1, k2, v1, v2 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT v1, v2 FROM %s WHERE k1 = 1 AND k2 = 2");
+
+ // token restrictions
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) < 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) >= 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) <= 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) = 0",
+ "SELECT * FROM %s WHERE token(k1, k2) >= 0 AND token(k1, k2) <= 0");
+
+ // row filter without indexed column
+ test("SELECT * FROM %s WHERE k1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND v2 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 2 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 2 AND v2 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 0 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+
+ // row filter with indexed column
+ createIndex("CREATE INDEX ON %s (k1)");
+ createIndex("CREATE INDEX ON %s (k2)");
+ createIndex("CREATE INDEX ON %s (v1)");
+ createIndex("CREATE INDEX ON %s (v2)");
+ test("SELECT * FROM %s WHERE k1 = 1");
+ test("SELECT * FROM %s WHERE k2 = 2");
+ test("SELECT * FROM %s WHERE v1 = 1");
+ test("SELECT * FROM %s WHERE v2 = 2");
+ test("SELECT * FROM %s WHERE k1 > 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 > 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 > 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v2 > 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND v2 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 2 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 2 AND v2 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND k1 = 1");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v1 = 1");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND v1 = 1",
+ "SELECT * FROM %s WHERE token(k1, k2) >= token(1, 2) AND token(k1, k2) <= token(1, 2) AND v1 = 1");
+
+ // grouped partition-directed queries, maybe producing multiple queries
+ test("SELECT * FROM %s WHERE k1 IN (1) AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 IN (2)",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 IN (1) AND k2 IN (2)",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 IN (0, 1) AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 IN (2, 3)",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 3");
+ test("SELECT * FROM %s WHERE k1 IN (0, 1) AND k2 IN (2, 3)",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 3",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 3");
+ }
+
+ @Test
+ public void testWideTable() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k int, c int, v1 int, v2 int, s int static, PRIMARY KEY(k, c))");
+
+ // column selection on unrestricted partition range query
+ test("SELECT * FROM %s");
+ test("SELECT k FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT c FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT s FROM %s");
+ test("SELECT v1 FROM %s");
+ test("SELECT v2 FROM %s");
+ test("SELECT k, c, s, v1, v2 FROM %s",
+ "SELECT s, v1, v2 FROM %s");
+
+ // column selection on partition directed query
+ test("SELECT * FROM %s WHERE k = 0");
+ test("SELECT k FROM %s WHERE k = 0",
+ "SELECT * FROM %s WHERE k = 0");
+ test("SELECT s FROM %s WHERE k = 0");
+ test("SELECT v1 FROM %s WHERE k = 0");
+ test("SELECT v2 FROM %s WHERE k = 0");
+ test("SELECT k, c, s, v1, v2 FROM %s WHERE k = 0",
+ "SELECT s, v1, v2 FROM %s WHERE k = 0");
+
+ // clustering filters
+ test("SELECT * FROM %s WHERE k = 0 AND c = 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c < 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c > 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c <= 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c >= 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c > 1 AND c <= 2");
+ test("SELECT * FROM %s WHERE k = 0 AND c >= 1 AND c < 2");
+
+ // token restrictions
+ test("SELECT * FROM %s WHERE token(k) > 0");
+ test("SELECT * FROM %s WHERE token(k) < 0");
+ test("SELECT * FROM %s WHERE token(k) >= 0");
+ test("SELECT * FROM %s WHERE token(k) <= 0");
+ test("SELECT * FROM %s WHERE token(k) = 0",
+ "SELECT * FROM %s WHERE token(k) >= 0 AND token(k) <= 0");
+
+ // row filter without indexed column
+ test("SELECT * FROM %s WHERE c = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE s = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 0 AND c = 1 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 0 AND c = 1 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND c = 1 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+
+ // expression filter with indexed column
+ createIndex("CREATE INDEX ON %s (c)");
+ createIndex("CREATE INDEX ON %s (s)");
+ createIndex("CREATE INDEX ON %s (v1)");
+ test("SELECT * FROM %s WHERE c = 1");
+ test("SELECT * FROM %s WHERE v1 = 1");
+ test("SELECT * FROM %s WHERE s = 1");
+ test("SELECT * FROM %s WHERE v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1",
+ "SELECT * FROM %s WHERE token(k) >= token(0) AND token(k) <= token(0) AND v1 = 1");
+ test("SELECT * FROM %s WHERE k = 0 AND v1 = 1 AND c = 1",
+ "SELECT * FROM %s WHERE token(k) >= token(0) AND token(k) <= token(0) AND c = 1 AND v1 = 1 ALLOW FILTERING");
+
+ // grouped partition-directed queries, maybe producing multiple queries
+ test("SELECT * FROM %s WHERE k IN (0)",
+ "SELECT * FROM %s WHERE k = 0");
+ test("SELECT * FROM %s WHERE k IN (0, 1)",
+ "SELECT * FROM %s WHERE k = 0",
+ "SELECT * FROM %s WHERE k = 1");
+ test("SELECT * FROM %s WHERE k IN (0, 1) AND c = 0",
+ "SELECT * FROM %s WHERE k = 0 AND c = 0",
+ "SELECT * FROM %s WHERE k = 1 AND c = 0");
+ test("SELECT * FROM %s WHERE k IN (0, 1) AND c > 0",
+ "SELECT * FROM %s WHERE k = 0 AND c > 0",
+ "SELECT * FROM %s WHERE k = 1 AND c > 0");
+
+ // order by
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c",
+ "SELECT * FROM %s WHERE k = 0");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c ASC",
+ "SELECT * FROM %s WHERE k = 0");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c DESC");
+
+ // order by clustering filter
+ test("SELECT * FROM %s WHERE k = 0 AND c = 1 ORDER BY c",
+ "SELECT * FROM %s WHERE k = 0 AND c = 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c = 1 ORDER BY c ASC",
+ "SELECT * FROM %s WHERE k = 0 AND c = 1");
+ test("SELECT * FROM %s WHERE k = 0 AND c = 1 ORDER BY c DESC");
+ }
+
+ @Test
+ public void testWideTableWithMulticolumnKey() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k1 int, k2 int, c1 int, c2 int, c3 int, v1 int, v2 int, PRIMARY KEY((k1, k2), c1, c2, c3))");
+
+ // column selection on unrestricted partition range query
+ test("SELECT * FROM %s");
+ test("SELECT k1 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT k2 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT c1 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT c2 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT c3 FROM %s",
+ "SELECT * FROM %s");
+ test("SELECT v1 FROM %s");
+ test("SELECT v2 FROM %s");
+ test("SELECT k1, k2, c1, c2, c3, v1, v2 FROM %s",
+ "SELECT v1, v2 FROM %s");
+
+ // column selection on partition directed query
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT k1 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT k2 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT c1 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT c2 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT v1 FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT v2 FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT k1, k2, c1, c2, v1, v2 FROM %s WHERE k1 = 1 AND k2 = 2",
+ "SELECT v1, v2 FROM %s WHERE k1 = 1 AND k2 = 2");
+
+ // clustering filters
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 < 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 > 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 <= 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 >= 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 > 1 AND c1 < 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 > 1 AND c1 <= 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 >= 1 AND c1 < 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 > 1 AND c1 < 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 < 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 > 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 <= 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 >= 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 > 2 AND c2 < 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 > 2 AND c2 <= 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 >= 2 AND c2 < 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 > 2 AND c2 < 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 = 3",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND (c1, c2, c3) = (1, 2, 3)");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 > 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 < 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 >= 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 <= 3");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 > 3 AND c3 < 4");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 > 3 AND c3 <= 4");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 >= 3 AND c3 < 4");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND c2 = 2 AND c3 >= 3 AND c3 <= 4");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND (c1, c2, c3) = (1, 2, 3)");
+
+ // token restrictions
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) < 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) >= 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) <= 0");
+ test("SELECT * FROM %s WHERE token(k1, k2) = 0",
+ "SELECT * FROM %s WHERE token(k1, k2) >= 0 AND token(k1, k2) <= 0");
+
+ // row filter without indexed column
+ test("SELECT * FROM %s WHERE k1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE c1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE c2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE c3 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v1 = 1 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND c1 = 1 AND v1 = 1 AND v2 = 2 ALLOW FILTERING");
+
+ // expression filter with indexed column
+ createIndex("CREATE INDEX ON %s (k1)");
+ createIndex("CREATE INDEX ON %s (k2)");
+ createIndex("CREATE INDEX ON %s (c1)");
+ createIndex("CREATE INDEX ON %s (c2)");
+ createIndex("CREATE INDEX ON %s (c3)");
+ createIndex("CREATE INDEX ON %s (v1)");
+ createIndex("CREATE INDEX ON %s (v2)");
+ test("SELECT * FROM %s WHERE k1 = 1");
+ test("SELECT * FROM %s WHERE k2 = 2");
+ test("SELECT * FROM %s WHERE c1 = 1");
+ test("SELECT * FROM %s WHERE c2 = 2");
+ test("SELECT * FROM %s WHERE c3 = 3");
+ test("SELECT * FROM %s WHERE v1 = 1");
+ test("SELECT * FROM %s WHERE v2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT * FROM %s WHERE c1 = 1 AND c2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE c1 = 1 AND c2 = 2 AND c3 = 3 ALLOW FILTERING",
+ "SELECT * FROM %s WHERE (c1, c2, c3) = (1, 2, 3) ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE token(k1, k2) > 0 AND v1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND v1 = 1",
+ "SELECT * FROM %s WHERE token(k1, k2) >= token(1, 2) AND token(k1, k2) <= token(1, 2) AND v1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 1 AND k2 = 2 AND c1 = 1 AND v1 = 1",
+ "SELECT * FROM %s WHERE token(k1, k2) >= token(1, 2) AND token(k1, k2) <= token(1, 2) AND c1 = 1 AND v1 = 1 ALLOW FILTERING");
+
+ // grouped partition-directed queries, maybe producing multiple queries
+ test("SELECT * FROM %s WHERE k1 IN (1) AND k2 IN (2)",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 IN (1, 2) AND k2 IN (3, 4)",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 3",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 4",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 3",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 4");
+ test("SELECT * FROM %s WHERE k1 IN (1, 2) AND k2 IN (3, 4) AND c1 = 0",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 3 AND c1 = 0",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 4 AND c1 = 0",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 3 AND c1 = 0",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 4 AND c1 = 0");
+ test("SELECT * FROM %s WHERE k1 IN (1, 2) AND k2 IN (3, 4) AND c1 > 0",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 3 AND c1 > 0",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 4 AND c1 > 0",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 3 AND c1 > 0",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 4 AND c1 > 0");
+ test("SELECT * FROM %s WHERE k1 IN (1, 2) AND k2 IN (3, 4) AND (c1, c2, c3) IN ((5, 6, 7), (8, 9, 10))",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 3 AND (c1, c2, c3) IN ((5, 6, 7), (8, 9, 10))",
+ "SELECT * FROM %s WHERE k1 = 1 AND k2 = 4 AND (c1, c2, c3) IN ((5, 6, 7), (8, 9, 10))",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 3 AND (c1, c2, c3) IN ((5, 6, 7), (8, 9, 10))",
+ "SELECT * FROM %s WHERE k1 = 2 AND k2 = 4 AND (c1, c2, c3) IN ((5, 6, 7), (8, 9, 10))");
+
+ // order by
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1 ASC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1 DESC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1 DESC, c2 DESC, c3 DESC");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1, c2",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1, c2 ASC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1 ASC, c2",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1 ASC, c2 ASC, c3 ASC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 ORDER BY c1 DESC, c2 DESC, c3 DESC");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1 ASC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1");
+
+ // order by clustering filter
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1 DESC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1 DESC, c2 DESC, c3 DESC");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1, c2",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1, c2 ASC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1 ASC, c2",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1 ASC, c2 ASC, c3 ASC",
+ "SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1");
+ test("SELECT * FROM %s WHERE k1 = 0 AND k2 = 2 AND c1 = 1 ORDER BY c1 DESC, c2 DESC, c3 DESC");
+ }
+
+ @Test
+ public void testQuotedNames() throws Throwable
+ {
+ createKeyspace("CREATE KEYSPACE \"K\" WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
+ createTable("CREATE TABLE \"K\".\"T\" (\"K\" int, \"C\" int, \"S\" int static, \"V\" int, PRIMARY KEY(\"K\", \"C\"))");
+
+ // column selection on unrestricted partition range query
+ test("SELECT * FROM \"K\".\"T\"");
+ test("SELECT \"K\" FROM \"K\".\"T\"",
+ "SELECT * FROM \"K\".\"T\"");
+ test("SELECT \"S\" FROM \"K\".\"T\"");
+ test("SELECT \"V\" FROM \"K\".\"T\"");
+ test("SELECT \"K\", \"C\", \"S\", \"V\" FROM \"K\".\"T\"",
+ "SELECT \"S\", \"V\" FROM \"K\".\"T\"");
+
+ // column selection on partition directed query
+ test("SELECT * FROM \"K\".\"T\" WHERE \"K\" = 0");
+ test("SELECT \"K\" FROM \"K\".\"T\" WHERE \"K\" = 0",
+ "SELECT * FROM \"K\".\"T\" WHERE \"K\" = 0");
+ test("SELECT \"S\" FROM \"K\".\"T\" WHERE \"K\" = 0");
+ test("SELECT \"V\" FROM \"K\".\"T\" WHERE \"K\" = 0");
+ test("SELECT \"K\", \"C\", \"S\", \"V\" FROM \"K\".\"T\" WHERE \"K\" = 0",
+ "SELECT \"S\", \"V\" FROM \"K\".\"T\" WHERE \"K\" = 0");
+
+ // filters
+ test("SELECT * FROM \"K\".\"T\" WHERE \"K\" = 0 AND \"C\" = 1");
+ test("SELECT * FROM \"K\".\"T\" WHERE \"K\" = 0 AND \"C\" > 1 AND \"C\" <= 2");
+ test("SELECT * FROM \"K\".\"T\" WHERE \"V\" = 0 ALLOW FILTERING");
+ test("SELECT * FROM \"K\".\"T\" WHERE \"S\" = 0 ALLOW FILTERING");
+ test("SELECT * FROM \"K\".\"T\" WHERE \"C\" = 0 ALLOW FILTERING");
+
+ // order by
+ test("SELECT * FROM \"K\".\"T\" WHERE \"K\" = 0 ORDER BY \"C\" DESC");
+ test("SELECT * FROM \"K\".\"T\" WHERE \"K\" = 0 AND \"C\" = 1 ORDER BY \"C\" DESC");
+ }
+
+ @Test
+ public void testLiterals() throws Throwable
+ {
+ // skinny table
+ createTable("CREATE TABLE %s (k text, c text, v text, PRIMARY KEY(k, c))");
+ test("SELECT * FROM %s WHERE k = 'A'");
+ test("SELECT * FROM %s WHERE c = 'A' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE v = 'A' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k = 'A' AND c = 'B'");
+ test("SELECT * FROM %s WHERE k = 'A' AND v = 'B' ALLOW FILTERING");
+
+ // wide table
+ createTable("CREATE TABLE %s (k1 text, k2 text, c1 text, c2 text, v text, PRIMARY KEY((k1, k2), c1, c2))");
+ test("SELECT * FROM %s WHERE k1 = 'A' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k2 = 'A' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE c1 = 'A' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE c2 = 'A' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B'");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND c1 = 'C'");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND c1 > 'C'");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND c1 > 'C' AND c1 <= 'D'");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND c1 = 'C' AND c2 = 'D'",
+ "SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND (c1, c2) = ('C', 'D')");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND c1 = 'C' AND c2 > 'D'");
+ test("SELECT * FROM %s WHERE k1 = 'A' AND k2 = 'B' AND c1 = 'C' AND c2 > 'D' AND c2 <= 'E'");
+ }
+
+ @Test
+ public void testWideTableWithClusteringOrder() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k int, c1 int, c2 int, c3 int, PRIMARY KEY(k, c1, c2, c3)) WITH CLUSTERING ORDER BY (c1 DESC, c2 ASC, c3 DESC)");
+
+ // one column
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1 DESC",
+ "SELECT * FROM %s WHERE k = 0");
+
+ // two columns
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1, c2 DESC",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC",
+ "SELECT * FROM %s WHERE k = 0");
+
+ // three columns
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1, c2 DESC, c3 ASC",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1, c2 DESC, c3 ASC",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC",
+ "SELECT * FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC, c3 ASC");
+ test("SELECT * FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC, c3 DESC",
+ "SELECT * FROM %s WHERE k = 0");
+ }
+
+ @Test
+ public void testCollections() throws Throwable
+ {
+ String udt = createType("CREATE TYPE %s (a text, b int)");
+ createTable("CREATE TABLE %s (" +
+ "k int PRIMARY KEY, " +
+ "l list<text>, " +
+ "s set<text>, " +
+ "m map<text, text>, " +
+ "t tuple<text, int>, " +
+ "u " + udt + ")");
+
+ // column selections
+ test("SELECT l FROM %s");
+ test("SELECT s FROM %s");
+ test("SELECT m FROM %s");
+ test("SELECT t FROM %s");
+ test("SELECT u FROM %s");
+ testInvalid("SELECT l['a'] FROM %s");
+ test("SELECT s['a'] FROM %s");
+ test("SELECT m['a'] FROM %s");
+ test("SELECT u.a FROM %s",
+ "SELECT u FROM %s");
+ test("SELECT m['a'], m['b'], s['c'], s['d'], t, u.a, u.b FROM %s",
+ "SELECT m['a'], m['b'], s['c'], s['d'], t, u FROM %s");
+
+ // filtering
+ testInvalid("SELECT * FROM %s WHERE l = ['a', 'b'] ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE s = {'a', 'b'} ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE m = {'a': 'b', 'c': 'd'} ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE t = ('a', 1) ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u = {a: 'a', b: 1} ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE l['a'] = 'b' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE s['a'] = 'b' ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE m['a'] = 'b' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u.a = 'a' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u.b = 0 ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u.a = 'a' ANd u.b = 0 ALLOW FILTERING");
+ }
+
+ @Test
+ public void testFrozenCollections() throws Throwable
+ {
+ String udt = createType("CREATE TYPE %s (a text, b int)");
+ createTable("CREATE TABLE %s (" +
+ "k int PRIMARY KEY, " +
+ "l frozen<list<text>>, " +
+ "s frozen<set<text>>, " +
+ "m frozen<map<text, text>>, " +
+ "t frozen<tuple<text, int>>, " +
+ "u frozen<" + udt + ">)");
+
+ // column selections
+ test("SELECT l FROM %s");
+ test("SELECT s FROM %s");
+ test("SELECT m FROM %s");
+ test("SELECT t FROM %s");
+ test("SELECT u FROM %s");
+ testInvalid("SELECT l['a'] FROM %s");
+ test("SELECT s['a'] FROM %s",
+ "SELECT s FROM %s");
+ test("SELECT m['a'] FROM %s",
+ "SELECT m FROM %s");
+ test("SELECT u.a FROM %s",
+ "SELECT u FROM %s");
+ test("SELECT m['a'], m['b'], s['c'], s['d'], t, u.a, u.b FROM %s",
+ "SELECT m, s, t, u FROM %s");
+
+ // filtering
+ test("SELECT * FROM %s WHERE l = ['a', 'b'] ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE s = {'a', 'b'} ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE m = {'a': 'b', 'c': 'd'} ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE t = ('a', 1) ALLOW FILTERING");
+ test("SELECT * FROM %s WHERE u = {a: 'a', b: 1} ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE l['a'] = 'a' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE s['a'] = 'a' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE m['a'] = 'a' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u.a = 'a' ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u.b = 0 ALLOW FILTERING");
+ testInvalid("SELECT * FROM %s WHERE u.a = 'a' ANd u.b = 0 ALLOW FILTERING");
+ }
+
+ @Test
+ public void testVirtualTable() throws Throwable
+ {
+ TableMetadata metadata =
+ TableMetadata.builder("vk", "vt")
+ .kind(TableMetadata.Kind.VIRTUAL)
+ .addPartitionKeyColumn("k", Int32Type.instance)
+ .addClusteringColumn("c", Int32Type.instance)
+ .addRegularColumn("v", Int32Type.instance)
+ .addStaticColumn("s", Int32Type.instance)
+ .build();
+ SimpleDataSet data = new SimpleDataSet(metadata);
+ VirtualTable table = new AbstractVirtualTable(metadata)
+ {
+ public DataSet data()
+ {
+ return data;
+ }
+ };
+ VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace("vk", ImmutableList.of(table)));
+
+ // column selection on unrestricted partition range query
+ test("SELECT * FROM vk.vt");
+ test("SELECT k FROM vk.vt",
+ "SELECT * FROM vk.vt");
+ test("SELECT c FROM vk.vt",
+ "SELECT * FROM vk.vt");
+ test("SELECT s FROM vk.vt");
+ test("SELECT v FROM vk.vt");
+ test("SELECT k, c, s, v FROM vk.vt",
+ "SELECT s, v FROM vk.vt");
+
+ // column selection on partition directed query
+ test("SELECT * FROM vk.vt WHERE k = 1");
+ test("SELECT k FROM vk.vt WHERE k = 1",
+ "SELECT * FROM vk.vt WHERE k = 1");
+ test("SELECT c FROM vk.vt WHERE k = 1",
+ "SELECT * FROM vk.vt WHERE k = 1");
+ test("SELECT v FROM vk.vt WHERE k = 1");
+ test("SELECT s FROM vk.vt WHERE k = 1");
+ test("SELECT k, c, s, v FROM vk.vt WHERE k = 1",
+ "SELECT s, v FROM vk.vt WHERE k = 1");
+
+ // clustering filters
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c = 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c < 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c > 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c <= 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c >= 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c > 1 AND c <= 2");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c >= 1 AND c < 2");
+
+ // token restrictions
+ test("SELECT * FROM vk.vt WHERE token(k) > 0");
+ test("SELECT * FROM vk.vt WHERE token(k) < 0");
+ test("SELECT * FROM vk.vt WHERE token(k) >= 0");
+ test("SELECT * FROM vk.vt WHERE token(k) <= 0");
+ test("SELECT * FROM vk.vt WHERE token(k) = 0",
+ "SELECT * FROM vk.vt WHERE token(k) >= 0 AND token(k) <= 0");
+
+ // row filters
+ test("SELECT * FROM vk.vt WHERE c = 1 ALLOW FILTERING");
+ test("SELECT * FROM vk.vt WHERE s = 1 ALLOW FILTERING");
+ test("SELECT * FROM vk.vt WHERE v = 1 ALLOW FILTERING");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND v = 1 ALLOW FILTERING");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c = 1 AND v = 1 ALLOW FILTERING");
+ test("SELECT * FROM vk.vt WHERE token(k) > 0 AND v = 1 ALLOW FILTERING");
+ test("SELECT * FROM vk.vt WHERE token(k) > 0 AND c = 1 AND v = 1 ALLOW FILTERING");
+
+ // grouped partition-directed queries, maybe producing multiple queries
+ test("SELECT * FROM vk.vt WHERE k IN (0)",
+ "SELECT * FROM vk.vt WHERE k = 0");
+ test("SELECT * FROM vk.vt WHERE k IN (0, 1)",
+ "SELECT * FROM vk.vt WHERE k = 0",
+ "SELECT * FROM vk.vt WHERE k = 1");
+ test("SELECT * FROM vk.vt WHERE k IN (0, 1) AND c = 0",
+ "SELECT * FROM vk.vt WHERE k = 0 AND c = 0",
+ "SELECT * FROM vk.vt WHERE k = 1 AND c = 0");
+ test("SELECT * FROM vk.vt WHERE k IN (0, 1) AND c > 0",
+ "SELECT * FROM vk.vt WHERE k = 0 AND c > 0",
+ "SELECT * FROM vk.vt WHERE k = 1 AND c > 0");
+
+ // order by
+ test("SELECT * FROM vk.vt WHERE k = 0 ORDER BY c",
+ "SELECT * FROM vk.vt WHERE k = 0");
+ test("SELECT * FROM vk.vt WHERE k = 0 ORDER BY c ASC",
+ "SELECT * FROM vk.vt WHERE k = 0");
+ test("SELECT * FROM vk.vt WHERE k = 0 ORDER BY c DESC");
+
+ // order by clustering filter
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c = 1 ORDER BY c",
+ "SELECT * FROM vk.vt WHERE k = 0 AND c = 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c = 1 ORDER BY c ASC",
+ "SELECT * FROM vk.vt WHERE k = 0 AND c = 1");
+ test("SELECT * FROM vk.vt WHERE k = 0 AND c = 1 ORDER BY c DESC");
+ }
+
+ private List<String> toCQLString(String query)
+ {
+ String fullQuery = formatQuery(query);
+ ClientState state = ClientState.forInternalCalls();
+ CQLStatement statement = QueryProcessor.getStatement(fullQuery, state);
+
+ assertTrue(statement instanceof SelectStatement);
+ SelectStatement select = (SelectStatement) statement;
+
+ QueryOptions options = QueryOptions.forInternalCalls(Collections.emptyList());
+ ReadQuery readQuery = select.getQuery(options, FBUtilities.nowInSeconds());
+
+ if (readQuery instanceof SinglePartitionReadCommand.Group)
+ {
+ SinglePartitionReadCommand.Group group = (SinglePartitionReadCommand.Group) readQuery;
+ return group.queries.stream().map(AbstractReadQuery::toCQLString).collect(Collectors.toList());
+ }
+ else if (readQuery instanceof VirtualTableSinglePartitionReadQuery.Group)
+ {
+ VirtualTableSinglePartitionReadQuery.Group group = (VirtualTableSinglePartitionReadQuery.Group) readQuery;
+ return group.queries.stream().map(AbstractReadQuery::toCQLString).collect(Collectors.toList());
+ }
+ else
+ {
+ assertTrue(readQuery instanceof AbstractReadQuery);
+ return Collections.singletonList(((AbstractReadQuery) readQuery).toCQLString());
+ }
+ }
+
+ private void test(String query) throws Throwable
+ {
+ test(query, query);
+ }
+
+ private void test(String query, String... expected) throws Throwable
+ {
+ List<String> actual = toCQLString(query);
+ List<String> fullExpected = Stream.of(expected)
+ .map(this::formatQuery)
+ .map(s -> s.endsWith(" ALLOW FILTERING") ? s : s + " ALLOW FILTERING")
+ .collect(Collectors.toList());
+ assertEquals(fullExpected, actual);
+
+ // execute both the expected output commands to verify that they are valid CQL
+ for (String q : expected)
+ execute(q);
+ }
+
+ private void testInvalid(String query) throws Throwable
+ {
+ assertInvalidThrow(RequestValidationException.class, query);
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java b/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
index 4468f2c..8620f5c 100644
--- a/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
+++ b/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
@@ -38,6 +38,7 @@ import org.apache.cassandra.db.marshal.LongType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.schema.KeyspaceParams;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.junit.*;
@@ -651,6 +652,15 @@ public class OperationTest extends SchemaLoader
{
throw new UnsupportedOperationException();
}
+
+ @Override
+ protected String toString(boolean cql)
+ {
+ return String.format("%s %s %s",
+ cql ? column.name.toCQLString() : column.name.toString(),
+ operator,
+ ByteBufferUtil.bytesToHex(value));
+ }
}
private static Unfiltered buildRow(Cell<?>... cells)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org