You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ty...@apache.org on 2014/11/19 18:11:54 UTC
cassandra git commit: Fix IRE with ORDER BY,
treating all selections as fns
Repository: cassandra
Updated Branches:
refs/heads/cassandra-2.0 37d33b208 -> 1945384fd
Fix IRE with ORDER BY, treating all selections as fns
Patch by Tyler Hobbs; reviewed by Benjamin Lerer for CASSANDRA-8286
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/1945384f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1945384f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1945384f
Branch: refs/heads/cassandra-2.0
Commit: 1945384fdf1d0bac18d6f75e5f864f1aca5b49db
Parents: 37d33b2
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Wed Nov 19 11:06:03 2014 -0600
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Wed Nov 19 11:06:03 2014 -0600
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/cql3/ColumnIdentifier.java | 5 +
.../cql3/statements/ModificationStatement.java | 2 +-
.../cassandra/cql3/statements/RawSelector.java | 5 +
.../cql3/statements/SelectStatement.java | 9 +-
.../cassandra/cql3/statements/Selectable.java | 15 +
.../cassandra/cql3/statements/Selection.java | 72 +--
.../cassandra/cql3/SelectionOrderingTest.java | 452 +++++++++++++++++++
8 files changed, 520 insertions(+), 41 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 809a102..fff6d3a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.0.12:
+ * Fix InvalidRequestException with ORDER BY (CASSANDRA-8286)
* Disable SSLv3 for POODLE (CASSANDRA-8265)
* Fix millisecond timestamps in Tracing (CASSANDRA-8297)
* Include keyspace name in error message when there are insufficient
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
index f284436..2f3e481 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
@@ -109,6 +109,11 @@ public class ColumnIdentifier implements Selectable
return new ColumnIdentifier(cfm.comparator.fromString(rawText), text);
}
+ public boolean processesSelection()
+ {
+ return false;
+ }
+
@Override
public final int hashCode()
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index c098c92..61f65c1 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -684,7 +684,7 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
}
for (ColumnIdentifier id : columnsWithConditions)
names.add(cfDef.get(id));
- selection = Selection.forColumns(names);
+ selection = Selection.forColumns(new ArrayList<>(names));
}
long now = System.currentTimeMillis();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/src/java/org/apache/cassandra/cql3/statements/RawSelector.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/RawSelector.java b/src/java/org/apache/cassandra/cql3/statements/RawSelector.java
index 0194239..c2d4e20 100644
--- a/src/java/org/apache/cassandra/cql3/statements/RawSelector.java
+++ b/src/java/org/apache/cassandra/cql3/statements/RawSelector.java
@@ -30,4 +30,9 @@ public class RawSelector
this.selectable = selectable;
this.alias = alias;
}
+
+ public boolean processesSelection()
+ {
+ return selectable.processesSelection();
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/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 77d94e3..f1d1aab 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -1957,10 +1957,11 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
else
{
boolean hasColumn = false;
- for (int i = 0; i < selectClause.size(); i++)
+ List<Name> selectedColumns = stmt.selection.getColumns();
+ for (int i = 0; i < selectedColumns.size(); i++)
{
- RawSelector selector = selectClause.get(i);
- if (name.name.equals(selector.selectable))
+ Name selected = selectedColumns.get(i);
+ if (name.equals(selected))
{
stmt.orderingIndexes.put(name, i);
hasColumn = true;
@@ -1969,7 +1970,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
}
if (!hasColumn)
- throw new InvalidRequestException("ORDER BY could not be used on columns missing in select clause.");
+ throw new InvalidRequestException(String.format("ORDER BY can only be performed on columns in the select clause (got %s)", name.name));
}
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/src/java/org/apache/cassandra/cql3/statements/Selectable.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/Selectable.java b/src/java/org/apache/cassandra/cql3/statements/Selectable.java
index 633bf71..b7e3614 100644
--- a/src/java/org/apache/cassandra/cql3/statements/Selectable.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Selectable.java
@@ -30,6 +30,11 @@ public interface Selectable
public static interface Raw
{
public Selectable prepare(CFMetaData cfm);
+
+ /**
+ * Returns true if any processing is performed on the selected column.
+ **/
+ public boolean processesSelection();
}
public static class WritetimeOrTTL implements Selectable
@@ -64,6 +69,11 @@ public interface Selectable
{
return new WritetimeOrTTL(id.prepare(cfm), isWritetime);
}
+
+ public boolean processesSelection()
+ {
+ return true;
+ }
}
}
@@ -109,6 +119,11 @@ public interface Selectable
preparedArgs.add(arg.prepare(cfm));
return new WithFunction(functionName, preparedArgs);
}
+
+ public boolean processesSelection()
+ {
+ return true;
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/src/java/org/apache/cassandra/cql3/statements/Selection.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/Selection.java b/src/java/org/apache/cassandra/cql3/statements/Selection.java
index 0135a76..407f7d9 100644
--- a/src/java/org/apache/cassandra/cql3/statements/Selection.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Selection.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.cql3.statements;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import org.apache.cassandra.cql3.*;
@@ -37,12 +36,12 @@ import org.apache.cassandra.utils.ByteBufferUtil;
public abstract class Selection
{
- private final Collection<CFDefinition.Name> columns;
+ private final List<CFDefinition.Name> columns;
private final List<ColumnSpecification> metadata;
private final boolean collectTimestamps;
private final boolean collectTTLs;
- protected Selection(Collection<CFDefinition.Name> columns, List<ColumnSpecification> metadata, boolean collectTimestamps, boolean collectTTLs)
+ protected Selection(List<CFDefinition.Name> columns, List<ColumnSpecification> metadata, boolean collectTimestamps, boolean collectTTLs)
{
this.columns = columns;
this.metadata = metadata;
@@ -69,16 +68,16 @@ public abstract class Selection
return new SimpleSelection(all, true);
}
- public static Selection forColumns(Collection<CFDefinition.Name> columns)
+ public static Selection forColumns(List<CFDefinition.Name> columns)
{
return new SimpleSelection(columns, false);
}
- private static boolean isUsingFunction(List<RawSelector> rawSelectors)
+ private static boolean selectionsNeedProcessing(List<RawSelector> rawSelectors)
{
for (RawSelector rawSelector : rawSelectors)
{
- if (!(rawSelector.selectable instanceof ColumnIdentifier))
+ if (rawSelector.processesSelection())
return true;
}
return false;
@@ -174,9 +173,9 @@ public abstract class Selection
public static Selection fromSelectors(CFDefinition cfDef, List<RawSelector> rawSelectors) throws InvalidRequestException
{
- boolean usesFunction = isUsingFunction(rawSelectors);
+ boolean needsProcessing = selectionsNeedProcessing(rawSelectors);
- if (usesFunction)
+ if (needsProcessing)
{
List<CFDefinition.Name> names = new ArrayList<CFDefinition.Name>();
List<ColumnSpecification> metadata = new ArrayList<ColumnSpecification>(rawSelectors.size());
@@ -193,7 +192,7 @@ public abstract class Selection
collectTTLs |= !((WritetimeOrTTLSelector)selector).isWritetime;
}
}
- return new SelectionWithFunctions(names, metadata, selectors, collectTimestamps, collectTTLs);
+ return new SelectionWithProcessing(names, metadata, selectors, collectTimestamps, collectTTLs);
}
else
{
@@ -201,10 +200,11 @@ public abstract class Selection
List<ColumnSpecification> metadata = new ArrayList<ColumnSpecification>(rawSelectors.size());
for (RawSelector rawSelector : rawSelectors)
{
- assert rawSelector.selectable instanceof ColumnIdentifier;
- CFDefinition.Name name = cfDef.get((ColumnIdentifier)rawSelector.selectable);
+ assert rawSelector.selectable instanceof ColumnIdentifier.Raw;
+ ColumnIdentifier id = ((ColumnIdentifier.Raw)rawSelector.selectable).prepare(cfDef.cfm);
+ CFDefinition.Name name = cfDef.get(id);
if (name == null)
- throw new InvalidRequestException(String.format("Undefined name %s in selection clause", rawSelector.selectable));
+ throw new InvalidRequestException(String.format("Undefined name %s in selection clause", id));
names.add(name);
metadata.add(rawSelector.alias == null ? name : makeAliasSpec(cfDef, name.type, rawSelector.alias));
}
@@ -231,7 +231,7 @@ public abstract class Selection
/**
* @return the list of CQL3 columns value this SelectionClause needs.
*/
- public Collection<CFDefinition.Name> getColumns()
+ public List<CFDefinition.Name> getColumns()
{
return columns;
}
@@ -322,12 +322,12 @@ public abstract class Selection
{
private final boolean isWildcard;
- public SimpleSelection(Collection<CFDefinition.Name> columns, boolean isWildcard)
+ public SimpleSelection(List<CFDefinition.Name> columns, boolean isWildcard)
{
this(columns, new ArrayList<ColumnSpecification>(columns), isWildcard);
}
- public SimpleSelection(Collection<CFDefinition.Name> columns, List<ColumnSpecification> metadata, boolean isWildcard)
+ public SimpleSelection(List<CFDefinition.Name> columns, List<ColumnSpecification> metadata, boolean isWildcard)
{
/*
* In theory, even a simple selection could have multiple time the same column, so we
@@ -350,6 +350,27 @@ public abstract class Selection
}
}
+ private static class SelectionWithProcessing extends Selection
+ {
+ private final List<Selector> selectors;
+
+ public SelectionWithProcessing(List<CFDefinition.Name> columns, List<ColumnSpecification> metadata, List<Selector> selectors, boolean collectTimestamps, boolean collectTTLs)
+ {
+ super(columns, metadata, collectTimestamps, collectTTLs);
+ this.selectors = selectors;
+ }
+
+ protected List<ByteBuffer> handleRow(ResultSetBuilder rs) throws InvalidRequestException
+ {
+ List<ByteBuffer> result = new ArrayList<ByteBuffer>();
+ for (Selector selector : selectors)
+ {
+ result.add(selector.compute(rs));
+ }
+ return result;
+ }
+ }
+
private interface Selector extends AssignementTestable
{
public ByteBuffer compute(ResultSetBuilder rs) throws InvalidRequestException;
@@ -461,25 +482,4 @@ public abstract class Selection
return columnName;
}
}
-
- private static class SelectionWithFunctions extends Selection
- {
- private final List<Selector> selectors;
-
- public SelectionWithFunctions(Collection<CFDefinition.Name> columns, List<ColumnSpecification> metadata, List<Selector> selectors, boolean collectTimestamps, boolean collectTTLs)
- {
- super(columns, metadata, collectTimestamps, collectTTLs);
- this.selectors = selectors;
- }
-
- protected List<ByteBuffer> handleRow(ResultSetBuilder rs) throws InvalidRequestException
- {
- List<ByteBuffer> result = new ArrayList<ByteBuffer>();
- for (Selector selector : selectors)
- {
- result.add(selector.compute(rs));
- }
- return result;
- }
- }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1945384f/test/unit/org/apache/cassandra/cql3/SelectionOrderingTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/SelectionOrderingTest.java b/test/unit/org/apache/cassandra/cql3/SelectionOrderingTest.java
new file mode 100644
index 0000000..305d91e
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/SelectionOrderingTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.service.ClientState;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+
+import static org.apache.cassandra.cql3.QueryProcessor.process;
+import static org.apache.cassandra.cql3.QueryProcessor.processInternal;
+import static org.junit.Assert.assertEquals;
+
+public class SelectionOrderingTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(SelectWithTokenFunctionTest.class);
+ static ClientState clientState;
+ static String keyspace = "select_with_ordering_test";
+
+ @BeforeClass
+ public static void setUpClass() throws Throwable
+ {
+ SchemaLoader.loadSchema();
+ executeSchemaChange("CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}");
+ executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.single_clustering (a int, b int, c int, PRIMARY KEY (a, b))");
+ executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.single_clustering_desc (a int, b int, c int, PRIMARY KEY (a, b)) WITH CLUSTERING ORDER BY (b DESC)");
+ executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.multiple_clustering (a int, b int, c int, d int, PRIMARY KEY (a, b, c))");
+ clientState = ClientState.forInternalCalls();
+ }
+
+ @AfterClass
+ public static void stopGossiper()
+ {
+ Gossiper.instance.stop();
+ }
+
+ private static void executeSchemaChange(String query) throws Throwable
+ {
+ try
+ {
+ process(String.format(query, keyspace), ConsistencyLevel.ONE);
+ }
+ catch (RuntimeException exc)
+ {
+ throw exc.getCause();
+ }
+ }
+
+ private static UntypedResultSet execute(String query) throws Throwable
+ {
+ try
+ {
+ return processInternal(String.format(query, keyspace));
+ }
+ catch (RuntimeException exc)
+ {
+ if (exc.getCause() != null)
+ throw exc.getCause();
+ throw exc;
+ }
+ }
+
+ @Test
+ public void testNormalSelectionOrderSingleClustering() throws Throwable
+ {
+ for (String descOption : new String[]{"", "_desc"})
+ {
+ execute("INSERT INTO %s.single_clustering" + descOption + " (a, b, c) VALUES (0, 0, 0)");
+ execute("INSERT INTO %s.single_clustering" + descOption + " (a, b, c) VALUES (0, 1, 1)");
+ execute("INSERT INTO %s.single_clustering" + descOption + " (a, b, c) VALUES (0, 2, 2)");
+
+ try
+ {
+ UntypedResultSet results = execute("SELECT * FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b ASC");
+ assertEquals(3, results.size());
+ Iterator<UntypedResultSet.Row> rows = results.iterator();
+ for (int i = 0; i < 3; i++)
+ assertEquals(i, rows.next().getInt("b"));
+
+ results = execute("SELECT * FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b DESC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 2; i >= 0; i--)
+ assertEquals(i, rows.next().getInt("b"));
+
+ // order by the only column in the selection
+ results = execute("SELECT b FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b ASC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 0; i < 3; i++)
+ assertEquals(i, rows.next().getInt("b"));
+
+ results = execute("SELECT b FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b DESC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 2; i >= 0; i--)
+ assertEquals(i, rows.next().getInt("b"));
+
+ // order by a column not in the selection
+ results = execute("SELECT c FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b ASC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 0; i < 3; i++)
+ assertEquals(i, rows.next().getInt("c"));
+
+ results = execute("SELECT c FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b DESC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 2; i >= 0; i--)
+ assertEquals(i, rows.next().getInt("c"));
+ }
+ finally
+ {
+ execute("DELETE FROM %s.single_clustering" + descOption + " WHERE a = 0");
+ }
+ }
+ }
+
+ @Test
+ public void testFunctionSelectionOrderSingleClustering() throws Throwable
+ {
+ for (String descOption : new String[]{"", "_desc"})
+ {
+ execute("INSERT INTO %s.single_clustering" + descOption + " (a, b, c) VALUES (0, 0, 0)");
+ execute("INSERT INTO %s.single_clustering" + descOption + " (a, b, c) VALUES (0, 1, 1)");
+ execute("INSERT INTO %s.single_clustering" + descOption + " (a, b, c) VALUES (0, 2, 2)");
+
+ try
+ {
+ // order by a column in the selection (wrapped in a function)
+ UntypedResultSet results = execute("SELECT blobAsInt(intAsBlob(b)) as col FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b ASC");
+ assertEquals(3, results.size());
+ Iterator<UntypedResultSet.Row> rows = results.iterator();
+ for (int i = 0; i < 3; i++)
+ assertEquals(i, rows.next().getInt("col"));
+
+ results = execute("SELECT blobAsInt(intAsBlob(b)) as col FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b DESC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 2; i >= 0; i--)
+ assertEquals(i, rows.next().getInt("col"));
+
+ // order by a column in the selection, plus the column wrapped in a function
+ results = execute("SELECT b, blobAsInt(intAsBlob(b)) as col FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b ASC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 0; i < 3; i++)
+ {
+ UntypedResultSet.Row row = rows.next();
+ assertEquals(i, row.getInt("b"));
+ assertEquals(i, row.getInt("col"));
+ }
+
+ results = execute("SELECT b, blobAsInt(intAsBlob(b)) as col FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b DESC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 2; i >= 0; i--)
+ {
+ UntypedResultSet.Row row = rows.next();
+ assertEquals(i, row.getInt("b"));
+ assertEquals(i, row.getInt("col"));
+ }
+
+ // order by a column not in the selection (wrapped in a function)
+ results = execute("SELECT blobAsInt(intAsBlob(c)) as col FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b ASC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 0; i < 3; i++)
+ assertEquals(i, rows.next().getInt("col"));
+
+ results = execute("SELECT blobAsInt(intAsBlob(c)) as col FROM %s.single_clustering" + descOption + " WHERE a=0 ORDER BY b DESC");
+ assertEquals(3, results.size());
+ rows = results.iterator();
+ for (int i = 2; i >= 0; i--)
+ assertEquals(i, rows.next().getInt("col"));
+ }
+ finally
+ {
+ execute("DELETE FROM %s.single_clustering" + descOption + " WHERE a = 0");
+ }
+ }
+ }
+
+ @Test
+ public void testNormalSelectionOrderMultipleClustering() throws Throwable
+ {
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 0, 0, 0)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 0, 1, 1)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 0, 2, 2)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 1, 0, 3)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 1, 1, 4)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 1, 2, 5)");
+ try
+ {
+ UntypedResultSet results = execute("SELECT * FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC");
+ assertEquals(6, results.size());
+ Iterator<UntypedResultSet.Row> rows = results.iterator();
+ assertEquals(0, rows.next().getInt("d"));
+ assertEquals(1, rows.next().getInt("d"));
+ assertEquals(2, rows.next().getInt("d"));
+ assertEquals(3, rows.next().getInt("d"));
+ assertEquals(4, rows.next().getInt("d"));
+ assertEquals(5, rows.next().getInt("d"));
+
+ results = execute("SELECT * FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(5, rows.next().getInt("d"));
+ assertEquals(4, rows.next().getInt("d"));
+ assertEquals(3, rows.next().getInt("d"));
+ assertEquals(2, rows.next().getInt("d"));
+ assertEquals(1, rows.next().getInt("d"));
+ assertEquals(0, rows.next().getInt("d"));
+
+ results = execute("SELECT * FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC, c DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(5, rows.next().getInt("d"));
+ assertEquals(4, rows.next().getInt("d"));
+ assertEquals(3, rows.next().getInt("d"));
+ assertEquals(2, rows.next().getInt("d"));
+ assertEquals(1, rows.next().getInt("d"));
+ assertEquals(0, rows.next().getInt("d"));
+
+ // select and order by b
+ results = execute("SELECT b FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("b"));
+ assertEquals(0, rows.next().getInt("b"));
+ assertEquals(0, rows.next().getInt("b"));
+ assertEquals(1, rows.next().getInt("b"));
+ assertEquals(1, rows.next().getInt("b"));
+ assertEquals(1, rows.next().getInt("b"));
+
+ results = execute("SELECT b FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(1, rows.next().getInt("b"));
+ assertEquals(1, rows.next().getInt("b"));
+ assertEquals(1, rows.next().getInt("b"));
+ assertEquals(0, rows.next().getInt("b"));
+ assertEquals(0, rows.next().getInt("b"));
+ assertEquals(0, rows.next().getInt("b"));
+
+ // select c, order by b
+ results = execute("SELECT c FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC");
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(2, rows.next().getInt("c"));
+ assertEquals(0, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(2, rows.next().getInt("c"));
+
+ results = execute("SELECT c FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(2, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(0, rows.next().getInt("c"));
+ assertEquals(2, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(0, rows.next().getInt("c"));
+
+ // select c, order by b, c
+ results = execute("SELECT c FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC, c ASC");
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(2, rows.next().getInt("c"));
+ assertEquals(0, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(2, rows.next().getInt("c"));
+
+ results = execute("SELECT c FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC, c DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(2, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(0, rows.next().getInt("c"));
+ assertEquals(2, rows.next().getInt("c"));
+ assertEquals(1, rows.next().getInt("c"));
+ assertEquals(0, rows.next().getInt("c"));
+
+ // select d, order by b, c
+ results = execute("SELECT d FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC, c ASC");
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("d"));
+ assertEquals(1, rows.next().getInt("d"));
+ assertEquals(2, rows.next().getInt("d"));
+ assertEquals(3, rows.next().getInt("d"));
+ assertEquals(4, rows.next().getInt("d"));
+ assertEquals(5, rows.next().getInt("d"));
+
+ results = execute("SELECT d FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC, c DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(5, rows.next().getInt("d"));
+ assertEquals(4, rows.next().getInt("d"));
+ assertEquals(3, rows.next().getInt("d"));
+ assertEquals(2, rows.next().getInt("d"));
+ assertEquals(1, rows.next().getInt("d"));
+ assertEquals(0, rows.next().getInt("d"));
+ }
+ finally
+ {
+ execute("DELETE FROM %s.multiple_clustering WHERE a = 0");
+ }
+ }
+
+ @Test
+ public void testFunctionSelectionOrderMultipleClustering() throws Throwable
+ {
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 0, 0, 0)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 0, 1, 1)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 0, 2, 2)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 1, 0, 3)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 1, 1, 4)");
+ execute("INSERT INTO %s.multiple_clustering (a, b, c, d) VALUES (0, 1, 2, 5)");
+ try
+ {
+ // select function of b, order by b
+ UntypedResultSet results = execute("SELECT blobAsInt(intAsBlob(b)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC");
+ assertEquals(6, results.size());
+ Iterator<UntypedResultSet.Row> rows = results.iterator();
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+
+ results = execute("SELECT blobAsInt(intAsBlob(b)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+
+ // select b and function of b, order by b
+ results = execute("SELECT b, blobAsInt(intAsBlob(b)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+
+ results = execute("SELECT b, blobAsInt(intAsBlob(b)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+
+ // select c, order by b
+ results = execute("SELECT blobAsInt(intAsBlob(c)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC");
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+
+ results = execute("SELECT blobAsInt(intAsBlob(c)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+
+ // select c, order by b, c
+ results = execute("SELECT blobAsInt(intAsBlob(c)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC, c ASC");
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+
+ results = execute("SELECT blobAsInt(intAsBlob(c)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC, c DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+
+ // select d, order by b, c
+ results = execute("SELECT blobAsInt(intAsBlob(d)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b ASC, c ASC");
+ rows = results.iterator();
+ assertEquals(0, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(3, rows.next().getInt("col"));
+ assertEquals(4, rows.next().getInt("col"));
+ assertEquals(5, rows.next().getInt("col"));
+
+ results = execute("SELECT blobAsInt(intAsBlob(d)) as col FROM %s.multiple_clustering WHERE a=0 ORDER BY b DESC, c DESC");
+ assertEquals(6, results.size());
+ rows = results.iterator();
+ assertEquals(5, rows.next().getInt("col"));
+ assertEquals(4, rows.next().getInt("col"));
+ assertEquals(3, rows.next().getInt("col"));
+ assertEquals(2, rows.next().getInt("col"));
+ assertEquals(1, rows.next().getInt("col"));
+ assertEquals(0, rows.next().getInt("col"));
+ }
+ finally
+ {
+ execute("DELETE FROM %s.multiple_clustering WHERE a = 0");
+ }
+ }
+}