You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by xe...@apache.org on 2012/07/07 01:19:11 UTC
git commit: (cql3) Support ORDER BY when IN condition is given in
WHERE clause patch by Pavel Yaskevich;
reviewed by Jonathan Ellis for CASSANDRA-4327
Updated Branches:
refs/heads/cassandra-1.1 aa2c28ead -> 4fd6fe3fa
(cql3) Support ORDER BY when IN condition is given in WHERE clause
patch by Pavel Yaskevich; reviewed by Jonathan Ellis for CASSANDRA-4327
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4fd6fe3f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4fd6fe3f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4fd6fe3f
Branch: refs/heads/cassandra-1.1
Commit: 4fd6fe3fa6a1371f5f9c16a7199fbf81320d98c7
Parents: aa2c28e
Author: Pavel Yaskevich <xe...@apache.org>
Authored: Thu Jul 5 15:14:57 2012 +0300
Committer: Pavel Yaskevich <xe...@apache.org>
Committed: Sat Jul 7 02:15:19 2012 +0300
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/cql3/statements/SelectStatement.java | 124 ++++++++++++--
2 files changed, 107 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fd6fe3f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 1857671..15a1a30 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -7,6 +7,7 @@
from row cache during compaction (CASSANDRA-4364)
* (stress) support for CQL prepared statements (CASSANDRA-3633)
* Correctly catch exception when Snappy cannot be loaded (CASSANDRA-4400)
+ * (cql3) Support ORDER BY when IN condition is given in WHERE clause (CASSANDRA-4327)
Merged from 1.0:
* allow dropping columns shadowed by not-yet-expired supercolumn or row
tombstones in PrecompactedRow (CASSANDRA-4396)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fd6fe3f/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 2f121e3..0c34eb0 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -30,17 +30,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.db.IColumn;
-import org.apache.cassandra.db.CounterColumn;
-import org.apache.cassandra.db.ColumnFamily;
-import org.apache.cassandra.db.ExpiringColumn;
-import org.apache.cassandra.db.RangeSliceCommand;
-import org.apache.cassandra.db.ReadCommand;
-import org.apache.cassandra.db.Row;
-import org.apache.cassandra.db.RowPosition;
-import org.apache.cassandra.db.SliceByNamesReadCommand;
-import org.apache.cassandra.db.SliceFromReadCommand;
-import org.apache.cassandra.db.Table;
+import org.apache.cassandra.db.*;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -814,6 +804,8 @@ public class SelectStatement implements CQLStatement
}
}
+ orderResults(cqlRows);
+
// Internal calls always return columns in the comparator order, even when reverse was set
if (isReversed)
Collections.reverse(cqlRows);
@@ -825,6 +817,48 @@ public class SelectStatement implements CQLStatement
}
/**
+ * Orders results when multiple keys are selected (using IN)
+ */
+ private void orderResults(List<CqlRow> cqlRows)
+ {
+ // There is nothing to do if
+ // a. there are no results,
+ // b. no ordering information where given,
+ // c. key restriction wasn't given or it's not an IN expression
+ if (cqlRows.isEmpty() || parameters.orderings.isEmpty() || keyRestriction == null || keyRestriction.eqValues.size() < 2)
+ return;
+
+ // optimization when only *one* order condition was given
+ // because there is no point of using composite comparator if there is only one order condition
+ if (parameters.orderings.size() == 1)
+ {
+ CFDefinition.Name ordering = cfDef.get(parameters.orderings.keySet().iterator().next());
+ Collections.sort(cqlRows, new SingleColumnComparator(ordering.position + 1, ordering.type));
+ return;
+ }
+
+ // figures out where ordering would start in results (startPosition),
+ // builds a composite type for multi-column comparison from the comparators of the ordering components
+ // and passes collected position information and built composite comparator to CompositeComparator to do
+ // an actual comparison of the CQL rows.
+ int startPosition = -1;
+ List<AbstractType<?>> types = new ArrayList<AbstractType<?>>();
+
+ for (ColumnIdentifier identifier : parameters.orderings.keySet())
+ {
+ CFDefinition.Name orderingColumn = cfDef.get(identifier);
+
+ if (startPosition == -1)
+ startPosition = orderingColumn.position + 1;
+
+ types.add(orderingColumn.type);
+ }
+
+ Collections.sort(cqlRows, new CompositeComparator(startPosition, types));
+ }
+
+
+ /**
* For sparse composite, returns wheter two columns belong to the same
* cqlRow base on the full list of component in the name.
* Two columns do belong together if they differ only by the last
@@ -1036,6 +1070,9 @@ public class SelectStatement implements CQLStatement
if (!stmt.parameters.orderings.isEmpty())
{
+ if (whereClause.isEmpty())
+ throw new InvalidRequestException("ORDER BY is only supported in combination with WHERE clause.");
+
Boolean[] reversedMap = new Boolean[cfDef.columns.size()];
int i = 0;
for (Map.Entry<ColumnIdentifier, Boolean> entry : stmt.parameters.orderings.entrySet())
@@ -1074,13 +1111,6 @@ public class SelectStatement implements CQLStatement
}
assert isReversed != null;
stmt.isReversed = isReversed;
-
- // Only allow ordering if the row key restriction is an equality,
- // since otherwise the order will be primarily on the row key.
- // TODO: we could allow ordering for IN queries, as we can do the
- // sorting post-query easily, but we will have to add it
- if (stmt.keyRestriction == null || !stmt.keyRestriction.isEquality() || stmt.keyRestriction.eqValues.size() != 1)
- throw new InvalidRequestException("Ordering is only supported if the first part of the PRIMARY KEY is restricted by an Equal");
}
// If this is a query on tokens, it's necessary a range query (there can be more than one key per token), so reject IN queries (as we don't know how to do them)
@@ -1284,4 +1314,62 @@ public class SelectStatement implements CQLStatement
this.isCount = isCount;
}
}
+
+ /**
+ * Used in orderResults(...) method when single 'ORDER BY' condition where given
+ */
+ private static class SingleColumnComparator implements Comparator<CqlRow>
+ {
+ private final int index;
+ private final AbstractType<?> comparator;
+
+ public SingleColumnComparator(int columnIndex, AbstractType<?> orderer)
+ {
+ index = columnIndex;
+ comparator = orderer;
+ }
+
+ public int compare(CqlRow a, CqlRow b)
+ {
+ Column columnA = a.getColumns().get(index);
+ Column columnB = b.getColumns().get(index);
+
+ return comparator.compare(columnA.bufferForValue(), columnB.bufferForValue());
+ }
+ }
+
+ /**
+ * Used in orderResults(...) method when multiple 'ORDER BY' conditions where given
+ */
+ private static class CompositeComparator implements Comparator<CqlRow>
+ {
+ private final int startColumnIndex;
+ private final List<AbstractType<?>> orderings;
+
+ private CompositeComparator(int startIndex, List<AbstractType<?>> orderComparators)
+ {
+ startColumnIndex = startIndex;
+ orderings = orderComparators;
+ }
+
+ public int compare(CqlRow a, CqlRow b)
+ {
+ int currentIndex = startColumnIndex;
+
+ for (AbstractType<?> comparator : orderings)
+ {
+ ByteBuffer aValue = a.getColumns().get(currentIndex).bufferForValue();
+ ByteBuffer bValue = b.getColumns().get(currentIndex).bufferForValue();
+
+ int comparison = comparator.compare(aValue, bValue);
+
+ if (comparison != 0)
+ return comparison;
+
+ currentIndex++;
+ }
+
+ return 0;
+ }
+ }
}